Skip to content
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
68 changes: 52 additions & 16 deletions Private/Resolve-GitHubManifest.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -37,32 +37,68 @@ function Resolve-GitHubManifest {
param (
[Parameter(Mandatory=$true)][string]$Id,
[Parameter(Mandatory=$true)][string]$SysArch,
[Parameter(Mandatory=$true)][hashtable]$Headers
[Parameter(Mandatory=$true)][hashtable]$Headers,

# winget's own latest_version for this package, from the detection
# index. Authoritative, so it skips the directory listing entirely -
# which is both correct and one fewer API call.
[string]$KnownVersion
)

# 2. Construct API Path
$IdPath = $Id.Replace(".", "/")
$FirstChar = $Id.Substring(0,1).ToLower()
$BaseApi = "https://api.github.com/repos/microsoft/winget-pkgs/contents/manifests/$FirstChar/$IdPath"

# 3. Get Version (Latest) [API call 1 of 2]
$VersionsResponse = Invoke-RestMethod -Uri $BaseApi -Method Get -Headers $Headers -ErrorAction Stop
# 3. Candidate version folders, best first.
#
# A package folder can contain entries that are not versions at all. Some
# are architectures or channels (Discord carries x86, arm64, Canary, PTB).
# Worse, some are nested PACKAGE namespaces that look exactly like a
# version: manifests/m/Microsoft/Office contains "2010", which is the
# Microsoft.Office.2010.* family, not a release. Sorting numerically put
# 2010 above 16.0.20228.20124, resolution then looked for an installer
# manifest inside it and threw, and Install-TecharyApp reported
# "not found in GitHub OR Custom Catalog" for an app that is published.
#
# So a folder is only a version if it actually contains an installer
# manifest. Walk the candidates until one does.
$Candidates = @()

if ($KnownVersion) {
$Candidates += $KnownVersion
}
else {
$VersionsResponse = Invoke-RestMethod -Uri $BaseApi -Method Get -Headers $Headers -ErrorAction Stop
$Candidates = @($VersionsResponse |
Where-Object { $_.type -eq "dir" } |
Select-Object *, @{N='SortKey'; E={ Get-ManifestVersionKey -Name $_.name }} |
Where-Object { $null -ne $_.SortKey } |
Sort-Object SortKey -Descending |
Select-Object -ExpandProperty name)

if ($Candidates.Count -eq 0) { throw "Could not determine a valid version folder for '$Id'." }
}

# 4. Get Manifest
$LatestVersion = $null
$InstallerFile = $null
$Tried = New-Object System.Collections.Generic.List[string]

$LatestVersionObj = $VersionsResponse |
Where-Object { $_.type -eq "dir" } |
Select-Object *, @{N='SortKey'; E={ Get-ManifestVersionKey -Name $_.name }} |
Where-Object { $null -ne $_.SortKey } |
Sort-Object SortKey -Descending |
Select-Object -First 1
# Bounded: each probe is an API call, and that allowance is scarce.
foreach ($Candidate in ($Candidates | Select-Object -First 5)) {
$Tried.Add($Candidate)
try {
$VersionFiles = Invoke-RestMethod -Uri "$BaseApi/$Candidate" -Method Get -Headers $Headers -ErrorAction Stop
} catch { continue }

if (-not $LatestVersionObj) { throw "Could not determine a valid version folder for '$Id'." }
$LatestVersion = $LatestVersionObj.Name
$File = $VersionFiles | Where-Object { $_.name -like "*.installer.yaml" } | Select-Object -First 1
if ($File) { $LatestVersion = $Candidate; $InstallerFile = $File; break }
}

# 4. Get Manifest [API call 2 of 2]
$VersionPath = "$BaseApi/$LatestVersion"
$VersionFiles = Invoke-RestMethod -Uri $VersionPath -Method Get -Headers $Headers -ErrorAction Stop
$InstallerFile = $VersionFiles | Where-Object { $_.name -like "*.installer.yaml" } | Select-Object -First 1
if (-not $InstallerFile) { throw "No installer YAML found for '$Id' $LatestVersion." }
if (-not $InstallerFile) {
throw ("No installer YAML found for '{0}'. Tried: {1}." -f $Id, ($Tried -join ', '))
}

# Served from raw.githubusercontent.com, which is CDN-backed and not
# subject to the API rate limit, so no credentials are sent here.
Expand Down
14 changes: 13 additions & 1 deletion Public/Get-GitHubInstaller.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,20 @@ function Get-GitHubInstaller {
}

if (-not $Meta) {
# winget's own latest_version for this package. Authoritative, and it
# avoids re-deriving the version from a directory listing that also
# contains architectures, channels and nested package namespaces.
$KnownVersion = $null
try {
$Meta = Resolve-GitHubManifest -Id $Id -SysArch $SysArch -Headers $Headers
$Entry = Get-DetectionEntry -Id $Id
if ($Entry -and $Entry.Version) {
$KnownVersion = $Entry.Version
Write-PackagerLog -Message "Index gives $Id latest version $KnownVersion; skipping version discovery."
}
} catch { }

try {
$Meta = Resolve-GitHubManifest -Id $Id -SysArch $SysArch -Headers $Headers -KnownVersion $KnownVersion

try {
if (-not (Test-Path $CacheDir)) { New-Item -ItemType Directory -Path $CacheDir -Force -ErrorAction Stop | Out-Null }
Expand Down
Loading