Verify and Test Packages #115
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Verify and Test Packages | |
| on: | |
| workflow_dispatch: | |
| schedule: | |
| - cron: '0 0 * * *' | |
| release: | |
| types: [published] | |
| jobs: | |
| setup: | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| outputs: | |
| release_version: ${{ steps.get_version.outputs.RELEASE_VERSION }} | |
| cache-key: ${{ steps.cache_check.outputs.cache-primary-key }} | |
| cache-hit: ${{ steps.cache_restore.outputs.cache-hit }} | |
| steps: | |
| - name: "⏳ Add grace period after release publication" | |
| if: github.event_name == 'release' | |
| run: sleep 14400 | |
| - name: "🔽 Checkout Repository" | |
| uses: actions/checkout@v4 | |
| - name: "🏷️ Get Latest Release Version" | |
| id: get_version | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| latest_tag=$(gh release view --json tagName --jq .tagName) | |
| if [ -z "$latest_tag" ]; then | |
| echo "No releases found. Halting workflow." | |
| exit 1 | |
| fi | |
| version="$latest_tag" | |
| echo "RELEASE_VERSION=$version" >> "$GITHUB_OUTPUT" | |
| echo "Checking for version: $version" | |
| - name: "🗄️ Set cache key" | |
| id: cache_check | |
| run: | | |
| echo "cache-primary-key=verification-flag-v${{ steps.get_version.outputs.RELEASE_VERSION }}" >> $GITHUB_OUTPUT | |
| - name: "🗄️ Restore Publication Success Cache" | |
| id: cache_restore | |
| uses: actions/cache/restore@v4 | |
| with: | |
| path: ./.cache | |
| key: verification-flag-v${{ steps.get_version.outputs.RELEASE_VERSION }} | |
| choco: | |
| needs: setup | |
| if: needs.setup.outputs.cache-hit != 'true' | |
| runs-on: windows-latest | |
| timeout-minutes: 15 | |
| outputs: | |
| published: ${{ steps.choco.outputs.published }} | |
| e2e: ${{ steps.choco.outputs.e2e_result }} | |
| steps: | |
| - name: "🍫 Chocolatey Verify and Test" | |
| id: choco | |
| shell: pwsh | |
| env: | |
| RELEASE_VERSION: ${{ needs.setup.outputs.release_version }} | |
| run: | | |
| $ErrorActionPreference = 'Stop' | |
| $version = "$env:RELEASE_VERSION" | |
| $packageName = "winmemorycleaner" | |
| $e2e_result = "fail" | |
| $published = "false" | |
| function Set-Outputs { | |
| param($Published, $E2E) | |
| echo "published=$Published" | Out-File -FilePath $env:GITHUB_OUTPUT -Append | |
| echo "e2e_result=$E2E" | Out-File -FilePath $env:GITHUB_OUTPUT -Append | |
| } | |
| Write-Host "===[Chocolatey] Checking $packageName version $version===" | |
| try { | |
| $choco_output = choco search $packageName --exact --all-versions -r | |
| Write-Host "Chocolatey search output:`n$choco_output" | |
| if ($choco_output -match "\b$([regex]::Escape($version))\b") { | |
| Write-Host "[Chocolatey] ✅ Version found." | |
| $published = "true" | |
| Write-Host("[Chocolatey] Installing package...") | |
| choco install $packageName --version $version -y --no-progress | |
| $chocoRoot = $env:ChocolateyInstall | |
| if (-not $chocoRoot) { $chocoRoot = "C:\ProgramData\chocolatey" } | |
| $exePath = Get-ChildItem -Path (Join-Path $chocoRoot "lib\$packageName") -Recurse -Filter "WinMemoryCleaner.exe" -File -ErrorAction SilentlyContinue | Select-Object -ExpandProperty FullName -First 1 | |
| if (-not $exePath) { | |
| $cmd = Get-Command WinMemoryCleaner.exe -ErrorAction SilentlyContinue | |
| if ($cmd -and (Test-Path $cmd.Source)) { | |
| $exePath = $cmd.Source | |
| } | |
| } | |
| if (-not $exePath) { | |
| Write-Host "[Chocolatey] ❌ WinMemoryCleaner.exe not found after install" | |
| $e2e_result = "fail" | |
| Set-Outputs -Published $published -E2E $e2e_result | |
| exit 1 | |
| } | |
| Write-Host "[Chocolatey] Found executable: $exePath" | |
| $verInfo = (Get-Item $exePath).VersionInfo | |
| $fileVersion = $verInfo.ProductVersion | |
| if ([string]::IsNullOrWhiteSpace($fileVersion)) { $fileVersion = $verInfo.FileVersion } | |
| $expectedRegex = '^' + [regex]::Escape($version) + '(\.0)?$' | |
| Write-Host "[Chocolatey] Installed file version: $fileVersion" | |
| if ($fileVersion -match $expectedRegex) { | |
| $e2e_result = "success" | |
| Write-Host "[Chocolatey] ✅ E2E Test successful (version matches)." | |
| } else { | |
| Write-Host "[Chocolatey] ❌ Version mismatch. Expected $version, got $fileVersion" | |
| $e2e_result = "fail" | |
| Set-Outputs -Published $published -E2E $e2e_result | |
| exit 1 | |
| } | |
| } else { | |
| Write-Host "[Chocolatey] ❌ Version not found." | |
| $published = "false" | |
| $e2e_result = "skipped" | |
| Set-Outputs -Published $published -E2E $e2e_result | |
| exit 1 | |
| } | |
| } catch { | |
| Write-Host "[Chocolatey] ❌ Error: $($_.Exception.Message)" | |
| $published = "false" | |
| $e2e_result = "skipped" | |
| Set-Outputs -Published $published -E2E $e2e_result | |
| exit 1 | |
| } | |
| Set-Outputs -Published $published -E2E $e2e_result | |
| scoop: | |
| needs: setup | |
| if: needs.setup.outputs.cache-hit != 'true' | |
| runs-on: windows-latest | |
| timeout-minutes: 15 | |
| outputs: | |
| published: ${{ steps.scoop.outputs.published }} | |
| e2e: ${{ steps.scoop.outputs.e2e_result }} | |
| steps: | |
| - name: "🍦 Scoop Verify and Test (Extras bucket)" | |
| id: scoop | |
| shell: pwsh | |
| env: | |
| RELEASE_VERSION: ${{ needs.setup.outputs.release_version }} | |
| run: | | |
| $ErrorActionPreference = 'Stop' | |
| $version = "$env:RELEASE_VERSION" | |
| $bucket = "extras" | |
| $packageName = "winmemorycleaner" | |
| $qualified = "$bucket/$packageName" | |
| $e2e_result = "fail" | |
| $published = "false" | |
| function Set-Outputs { | |
| param($Published, $E2E) | |
| echo "published=$Published" | Out-File -FilePath $env:GITHUB_OUTPUT -Append | |
| echo "e2e_result=$E2E" | Out-File -FilePath $env:GITHUB_OUTPUT -Append | |
| } | |
| Write-Host "===[Scoop] Ensuring Scoop is installed===" | |
| try { | |
| if (-not (Get-Command scoop -ErrorAction SilentlyContinue)) { | |
| irm get.scoop.sh | iex | |
| } | |
| if (-not (scoop bucket list | Select-String -Pattern '^\s*extras(\s|$)' -Quiet)) { | |
| scoop bucket add $bucket | |
| } | |
| scoop bucket update $bucket | |
| Write-Host "===[Scoop] Checking $qualified version $version===" | |
| $manifestRaw = "" | |
| try { $manifestRaw = scoop cat $qualified 2>$null | Out-String } catch {} | |
| if (-not $manifestRaw) { | |
| Write-Host "[Scoop] ❌ Manifest $qualified not found." | |
| $published = "false" | |
| $e2e_result = "skipped" | |
| Set-Outputs -Published $published -E2E $e2e_result | |
| exit 1 | |
| } | |
| try { $manifest = $manifestRaw | ConvertFrom-Json } catch { | |
| Write-Host "[Scoop] ❌ Failed to parse manifest JSON." | |
| $published = "false" | |
| $e2e_result = "skipped" | |
| Set-Outputs -Published $published -E2E $e2e_result | |
| exit 1 | |
| } | |
| $manifestVersion = "$($manifest.version)" | |
| Write-Host "[Scoop] Manifest version: $manifestVersion" | |
| if ($manifestVersion -eq $version) { | |
| Write-Host "[Scoop] ✅ Version found in '$bucket'." | |
| $published = "true" | |
| } else { | |
| Write-Host "[Scoop] ❌ Version mismatch in manifest. Expected $version, got $manifestVersion" | |
| $published = "false" | |
| $e2e_result = "skipped" | |
| Set-Outputs -Published $published -E2E $e2e_result | |
| exit 1 | |
| } | |
| Write-Host "[Scoop] Installing package from '$bucket'..." | |
| scoop install $qualified | |
| $exe = "$(scoop prefix $packageName)\WinMemoryCleaner.exe" | |
| if (-not (Test-Path $exe)) { | |
| Write-Host "[Scoop] ❌ WinMemoryCleaner.exe not found after install" | |
| $e2e_result = "fail" | |
| Set-Outputs -Published $published -E2E $e2e_result | |
| exit 1 | |
| } | |
| $verInfo = (Get-Item $exe).VersionInfo | |
| $fileVersion = $verInfo.ProductVersion | |
| if ([string]::IsNullOrWhiteSpace($fileVersion)) { $fileVersion = $verInfo.FileVersion } | |
| $expectedRegex = '^' + [regex]::Escape($version) + '(\.0)?$' | |
| Write-Host "[Scoop] Installed file version: $fileVersion" | |
| if ($fileVersion -match $expectedRegex) { | |
| $e2e_result = "success" | |
| Write-Host "[Scoop] ✅ E2E Test successful (version matches)." | |
| } else { | |
| Write-Host "[Scoop] ❌ Version mismatch. Expected $version, got $fileVersion" | |
| $e2e_result = "fail" | |
| Set-Outputs -Published $published -E2E $e2e_result | |
| exit 1 | |
| } | |
| } catch { | |
| Write-Host "[Scoop] ❌ Error: $($_.Exception.Message)" | |
| $published = "false" | |
| $e2e_result = "skipped" | |
| Set-Outputs -Published $published -E2E $e2e_result | |
| exit 1 | |
| } | |
| Set-Outputs -Published $published -E2E $e2e_result | |
| winget_cli: | |
| needs: setup | |
| if: needs.setup.outputs.cache-hit != 'true' | |
| runs-on: windows-latest | |
| continue-on-error: true | |
| timeout-minutes: 20 | |
| outputs: | |
| found: ${{ steps.detect_winget.outputs.found }} | |
| published: ${{ steps.winget.outputs.published }} | |
| e2e: ${{ steps.winget.outputs.e2e_result }} | |
| steps: | |
| - name: "🔎 Detect WinGet CLI & Version" | |
| id: detect_winget | |
| shell: pwsh | |
| run: | | |
| $found = 'false' | |
| $wingetPath = '' | |
| try { | |
| $wingetCmd = Get-Command winget -ErrorAction Stop | |
| $found = 'true' | |
| $wingetPath = $wingetCmd.Source | |
| } catch { | |
| $possiblePath = "$env:LOCALAPPDATA\Microsoft\WindowsApps\winget.exe" | |
| if (Test-Path $possiblePath) { | |
| $found = 'true' | |
| $wingetPath = $possiblePath | |
| } | |
| } | |
| Write-Host "===[WinGet] Found: $found" | |
| Write-Host "===[WinGet] Path: $wingetPath" | |
| echo "found=$found" | Out-File -FilePath $env:GITHUB_OUTPUT -Append | |
| echo "path=$wingetPath" | Out-File -FilePath $env:GITHUB_OUTPUT -Append | |
| - name: "📦 WinGet CLI Verify and Test" | |
| id: winget | |
| if: steps.detect_winget.outputs.found == 'true' | |
| shell: pwsh | |
| env: | |
| RELEASE_VERSION: ${{ needs.setup.outputs.release_version }} | |
| run: | | |
| $ErrorActionPreference = 'Stop' | |
| $expected = "$env:RELEASE_VERSION" | |
| $expectedRegex = '^' + [regex]::Escape($expected) + '(\.0)?$' | |
| $wingetExe = "${{ steps.detect_winget.outputs.path }}" | |
| if (-not (Test-Path $wingetExe)) { $wingetExe = "winget.exe" } | |
| $pkgName = "IgorMundstein.WinMemoryCleaner" | |
| $published = "false" | |
| $e2e_result = "fail" | |
| function Set-Outputs { | |
| param($Published, $E2E) | |
| echo "published=$Published" | Out-File -FilePath $env:GITHUB_OUTPUT -Append | |
| echo "e2e_result=$E2E" | Out-File -FilePath $env:GITHUB_OUTPUT -Append | |
| } | |
| Write-Host "===[WinGet] Using $wingetExe" | |
| try { | |
| Write-Host "[WinGet] Updating sources..." | |
| try { & $wingetExe source update } catch { Write-Host "[WinGet] source update failed (non-fatal)" } | |
| # Install to an absolute output folder; also add exact-match and disable interactivity | |
| $outDir = (Resolve-Path '.').Path | |
| Write-Host "[WinGet] Installing exact version $expected to: $outDir" | |
| & $wingetExe install --id $pkgName --version $expected --source winget --accept-source-agreements --accept-package-agreements --silent --disable-interactivity -e -o "$outDir" | |
| # Try multiple locations for the portable binary | |
| $candidates = @() | |
| # 1) Output directory | |
| $candidates += (Join-Path $outDir "WinMemoryCleaner.exe") | |
| # 2) WinGet alias link location | |
| $link = Join-Path $env:LOCALAPPDATA "Microsoft\WinGet\Links\WinMemoryCleaner.exe" | |
| $candidates += $link | |
| # 3) Search in WinGet Packages | |
| $pkgRoot = Join-Path $env:LOCALAPPDATA "Microsoft\WinGet\Packages" | |
| if (Test-Path $pkgRoot) { | |
| $foundPkgExe = Get-ChildItem -Path $pkgRoot -Recurse -Filter "WinMemoryCleaner.exe" -File -ErrorAction SilentlyContinue | Select-Object -ExpandProperty FullName -First 1 | |
| if ($foundPkgExe) { $candidates += $foundPkgExe } | |
| } | |
| # 4) Refresh PATH from registry and try alias | |
| $machinePath = [Environment]::GetEnvironmentVariable('Path','Machine') | |
| $userPath = [Environment]::GetEnvironmentVariable('Path','User') | |
| if ($machinePath -or $userPath) { $env:Path = "$machinePath;$userPath" } | |
| $cmd = Get-Command WinMemoryCleaner -ErrorAction SilentlyContinue | |
| if ($cmd -and (Test-Path $cmd.Source)) { $candidates += $cmd.Source } | |
| $exePath = $candidates | Where-Object { $_ -and (Test-Path $_) } | Select-Object -First 1 | |
| if (-not $exePath) { | |
| Write-Host "[WinGet] ❌ WinMemoryCleaner.exe not found after install" | |
| $e2e_result = "fail" | |
| Set-Outputs -Published $published -E2E $e2e_result | |
| exit 1 | |
| } | |
| Write-Host "[WinGet] Found executable: $exePath" | |
| $verInfo = (Get-Item $exePath).VersionInfo | |
| $fileVersion = $verInfo.ProductVersion | |
| if ([string]::IsNullOrWhiteSpace($fileVersion)) { $fileVersion = $verInfo.FileVersion } | |
| Write-Host "[WinGet] Installed file version: $fileVersion" | |
| if ($fileVersion -match $expectedRegex) { | |
| $published = "true" | |
| $e2e_result = "success" | |
| Write-Host "[WinGet] ✅ E2E Test successful (version matches)." | |
| } else { | |
| Write-Host "[WinGet] ❌ Version mismatch. Expected $expected, got $fileVersion" | |
| $published = "true" # installed but mismatch | |
| $e2e_result = "fail" | |
| Set-Outputs -Published $published -E2E $e2e_result | |
| exit 1 | |
| } | |
| } catch { | |
| Write-Host "[WinGet] ❌ Error: $($_.Exception.Message)" | |
| $published = "false" | |
| $e2e_result = "skipped" | |
| Set-Outputs -Published $published -E2E $e2e_result | |
| exit 1 | |
| } | |
| Set-Outputs -Published $published -E2E $e2e_result | |
| winget_manifest: | |
| needs: setup | |
| if: needs.setup.outputs.cache-hit != 'true' | |
| runs-on: ubuntu-latest | |
| outputs: | |
| published: ${{ steps.check_manifest.outputs.published }} | |
| manifest_found: ${{ steps.check_manifest_url.outputs.manifest_found }} | |
| continue-on-error: true | |
| steps: | |
| - name: "🌐 Check WinGet Manifest in winget-pkgs repo (API)" | |
| id: check_manifest | |
| shell: bash | |
| env: | |
| RELEASE_VERSION: ${{ needs.setup.outputs.release_version }} | |
| run: | | |
| file_path="manifests/i/IgorMundstein/WinMemoryCleaner/${RELEASE_VERSION}/IgorMundstein.WinMemoryCleaner.installer.yaml" | |
| url="https://api.github.com/repos/microsoft/winget-pkgs/contents/${file_path}" | |
| echo "Checking manifest existence: $url" | |
| if curl --fail -s "$url" > /dev/null; then | |
| echo "published=true" >> $GITHUB_OUTPUT | |
| echo "✅ Published" | |
| else | |
| echo "published=false" >> $GITHUB_OUTPUT | |
| echo "❌ Not Published" | |
| fi | |
| - name: "🌐 Check WinGet Manifest in winget-pkgs repo (raw URL, non-blocking)" | |
| id: check_manifest_url | |
| shell: bash | |
| continue-on-error: true | |
| env: | |
| RELEASE_VERSION: ${{ needs.setup.outputs.release_version }} | |
| run: | | |
| file_path="manifests/i/IgorMundstein/WinMemoryCleaner/${RELEASE_VERSION}/IgorMundstein.WinMemoryCleaner.installer.yaml" | |
| manifest_url="https://raw.githubusercontent.com/microsoft/winget-pkgs/master/${file_path}" | |
| if curl --fail -s "$manifest_url" > /dev/null; then | |
| echo "manifest_found=true" >> $GITHUB_OUTPUT | |
| echo "✅ Published" | |
| else | |
| echo "manifest_found=false" >> $GITHUB_OUTPUT | |
| echo "❌ Not Published" | |
| fi | |
| summarize: | |
| needs: [setup, choco, scoop, winget_cli, winget_manifest] | |
| runs-on: ubuntu-latest | |
| if: always() | |
| steps: | |
| - name: "📝 Publication Summary" | |
| run: | | |
| VERSION="${{ needs.setup.outputs.release_version }}" | |
| echo "### Publication Verification for $VERSION" >> $GITHUB_STEP_SUMMARY | |
| echo "| Package Manager | Publication Status | E2 Test |" >> $GITHUB_STEP_SUMMARY | |
| echo "|--------------------------|------------------------|----------------|" >> $GITHUB_STEP_SUMMARY | |
| CH_PUB="${{ needs.choco.outputs.published }}" | |
| CH_E2E="${{ needs.choco.outputs.e2e }}" | |
| if [ "$CH_PUB" == "true" ]; then CH_STATUS="✅ Published"; else CH_STATUS="❌ Not Published"; fi | |
| if [ "$CH_E2E" == "success" ]; then CH_E2E_STATUS="✅ Passed"; elif [ "$CH_PUB" == "true" ]; then CH_E2E_STATUS="❌ Failed"; else CH_E2E_STATUS="⚠️ Skipped"; fi | |
| echo "| 🍫 Chocolatey | $CH_STATUS | $CH_E2E_STATUS |" >> $GITHUB_STEP_SUMMARY | |
| SC_PUB="${{ needs.scoop.outputs.published }}" | |
| SC_E2E="${{ needs.scoop.outputs.e2e }}" | |
| if [ "$SC_PUB" == "true" ]; then SC_STATUS="✅ Published"; else SC_STATUS="❌ Not Published"; fi | |
| if [ "$SC_E2E" == "success" ]; then SC_E2E_STATUS="✅ Passed"; elif [ "$SC_PUB" == "true" ]; then SC_E2E_STATUS="❌ Failed"; else SC_E2E_STATUS="⚠️ Skipped"; fi | |
| echo "| 🍦 Scoop (Extras) | $SC_STATUS | $SC_E2E_STATUS |" >> $GITHUB_STEP_SUMMARY | |
| WG_CLI_FOUND="${{ needs.winget_cli.outputs.found }}" | |
| WG_CLI_PUB="${{ needs.winget_cli.outputs.published }}" | |
| if [ "$WG_CLI_FOUND" == "true" ]; then | |
| if [ "$WG_CLI_PUB" == "true" ]; then WG_CLI_STATUS="✅ Published"; else WG_CLI_STATUS="❌ Not Published"; fi | |
| WG_CLI_E2E_RAW="${{ needs.winget_cli.outputs.e2e }}" | |
| if [ "$WG_CLI_E2E_RAW" == "success" ]; then WG_CLI_E2E_STATUS="✅ Passed" | |
| elif [ "$WG_CLI_PUB" == "true" ]; then WG_CLI_E2E_STATUS="❌ Failed" | |
| else WG_CLI_E2E_STATUS="⚠️ Skipped"; fi | |
| else | |
| WG_CLI_STATUS="⚠️ Not Installed" | |
| WG_CLI_E2E_STATUS="N/A" | |
| fi | |
| echo "| 📦 WinGet CLI | $WG_CLI_STATUS | $WG_CLI_E2E_STATUS |" >> $GITHUB_STEP_SUMMARY | |
| WG_MANIFEST_PUB="${{ needs.winget_manifest.outputs.published }}" | |
| if [ "$WG_MANIFEST_PUB" == "true" ]; then | |
| WG_MANIFEST_STATUS="✅ Published" | |
| else | |
| WG_MANIFEST_STATUS="❌ Not Published" | |
| fi | |
| echo "| 🌐 WinGet Manifest (API) | $WG_MANIFEST_STATUS | N/A |" >> $GITHUB_STEP_SUMMARY | |
| WG_MANIFEST_FOUND="${{ needs.winget_manifest.outputs.manifest_found }}" | |
| if [ "$WG_MANIFEST_FOUND" == "true" ]; then | |
| WG_MANIFEST_RAW_STATUS="✅ Published" | |
| else | |
| WG_MANIFEST_RAW_STATUS="❌ Not Published" | |
| fi | |
| echo "| 🌐 WinGet Manifest (RAW) | $WG_MANIFEST_RAW_STATUS| N/A |" >> $GITHUB_STEP_SUMMARY | |
| echo "" | |
| echo "_WinGet CLI test is authoritative; manifest checks are informational only._" >> $GITHUB_STEP_SUMMARY | |
| - name: "Set cache flag if all published" | |
| id: allpublished | |
| run: | | |
| ALL_PUBLISHED=true | |
| if [ "${{ needs.choco.outputs.published }}" != "true" ]; then ALL_PUBLISHED=false; fi | |
| if [ "${{ needs.scoop.outputs.published }}" != "true" ]; then ALL_PUBLISHED=false; fi | |
| if [ "${{ needs.winget_cli.outputs.published }}" != "true" ]; then ALL_PUBLISHED=false; fi | |
| if [ "${{ needs.winget_manifest.outputs.published }}" != "true" ]; then ALL_PUBLISHED=false; fi | |
| echo "ALL_PUBLISHED=$ALL_PUBLISHED" >> $GITHUB_ENV | |
| if [ "$ALL_PUBLISHED" = "true" ]; then | |
| mkdir -p .cache | |
| echo "all-published" > .cache/success.flag | |
| echo "All package managers published. Cache will be saved." | |
| else | |
| echo "Not all package managers published. Cache will NOT be saved." | |
| fi | |
| - name: "💾 Save Success Flag to Cache" | |
| if: env.ALL_PUBLISHED == 'true' | |
| uses: actions/cache/save@v4 | |
| with: | |
| path: ./.cache | |
| key: ${{ needs.setup.outputs.cache-key }} |