From 0dee8df71a3455de4fe1273e789937bca1395aa4 Mon Sep 17 00:00:00 2001 From: FTCHD <144691102+FTCHD@users.noreply.github.com> Date: Mon, 22 Sep 2025 15:32:09 +0200 Subject: [PATCH] update winget workflow Signed-off-by: FTCHD <144691102+FTCHD@users.noreply.github.com> --- .github/workflows/winget2.yml | 276 +++++++++++++++++----------------- 1 file changed, 142 insertions(+), 134 deletions(-) diff --git a/.github/workflows/winget2.yml b/.github/workflows/winget2.yml index a43ea88..874baf0 100644 --- a/.github/workflows/winget2.yml +++ b/.github/workflows/winget2.yml @@ -1,157 +1,165 @@ name: WinGet2 on: - workflow_dispatch: + workflow_dispatch: jobs: - validate-and-test: - runs-on: windows-2025 + validate-and-test: + runs-on: windows-2025 - env: - MANIFEST_DIR: winget - PACKAGE_ID: RcloneUI.RcloneUI - EXPECTED_NAME: Rclone UI - EXPECTED_PUBLISHER: Rclone UI - EXPECTED_VERSION: 2.6.0 + env: + MANIFEST_DIR: winget + PACKAGE_ID: RcloneUI.RcloneUI + EXPECTED_NAME: Rclone UI + EXPECTED_PUBLISHER: Rclone UI + EXPECTED_VERSION: 2.6.0 - steps: - - name: Checkout - uses: actions/checkout@v4 + steps: + - name: Checkout + uses: actions/checkout@v4 - - name: Show winget version - shell: pwsh - run: winget --version + - name: Show winget version + shell: pwsh + run: winget --version - - name: Validate manifest (schema/rules) - shell: pwsh - run: | - $ErrorActionPreference = 'Stop' - if (-not (Test-Path $env:MANIFEST_DIR)) { throw "Missing $env:MANIFEST_DIR" } + - name: Validate manifest (schema/rules) + shell: pwsh + run: | + $ErrorActionPreference = 'Stop' + if (-not (Test-Path $env:MANIFEST_DIR)) { throw "Missing $env:MANIFEST_DIR" } - winget validate --manifest $env:MANIFEST_DIR --disable-interactivity --verbose-logs ` - | Tee-Object -FilePath "$pwd\winget-validate.txt" + winget validate --manifest $env:MANIFEST_DIR --disable-interactivity --verbose-logs ` + | Tee-Object -FilePath "$pwd\winget-validate.txt" - - name: Enable local manifest installs - shell: pwsh - run: | - # Required to install directly from a local manifest folder. :contentReference[oaicite:2]{index=2} - winget settings --enable LocalManifestFiles + - name: Enable local manifest installs + shell: pwsh + run: | + # Required to install directly from a local manifest folder. :contentReference[oaicite:2]{index=2} + winget settings --enable LocalManifestFiles - - name: Install PowerShell YAML module (for parsing the manifest) - shell: pwsh - run: | - Install-Module -Name powershell-yaml -Force -Scope CurrentUser + - name: Install PowerShell YAML module (for parsing the manifest) + shell: pwsh + run: | + Install-Module -Name powershell-yaml -Force -Scope CurrentUser - - name: Check installer URLs + verify SHA256 for all installers (x64 & arm64) - shell: pwsh - run: | - Import-Module powershell-yaml - $manifestFiles = Get-ChildItem -Path $env:MANIFEST_DIR -Include *.yml,*.yaml -File -Recurse - if (-not $manifestFiles) { throw "No manifest files found in $env:MANIFEST_DIR" } + - name: Check installer URLs + verify SHA256 for all installers (x64 & arm64) + shell: pwsh + run: | + Import-Module powershell-yaml + $manifestFiles = Get-ChildItem -Path $env:MANIFEST_DIR -Include *.yml,*.yaml -File -Recurse + if (-not $manifestFiles) { throw "No manifest files found in $env:MANIFEST_DIR" } - $rows = @() - foreach ($f in $manifestFiles) { - $y = (Get-Content -Raw $f.FullName) | ConvertFrom-Yaml - if ($y.ManifestType -ne 'installer') { continue } + $rows = @() + foreach ($f in $manifestFiles) { + $y = (Get-Content -Raw $f.FullName) | ConvertFrom-Yaml + if ($y.ManifestType -ne 'installer') { continue } - foreach ($inst in $y.Installers) { - $url = $inst.InstallerUrl - $sha = ($inst.InstallerSha256 ?? $inst.InstallerSha256Hash ?? $inst.Sha256) # tolerate naming - $arch = $inst.Architecture - $scope = $inst.Scope + foreach ($inst in $y.Installers) { + $url = $inst.InstallerUrl + $sha = ($inst.InstallerSha256 ?? $inst.InstallerSha256Hash ?? $inst.Sha256) # tolerate naming + $arch = $inst.Architecture + $scope = $inst.Scope - $out = Join-Path $env:RUNNER_TEMP ("installer_{0}_{1}{2}" -f $arch, $scope, [IO.Path]::GetExtension($url)) - Write-Host "Downloading $url -> $out" - Invoke-WebRequest -Uri $url -OutFile $out -UseBasicParsing + $out = Join-Path $env:RUNNER_TEMP ("installer_{0}_{1}{2}" -f $arch, $scope, [IO.Path]::GetExtension($url)) + Write-Host "Downloading $url -> $out" + Invoke-WebRequest -Uri $url -OutFile $out -UseBasicParsing - $got = (Get-FileHash -Path $out -Algorithm SHA256).Hash.ToLower() - $ok = $got -eq $sha.ToLower() - $rows += [pscustomobject]@{ - File = $out; Architecture=$arch; Scope=$scope; Url=$url; ExpectedSHA=$sha; ActualSHA=$got; Match=$ok - } - if (-not $ok) { throw "SHA256 mismatch for $url" } - } - } - $rows | Format-Table | Out-String | Tee-Object "$pwd\hash-check.txt" - - - name: Matrix install tests (x64 / user & machine) - shell: pwsh - run: | - $ErrorActionPreference = 'Stop' - $scopes = @('user','machine') - foreach ($scope in $scopes) { - Write-Host "=== INSTALL TEST scope=$scope (x64) ===" - - # Ensure any previous install is gone for this scope - winget list --id $env:PACKAGE_ID -e | Tee-Object -FilePath "$pwd\winget-list-before-$scope.txt" - winget uninstall --id $env:PACKAGE_ID -e --scope $scope --silent --accept-source-agreements --disable-interactivity --source winget ` - --verbose-logs 2>$null; $true | Out-Null # ignore errors if not installed - # Uninstall flags & scope filtering. :contentReference[oaicite:5]{index=5} - - # Install from the local manifest (requires setting enabled earlier) - winget install --manifest $env:MANIFEST_DIR --scope $scope --architecture x64 ` - --silent --accept-package-agreements --disable-interactivity --verbose-logs ` - | Tee-Object -FilePath "$pwd\winget-install-$scope.txt" - # Local manifest install & options. :contentReference[oaicite:6]{index=6} - - # Post-install: verify presence via winget list - winget list --id $env:PACKAGE_ID -e | Tee-Object -FilePath "$pwd\winget-list-after-$scope.txt" - - # Verify declared AppsAndFeaturesEntries in the registry - if ($scope -eq 'machine') { - $roots = @( - 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall', - 'HKLM:\Software\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall' - ) - } else { - $roots = @('HKCU:\Software\Microsoft\Windows\CurrentVersion\Uninstall') - } - - $found = $false - foreach ($root in $roots) { - if (Test-Path $root) { - Get-ChildItem $root | ForEach-Object { - $dispName = (Get-ItemProperty $_.PsPath -ErrorAction SilentlyContinue).DisplayName - $publisher = (Get-ItemProperty $_.PsPath -ErrorAction SilentlyContinue).Publisher - $version = (Get-ItemProperty $_.PsPath -ErrorAction SilentlyContinue).DisplayVersion - if ($dispName -eq $env:EXPECTED_NAME) { - $found = $true - "$scope ARP entry: Name=$dispName Publisher=$publisher Version=$version" ` - | Tee-Object -FilePath "$pwd\arp-$scope.txt" - if ($publisher -ne $env:EXPECTED_PUBLISHER -or $version -ne $env:EXPECTED_VERSION) { - throw "ARP mismatch for $scope. Expected Publisher='$env:EXPECTED_PUBLISHER' Version='$env:EXPECTED_VERSION'. Got Publisher='$publisher' Version='$version'." + $got = (Get-FileHash -Path $out -Algorithm SHA256).Hash.ToLower() + $ok = $got -eq $sha.ToLower() + $rows += [pscustomobject]@{ + File = $out; Architecture=$arch; Scope=$scope; Url=$url; ExpectedSHA=$sha; ActualSHA=$got; Match=$ok + } + if (-not $ok) { throw "SHA256 mismatch for $url" } } } - } - } - } - if (-not $found) { throw "ARP entry '$env:EXPECTED_NAME' not found for $scope scope." } + $rows | Format-Table | Out-String | Tee-Object "$pwd\hash-check.txt" - # Dependency check: Edge WebView2 Runtime present? - winget list --id Microsoft.EdgeWebView2Runtime -e | Tee-Object -FilePath "$pwd\webview2-$scope.txt" - # Quick dependency presence check. :contentReference[oaicite:7]{index=7} + - name: Matrix install tests (x64 / user & machine) + shell: pwsh + run: | + $ErrorActionPreference = 'Stop' + $scopes = @('user','machine') - # Uninstall and verify it’s gone - winget uninstall --id $env:PACKAGE_ID -e --scope $scope --silent --accept-source-agreements --disable-interactivity --source winget ` - --verbose-logs | Tee-Object -FilePath "$pwd\winget-uninstall-$scope.txt" - winget list --id $env:PACKAGE_ID -e | Tee-Object -FilePath "$pwd\winget-list-final-$scope.txt" - } + # Prime sources once to avoid first-run prompts + winget source list --accept-source-agreements | Out-Null + winget source update --accept-source-agreements | Out-Null - # - name: Collect WinGet logs + step outputs - # if: always() - # uses: actions/upload-artifact@v4 - # with: - # name: winget-ci-logs - # path: | - # *.txt - # ${{ env.LOCALAPPDATA }}\Packages\Microsoft.DesktopAppInstaller_8wekyb3d8bbwe\LocalState\DiagOutputDir\*.log - # if-no-files-found: ignore + foreach ($scope in $scopes) { + Write-Host "=== INSTALL TEST scope=$scope (x64) ===" - - name: CI summary - if: always() - shell: pwsh - run: | - "## WinGet CI Summary`n" | Out-File $env:GITHUB_STEP_SUMMARY -Append - Get-Content "$pwd\winget-validate.txt" -ErrorAction SilentlyContinue | Out-File $env:GITHUB_STEP_SUMMARY -Append - "`n---`nHashes:`n" | Out-File $env:GITHUB_STEP_SUMMARY -Append - Get-Content "$pwd\hash-check.txt" -ErrorAction SilentlyContinue | Out-File $env:GITHUB_STEP_SUMMARY -Append + # Before: plain 'list' caused the msstore prompt + winget list --id $env:PACKAGE_ID -e --source winget --accept-source-agreements ` + | Tee-Object -FilePath "$pwd\winget-list-before-$scope.txt" + + # Best-effort uninstall in case a prior run left state around + winget uninstall --id $env:PACKAGE_ID -e --scope $scope --silent ` + --source winget --accept-source-agreements --disable-interactivity --verbose-logs 2>$null + + # Install from the local manifest, but constrain dependency resolution to 'winget' + winget install --manifest $env:MANIFEST_DIR --scope $scope --architecture x64 --silent ` + --source winget --accept-source-agreements --accept-package-agreements ` + --disable-interactivity --verbose-logs ` + | Tee-Object -FilePath "$pwd\winget-install-$scope.txt" + + # Post-install presence check (also pinned to 'winget' to avoid msstore) + winget list --id $env:PACKAGE_ID -e --source winget --accept-source-agreements ` + | Tee-Object -FilePath "$pwd\winget-list-after-$scope.txt" + + # Verify ARP entry in the appropriate registry hive (unchanged) + if ($scope -eq 'machine') { + $roots = @( + 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall', + 'HKLM:\Software\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall' + ) + } else { + $roots = @('HKCU:\Software\Microsoft\Windows\CurrentVersion\Uninstall') + } + $found = $false + foreach ($root in $roots) { + if (Test-Path $root) { + Get-ChildItem $root | ForEach-Object { + $p = Get-ItemProperty $_.PsPath -ErrorAction SilentlyContinue + if ($p.DisplayName -eq $env:EXPECTED_NAME) { + $found = $true + "ARP ($scope): Name=$($p.DisplayName) Publisher=$($p.Publisher) Version=$($p.DisplayVersion)" ` + | Tee-Object -FilePath "$pwd\arp-$scope.txt" + if ($p.Publisher -ne $env:EXPECTED_PUBLISHER -or $p.DisplayVersion -ne $env:EXPECTED_VERSION) { + throw "ARP mismatch for $scope." + } + } + } + } + } + if (-not $found) { throw "ARP entry '$env:EXPECTED_NAME' not found for $scope scope." } + + # Dependency presence check (pin to 'winget' to avoid msstore) + winget list --id Microsoft.EdgeWebView2Runtime -e --source winget --accept-source-agreements ` + | Tee-Object -FilePath "$pwd\webview2-$scope.txt" + + # Clean uninstall (also pinned) + winget uninstall --id $env:PACKAGE_ID -e --scope $scope --silent ` + --source winget --accept-source-agreements --disable-interactivity --verbose-logs ` + | Tee-Object -FilePath "$pwd\winget-uninstall-$scope.txt" + + winget list --id $env:PACKAGE_ID -e --source winget --accept-source-agreements ` + | Tee-Object -FilePath "$pwd\winget-list-final-$scope.txt" + } + + # - name: Collect WinGet logs + step outputs + # if: always() + # uses: actions/upload-artifact@v4 + # with: + # name: winget-ci-logs + # path: | + # *.txt + # ${{ env.LOCALAPPDATA }}\Packages\Microsoft.DesktopAppInstaller_8wekyb3d8bbwe\LocalState\DiagOutputDir\*.log + # if-no-files-found: ignore + + - name: CI summary + if: always() + shell: pwsh + run: | + "## WinGet CI Summary`n" | Out-File $env:GITHUB_STEP_SUMMARY -Append + Get-Content "$pwd\winget-validate.txt" -ErrorAction SilentlyContinue | Out-File $env:GITHUB_STEP_SUMMARY -Append + "`n---`nHashes:`n" | Out-File $env:GITHUB_STEP_SUMMARY -Append + Get-Content "$pwd\hash-check.txt" -ErrorAction SilentlyContinue | Out-File $env:GITHUB_STEP_SUMMARY -Append