Skip to content

feat: add update notifications and one-click updates - #148

Merged
cpojer merged 94 commits into
nkzw-tech:mainfrom
AbdelrahmanHafez:feat/update-notifications
Aug 3, 2026
Merged

feat: add update notifications and one-click updates#148
cpojer merged 94 commits into
nkzw-tech:mainfrom
AbdelrahmanHafez:feat/update-notifications

Conversation

@AbdelrahmanHafez

Copy link
Copy Markdown
Contributor

This PR adds update notifications and in-place updates, using GH releases as the source of truth.

This PR:

  • Checks GitHub Releases once a day and caches the result in ~/.codiff/update-state.json, shared by the app and the CLI
  • Shows a small pill at the bottom of the sidebar when an update is available. One click applies it: macOS and Squirrel Windows download the update and restart into the new version, Linux and non-Squirrel Windows download the matching release asset and open it, then the pill asks you to quit to finish
  • The pill tracks progress (updating, quit to finish, failed with one-click retry), and its tooltip says what clicking will do for the platform's update strategy
  • Adds codiff update to the CLI, which upgrades through Homebrew when the cask owns the install, opens the app to update itself otherwise, and points at the release page for source checkouts
  • Prints a one-line notice after codiff --version when a newer version is cached, implemented in the Node CLI and mirrored in the POSIX sh wrapper with the same version comparison and state validation so a broken or stale state file stays silent
Screenshots image image
Previous iterations This iteration was a two-click update, which after some thought it felt like it can be simplified to match modern tools like VSCode and Codex, an update is just a single-click and self-explanatory. So I decided to match it. image image

Used Fable 5 xhigh with 20 iterations of review with Sol 5.6 xhigh.

@AbdelrahmanHafez
AbdelrahmanHafez marked this pull request as ready for review July 29, 2026 17:10
@AbdelrahmanHafez

Copy link
Copy Markdown
Contributor Author

All addressed, and updated the button

image
  1. Windows now has its own manual strategy: since the releases deliberately ship only a ZIP, the update button opens the release page instead of downloading an archive that can't install itself. The status stays available because nothing was installed.
  2. codiff update no longer special-cases brew, it hands every packaged install to the app's self-updater. On the tap side I'd add auto_updates true together with the first release that ships this updater, not before: brew upgrade skips auto_updates casks unless --greedy, so flipping it while 1.9.2 is current would leave existing brew installs with no update path at all.
  3. Downloads now stream to disk through a SHA-256 hash and are verified against the digest GitHub publishes for the asset before anything is opened. A missing or mismatched digest deletes the file and surfaces an error. This also removes the full in-memory buffering and sync write.
  4. Linux assets are matched against process.arch (amd64/arm64 for deb, x86_64/aarch64 for rpm), with a fallback to an unmarked asset. An installer marked for another architecture is refused rather than opened.
  5. All the update pill CSS (and its variables) moved to Desktop.css.

The pill itself now renders through the shared <Button> component and only layers an accent fill on top, so it picks up the squircle corners and sizing from the shared styles.

@cpojer

cpojer commented Jul 31, 2026

Copy link
Copy Markdown
Member

Awesome! I think we are getting close with this PR. I have a bit more feedback from my agent's review here:

Blocking: installer downloads can destroy an existing file

The updater downloads directly to the final filename in Downloads using createWriteStream(path). That truncates any existing same-named installer. If downloading or verification fails, the catch handler then deletes that path.

I reproduced this: an existing codiff_1.9.3_amd64.deb containing unrelated data was overwritten, failed verification, and disappeared entirely.

Relevant code: electron/updater.cjs lines 326–345⁠.

It should download to a uniquely created temporary path and only expose/open that file after verification. It must never truncate or remove a pre-existing user file.

Smaller bug: disabling update checks can leave the notification visible

checkForUpdates: false prevents future scheduled checks, but the updater initializes its status from the cached state before considering that setting. Someone who disables updates after an update was cached will continue seeing the pill indefinitely.

Relevant paths:

This is less severe, but it contradicts the setting’s description and centrally managed installations.

@AbdelrahmanHafez

Copy link
Copy Markdown
Contributor Author

Addressed both 👍


  1. The download now streams into a uniquely created staging directory inside the download folder and gets verified there. Only a verified installer moves into the folder itself, and it never overwrites anything: if the name is already taken it lands as name (1).ext instead, like a browser download would. A failed download or verification only ever deletes the staging copy, so a pre-existing codiff_1.9.3_amd64.deb with unrelated data survives both the write and the cleanup.
  2. With checkForUpdates: false the updater no longer surfaces a cached update at launch, the pill starts idle regardless of what an earlier check persisted. An explicit "Check for Updates" from the menu still reports and shows the pill, since the setting only covers the automatic daily checks and their notifications.

The staging change also made us treat the asset name from the release metadata as untrusted, since it ends up as a path on disk. Names that aren't plain file names are now rejected before any filesystem operation. GitHub normalizes asset names on upload so this shouldn't ever trigger for a real release, the updater just no longer depends on it.

@cpojer

cpojer commented Aug 3, 2026

Copy link
Copy Markdown
Member

Great work! Thanks for making this.

@cpojer
cpojer merged commit 92a73a5 into nkzw-tech:main Aug 3, 2026
1 check passed
@AbdelrahmanHafez
AbdelrahmanHafez deleted the feat/update-notifications branch August 3, 2026 05:24
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.

2 participants