ps/Modules/Alkami.PowerShell.Choco/Public/Set-ChocoPackageSourceFeeds.ps1

178 lines
7.3 KiB
PowerShell
Raw Normal View History

2023-05-30 22:51:22 -07:00
function Set-ChocoPackageSourceFeeds {
<#
.SYNOPSIS
Assigns package sources to the $package.Feed of the packages passed in.
THIS IS A SIDE EFFECT because collections in PowerShell are byRef, not byValue
.PARAMETER Packages
Array of Package Objects to assign Feed members to
.PARAMETER Hostname
Hostname of computer ON WHICH to get sources and package list for those sources. Default is localhost.
.PARAMETER MissingPackageLogLevel
Whether to Write-Error or Write-Warning when there are missing packages. Default is ERROR.
#>
[CmdletBinding()]
Param(
[Parameter(Mandatory = $true)]
[object[]]$Packages,
[Parameter(Mandatory = $false)]
[string]$Hostname = "localhost",
[Parameter(Mandatory = $false)]
[ValidateSet("ERROR", "WARNING")]
[string]$MissingPackageLogLevel = "ERROR"
)
$logLead = (Get-logLeadName)
# Early out if all of the packages already have feeds.
if ($Packages.Feed -notcontains $null) {
Write-Warning "$logLead : All packages have feeds already, no work to do, returning early."
return
}
$PASSTHRU_CHOCOLATEY_PUBLIC_URL = Get-ChocoPublicPassThruFeedUrl
$REAL_CHOCOLATEY_PUBLIC_URL = "https://chocolatey.org/api/v2*"
Write-Host "$logLead : Now determining source feeds for packages."
# Grab feeds from the server, and filter out the chocolatey/nameless feeds.
$feeds = [array](Get-ChocolateySources -hostname $Hostname -includeDisabledSources)
# Get the public choco feed passthru.
$passthroughPublicFeed = $feeds | Where-Object { $_.Source -eq $PASSTHRU_CHOCOLATEY_PUBLIC_URL }
# Get the other feeds where Name, Source are not empty, are not Disabled and are not the Passthru feed.
$filteredFeeds = $feeds | Where-Object {
(![string]::IsNullOrWhiteSpace($_.Name) -and
![string]::IsNullOrWhiteSpace($_.Source)) -and
!$_.Disabled -and
$_.Source -ne $PASSTHRU_CHOCOLATEY_PUBLIC_URL -and
$_.Source -notlike $REAL_CHOCOLATEY_PUBLIC_URL
}
# Get feeds where (Name OR Source) is empty AND NOT Disabled. These are invalid feeds.
$invalidFeeds = $feeds | Where-Object {
([string]::IsNullOrWhiteSpace($_.Name) -or
[string]::IsNullOrWhiteSpace($_.Source)) -and
!$_.Disabled
}
# Get any feeds that are Disabled.
$disabledFeeds = $feeds | Where-Object { $_.Disabled }
if (!(Test-IsCollectionNullOrEmpty $invalidFeeds)) {
Write-Warning "$logLead : the following feeds are misconfigured`n$($invalidFeeds.Name)"
}
if(Test-IsCollectionNullOrEmpty $filteredFeeds) {
Write-Error "$logLead : No filtered feeds found!"
throw "$logLead : No filtered feeds found!"
return
}
# These feeds were flagged as disabled so we'll let everyone know we're skipping them.
foreach ($disabledFeed in $disabledFeeds) {
Write-Host "$logLead : Feed '$($disabledFeed.Name)' is flagged as Disabled, ignoring."
}
# If there is not public choco feed configured, add one that is the Proget passthru.
if ($null -eq $passthroughPublicFeed) {
$properties = @{
Name = 'Chocolatey Passthrough'
Source = $PASSTHRU_CHOCOLATEY_PUBLIC_URL
Priority = 0
Disabled = $false
IsDefault = $true
IsSDK = $false
};
$passthroughPublicFeed = New-Object -TypeName PSObject -Prop $properties;
Write-Host "$logLead : Setting default choco passthru '$($passthroughPublicFeed.Name)' '$($passthroughPublicFeed.Source)'"
}
# Get all of the packages from each feed in parallel.
Write-Host "$logLead : Get all packages from each feed in parallel."
$feedPackageResults = Invoke-Parallel -objects $filteredFeeds -returnObjects -arguments $Hostname -script {
Param(
$sbFeed,
$sbHostname
)
# Collect the names of packages in this feed.
$feedName = $sbFeed.Name
$feedSource = $sbFeed.Source
$loglead = "[Set-ChocoPackageSourceFeeds]"
Write-Host "$loglead : Pulling packages names from source '$feedName' - '$feedSource'"
$scriptPackages = @()
$scriptPackages = Get-ChocoState -s $feedSource -pre
if (($scriptPackages.Count -eq 0) -or (($scriptPackages.Count -eq 1) -and ([string]::IsNullOrWhiteSpace($scriptPackages[0])))) {
$scriptPackages = $null
}
$result = @{
FeedName = $sbFeed.Name
Packages = $scriptPackages
}
return $result
}
if(!(Test-IsCollectionNullOrEmpty $feedPackageResults)) {
$feedPackageResults = $feedPackageResults | Where-Object { $null -ne ($_["Packages"]) }
}
# Match every package name to a feed.
$packageToFeed = @{}
foreach ($feedResult in $feedPackageResults) {
$feedName = $feedResult["FeedName"]
$feed = $filteredFeeds | Where-Object { $_.Name -eq $feedName }
[array]$packagesInFeed = $feedResult["Packages"]
foreach ($feedPackage in $packagesInFeed) {
$packageName = $feedPackage.Name
if ($packageToFeed.ContainsKey($packageName)) {
$firstSourceName = $packageToFeed[$packageName].Name
Write-Error "$logLead : Multiple sources contain a package named '$packageName'. Found in sources '$firstSourceName' and '$feedName'"
}
$packageToFeed.Set_Item($packageName, $feed)
}
}
# Run back through all the packages, and assign feeds based on package names.
foreach ($pkg in $Packages) {
$packageName = $pkg.Name
if ($packageToFeed.ContainsKey($packageName)) {
$pkg.Feed = $packageToFeed.Get_Item($packageName)
} else {
# For "Disabled" choco sources, you can only query them using the source param with a feed URL
# For public packages, "exact" matching is recommended. OpenSSH returns 8 records, only 1 is actually "openssh"
Write-Host "$logLead : Checking chocolatey.org passthru for '$packageName'"
$packagePublicRecord = Get-ChocoState -source $PASSTHRU_CHOCOLATEY_PUBLIC_URL -packageName $packageName -pre -exact
if ($null -eq $packagePublicRecord) {
$pkg.Feed = $null
} else {
$pkg.Feed = $passthroughPublicFeed
Write-Warning "$logLead : Package ONLY found on passthru for public chocolatey feed '$packageName'"
}
}
if (!$pkg.Feed) {
Write-Warning "$logLead : Was not able to determine a source feed for package '$packageName'"
}
}
[string]$missingPackageOutputText = $null
$missingFeedPackages = $Packages | Where-Object { $null -eq $_.Feed }
foreach ($missingFeedPackage in $missingFeedPackages) {
$missingPackageOutputText += "$($missingFeedPackage.Name)|$($missingFeedPackage.Version)`n"
}
if (![string]::IsNullOrWhiteSpace($missingPackageOutputText)) {
if ($MissingPackageLogLevel -eq "ERROR") {
Write-Error "$logLead : Was not able to find the following package(s) in any feed:`n $missingPackageOutputText"
} else {
Write-Warning "$logLead : Was not able to find the following package(s) in any feed:`n $missingPackageOutputText"
}
}
Write-Host "$logLead : Package names matched to feeds."
}