From 51b96625fcc44144b4c90ab806ec32691728c765 Mon Sep 17 00:00:00 2001 From: Algis Dumbris Date: Thu, 24 Sep 2026 18:46:00 +0300 Subject: [PATCH] fix(release): attach Sparkle enclosures in retry-sign-release v0.69.0 was published by retry-sign-release.yml, whose release job never downloaded the sparkle-enclosure-* artifacts. The appcast feeds went live pointing at mcpproxy-0.69.0-darwin-{arm64,amd64}.app.zip, which 404'd, so every tray auto-update failed with "couldn't download the update". - release job: download sparkle-enclosure-* (MIN_ASSETS 22 -> 24) - sparkle-appcast: upload the enclosures together with the feeds, so a post_release_only run can't publish a feed without its payload - both workflows: live-feed verification now also checks each feed's enclosure URL is downloadable Co-Authored-By: Claude Opus 5.5 --- .github/workflows/release.yml | 11 ++++++++ .github/workflows/retry-sign-release.yml | 35 +++++++++++++++++++++--- 2 files changed, 42 insertions(+), 4 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3123a03f5..6daab24a5 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1833,6 +1833,17 @@ jobs: FAILED=1 fi done + # A fresh feed is useless if its enclosure 404s — Sparkle then fails + # every update with "couldn't download the update" (v0.69.0). + for f in appcast-out/*.xml; do + URL=$(sed -n 's/.*enclosure url="\([^"]*\)".*/\1/p' "$f" | head -1) + if [ -z "$URL" ] || ! curl -fsIL -o /dev/null "$URL"; then + echo "::error::$(basename "$f") enclosure is not downloadable: ${URL:-}" + FAILED=1 + else + echo "✅ $(basename "$f") enclosure downloadable: ${URL}" + fi + done exit "$FAILED" # Open the PR that deletes .github/RELEASE_NOTICE.md once this release has diff --git a/.github/workflows/retry-sign-release.yml b/.github/workflows/retry-sign-release.yml index 5958f4109..2936184a1 100644 --- a/.github/workflows/retry-sign-release.yml +++ b/.github/workflows/retry-sign-release.yml @@ -164,6 +164,16 @@ jobs: --dir dist \ --pattern 'installers-*' || echo "No macOS installer artifacts found" + # Sparkle update enclosures: mcpproxy-X.Y.Z-darwin-{arch}.app.zip + # (artifact name pattern: sparkle-enclosure-{arch}). release.yml gets + # these for free via its pattern-less download-artifact; omitting them + # here shipped v0.69.0 with appcast feeds whose enclosure URL 404'd, so + # every tray auto-update failed with "couldn't download the update". + gh run download ${{ inputs.run_id }} \ + --repo ${{ github.repository }} \ + --dir dist \ + --pattern 'sparkle-enclosure-*' || echo "No Sparkle enclosure artifacts found" + # Release notes (AI-generated by original run) gh run download ${{ inputs.run_id }} \ --repo ${{ github.repository }} \ @@ -257,13 +267,14 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Fail loudly if the assembled set is missing platforms or package types. - # A complete release has ≥22 files at this point: + # A complete release has ≥24 files at this point: # 6 versioned archives + 6 latest archives + 4 Linux packages - # + 2 Windows EXEs + 2 macOS DMGs + 1 SBOM + 1 CHANGELOG = 22 + # + 2 Windows EXEs + 2 macOS DMGs + 2 Sparkle enclosures + # + 1 SBOM + 1 CHANGELOG = 24 # (checksums.txt and cosign bundle are added in subsequent steps) - name: Assert minimum asset count run: | - MIN_ASSETS=22 + MIN_ASSETS=24 ASSET_COUNT=$(find release-files -maxdepth 1 -type f | wc -l) echo "Asset count: $ASSET_COUNT (minimum: $MIN_ASSETS)" if [ "$ASSET_COUNT" -lt "$MIN_ASSETS" ]; then @@ -275,6 +286,7 @@ jobs: echo " 4 Linux packages (linux-packages-* pattern from original run)" echo " 2 Windows EXEs (installer-windows-* from this run's sign-windows job)" echo " 2 macOS DMGs (installers-* pattern from original run)" + echo " 2 Sparkle enclosures (sparkle-enclosure-* pattern from original run)" echo " 1 SBOM (.spdx.json)" echo " 1 CHANGELOG.md" echo "" @@ -842,7 +854,11 @@ jobs: fi done - gh release upload "${TAG}" appcast-out/*.xml --clobber + # Upload the enclosures WITH the feeds: a feed must never go live + # pointing at an asset the release doesn't carry (the v0.69.0 outage). + # The release job normally attached them already (--clobber re-uploads + # identical bytes); under post_release_only this is what attaches them. + gh release upload "${TAG}" enclosures/*/*.app.zip appcast-out/*.xml --clobber echo "generated=true" >> "$GITHUB_OUTPUT" - name: Upload appcast artifact for the website repo @@ -901,6 +917,17 @@ jobs: FAILED=1 fi done + # A fresh feed is useless if its enclosure 404s — Sparkle then fails + # every update with "couldn't download the update" (v0.69.0). + for f in appcast-out/*.xml; do + URL=$(sed -n 's/.*enclosure url="\([^"]*\)".*/\1/p' "$f" | head -1) + if [ -z "$URL" ] || ! curl -fsIL -o /dev/null "$URL"; then + echo "::error::$(basename "$f") enclosure is not downloadable: ${URL:-}" + FAILED=1 + else + echo "✅ $(basename "$f") enclosure downloadable: ${URL}" + fi + done exit "$FAILED" # Opens the PR that deletes .github/RELEASE_NOTICE.md once this release has