cmux-next updates: pulled-build banner (Wait for the Fix / Roll Back) from pulled.json

Merged
TE
16d ago
+1124-12

Nightly train PR 4 (teamleaderleo/leo-workspace#624, section 2): the pulled-build banner. Follows #22590 (Roll Back to Previous Version), which merged as 010e772.

Problem. When nightly-next-rollback.yml pulls a bad build off the feed, Macs already on it keep running it with no signal, and the only way back is knowing about updates.rollback.

Behavior.

  • The rollback workflow now also writes pulled.json next to the appcast (nightly-next/next/pulled.json in R2): [{build, reason, pulledAt, serve}], newest first, merged with the existing list, guarded by nightly-next-guard.py r2-key and a write-once / --replace-sha256 conditional upload. A build served again is dropped from the list.
  • The app (PulledBuildWatch) reads that list at launch and after every Sparkle update cycle (no polling). The legacy NIGHTLY feed also reads /nightly-next/next/pulled.json.
  • If the running build is listed, the update notice card says This Nightly Was Pulled with the reason, and offers Wait for the Fix (default, prominent, also the ×; remembered per build) and Roll Back, which runs UpdateDropBack and only shows when that rollback would run. A failed rollback shows its reason on the card.

Tests. PulledBuildWatchTests (list URLs, decode, wait-first/rollback actions, nothing-kept, not-listed, wait remembered across launches, rollback failure shown); tests/test_nightly_next_feed_rollback.py PulledListTests. Run on CI; not dogfooded in the app.

Localization. 5 new updater.pulled.* keys in Localizable.xcstrings with all 9 app locales.

Not in scope: the Updated toast / What's New work.

Agent session: cosmic-gray

🤖 Generated with Claude Code


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.


Summary by cubic

Adds a pulled-build banner to nightly updates so Macs running a build pulled from the feed get a signal instead of silently continuing.

Behavior

  • The rollback workflow now writes pulled.json next to the appcast, listing pulled builds with reason, timestamp, and the build being served instead; a failed write only warns (and the rollback summary says so), so the fix-forward still runs.
  • The app reads that list at launch and after every Sparkle update cycle; a pulled build shows a This Nightly Was Pulled notice with the reason, offering Wait for the Fix (default, remembered per build) and Roll Back to the kept build, shown only when a rollback would run.
  • The kept-build check runs once per pulled build, and a second Roll Back press while one runs is ignored.
  • A failed rollback shows its reason on the card; a build served again is removed from the list, and a build leaving the list clears a stale rollback failure.
  • The banner shows even when that update was snoozed.