fix(release): validate updater signing from workflow revision (#1726)

This commit is contained in:
Carlos Florêncio
2026-07-16 10:59:13 +01:00
committed by GitHub
parent 486baa8e54
commit 460ec9ff2f
10 changed files with 90 additions and 25 deletions
@@ -81,6 +81,26 @@ class ReleaseWorkflowContractTest(unittest.TestCase):
self.assertIn("if: ${{ !inputs.dry_run", block)
self.assertNotIn("inputs.backfill_tag == ''", block)
def test_updater_signing_validation_uses_workflow_control_revision(self) -> None:
build_desktop = job_block("build-desktop")
self.assertIn("ref: ${{ needs.prepare.outputs.ref }}", build_desktop)
detect = step_block("Detect Tauri updater signing input")
self.assertIn("GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}", detect)
self.assertIn("gh api", detect)
self.assertIn("$RUNNER_TEMP/updater-signing-ready.sh", detect)
self.assertIn(
"scripts/release/updater-signing-ready.sh?ref=${{ github.workflow_sha }}",
detect,
)
self.assertIn('if [ -z "$TAURI_SIGNING_PRIVATE_KEY" ]', detect)
self.assertLess(
detect.index('if [ -z "$TAURI_SIGNING_PRIVATE_KEY" ]'),
detect.index("gh api"),
)
self.assertIn('bash "$helper"', detect)
self.assertNotIn("bash scripts/release/updater-signing-ready.sh", detect)
def test_macos_dmg_build_has_retry_timeout_and_diagnostics(self) -> None:
build = step_block("Build Tauri desktop app")
self.assertIn("timeout-minutes: 70", build)
+7 -1
View File
@@ -772,6 +772,7 @@ jobs:
- name: Detect Tauri updater signing input
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
run: |
set -euo pipefail
@@ -780,7 +781,12 @@ jobs:
echo "::notice::Tauri updater signing key is absent; signed updater artifacts and latest.json will be skipped."
exit 0
fi
if ! bash scripts/release/updater-signing-ready.sh "${{ matrix.platform }}"; then
helper="$RUNNER_TEMP/updater-signing-ready.sh"
gh api \
--header "Accept: application/vnd.github.raw+json" \
"repos/${{ github.repository }}/contents/scripts/release/updater-signing-ready.sh?ref=${{ github.workflow_sha }}" \
> "$helper"
if ! bash "$helper" "${{ matrix.platform }}"; then
echo "::error::Updater signing prerequisites failed for ${{ matrix.platform }}." >&2
exit 1
fi
+1 -1
View File
@@ -365,7 +365,7 @@ describe("release desktop artifacts", () => {
expect(signingDocs).toContain("distinct from macOS and Windows code-signing credentials");
expect(signingDocs).toContain("Installers remain available");
expect(signingDocs).toContain("`latest.json` is not");
expect(workflow).toContain('scripts/release/updater-signing-ready.sh "${{ matrix.platform }}"');
expect(workflow).toContain('bash "$helper" "${{ matrix.platform }}"');
expect(updaterSigningScript).not.toContain("MACOS_SIGNING_ENABLED");
expect(updaterSigningScript).not.toContain("WINDOWS_SIGNING_ENABLED");
expect(signingDocs).toContain("Missing OS credentials do not block Tauri-signed updater");
@@ -2,6 +2,7 @@
**Status:** accepted
**Date:** 2026-07-01
**Amended:** 2026-07-16
**Area:** infra, workflow
## Context
@@ -14,6 +15,8 @@ The first desktop macOS failure mode we observed happened after Rust compilation
Add a maintainer-only release backfill mode to `.github/workflows/release.yml`. The `backfill_tag` input accepts the current highest existing `vX.Y.Z` release tag, verifies that the tag exists, and verifies that all release-critical manifests at that tag match the version. It then uses the existing tag as the build and publish ref. In this mode the workflow skips version bumping, changelog generation, release PR creation, and tag creation, while still running the build, GitHub release, npm publish, and Homebrew update jobs.
Keep artifact and application inputs pinned to that immutable release tag, but source release control-plane helpers from `github.workflow_sha`. This allows a corrected workflow revision to recover a broken tag without changing the tag or silently rebuilding application source from main.
Add macOS desktop packaging guardrails to the Tauri build job. macOS DMG builds now run with a bounded timeout and a retry, and failed macOS desktop builds upload diagnostics that include disk state, `hdiutil info`, related packaging processes, bundle directory listings, and Tauri's generated `bundle_dmg.sh` when present.
Keep desktop installer builds on native OS runners. Linux container images remain useful for Linux CI and e2e dependency stability, but they are not the mechanism for macOS DMG recovery.
@@ -24,6 +27,8 @@ Release recovery becomes explicit and repeatable for tag-only or partially publi
The release workflow has more branching logic, and maintainers must choose between normal release, desktop validation, dry run, and tag backfill modes intentionally. The workflow contract is covered by a small CI test so later YAML changes do not silently remove the recovery path or macOS diagnostics.
Backfills therefore combine immutable tagged application inputs with current workflow control logic. Control-plane helper changes must remain compatible with the tagged inputs they validate.
macOS package failures should now leave actionable artifacts instead of only the final Tauri command line. The retry may add time to a failing macOS job, but the step-level timeout caps total time.
## Alternatives Considered
+1 -1
View File
@@ -34,7 +34,7 @@ Read individual ADRs for full context. Create new ones via `/record decision` or
| 0026 | [Tauri desktop shell over native runtime](0026-tauri-desktop-shell.md) | accepted (amended 2026-07-15) | frontend, backend, cli, infra | 2026-06-23 |
| 0027 | [Replayable schema migrations across SQLite and Postgres](0027-replayable-schema-migrations.md) | accepted | backend | 2026-06-24 |
| 0028 | [Backend-owned task-create last-used preferences](0028-task-create-last-used-source-of-truth.md) | accepted (amended by 0041) | backend, frontend | 2026-06-29 |
| 0029 | [Release backfill and desktop diagnostics](0029-release-backfill-and-desktop-diagnostics.md) | accepted | infra, workflow | 2026-07-01 |
| 0029 | [Release backfill and desktop diagnostics](0029-release-backfill-and-desktop-diagnostics.md) | accepted (amended 2026-07-16) | infra, workflow | 2026-07-01 |
| 0030 | [Workspace-scoped integration settings](0030-workspace-scoped-integration-settings.md) | accepted | backend, frontend | 2026-07-01 |
| 0031 | [Office skill reference files](0031-office-skill-reference-files.md) | accepted | backend | 2026-07-06 |
| 0032 | [Configurable worktree branch names](0032-configurable-worktree-branch-names.md) | accepted | backend, frontend | 2026-07-07 |
+2
View File
@@ -6,6 +6,8 @@ Unsigned desktop artifacts may require manual OS security bypasses and must not
Use `desktop_validation_only=true` for maintainer inspection builds. That mode uploads workflow artifacts but does not publish a GitHub release, npm packages, Homebrew updates, or public container tags.
Release backfills build application code and artifacts from the requested immutable tag. Updater signing-readiness validation is control-plane logic and runs from `github.workflow_sha`, so a workflow fix on main can recover an existing tag without moving it or substituting main-branch application source.
## macOS
Signing inputs:
+1
View File
@@ -225,4 +225,5 @@ fi
grep -q "Checksum verification failed" "$ERR_FILE" || fail "verify-desktop-assets did not explain checksum mismatch"
pass "verify-desktop-assets rejects checksum mismatches"
bash "$ROOT_DIR/scripts/release/updater-signing-ready.test.sh"
bash "$ROOT_DIR/scripts/release/updater-manifest.test.sh"
-21
View File
@@ -3,7 +3,6 @@ set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
SCRIPT="$ROOT_DIR/scripts/release/updater-manifest.mjs"
SIGNING_READY_SCRIPT="$ROOT_DIR/scripts/release/updater-signing-ready.sh"
TMP_DIR="$(mktemp -d)"
trap 'rm -rf "$TMP_DIR"' EXIT
@@ -224,23 +223,3 @@ if generate_manifest "$partial_assets" --allow-unsigned >"$TMP_DIR/out" 2>"$TMP_
fi
grep -q "Incomplete updater artifact set" "$TMP_DIR/err" || fail "partial set error was not actionable"
pass "partial signed updater feeds fail closed"
TAURI_SIGNING_PRIVATE_KEY=test-key MACOS_SIGNING_ENABLED=false \
bash "$SIGNING_READY_SCRIPT" macos-arm64 >/dev/null
pass "macOS updater artifacts require only the updater key"
TAURI_SIGNING_PRIVATE_KEY=test-key WINDOWS_SIGNING_ENABLED=false \
bash "$SIGNING_READY_SCRIPT" windows-x64 >/dev/null
pass "Windows updater artifacts require only the updater key"
TAURI_SIGNING_PRIVATE_KEY=test-key MACOS_SIGNING_ENABLED=false WINDOWS_SIGNING_ENABLED=false \
bash "$SIGNING_READY_SCRIPT" linux-arm64 >/dev/null
pass "Linux updater artifacts require only the updater key"
if env -u TAURI_SIGNING_PRIVATE_KEY bash "$SIGNING_READY_SCRIPT" macos-arm64 \
>"$TMP_DIR/out" 2>"$TMP_DIR/err"; then
fail "updater artifacts should require the Tauri updater key on every platform"
fi
grep -q "require TAURI_SIGNING_PRIVATE_KEY" "$TMP_DIR/err" || \
fail "missing updater key failure was not actionable"
pass "every updater artifact requires the Tauri updater key"
+2 -1
View File
@@ -10,9 +10,10 @@ if [ -z "${TAURI_SIGNING_PRIVATE_KEY:-}" ]; then
fi
case "$platform" in
macos-aarch64 | macos-x86_64 | windows-x86_64 | linux-x86_64 | linux-aarch64) ;;
macos-arm64 | macos-x64 | linux-x64 | linux-arm64 | windows-x64) ;;
*)
echo "Unsupported updater platform: $platform" >&2
echo "Expected one of: macos-arm64, macos-x64, linux-x64, linux-arm64, windows-x64" >&2
exit 1
;;
esac
+51
View File
@@ -0,0 +1,51 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
SCRIPT="$ROOT_DIR/scripts/release/updater-signing-ready.sh"
TMP_DIR="$(mktemp -d)"
trap 'rm -rf "$TMP_DIR"' EXIT
fail() {
echo "FAIL: $*" >&2
exit 1
}
valid_platforms=(macos-arm64 macos-x64 linux-x64 linux-arm64 windows-x64)
for platform in "${valid_platforms[@]}"; do
TAURI_SIGNING_PRIVATE_KEY=test-key \
MACOS_SIGNING_ENABLED=false \
WINDOWS_SIGNING_ENABLED=false \
bash "$SCRIPT" "$platform" >"$TMP_DIR/out" 2>"$TMP_DIR/err" || \
fail "updater signing readiness rejected release platform $platform"
grep -q "complete for $platform" "$TMP_DIR/out" || \
fail "updater signing readiness did not confirm $platform"
done
unsupported_platforms=(
macos-aarch64
macos-x86_64
linux-x86_64
linux-aarch64
windows-x86_64
)
misspelled_platforms=(linux-x65)
for platform in "${unsupported_platforms[@]}" "${misspelled_platforms[@]}"; do
if TAURI_SIGNING_PRIVATE_KEY=test-key bash "$SCRIPT" "$platform" \
>"$TMP_DIR/out" 2>"$TMP_DIR/err"; then
fail "updater signing readiness accepted unsupported platform $platform"
fi
grep -q "Unsupported updater platform: $platform" "$TMP_DIR/err" || \
fail "unsupported platform error did not identify $platform"
grep -q "macos-arm64, macos-x64, linux-x64, linux-arm64, windows-x64" \
"$TMP_DIR/err" || fail "unsupported platform error did not list accepted values"
done
if env -u TAURI_SIGNING_PRIVATE_KEY bash "$SCRIPT" linux-x64 \
>"$TMP_DIR/out" 2>"$TMP_DIR/err"; then
fail "updater signing readiness accepted a missing updater key"
fi
grep -q "require TAURI_SIGNING_PRIVATE_KEY" "$TMP_DIR/err" || \
fail "missing updater key error was not actionable"
echo "PASS: updater signing readiness accepts the release matrix and only requires the updater key"