Legacy Chocolatey publisher (disabled) #5
Workflow file for this run
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: Legacy Chocolatey publisher (disabled) | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| ref: | |
| description: Published RMUX tag, for example v0.9.0 | |
| required: true | |
| type: string | |
| permissions: | |
| contents: read | |
| concurrency: | |
| group: rmux-chocolatey-${{ inputs.ref }} | |
| cancel-in-progress: false | |
| jobs: | |
| publish: | |
| name: Validate and publish Chocolatey package | |
| if: inputs.ref == 'v0.9.1' | |
| runs-on: windows-latest | |
| timeout-minutes: 45 | |
| environment: release | |
| env: | |
| RELEASE_REF: ${{ inputs.ref }} | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 | |
| with: | |
| ref: ${{ env.RELEASE_REF }} | |
| persist-credentials: false | |
| - name: Verify signed tag and public release identity | |
| id: identity | |
| shell: pwsh | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| $ErrorActionPreference = "Stop" | |
| $tag = $env:RELEASE_REF | |
| if ($tag -notmatch '^v(?<version>[0-9]+\.[0-9]+\.[0-9]+)$') { | |
| throw "ref must be a stable RMUX tag such as v0.9.0" | |
| } | |
| $version = $Matches.version | |
| $actualSha = (& git rev-parse HEAD).Trim() | |
| $tagRef = gh api "repos/${{ github.repository }}/git/ref/tags/$tag" | ConvertFrom-Json | |
| if ($tagRef.object.type -ne "tag") { | |
| throw "release tag must be annotated and signed" | |
| } | |
| $tagObject = gh api "repos/${{ github.repository }}/git/tags/$($tagRef.object.sha)" | ConvertFrom-Json | |
| if (-not $tagObject.verification.verified) { | |
| throw "release tag signature is not verified by GitHub" | |
| } | |
| if ($tagObject.object.type -ne "commit" -or $tagObject.object.sha -ne $actualSha) { | |
| throw "signed tag target does not match checked-out source" | |
| } | |
| $release = gh api "repos/${{ github.repository }}/releases/tags/$tag" | ConvertFrom-Json | |
| if ($release.draft -or $release.target_commitish -ne $actualSha) { | |
| throw "public release target does not match the signed tag" | |
| } | |
| $manifest = Get-Content -LiteralPath Cargo.toml -Raw | |
| $workspaceVersion = [regex]::Match( | |
| $manifest, | |
| '(?ms)^\[workspace\.package\]\s*.*?^version\s*=\s*"(?<version>[^"]+)"\s*$' | |
| ).Groups['version'].Value | |
| if ($workspaceVersion -ne $version) { | |
| throw "workspace version $workspaceVersion does not match $tag" | |
| } | |
| "version=$version" | Out-File -FilePath $env:GITHUB_OUTPUT -Append | |
| "source_sha=$actualSha" | Out-File -FilePath $env:GITHUB_OUTPUT -Append | |
| - name: Download canonical release inputs | |
| shell: pwsh | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| $ErrorActionPreference = "Stop" | |
| New-Item -ItemType Directory -Force -Path release-assets | Out-Null | |
| gh release download $env:RELEASE_REF ` | |
| --repo "${{ github.repository }}" ` | |
| --pattern SHA256SUMS ` | |
| --pattern "rmux-${{ steps.identity.outputs.version }}-windows-x86_64.zip" ` | |
| --dir release-assets | |
| - name: Verify canonical Windows archive checksum | |
| shell: pwsh | |
| run: | | |
| $ErrorActionPreference = "Stop" | |
| $version = "${{ steps.identity.outputs.version }}" | |
| $archiveName = "rmux-$version-windows-x86_64.zip" | |
| $archive = Join-Path release-assets $archiveName | |
| $lines = @( | |
| Get-Content -LiteralPath release-assets\SHA256SUMS | | |
| Where-Object { $_ -match "^[0-9a-fA-F]{64}\s+\*?$([regex]::Escape($archiveName))$" } | |
| ) | |
| if ($lines.Count -ne 1) { | |
| throw "SHA256SUMS has no unique entry for $archiveName" | |
| } | |
| $line = $lines[0] | |
| $expected = ($line -split '\s+')[0].ToLowerInvariant() | |
| $actual = (Get-FileHash -Algorithm SHA256 -LiteralPath $archive).Hash.ToLowerInvariant() | |
| if ($actual -ne $expected) { | |
| throw "checksum mismatch for $archiveName" | |
| } | |
| - name: Generate and pack Chocolatey package | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| version='${{ steps.identity.outputs.version }}' | |
| scripts/generate-chocolatey-package.sh \ | |
| --version "$version" \ | |
| --checksums release-assets/SHA256SUMS \ | |
| --output-dir target/chocolatey/rmux | |
| cd target/chocolatey/rmux | |
| choco pack rmux.nuspec --output-directory .. --limit-output | |
| - name: Verify exact receipt payload identity | |
| shell: bash | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| python3 - <<'PY' | |
| import json | |
| import os | |
| import urllib.request | |
| def get(path): | |
| request = urllib.request.Request( | |
| f"https://api.github.com{path}", | |
| headers={ | |
| "Accept": "application/vnd.github+json", | |
| "Authorization": f"Bearer {os.environ['GH_TOKEN']}", | |
| "User-Agent": "rmux-chocolatey-recovery/1", | |
| "X-GitHub-Api-Version": "2022-11-28", | |
| }, | |
| ) | |
| with urllib.request.urlopen(request, timeout=30) as response: | |
| return json.load(response) | |
| source = "fb827cd7adf206995bab274aeafc58ddd09ac5b5" | |
| run = get("/repos/Helvesec/rmux/actions/runs/30112540725") | |
| assert { | |
| "id": run["id"], | |
| "run_attempt": run["run_attempt"], | |
| "workflow_id": run["workflow_id"], | |
| "path": run["path"], | |
| "event": run["event"], | |
| "head_branch": run["head_branch"], | |
| "head_sha": run["head_sha"], | |
| "status": run["status"], | |
| "conclusion": run["conclusion"], | |
| } == { | |
| "id": 30112540725, | |
| "run_attempt": 1, | |
| "workflow_id": 316435347, | |
| "path": ".github/workflows/release-receipt.yml", | |
| "event": "workflow_dispatch", | |
| "head_branch": "v0.9.1", | |
| "head_sha": source, | |
| "status": "completed", | |
| "conclusion": "failure", | |
| } | |
| artifact = get("/repos/Helvesec/rmux/actions/artifacts/8604206888") | |
| assert artifact["id"] == 8604206888 | |
| assert artifact["name"] == ( | |
| "rmux-downstream-chocolatey-payload-" | |
| f"{source}-359441935" | |
| ) | |
| assert artifact["digest"] == ( | |
| "sha256:0fa0195e4139752fbbd2f68e2a015b4f50c759cb7e94bcb34385398c8661134d" | |
| ) | |
| assert artifact["expired"] is False | |
| assert artifact["workflow_run"]["id"] == 30112540725 | |
| assert artifact["workflow_run"]["head_sha"] == source | |
| jobs = get( | |
| "/repos/Helvesec/rmux/actions/runs/30112540725/jobs" | |
| "?per_page=100&filter=latest" | |
| )["jobs"] | |
| producer = [ | |
| job | |
| for job in jobs | |
| if job["name"].endswith( | |
| "Prepare exact downstream payloads / Materialize exact channel payloads" | |
| ) | |
| ] | |
| assert len(producer) == 1 and producer[0]["conclusion"] == "success" | |
| PY | |
| - name: Download exact receipt payload | |
| uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 | |
| with: | |
| artifact-ids: 8604206888 | |
| merge-multiple: true | |
| path: target/chocolatey/expected | |
| github-token: ${{ github.token }} | |
| repository: ${{ github.repository }} | |
| run-id: 30112540725 | |
| - name: Select the exact receipt package | |
| shell: pwsh | |
| run: | | |
| $generated = "target/chocolatey/rmux.0.9.1.nupkg" | |
| $expected = "target/chocolatey/expected/rmux.0.9.1.nupkg" | |
| Remove-Item -LiteralPath $generated -Force | |
| Copy-Item -LiteralPath $expected -Destination $generated | |
| $selectedHash = (Get-FileHash -Algorithm SHA256 -LiteralPath $generated).Hash | |
| $expectedHash = (Get-FileHash -Algorithm SHA256 -LiteralPath $expected).Hash | |
| if ($selectedHash -ne $expectedHash) { throw "Exact Chocolatey package copy failed" } | |
| - name: Install and smoke-test package | |
| shell: pwsh | |
| run: | | |
| $ErrorActionPreference = "Stop" | |
| $version = "${{ steps.identity.outputs.version }}" | |
| $packageRoot = (Resolve-Path target\chocolatey).Path | |
| $package = Join-Path $packageRoot "rmux.$version.nupkg" | |
| if (-not (Test-Path -LiteralPath $package)) { | |
| throw "Chocolatey package was not generated: $package" | |
| } | |
| choco install rmux ` | |
| --source $packageRoot ` | |
| --version $version ` | |
| --yes ` | |
| --no-progress ` | |
| --force | |
| $versionOutput = (& rmux -V).Trim() | |
| if ($versionOutput -ne "rmux $version") { | |
| throw "Unexpected rmux version from Chocolatey install: $versionOutput" | |
| } | |
| .\scripts\smoke-installed-rmux.ps1 ` | |
| -Rmux rmux ` | |
| -SkipDaemon ` | |
| -RequireDaemonCommand | |
| choco uninstall rmux --yes --no-progress | |
| "CHOCOLATEY_PACKAGE=$package" | Out-File -FilePath $env:GITHUB_ENV -Append | |
| - name: Push package once | |
| shell: pwsh | |
| env: | |
| CHOCOLATEY_API_KEY: ${{ secrets.CHOCOLATEY_API_KEY }} | |
| run: | | |
| $ErrorActionPreference = "Stop" | |
| if ([string]::IsNullOrWhiteSpace($env:CHOCOLATEY_API_KEY)) { | |
| throw "Missing required release environment secret: CHOCOLATEY_API_KEY" | |
| } | |
| $version = "${{ steps.identity.outputs.version }}" | |
| $query = "https://community.chocolatey.org/api/v2/FindPackagesById()?id='rmux'" | |
| $response = Invoke-WebRequest -Uri $query -Method Get | |
| [xml]$feed = $response.Content | |
| $namespaces = [System.Xml.XmlNamespaceManager]::new($feed.NameTable) | |
| $namespaces.AddNamespace("atom", "http://www.w3.org/2005/Atom") | |
| $namespaces.AddNamespace("m", "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata") | |
| $namespaces.AddNamespace("d", "http://schemas.microsoft.com/ado/2007/08/dataservices") | |
| $existing = $feed.SelectSingleNode( | |
| "//atom:entry/m:properties[d:Version='$version']/d:Version", | |
| $namespaces | |
| ) | |
| if ($existing) { | |
| Write-Host "Chocolatey package rmux $version already exists; skipping push." | |
| exit 0 | |
| } | |
| choco push $env:CHOCOLATEY_PACKAGE ` | |
| --source "https://push.chocolatey.org/" ` | |
| --api-key $env:CHOCOLATEY_API_KEY ` | |
| --yes ` | |
| --no-progress |