Skip to content

Verify and Test Packages #115

Verify and Test Packages

Verify and Test Packages #115

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 }}