Skip to content

GitHub Packages stopped working #3907

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 15 commits into from
Apr 20, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 16 additions & 31 deletions NuGet/Download-BcNuGetPackageToFolder.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -145,51 +145,32 @@ try {
else {
Write-Host "Best match for package name $($packageName) Version $($version): $packageId Version $packageVersion from $($feed.Url)"
$package = ''
$manifest = $feed.DownloadNuSpec($packageId, $packageVersion)
$packageSpec = $feed.DownloadPackageSpec($packageId, $packageVersion)

if ($VerbosePreference -ne 'SilentlyContinue') {
Write-Verbose "NUSPEC:"
$manifestStringWriter = New-Object System.IO.StringWriter
$manifestXmlWriter = [System.Xml.XmlWriter]::Create($manifestStringWriter, (New-Object System.Xml.XmlWriterSettings -Property @{ Indent = $true}))
$manifest.Save($manifestXmlWriter)
$manifestXmlWriter.Dispose()
$manifestStringWriter.ToString() -split [System.Environment]::NewLine | Write-Verbose
$manifestStringWriter.Dispose()
Write-Verbose "Package spec:"
$packageSpec | ConvertTo-Json -Depth 10 | Write-Verbose
}

$appId = ''
if ($manifest.package.metadata.PSObject.Properties.Name -eq 'title') {
$appName = $manifest.package.metadata.title
}
elseif ($manifest.package.metadata.PSObject.Properties.Name -eq 'description') {
$appName = $manifest.package.metadata.description
}
else {
$appName = $manifest.package.metadata.id
}
if ($manifest.package.metadata.id -match '^.*([0-9A-Fa-f]{8}\-[0-9A-Fa-f]{4}\-[0-9A-Fa-f]{4}\-[0-9A-Fa-f]{4}\-[0-9A-Fa-f]{12})$') {
$appName = $packageSpec.name
if ($packageSpec.id -match '^.*([0-9A-Fa-f]{8}\-[0-9A-Fa-f]{4}\-[0-9A-Fa-f]{4}\-[0-9A-Fa-f]{4}\-[0-9A-Fa-f]{12})$') {
# If packageId ends in a GUID (AppID) then use the AppId for the packageId
$appId = "$($matches[1])"
}
elseif ($manifest.package.metadata.id -like 'Microsoft.Platform*') {
elseif ($packageSpec.id -like 'Microsoft.Platform*') {
# If packageId starts with Microsoft.Platform then use the packageId for the packageId
$appName = 'Platform'
}
$returnValue = @([PSCustomObject]@{
"Publisher" = $manifest.package.metadata.authors
"Publisher" = $packageSpec.authors
"Name" = $appName
"id" = $appId
"Version" = $manifest.package.metadata.version
"Version" = $packageSpec.version
})
$dependenciesErr = ''
if ($manifest.package.metadata.PSObject.Properties.Name -eq 'Dependencies') {
$dependencies = $manifest.package.metadata.Dependencies.GetEnumerator()
}
else {
$dependencies = @()
}
$dependenciesToDownload = @()
foreach($dependency in $dependencies) {
foreach($dependency in $packageSpec.dependencies) {
if (-not $installedPlatform) {
$installedPlatform = $installedApps+$returnValue | Where-Object { $_ -and $_.Name -eq 'Platform' } | Select-Object -ExpandProperty Version
}
Expand Down Expand Up @@ -228,12 +209,16 @@ try {
}
else {
$dependencyPublisher = ''
if ($dependencyId -match '^([^\.]+)\.([^\.]+)(\.[^\.][^\.])?(\.symbols)?(\.[0-9A-Fa-f]{8}\-[0-9A-Fa-f]{4}\-[0-9A-Fa-f]{4}\-[0-9A-Fa-f]{4}\-[0-9A-Fa-f]{12})?$') {
if ($dependencyId -match '^([^\.]+)\.([^\.]+)(\.[^\.][^\.])?(\.symbols)?\.?([0-9A-Fa-f]{8}\-[0-9A-Fa-f]{4}\-[0-9A-Fa-f]{4}\-[0-9A-Fa-f]{4}\-[0-9A-Fa-f]{12})?$') {
# Matches publisher.name[.country][.symbols][.appId] format (country section is only for microsoft apps)
$dependencyPublisher = $matches[1]
if ($dependencyPublisher -eq 'microsoft') {
$dependencyCountry = "$($matches[3])".TrimStart('.')
}
if ($Matches[5]) {
# If the dependencyId ends in a GUID (AppID) then use the AppId for the dependencyId
$dependencyId = "$($matches[5])"
}
}
$installedApp = $installedApps | Where-Object { $_ -and $_.id -and $dependencyId -like "*$($_.id)*" } | Sort-Object -Property @{ "Expression" = "[System.Version]Version" } -Descending | Select-Object -First 1
if ($installedApp) {
Expand All @@ -244,7 +229,7 @@ try {
}
}
elseif ($downloadDependencies -eq 'own') {
$downloadIt = ($dependencyPublisher -eq [NugetFeed]::Normalize($manifest.package.metadata.authors))
$downloadIt = ($dependencyPublisher -eq [NugetFeed]::Normalize($packageSpec.authors))
}
elseif ($downloadDependencies -eq 'allButMicrosoft') {
# Download if publisher isn't Microsoft (including if publisher is empty)
Expand Down Expand Up @@ -276,7 +261,7 @@ try {
}
}
if ($downloadIt) {
$dependenciesToDownload += @( $dependency )
$dependenciesToDownload += @( @{ "id" = $dependencyId; "version" = $dependencyVersion } )
}
}
if ($dependenciesErr) {
Expand Down
126 changes: 97 additions & 29 deletions NuGet/NuGetFeedClass.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -129,18 +129,44 @@ class NuGetFeed {
$this.orgType[$organization] = 'users'
}
}
$queryUrl = "https://api.github.com/$($this.orgType[$organization])/$organization/packages?package_type=nuget&per_page=100&page="
$page = 1
Write-Host -ForegroundColor Yellow "Search package using $queryUrl$page"
$cacheKey = "GitHubPackages:$($this.orgType[$organization])/$organization"
$matching = @()
while ($true) {
$result = Invoke-RestMethod -Method GET -Headers $headers -Uri "$queryUrl$page"
if ($result.Count -eq 0) {
break
if ($this.searchResultsCacheRetentionPeriod -gt 0 -and $this.searchResultsCache.ContainsKey($cacheKey)) {
if ($this.searchResultsCache[$cacheKey].timestamp.AddSeconds($this.searchResultsCacheRetentionPeriod) -lt (Get-Date)) {
Write-Host "Cache expired, removing cache $cacheKey"
$this.searchResultsCache.Remove($cacheKey)
}
else {
Write-Host "Search available packages using cache $cacheKey"
$matching = $this.searchResultsCache[$cacheKey].matching
Write-Host "$($matching.Count) packages found"
}
}
if (-not $matching) {
$per_page = 50
$queryUrl = "https://api.github.com/$($this.orgType[$organization])/$organization/packages?package_type=nuget&per_page=$($per_page)&page="
$page = 1
$matching = @()
while ($true) {
Write-Host -ForegroundColor Yellow "Search package using $queryUrl$page"
$result = Invoke-RestMethod -UseBasicParsing -Method GET -Headers $headers -Uri "$queryUrl$page"
Write-Host "$($result.Count) packages found"
if ($result.Count -eq 0) {
break
}
$matching += @($result)
if ($result.Count -ne $per_page) {
break
}
$page++
}
Write-Host "Total of $($matching.Count) packages found"
$this.searchResultsCache[$cacheKey] = @{
matching = $matching
timestamp = (Get-Date)
}
$matching += @($result | Where-Object { $_.name -like "*$packageName*" -and $this.IsTrusted($_.name) } | Sort-Object { $_.name.replace('.symbols','') } | ForEach-Object { @{ "id" = $_.name; "versions" = @() } } )
$page++
}
$matching = @($matching | Where-Object { $_.name -like "*$packageName*" -and $this.IsTrusted($_.name) } | Sort-Object { $_.name.replace('.symbols','') } | ForEach-Object { @{ "id" = $_.name; "versions" = @() } } )
}
else {
$queryUrl = "$($this.searchQueryServiceUrl)?q=$packageName&take=50"
Expand Down Expand Up @@ -180,10 +206,7 @@ class NuGetFeed {
if (!$this.IsTrusted($package.id)) {
throw "Package $($package.id) is not trusted on $($this.url)"
}
if ($package.versions.count -ne 0) {
$versionsArr = $package.versions
}
else {
if ($package.versions.count -eq 0) {
$queryUrl = "$($this.packageBaseAddressUrl.TrimEnd('/'))/$($package.Id.ToLowerInvariant())/index.json"
try {
Write-Host -ForegroundColor Yellow "Get versions using $queryUrl"
Expand All @@ -194,9 +217,9 @@ class NuGetFeed {
catch {
throw (GetExtendedErrorMessage $_)
}
$versionsArr = @($versions.versions)

$package.versions = @($versions.versions)
}
$versionsArr = $package.versions
Write-Host "$($versionsArr.count) versions found"
$versionsArr = @($versionsArr | Where-Object { $allowPrerelease -or !$_.Contains('-') } | Sort-Object { ($_ -replace '-.+$') -as [System.Version] }, { "$($_)z" } -Descending:$descending | ForEach-Object { "$_" })
Write-Host "First version is $($versionsArr[0])"
Expand Down Expand Up @@ -322,24 +345,69 @@ class NuGetFeed {
return ''
}

[xml] DownloadNuSpec([string] $packageId, [string] $version) {
# Download the specs for the package with id = packageId and version = version
# The following properties are returned:
# - id: the package id
# - name: the package name (either title, description or id from the nuspec)
# - version: the package version
# - authors: the package authors
# - dependencies: the package dependencies (id and version range)
[PSCustomObject] DownloadPackageSpec([string] $packageId, [string] $version) {
if (!$this.IsTrusted($packageId)) {
throw "Package $packageId is not trusted on $($this.url)"
}
$queryUrl = "$($this.packageBaseAddressUrl.TrimEnd('/'))/$($packageId.ToLowerInvariant())/$($version.ToLowerInvariant())/$($packageId.ToLowerInvariant()).nuspec"
try {
Write-Host "Download nuspec using $queryUrl"
$prev = $global:ProgressPreference; $global:ProgressPreference = "SilentlyContinue"
$tmpFile = Join-Path ([System.IO.Path]::GetTempPath()) "$([GUID]::NewGuid().ToString()).nuspec"
Invoke-RestMethod -UseBasicParsing -Method GET -Headers ($this.GetHeaders()) -Uri $queryUrl -OutFile $tmpFile
$nuspec = Get-Content -Path $tmpfile -Encoding UTF8 -Raw
Remove-Item -Path $tmpFile -Force
$global:ProgressPreference = $prev
if ($this.packageBaseAddressUrl -like 'https://nuget.pkg.github.com/*') {
$queryUrl = "$($this.packageBaseAddressUrl.SubString(0,$this.packageBaseAddressUrl.LastIndexOf('/')))/$($packageId.ToLowerInvariant())/$($version.ToLowerInvariant()).json"
$response = Invoke-WebRequest -UseBasicParsing -Method GET -Headers ($this.GetHeaders()) -Uri $queryUrl
$content = $response.Content | ConvertFrom-Json
if (!($content.PSObject.Properties.Name -eq 'catalogEntry') -or ($null -eq $content.catalogEntry)) {
throw "Package $packageId version $version not found on"
}
return @{
"id" = $content.catalogEntry.id
"name" = $content.catalogEntry.description
"version" = $content.catalogEntry.version
"authors" = $content.catalogEntry.authors
"dependencies" = $content.catalogEntry.dependencyGroups | ForEach-Object { $_.dependencies | ForEach-Object { @{"id" = $_.id; "version" = $_.range.replace(' ','') } } }
}
}
catch {
throw (GetExtendedErrorMessage $_)
else {
$queryUrl = "$($this.packageBaseAddressUrl.TrimEnd('/'))/$($packageId.ToLowerInvariant())/$($version.ToLowerInvariant())/$($packageId.ToLowerInvariant()).nuspec"
try {
Write-Host "Download nuspec using $queryUrl"
$prev = $global:ProgressPreference; $global:ProgressPreference = "SilentlyContinue"
$tmpFile = Join-Path ([System.IO.Path]::GetTempPath()) "$([GUID]::NewGuid().ToString()).nuspec"
Invoke-RestMethod -UseBasicParsing -Method GET -Headers ($this.GetHeaders()) -Uri $queryUrl -OutFile $tmpFile
$nuspec = [xml](Get-Content -Path $tmpfile -Encoding UTF8 -Raw)
Remove-Item -Path $tmpFile -Force
$global:ProgressPreference = $prev
}
catch {
throw (GetExtendedErrorMessage $_)
}
if ($nuspec.package.metadata.PSObject.Properties.Name -eq 'title') {
$appName = $nuspec.package.metadata.title
}
elseif ($nuspec.package.metadata.PSObject.Properties.Name -eq 'description') {
$appName = $nuspec.package.metadata.description
}
else {
$appName = $nuspec.package.metadata.id
}
if ($nuspec.package.metadata.PSObject.Properties.Name -eq 'Dependencies') {
$dependencies = @($nuspec.package.metadata.Dependencies.GetEnumerator() | ForEach-Object { @{"id" = $_.id; "version" = $_.version } })
}
else {
$dependencies = @()
}
return @{
"id" = $nuspec.package.metadata.id
"name" = $appName
"version" = $nuspec.package.metadata.version
"authors" = $nuspec.package.metadata.authors
"dependencies" = $dependencies
}
}
return [xml]$nuspec
}

[string] DownloadPackage([string] $packageId, [string] $version) {
Expand Down Expand Up @@ -406,7 +474,7 @@ class NuGetFeed {

# Clear matching search results caches
@( $this.searchResultsCache.Keys ) |
Where-Object { $package -like "*$($_)*" } |
Where-Object { $package -like "*$($_)*" -or $_ -like 'GitHubPackages:*' } |
ForEach-Object { $this.searchResultsCache.Remove($_) }
}
catch [System.Net.WebException] {
Expand Down