You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
fix(desktop): stage and verify AppImage replacements - #21621
A failed cross-filesystem AppImage update can remove the installed executable before the downloaded replacement finishes copying. Stage the replacement beside its destination, verify release size and SHA-512, set executable permissions, sync it, and commit with one rename. Cleanup runs after the commit and preserves aliases of the installed file.
This is the file-replacement layer rebuilt from @MarMarLabs's #20117. The dependent layer activates it in electron-updater. The original identified the failure correctly; the rebuild keeps its verified staging behavior and drops the separate shutdown/telemetry drain work.
This layer: 15 passing tests in the existing ElectronUpdater.test.ts (5 existing + 10 behavioral cases). Removing SHA verification makes the corruption regression fail.
The complete stack: 22 passing updater tests, plus 43 passing existing desktop update/lifecycle tests; desktop typecheck, scoped lint, and formatting pass.
Real Linux mount-namespace proof uses the actual AppImageUpdater.install with 40 MiB executable fixtures, separate tmpfs filesystems, and real ENOSPC. Stock updater leaves a 24 MiB partial executable that cannot run; the rebuilt stack keeps the runnable 40 MiB old executable and complete download. A separate real bind-mounted cache test verifies that cleanup preserves the committed executable.
The live fixtures are shell executables, not packaged Electron/FUSE AppImages. Packaged Linux success/failure, real Electron shutdown/relaunch and single-instance locking still need human qualification before landing. macOS, Windows, iOS, Android and Electron desktop UI were not exercised. This nonvisual filesystem change has test/CLI evidence instead of screenshots.
Staging always copies the download so a later verification, sync, or final-rename failure leaves the cached download available. This requires another AppImage-sized allocation even on one filesystem, and copy/hash/sync run synchronously during installation. Rename-first staging would need rollback handling to preserve a valid cache after later failures; that optimization is outside this repair.
Two independent full-stack review rounds (Claude Opus 5.5 and GPT-6-Sol) found no remaining code defects. The full-copy allocation/I/O cost is accepted and documented; packaged qualification remains a landing gate.
Implementation and tests: GPT-6.1-Sol and GPT-6-Astra through Codex in T3 Code; independent review: Claude Opus 5.5 and GPT-6-Sol in T3 Code.
Macroscope's review found this PR requires human approval — The helper stages and verifies AppImage replacements before atomic installation. Its new file-level static-analysis suppression requires human review to confirm the exception is justified.
You can add or adjust custom eligibility rules. Learn more.
No actionable comments were generated in the recent review.
Walkthrough
The change adds installAppImageUpdate to validate a downloaded AppImage, stage and verify it beside the destination, and replace the destination. Filesystem-backed tests cover successful installs, validation and commit failures, path aliases, and cleanup failures.
The installer checks the download, stages it on the destination filesystem, verifies its size and SHA-512 digest, sets executable permissions, syncs it, and renames it into place. It handles obsolete paths and cleanup failures. Tests cover generic and versioned destinations, verification and rename failures, path aliases, and cleanup failure after commit.
sequenceDiagram
participant installAppImageUpdate
participant installerPath
participant StagedFile
participant Destination
installAppImageUpdate->>installerPath: check download size
installAppImageUpdate->>StagedFile: copy and verify staged file
installAppImageUpdate->>StagedFile: set permissions and sync file
installAppImageUpdate->>Destination: rename staged file into place
installAppImageUpdate->>Destination: sync directory and remove obsolete paths
Loading
Merge Risk: Minimal · up to 4f41f
This change makes AppImage updates safer: the installed executable is kept until a verified replacement is ready. No blocking issues were found, and packaged-app qualification remains a normal follow-up.
Pre-merge checks: 3 · 1
Failed checks: 1 inconclusive
Check name
Status
Explanation
Resolution
Linked Issues check
Objectives from #10685, with stack status:
1. Stage the download beside the destination, then rename. Addressed in this PR. installAppImageUpdate in apps/desktop/src/electron/AppImageInstall.ts c…
Provide current review evidence for #21622. Show that the updater install path calls installAppImageUpdate in place of unlinkSync followed by mv. Show that install-step logging is emit…
Passed checks: 3 passed
Check name
Status
Explanation
Title check
The title "fix(desktop): stage and verify AppImage replacements" matches the main change. The changeset adds installAppImageUpdate, which stages the download beside the destination, verifies size and …
Description check
The description covers the problem, the change, scope with a linked issue (#10685), and verification with test results and manual-check limits. It follows the template's section structure. Two gaps: t…
Out of Scope Changes check
The current PR changes apps/desktop/src/electron/AppImageInstall.ts (new helper) and apps/desktop/src/electron/AppImageInstall.test.ts (filesystem tests). Every changed path implements the file-re…
Full details: Title check
Explanation
The title "fix(desktop): stage and verify AppImage replacements" matches the main change. The changeset adds installAppImageUpdate, which stages the download beside the destination, verifies size and SHA-512, and commits with a rename. It follows the conventional commit format from the template.
Full details: Description check
Explanation
The description covers the problem, the change, scope with a linked issue (#10685), and verification with test results and manual-check limits. It follows the template's section structure. Two gaps: the Problem section has no explicit reproduction steps or environment, and the Scope section does not explain why a stacked dependent PR is needed. The description also has no agent/model footer, though it names the models used in the Verification section, so the template's trailing agent line is covered in substance.
Full details: Out of Scope Changes check
Explanation
The current PR changes apps/desktop/src/electron/AppImageInstall.ts (new helper) and apps/desktop/src/electron/AppImageInstall.test.ts (filesystem tests). Every changed path implements the file-replacement objective of #10685 or tests that objective. The PR description says the shutdown and telemetry drain work from #20117 was dropped, and no such code appears in the reviewed summary. The test cases cover the staging, verification, rename, cleanup, and alias behavior described in the helper. No unrelated change was found.
Full details: Linked Issues check
Explanation
Objectives from #10685, with stack status:
Stage the download beside the destination, then rename. Addressed in this PR. installAppImageUpdate in apps/desktop/src/electron/AppImageInstall.ts creates a private stage directory in dirname(destination), copies with COPYFILE_EXCL, and calls renameSync (same filesystem, atomic). Evidence: current PR.
Verify size and SHA-512 before commit. Addressed in this PR. verifyAndSyncStage checks the staged size and the base64 SHA-512 digest before the rename. It also rejects empty or size-mismatched downloads. Evidence: current PR.
Keep the old AppImage until the replacement is verified. Addressed in the helper. The installed file is changed only by the final rename. The helper does not unlink it first. Replacing the unlinkSync-first call in electron-updater is not shown in this PR. Evidence: current PR plus #21622 (uncertain).
Route the real install path through the helper. Uncertain. The helper is not called from ElectronUpdater.ts or DesktopUpdates.ts in this PR. Its activation is in #21622, which has no current review evidence. Stack objective is Uncertain.
Log the install step and wire the updater logger into the trace. Partially addressed. The helper accepts an optional logger and logs only cleanup or directory-sync failures. It does not log the install step. Trace wiring is not shown in the reviewed evidence. Evidence: current PR plus #21622 (uncertain).
Unresolved: the stack-level objective 4 and the trace-wiring part of objective 5 cannot be established. The PR states that packaged Linux qualification is still outstanding. Those points are landing gates and are not assessed here.
Resolution
Provide current review evidence for #21622. Show that the updater install path calls installAppImageUpdate in place of unlinkSync followed by mv. Show that install-step logging is emitted and that the updater logger is connected to the desktop trace. Then reassess objectives 4 and 5.
Finishing Touches
Generate unit tests (beta)
Commit to this branch
Create a new PR
Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts
Reviewing files that changed from the base of the PR and between 4e41d35 and 4f41f68.
Files selected for processing: 2
apps/desktop/src/electron/AppImageInstall.test.ts
apps/desktop/src/electron/AppImageInstall.ts
Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 0 remain after this review.
Main rewrote ElectronUpdater.test.ts around the real electron-updater, so
the helper's tests get their own file and mock copyFileSync instead of a
test-only copyFile parameter. Overlapping failure cases are folded into one
table.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The automated pipeline stopped on this pull request and will not merge it:
in the real app the problem is still there, and the rework changed nothing:
No code change. The reported failure is electron-updater's stock AppImage install path, which this layer does not change. On 4f41f68, installAppImageUpdate is imported only by AppImageInstall.test.ts. ElectronUpdater.ts and the rest of production never call it; the wiring is the dependent PR #21622, as this PR's scope already states. The packaged build therefore still runs electron-updater's own install, which deletes the old AppImage and then copies the new one. That produces exactly the reported 16,777,216-byte, mode-0700 partial file, and it is why both proof builds behaved identically. To check that the helper is right for this scenario, I mounted a real 16,777,216-byte tmpfs as the install directory, put a 315,634,226-byte update with a matching SHA-512 in a cache on ext4, and called installAppImageUpdate with the real fs. The staged copy hit ENOSPC and the helper threw. The installed AppImage was left as it was (mode 0755) and still ran (exit 0), the private staging directory was removed, and the cached download stayed complete. Packaged-app proof that #10685 is fixed has to run on a build that includes #21622's integration.
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
macroscope-reviewOpt PRs made by unvouched contributors in for Macroscope review. Vouched contributors auto-reviewssize:M30-99 changed lines (additions + deletions).takeover:blockedTakeover blocked; reason in the PR bodyvouch:trustedAuthor is trusted by repo permissions or the VOUCHED list.
2 participants
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.
A failed cross-filesystem AppImage update can remove the installed executable before the downloaded replacement finishes copying. Stage the replacement beside its destination, verify release size and SHA-512, set executable permissions, sync it, and commit with one rename. Cleanup runs after the commit and preserves aliases of the installed file.
This is the file-replacement layer rebuilt from @MarMarLabs's #20117. The dependent layer activates it in
electron-updater. The original identified the failure correctly; the rebuild keeps its verified staging behavior and drops the separate shutdown/telemetry drain work.Stack: #21622 is the dependent integration layer.
Verification:
ElectronUpdater.test.ts(5 existing + 10 behavioral cases). Removing SHA verification makes the corruption regression fail.AppImageUpdater.installwith 40 MiB executable fixtures, separate tmpfs filesystems, and real ENOSPC. Stock updater leaves a 24 MiB partial executable that cannot run; the rebuilt stack keeps the runnable 40 MiB old executable and complete download. A separate real bind-mounted cache test verifies that cleanup preserves the committed executable.The live fixtures are shell executables, not packaged Electron/FUSE AppImages. Packaged Linux success/failure, real Electron shutdown/relaunch and single-instance locking still need human qualification before landing. macOS, Windows, iOS, Android and Electron desktop UI were not exercised. This nonvisual filesystem change has test/CLI evidence instead of screenshots.
Staging always copies the download so a later verification, sync, or final-rename failure leaves the cached download available. This requires another AppImage-sized allocation even on one filesystem, and copy/hash/sync run synchronously during installation. Rename-first staging would need rollback handling to preserve a valid cache after later failures; that optimization is outside this repair.
Two independent full-stack review rounds (Claude Opus 5.5 and GPT-6-Sol) found no remaining code defects. The full-copy allocation/I/O cost is accepted and documented; packaged qualification remains a landing gate.
Implementation and tests: GPT-6.1-Sol and GPT-6-Astra through Codex in T3 Code; independent review: Claude Opus 5.5 and GPT-6-Sol in T3 Code.
Closes #10685