From c784dfcc916874347d435e69828f7f508d2bd42a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Flor=C3=AAncio?= Date: Thu, 16 Jul 2026 12:31:31 +0100 Subject: [PATCH] fix(release): use Bash 3.2-safe artifact verifier (#1730) --- .../scripts/release-workflow-contract_test.py | 13 +++++++++ .github/workflows/release.yml | 28 +++++++++++++++++-- docs/desktop-tauri-signing.md | 2 +- scripts/release-desktop.test.sh | 26 +++++++++++++++++ scripts/release/verify-desktop-assets.sh | 9 ++---- 5 files changed, 69 insertions(+), 9 deletions(-) diff --git a/.github/scripts/release-workflow-contract_test.py b/.github/scripts/release-workflow-contract_test.py index 12cc40b4e3..b3a8ae4f1e 100644 --- a/.github/scripts/release-workflow-contract_test.py +++ b/.github/scripts/release-workflow-contract_test.py @@ -101,6 +101,19 @@ class ReleaseWorkflowContractTest(unittest.TestCase): self.assertIn('bash "$helper"', detect) self.assertNotIn("bash scripts/release/updater-signing-ready.sh", detect) + def test_desktop_asset_validation_uses_workflow_control_revision(self) -> None: + for job in ("build-desktop", "publish-release"): + block = job_block(job) + self.assertIn("ref: ${{ needs.prepare.outputs.ref }}", block) + self.assertIn("GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}", block) + self.assertIn( + "scripts/release/verify-desktop-assets.sh?ref=${{ github.workflow_sha }}", + block, + ) + self.assertIn("DESKTOP_ASSET_VERIFIER=$helper", block) + self.assertIn('"$DESKTOP_ASSET_VERIFIER"', block) + self.assertNotIn('scripts/release/verify-desktop-assets.sh "', block) + 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) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1b638b5249..4c74bd1d25 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -585,6 +585,18 @@ jobs: ref: ${{ needs.prepare.outputs.ref }} persist-credentials: false + - name: Prepare desktop asset verifier + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + set -euo pipefail + helper="$RUNNER_TEMP/verify-desktop-assets.sh" + gh api \ + --header "Accept: application/vnd.github.raw+json" \ + "repos/${{ github.repository }}/contents/scripts/release/verify-desktop-assets.sh?ref=${{ github.workflow_sha }}" \ + > "$helper" + echo "DESKTOP_ASSET_VERIFIER=$helper" >> "$GITHUB_ENV" + - name: Prepare macOS desktop diagnostics helper if: startsWith(matrix.platform, 'macos-') continue-on-error: true @@ -1056,7 +1068,7 @@ jobs: exit 1 fi - scripts/release/verify-desktop-assets.sh "$out_dir" "${{ matrix.platform }}" + bash "$DESKTOP_ASSET_VERIFIER" "$out_dir" "${{ matrix.platform }}" ls -lh "$out_dir" - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5 @@ -1427,6 +1439,18 @@ jobs: ref: ${{ needs.prepare.outputs.ref }} fetch-depth: 0 + - name: Prepare desktop asset verifier + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + set -euo pipefail + helper="$RUNNER_TEMP/verify-desktop-assets.sh" + gh api \ + --header "Accept: application/vnd.github.raw+json" \ + "repos/${{ github.repository }}/contents/scripts/release/verify-desktop-assets.sh?ref=${{ github.workflow_sha }}" \ + > "$helper" + echo "DESKTOP_ASSET_VERIFIER=$helper" >> "$GITHUB_ENV" + - name: Install updater signature verifier toolchain run: rustup toolchain install 1.88.0 --profile minimal @@ -1508,7 +1532,7 @@ jobs: exit 1 fi done - scripts/release/verify-desktop-assets.sh dist/release-assets + bash "$DESKTOP_ASSET_VERIFIER" dist/release-assets ls -lh dist/release-assets - name: Generate and verify updater manifest diff --git a/docs/desktop-tauri-signing.md b/docs/desktop-tauri-signing.md index 45870badf5..55ed4a9f29 100644 --- a/docs/desktop-tauri-signing.md +++ b/docs/desktop-tauri-signing.md @@ -6,7 +6,7 @@ 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. +Release backfills build application code and artifacts from the requested immutable tag. Updater signing-readiness and desktop artifact validation are control-plane logic and run 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 diff --git a/scripts/release-desktop.test.sh b/scripts/release-desktop.test.sh index b894c60fcc..44947879d6 100644 --- a/scripts/release-desktop.test.sh +++ b/scripts/release-desktop.test.sh @@ -162,6 +162,32 @@ printf 'desktop artifact\n' > "$artifact" "$ROOT_DIR/scripts/release/verify-desktop-assets.sh" "$assets_dir" linux-x64 >/dev/null pass "verify-desktop-assets accepts matching checksums" +macos_assets_dir="$TMP_DIR/macos-assets" +mkdir -p "$macos_assets_dir" +macos_artifact="$macos_assets_dir/kandev-desktop-macos-arm64-Kandev.dmg" +printf 'unsigned macOS artifact\n' > "$macos_artifact" +"$ROOT_DIR/scripts/release/write-sha256.sh" \ + "$macos_artifact" "$macos_artifact.sha256" +"$ROOT_DIR/scripts/release/verify-desktop-assets.sh" \ + "$macos_assets_dir" macos-arm64 >/dev/null +pass "verify-desktop-assets accepts macOS artifacts without optional updater bundles" + +missing_assets_dir="$TMP_DIR/missing-assets" +mkdir -p "$missing_assets_dir" +if "$ROOT_DIR/scripts/release/verify-desktop-assets.sh" \ + "$missing_assets_dir" macos-arm64 >"$OUT_FILE" 2>"$ERR_FILE"; then + fail "verify-desktop-assets should reject a missing platform artifact" +fi +grep -q "Missing desktop artifact for platform: macos-arm64" "$ERR_FILE" || \ + fail "verify-desktop-assets did not explain the missing platform artifact" +pass "verify-desktop-assets reports missing platform artifacts" + +if grep -Eq '(artifacts|updater_bundles|updater_signatures)=\(' \ + "$ROOT_DIR/scripts/release/verify-desktop-assets.sh"; then + fail "verify-desktop-assets uses optional arrays that fail under Bash 3.2 nounset" +fi +pass "verify-desktop-assets avoids Bash 3.2 empty-array nounset failures" + if signing_secret_env "$ROOT_DIR/scripts/release/desktop-signing-ready.sh" macos >"$OUT_FILE" 2>"$ERR_FILE"; then fail "desktop-signing-ready should report macOS signing not ready without secrets" fi diff --git a/scripts/release/verify-desktop-assets.sh b/scripts/release/verify-desktop-assets.sh index d9badeec8a..9f5c133da7 100755 --- a/scripts/release/verify-desktop-assets.sh +++ b/scripts/release/verify-desktop-assets.sh @@ -62,10 +62,9 @@ updater_suffix_for_platform() { } for platform in "${REQUIRED_PLATFORMS[@]}"; do - artifacts=("$ASSETS_DIR"/kandev-desktop-"$platform"-*) found=0 - for artifact in "${artifacts[@]}"; do + for artifact in "$ASSETS_DIR"/kandev-desktop-"$platform"-*; do if [[ "$artifact" == *.sha256 ]]; then continue fi @@ -87,15 +86,13 @@ for platform in "${REQUIRED_PLATFORMS[@]}"; do fi updater_suffix="$(updater_suffix_for_platform "$platform")" - updater_bundles=("$ASSETS_DIR"/kandev-desktop-"$platform"-$updater_suffix) - updater_signatures=("$ASSETS_DIR"/kandev-desktop-"$platform"-$updater_suffix.sig) - for updater_bundle in "${updater_bundles[@]}"; do + for updater_bundle in "$ASSETS_DIR"/kandev-desktop-"$platform"-$updater_suffix; do if [ ! -f "$updater_bundle.sig" ]; then echo "Missing updater signature: $updater_bundle.sig" >&2 exit 1 fi done - for updater_signature in "${updater_signatures[@]}"; do + for updater_signature in "$ASSETS_DIR"/kandev-desktop-"$platform"-$updater_suffix.sig; do updater_bundle="${updater_signature%.sig}" if [ ! -f "$updater_bundle" ]; then echo "Missing updater bundle for signature: $updater_signature" >&2