From 405fec28a1b28906563ed0773ea3a45f625ba932 Mon Sep 17 00:00:00 2001 From: Thales <> Date: Thu, 20 Aug 2026 22:12:16 +0100 Subject: [PATCH] Revert "ci: sign the Windows release executable with SignPath (#394)" This reverts commit 7eba6340efb701aecd56b0e9016694b97969f4cb. --- .github/workflows/windows-release.yml | 120 ++---------------- .../policies/stemdeck/release-signing.yml | 9 -- README.md | 33 ----- packaging/windows/README-WINDOWS.txt | 7 - scripts/windows/make-portable.ps1 | 70 +++------- 5 files changed, 29 insertions(+), 210 deletions(-) delete mode 100644 .signpath/policies/stemdeck/release-signing.yml diff --git a/.github/workflows/windows-release.yml b/.github/workflows/windows-release.yml index 409674da..b9ec7fef 100644 --- a/.github/workflows/windows-release.yml +++ b/.github/workflows/windows-release.yml @@ -3,14 +3,6 @@ name: Windows Release on: release: types: [published] - # Dry-run entry point: builds and signs without publishing anything, so the SignPath - # integration can be exercised against the test-signing policy before a real tag is cut. - workflow_dispatch: - inputs: - signing_policy: - description: SignPath signing policy slug to submit against - required: false - default: test-signing permissions: {} @@ -18,111 +10,33 @@ concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true -env: - # Source the tag from the github context (evaluated by Actions) rather than - # $env:GITHUB_REF_NAME, which is only injected by runner >= 2.290. Keeps the - # build working on older self-hosted runners. (#212 follow-up) - # workflow_dispatch runs off a branch, so there is no vX.Y.Z tag to derive a version from. - PACKAGE_VERSION: ${{ github.event_name == 'release' && github.ref_name || '0.0.0-dev' }} - jobs: - # SignPath's OSS tier requires every job leading up to a signing request to run on a - # GitHub-hosted agent, so the executable is built here rather than on the self-hosted runner - # that packages it. This job builds nothing but StemDeck.exe; the heavy Python/torch bundling - # stays downstream. - sign-exe: - runs-on: windows-latest - timeout-minutes: 45 - permissions: - contents: read - actions: read # the SignPath action reads job details and downloads the artifact - defaults: - run: - shell: powershell - steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - - # Must run before the build: SignPath restricts Foundation projects on PE metadata, and - # Tauri stamps ProductName/FileVersion into the executable from these files. - - name: write version files - run: | - $version = $env:PACKAGE_VERSION -replace '^v', '' - if (-not $version) { throw "PACKAGE_VERSION is not set" } - (Get-Content "desktop/src-tauri/Cargo.toml") -replace '^version = ".*"', "version = `"$version`"" | - Set-Content "desktop/src-tauri/Cargo.toml" - (Get-Content "desktop/src-tauri/tauri.conf.json") -replace '"version": "[^"]*"', "`"version`": `"$version`"" | - Set-Content "desktop/src-tauri/tauri.conf.json" - Write-Host "Building StemDeck.exe version $version" - - - name: build executable - working-directory: desktop - env: - CI: "true" # Tauri only accepts true/false here - run: | - rustup default stable - npm ci --include=dev - node node_modules/@tauri-apps/cli/tauri.js build - - # The SignPath connector only accepts artifacts stored on the GitHub server. - - name: stage unsigned executable - run: | - New-Item -ItemType Directory -Force unsigned | Out-Null - Copy-Item -Force desktop/src-tauri/target/release/stemdeck.exe unsigned/ - - - name: upload unsigned executable - id: upload-unsigned - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 - with: - name: stemdeck-exe-unsigned - path: unsigned/stemdeck.exe - - - name: submit signing request - uses: signpath/github-action-submit-signing-request@c92b958760219087e01f8d67a1669ed57afe2627 # v2.3 - with: - api-token: ${{ secrets.SIGNPATH_API_TOKEN }} - organization-id: ${{ vars.SIGNPATH_ORGANIZATION_ID }} - project-slug: stemdeck - signing-policy-slug: ${{ github.event_name == 'release' && 'release-signing' || inputs.signing_policy }} - github-artifact-id: ${{ steps.upload-unsigned.outputs.artifact-id }} - wait-for-completion: true - # Release signing needs a human approval in the SignPath console; the 600s default - # expires long before an approver is likely to see the mail. - wait-for-completion-timeout-in-seconds: 3600 - output-artifact-directory: signed - - - name: upload signed executable - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 - with: - name: stemdeck-exe-signed - path: signed/stemdeck.exe - build-and-upload: - needs: sign-exe # Runner must be windows/x64 with PowerShell, Docker, and rustup. runs-on: [self-hosted, windows, x64] timeout-minutes: 90 permissions: contents: write + # Source the tag from the github context (evaluated by Actions) rather than + # $env:GITHUB_REF_NAME, which is only injected by runner >= 2.290. Keeps the + # build working on older self-hosted runners. (#212 follow-up) + env: + REF_NAME: ${{ github.ref_name }} defaults: run: shell: powershell steps: - name: clean workspace run: | - Remove-Item -Recurse -Force .build, dist, signed -ErrorAction SilentlyContinue + Remove-Item -Recurse -Force .build, dist -ErrorAction SilentlyContinue - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - - name: download signed executable - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 - with: - name: stemdeck-exe-signed - path: signed - - name: write version files run: | - $version = $env:PACKAGE_VERSION -replace '^v', '' - if (-not $version) { throw "PACKAGE_VERSION is not set" } + $tag = $env:REF_NAME + if (-not $tag) { throw "REF_NAME is not set" } + $version = $tag -replace '^v', '' $json = "{`"version`": `"$version`"}" Set-Content -Path "static/version.json" -Value $json -Encoding UTF8 (Get-Content "desktop/src-tauri/Cargo.toml") -replace '^version = ".*"', "version = `"$version`"" | @@ -135,22 +49,18 @@ jobs: Set-Content "desktop/package.json" Write-Host "Wrote version $version to all version files" - # Both variants package the one signed executable from sign-exe, so the Rust build is not - # repeated here. - name: build Windows NVIDIA run: | powershell -NoProfile -ExecutionPolicy Bypass -File scripts/windows/make-portable.ps1 ` -PackageName StemDeck-Windows-x64.NVIDIA ` - -PackageVersion "$env:PACKAGE_VERSION" ` - -PrebuiltExe "$PWD\signed\stemdeck.exe" ` + -PackageVersion "$env:REF_NAME" ` -StripVenv - name: build Windows CPU run: | powershell -NoProfile -ExecutionPolicy Bypass -File scripts/windows/make-portable.ps1 ` -PackageName StemDeck-Windows-x64 ` - -PackageVersion "$env:PACKAGE_VERSION" ` - -PrebuiltExe "$PWD\signed\stemdeck.exe" ` + -PackageVersion "$env:REF_NAME" ` -CpuOnly ` -StripVenv @@ -172,13 +82,6 @@ jobs: Write-Host " $($hash.Hash) $($_.Name)" } - Write-Host "Authenticode signatures on the packaged executables:" - Get-ChildItem -Path "dist" -Filter "StemDeck.exe" -Recurse -File | - ForEach-Object { - $sig = Get-AuthenticodeSignature -LiteralPath $_.FullName - Write-Host " $($_.FullName): $($sig.Status) $($sig.SignerCertificate.Subject)" - } - Write-Host "Pulling latest ClamAV scanner image..." docker pull clamav/clamav:latest @@ -191,7 +94,6 @@ jobs: Write-Host "ClamAV scan completed successfully. No infected files reported." - name: upload artifacts - if: github.event_name == 'release' uses: softprops/action-gh-release@3d0d9888cb7fd7b750713d6e236d1fcb99157228 # v3.0.2 with: files: | diff --git a/.signpath/policies/stemdeck/release-signing.yml b/.signpath/policies/stemdeck/release-signing.yml deleted file mode 100644 index d4e607d0..00000000 --- a/.signpath/policies/stemdeck/release-signing.yml +++ /dev/null @@ -1,9 +0,0 @@ -# Build policy enforced by SignPath on every signing request submitted against the -# "release-signing" policy of the "stemdeck" project. -# Reference: https://docs.signpath.io/trusted-build-systems/github -github-policies: - build: - # A failed build must not be re-run just to obtain a signature. - disallow_reruns: true - # Branch rulesets are intentionally left out until the repository has matching rulesets - # configured; SignPath rejects requests whose repository does not satisfy them. diff --git a/README.md b/README.md index 1b9ede17..4f5ba90a 100644 --- a/README.md +++ b/README.md @@ -402,39 +402,6 @@ The author(s) of StemDeck provide this software "as is", without warranty of any --- -## Code signing policy - -Windows releases of StemDeck are Authenticode-signed. Free code signing is provided by -[SignPath.io](https://signpath.io/), certificate by [SignPath Foundation](https://signpath.org/). - -The signed binary is `StemDeck.exe` inside `StemDeck-Windows-x64.zip` and -`StemDeck-Windows-x64.NVIDIA.zip`. The zip archives themselves are not signed; verify them with -the `.sha256` file published alongside each release asset. macOS and Linux builds are not -currently signed. - -**Team roles** - -| Role | Members | -|---|---| -| Committers | Thales Pereira ([@thcp](https://github.com/thcp)) | -| Reviewers | Thales Pereira ([@thcp](https://github.com/thcp)) | -| Approvers | Thales Pereira ([@thcp](https://github.com/thcp)) | - -**How signing works** - -The executable is built from this repository by the -[Windows Release workflow](.github/workflows/windows-release.yml) on a GitHub-hosted runner, -uploaded as a workflow artifact, and submitted to SignPath for signing. SignPath verifies the -build's origin (repository, branch, commit, and build job) before the certificate is applied. -Every release signing request is approved manually by an approver listed above. - -**Privacy** - -StemDeck does not transmit any personal data. All audio processing happens on the user's own -machine. See the [Disclaimer](#disclaimer) for details. - ---- - ## Community | Platform | Link | diff --git a/packaging/windows/README-WINDOWS.txt b/packaging/windows/README-WINDOWS.txt index 09098635..968658be 100644 --- a/packaging/windows/README-WINDOWS.txt +++ b/packaging/windows/README-WINDOWS.txt @@ -8,13 +8,6 @@ Run 2. Double-click StemDeck.exe. 3. Let first-run setup prepare local runtime assets. -Code signing ------------- - -StemDeck.exe is Authenticode-signed. Free code signing provided by SignPath.io, -certificate by SignPath Foundation. The zip itself is not signed; verify it with -the .sha256 file published next to it on the release page. - Notes ----- diff --git a/scripts/windows/make-portable.ps1 b/scripts/windows/make-portable.ps1 index 25c7673e..0111b472 100644 --- a/scripts/windows/make-portable.ps1 +++ b/scripts/windows/make-portable.ps1 @@ -4,7 +4,6 @@ param( [string]$PackageName = "StemDeck-Windows-x64", [string]$PackageVersion, [switch]$SkipTauriBuild, - [string]$PrebuiltExe, [switch]$CpuOnly, [switch]$StripVenv ) @@ -28,19 +27,6 @@ $DesktopDir = Join-Path $Root "desktop" $TauriDir = Join-Path $DesktopDir "src-tauri" $TargetExe = Join-Path $TauriDir "target\$Configuration\stemdeck.exe" -# -PrebuiltExe packages an executable produced (and signed) by an earlier job instead of -# building one here. The release pipeline uses it so both the CPU and NVIDIA packages ship the -# one SignPath-signed binary rather than rebuilding Rust twice. -if ($PrebuiltExe) { - if ($SkipTauriBuild) { - throw "-PrebuiltExe and -SkipTauriBuild are mutually exclusive." - } - if (-not (Test-Path -LiteralPath $PrebuiltExe)) { - throw "Prebuilt executable not found: $PrebuiltExe" - } - $TargetExe = (Resolve-Path -LiteralPath $PrebuiltExe).Path -} - function Require-Command([string]$Name) { if (-not (Get-Command $Name -ErrorAction SilentlyContinue)) { throw "Required command not found on PATH: $Name" @@ -158,11 +144,9 @@ Remove -SkipTauriBuild or run the NVIDIA package build first so the CPU package } } -if (-not $PrebuiltExe) { - Require-Command "node" - Require-Command "npm" - Require-Command "cargo" -} +Require-Command "node" +Require-Command "npm" +Require-Command "cargo" if (-not (Get-Command "py" -ErrorAction SilentlyContinue) -and -not (Get-Command "python" -ErrorAction SilentlyContinue)) { throw "Python launcher not found. Install Python 3.12 on the Windows build agent." @@ -253,47 +237,29 @@ if ($StripVenv) { Remove-Item -Force } -if (-not $PrebuiltExe) { - Push-Location $DesktopDir - try { - if (Test-Path "package-lock.json") { - npm ci --include=dev - } else { - npm install --include=dev - } +Push-Location $DesktopDir +try { + if (Test-Path "package-lock.json") { + npm ci --include=dev + } else { + npm install --include=dev + } - if (-not $SkipTauriBuild) { - $env:CI = "true" # Woodpecker sets CI=woodpecker; Tauri only accepts true/false - rustup default stable - Invoke-TauriBuild - } else { - Assert-Fresh-TauriBuild - } - } finally { - Pop-Location + if (-not $SkipTauriBuild) { + $env:CI = "true" # Woodpecker sets CI=woodpecker; Tauri only accepts true/false + rustup default stable + Invoke-TauriBuild + } else { + Assert-Fresh-TauriBuild } +} finally { + Pop-Location } if (-not (Test-Path $TargetExe)) { throw "Tauri executable not found at $TargetExe" } -if ($PrebuiltExe) { - # Fail loudly if the signing job handed back an unsigned binary. A test-signing policy uses a - # self-signed certificate, so anything other than NotSigned is accepted here and only warned - # about; the release certificate yields Valid. - $Signature = Get-AuthenticodeSignature -LiteralPath $TargetExe - if ($Signature.Status -eq "NotSigned") { - throw "Prebuilt executable carries no Authenticode signature: $TargetExe" - } - if ($Signature.Status -ne "Valid") { - Write-Warning "Authenticode status is $($Signature.Status) (expected for a test-signing certificate)." - } - if ($Signature.SignerCertificate) { - Write-Host "Signed by : $($Signature.SignerCertificate.Subject)" - } -} - Copy-Item -Force $TargetExe (Join-Path $Stage "StemDeck.exe") Compress-Archive -Path (Join-Path $Stage "*") -DestinationPath $ZipPath -Force