Repository navigation
cmux NEXT updater: an install that left the old build running says why, instead of Restart to Update again - #31178
Merged
teamleaderleo merged 2 commits intoNov 1, 2026
Conversation
…y, instead of Restart to Update again Leo-ask: ask-24cefc7e85 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-e1fe72acf384b8bd76b5
teamleaderleo
temporarily deployed
to
ci-routing-agent
November 1, 2026 17:48 — with
GitHub Actions
Inactive
Contributor
|
cmux status (one comment, edited in place; each line is updated by the job that owns it) Review App of this head (app.zip, kept 1 day)
web and size checks: green Shipped in:
|
This comment has been minimized.
This comment has been minimized.
The record, the launch read and the staged-build match live on UpdateInstallCheck and UpdateInstallFailure; UpdaterService grows by no line (2684, as on base). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-e1fe72acf384b8bd76b5
teamleaderleo
temporarily deployed
to
ci-routing-agent
November 1, 2026 18:00 — with
GitHub Actions
Inactive
teamleaderleo
temporarily deployed
to
ci-routing-agent
November 1, 2026 18:00 — with
GitHub Actions
Inactive
teamleaderleo
commented
Nov 1, 2026
teamleaderleo
left a comment
Collaborator
Author
There was a problem hiding this comment.
Review (codex gpt-6-astra high): SHIP at 8d4bdfd
Review (codex gpt-6-astra high, router-review): SHIP at 8d4bdfd
Follow-ups:
- [P1][follow-up] With
updates.notifysilent,readyCardneedsfooterPillorflow.userAsked, so after an automatic relaunch a failed install shows no card, reason or Try Again. Surface failed-install details independently of the silent card preference.
Filed: #31192
lawrencecchen
added a commit
that referenced
this pull request
Nov 3, 2026
…s sibling suites do (#33093) theRelaunchRecordsTheInstallAndTheNextLaunchJudgesIt and anInstallThatEndsWithTheAppRunningLeavesNoRecord call updaterWillRelaunchApplication(), which reads UpdaterService.frontmost. Its default is AppKit's answer (UpdateFrontmostApp.current()), which reads NSApp.isActive. The package-test lane runs each suite in its own process, and this suite makes no NSApplication, so NSApp is nil and the read traps (UpdateRelaunchFocus.swift:99, 'Unexpectedly found nil'): the whole suite crashes. It passed only when an earlier suite in the same process had made NSApplication.shared. The seam is there for tests ('AppKit in the app, a fake in tests'); UpdateRelaunchFocusTests and UpdateRelaunchHandoffTests set it. The helper now sets it too. No product change: the app always has NSApp. Culprit: 6229004 (#31178) added the suite without the fake. Red: fleet job 2bf90b6c18ca136f3fb6c15d at feat 11dfe91.
This branch was previously deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Leo asked (2026-10-21): "Find out why the newest nightly is not on his Mac. Is nightly publishing stuck, is the cmux-next appcast stale, or did Sparkle download a build and fail to install it on relaunch (check the Sparkle logs and ~/Library/Caches)? Fix the root cause in the pipeline or updater. If an installed update ever fails, the app must say why instead of offering Restart again."
Leo-ask: ask-24cefc7e85
What I found
files-next.cmux.com/nightly-next/appcast-arm64.xmlhad a build from 17:26 andnightly-next/next/appcast-arm64.xmlhad one from 17:34.UpdateInstallPreflight(folder not writable, no space, staged copy gone).Change
UpdateInstallCheck: just before Sparkle relaunches (updaterWillRelaunchApplication), the updater saves{fromBuild, targetBuild, recordedAt}in the app's own defaults. The next launch reads that record once. If the app is still onfromBuild, the install failed. If it is ontargetBuild, the install worked. A third build, a record older than a day, or a no-op install counts as neither. Every path where the install ends with the app still running clears the record (newabandonInstall()), so a later launch of the same build isn't reported as a failed install.UpdateInstallFailure.diagnose: on a failed install, the app checks for causes it can see on disk:If none of these applies, the card says the installer kept this version and gave no reason. Sparkle's installer leaves no error the app can read, so the app does not guess. The folder and space checks reuse the preflight's checks (
UpdateInstallPreflight.placeFailure, split out offailure()with no change in behavior).The staged update card, for the build whose install failed, now shows "Update Didn't Install", the one-line reason, and a Try Again button in place of "Restart to Update". The full reason and what to do are in the popover and in the button's help text. A newer staged build gets the normal card. While Try Again installs, the card shows Installing as before. The updater log records
install of X did not replace Y: <cause>.10 new strings, translated into all 21 locales (non-English marked
needs_review).Tests
Packages/macOS/CmuxNext/Tests/CmuxNextAppTests/UpdateInstallCheckTests.swift: judging the record, reading it only once, the relaunch writing it and the next launch reading it as failed, a relaunch into the new build counting as success, an abandoned install leaving no record, the card text for a failure and while installing, the reason applying only to the failed build, anddiagnose(translocated path, a writable folder, a read-only folder).Observed / Not run
scripts/cmux-next/prepush-gate.pyreported no new violations.scripts/lint-ios-package-conventions.shpassed. The appcast reads quoted above.risk: high (updater path).
Changelog
No capture: a native card state that needs a failed real install; the strings and layout reuse the existing staged update card.
🤖 Generated with Claude Code
Agent session: agent session
Need help on this PR? Tag
@codesmith-botwith what you need.Summary by cubic
When Sparkle's installer fails to replace cmux after it quits, the app relaunches the old build and showed "Restart to Update" again as if nothing happened. The updater now records which build it installs over which before relaunch, checks the outcome on the next launch, and a failed install makes the update card say why and offer Try Again instead.
Written for commit 58c0cdd. Summary will update on new commits.