Skip to content

fix(release): serve the Sparkle enclosure from rolling, not the draft candidate - #360

Merged
arzafran merged 2 commits into
mainfrom
fix/sparkle-enclosure-on-rolling
Sep 18, 2026
Merged

arzafran merged 2 commits into
mainfrom
fix/sparkle-enclosure-on-rolling

Conversation

@arzafran

@arzafran arzafran commented Sep 18, 2026 •

Copy link
Copy Markdown
Member

What this does

Fixes auto-update for everyone on 0.4.x. Sparkle was told to download 0.5.347 from the candidate draft release, and GitHub does not serve assets from drafts, so every client got a 404 (SUDownloadError 2001). The same thing happened again on 0.5.348. Both builds have been patched by hand on rolling; this makes every future ship do the right thing.

It also stops every promotion from ending red: deleting the old draft candidate asked GitHub to clean up a tag that drafts never have, and the 422 failed the job after rolling was already updated (seen on the 0.5.348 run).

Why it broke

dbe44fb made candidates drafts so the releases page shows only rolling. The appcast kept binding its enclosure URL to the candidate tag, and the candidate cleanup kept --cleanup-tag. 0.5.347 was the first ship after that change.

What changed

Start with scripts/publish_rolling_release.sh; the rest follows from it.

  1. publish_rolling_release.sh: the sealed appcast is validated against rolling. Promotion uploads programa-macos-<build>.dmg to rolling before the appcast that points at it, verifies it alongside the aliases, and prunes enclosures older than the keep window from scripts/sparkle_enclosure.js after final convergence. The just-promoted build is never pruned. Draft candidates are deleted without --cleanup-tag.
  2. release.yml: the appcast is generated against rolling, and the candidate publisher validates it against rolling too.
  3. tests/test_rolling_release_publication.sh: fixtures target rolling; converged assertions include the enclosure; bounded-growth expectations allow one enclosure per promotion; new coverage proves enclosure-before-feed ordering and that pruning runs only after convergence and keeps the window; retention assertions expect no tag cleanup.
  4. CLAUDE.md: the rolling asset-set note.

Test plan

  • tests/test_rolling_release_publication.sh prints PASS (local: PASS)
  • node --test scripts/rolling_release_state.test.js 114 pass, 0 fail (local: yes)
  • node --test scripts/sparkle_enclosure.test.js 14 pass, 0 fail (local: yes)
  • After this ships: curl -sL https://github.com/darkroomengineering/programa/releases/latest/download/appcast.xml | grep enclosure shows a releases/download/rolling/programa-macos-<build>.dmg URL, that URL returns 200, and the release run's promote-rolling job is green

… candidate

Since candidates became drafts (dbe44fb), the appcast enclosure pointed at
rolling-candidate-<build>/programa-macos-<build>.dmg. GitHub serves no assets
from a draft release, so every auto-updating client got SUDownloadError 2001
(404) on 0.5.347, the first ship after that change.

The appcast now binds to the public rolling release. Promotion uploads the
per-build DMG to rolling before the feed that points at it, verifies it with
the aliases, and prunes enclosures past the keep window after convergence.
dSYMs and the versioned EXE stay on the candidate draft.
Drafts have no git tag, so the tag cleanup after the release delete fails
with 422 and turns every promotion red after rolling has already been
updated (seen on the 0.5.348 ship).

@darkestdarky-bot darkestdarky-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The patch fixes Sparkle auto-update by serving enclosures from the public rolling release instead of the draft candidate, preventing 404 errors. It updates the release script, workflow, documentation, and tests accordingly, adds enclosure pruning on rolling, and removes the problematic --cleanup-tag on draft deletion. No security concerns or standards violations found; the changes are focused and well-tested.

Review coverage: 394/394 diff lines supplied. Partial input (truncated: standards). Inline comments are limited to fully visible, valid right-side hunks. Reviewed commit: 017a53b35477a814734fb34edef516c9fb9e69f4.

@arzafran
arzafran merged commit 5551de3 into main Sep 18, 2026
17 checks passed
@arzafran
arzafran deleted the fix/sparkle-enclosure-on-rolling branch September 18, 2026 12:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant