fix(desktop): install Windows updates without the installer UI - #225
Merged
Conversation
`installDownloadedUpdateNow()` called `quitAndInstall()` with no arguments, so `isSilent` defaulted to false and Restart to update launched the assisted NSIS installer UI instead of applying the downloaded update. Passing silent and force-run spawns the setup as `--updated /S --force-run`: no window, and the app relaunches itself. The NSIS templates make this safe for a per-user install: the assisted installer honours `/S`, reads the chosen directory back from the HKCU InstallLocation, and only elevates when a per-machine installation exists. `allowElevation: false` stops a normally launched installer from offering that per-machine path — it is soft hardening, not a guarantee, since an installer started as Administrator still offers both modes. A silent installer reports nothing back, so the startup receipt now also proves failure: a pending install version that does not match the running version opens the app in an error state naming both versions.
|
Warning Review limit reachedNext included review available in 2 minutes. View limit detailsLimit details: You’ve used the included review currently available. You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Review configuration: ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (5)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
commit: |
Merged
elkaix
pushed a commit
that referenced
this pull request
Aug 27, 2026
This PR was opened by the [Changesets release](https://github.com/changesets/action) GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated. # Releases ## @pymodel/pythinker-code@1.5.0 ### Minor Changes - [#222](#222) [`6fb46b0`](6fb46b0) Thanks [@elkaix](https://github.com/elkaix)! - Add Remote Control, which makes the local web UI reachable from a phone or another computer. Run `pythinker rc`, or use `/rc` in the terminal UI, and scan the printed QR code. Enable it with `PYTHINKER_CODE_EXPERIMENTAL_REMOTE_CONTROL=1`. - [#221](#221) [`e6778dc`](e6778dc) Thanks [@elkaix](https://github.com/elkaix)! - Add a task detach action to the server API. Call `POST /api/v1/sessions/{session_id}/tasks/{task_id}:detach` to move a running foreground task to the background. ### Patch Changes - [#221](#221) [`e6778dc`](e6778dc) Thanks [@elkaix](https://github.com/elkaix)! - Report a denied OpenAI Codex sign-in as cancelled instead of asking for the redirect URL. - [#223](#223) [`ad27021`](ad27021) Thanks [@elkaix](https://github.com/elkaix)! - Remote Control now authenticates to the relay with its own key instead of the local server token. Pass `--relay-key` or set `PYTHINKER_CODE_REMOTE_CONTROL_RELAY_KEY`. - [#221](#221) [`e6778dc`](e6778dc) Thanks [@elkaix](https://github.com/elkaix)! - Show the prompt that started a subagent turn in the transcript. - [#219](#219) [`882835e`](882835e) Thanks [@elkaix](https://github.com/elkaix)! - Fix sessions that fail to resume when their session journal is truncated or corrupted, for example after the disk fills up. - [#221](#221) [`e6778dc`](e6778dc) Thanks [@elkaix](https://github.com/elkaix)! - Retry a failed session journal repair before writing new records, so no message is appended behind a corrupted tail. ## @pymodel/pythinker-desktop@0.3.8 ### Patch Changes - [#225](#225) [`f27686a`](f27686a) Thanks [@elkaix](https://github.com/elkaix)! - Install Windows updates in the background instead of opening the installer wizard, and report an update that did not take effect. ## pythinker@0.9.7 ### Patch Changes - [#221](#221) [`e6778dc`](e6778dc) Thanks [@elkaix](https://github.com/elkaix)! - Fix duplicated streaming output when a session is opened twice at the same time. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Added Remote Control for accessing the local interface from other devices. - Added the ability to detach running tasks and continue them in the background. - Added dedicated authentication support for Remote Control connections. - **Bug Fixes** - Improved sign-in cancellation reporting and subagent prompt visibility. - Improved recovery and repair of corrupted session history. - Prevented duplicate streaming output when sessions are opened concurrently. - Windows updates now install silently in the background and report unsuccessful updates. <!-- end of auto-generated comment: release notes by coderabbit.ai --> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
elkaix
added a commit
that referenced
this pull request
Aug 27, 2026
## Related Issue No linked issue — reported from the app: the v0.3.8 release-notes popover read "Pythinker Desktop 0.3.8 (stable channel), built from PyModel/pythinker-code@`<tt>`0f49851`</tt>`.", with the tags printed literally. ## Problem Two separate causes produced that one popover. 1. **The body was a build stamp.** `desktop-release.yml` created the draft with a fixed `--notes "Pythinker Desktop <v> (<channel> channel), built from <commit>."`. Nothing else ever wrote the body, so the updater had nothing to say about a version. 2. **The notes arrive as HTML.** electron-updater's GitHub provider reads the releases Atom feed, whose `<content type="html">` is the body GitHub has already rendered. The renderer treats `releaseNotes` as Markdown, so the tags GitHub emits — `<p>`, `<a>`, `<tt>` — printed as text. Fixing only the body would have left the markup visible, since a real changelog renders to `<ul>`/`<li>`/`<a>` too. ## What changed - **`apps/desktop/scripts/desktop-release.mjs`** — new exported `desktopReleaseNotes()` and a `notes` subcommand. It takes the `## <version>` section of `apps/desktop/CHANGELOG.md`, strips the changesets prefix (`[#225](…) [`sha`](…) Thanks [@user](…)! - `), and returns the bullets plus a `Built from <commit>` footer. - **`.github/workflows/desktop-release.yml`** — the prepare job writes those notes to a file and passes `--notes-file`. A **stable** release whose version has no changelog entry now fails here instead of publishing; preview channels fall back to a one-line description, since a nightly version never appears in the changelog. The source-commit URL stays in the body because the draft-resume check on line 120 gates on `.body | contains($source_url)`. - **`apps/desktop/src/updater.ts`** — `releaseNotesText` reduces HTML notes to text (list items to `- `, block ends to newlines, entities decoded), and leaves notes without markup untouched. - **`AGENTS.md`** — records that changeset text is shipped text: it becomes the release body users read, and a body must never be a build stamp. ## Checklist - [x] I have read the [CONTRIBUTING](https://github.com/PyModel/pythinker-code/blob/main/CONTRIBUTING.md) document. - [ ] I have linked a related issue (external PRs: the issue must have a maintainer's `/approve`). - [x] I have added tests that prove my feature works. - [x] Ran `gen-changesets` skill, or this PR needs no changeset. - [x] Ran `gen-docs` skill, or this PR needs no doc update. ### Tests `scripts/release/desktop-release.test.mjs` covers extraction, the next-heading boundary, the stable-release gate, the preview fallback, and the required source URL. `updater.spec.ts` asserts the exact HTML from the live v0.3.8 feed comes out as text, and that plain notes are unchanged. `desktop-release-workflow.spec.ts` pins `--notes-file` and forbids the old literal. Reverting both product changes fails exactly 2 tests; 175 + 25 pass as shipped. ### What users will see Instead of the build stamp, the v0.3.9 popover will read: `- Show the changelog for the new version in the update dialog instead of a build stamp with raw HTML tags.` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Update dialogs now display readable changelog content for new versions. * Release notes preserve lists and links as clean, formatted text without raw HTML or script content. * Desktop releases now publish version-specific changelog entries instead of build information. * **Bug Fixes** * Improved handling of plain-text and HTML-formatted release notes. * Prevented empty or malformed release notes from appearing in update prompts. * **Reliability** * Stable releases without valid changelog entries are blocked from publication. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
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.
Related Issue
No linked issue — reported directly: on Windows, Restart to update opened the installer wizard and asked the user to click through Next / Next / Install.
Problem
installDownloadedUpdateNow()calledautoUpdater.quitAndInstall()with no arguments. In electron-updater 6.8.9 that meansisSilent = false, soNsisUpdater.doInstallspawns the setup with only--updated— the assisted NSIS installer UI — and relaunch falls toautoRunAppAfterInstallinstead ofisForceRunAfter.A silent installer also reports nothing back, so an install that does not take effect would be invisible. The existing startup receipt proved success only.
What changed
quitAndInstall(true, true)→Pythinker-x.y.z-x64-Setup.exe --updated /S --force-run. No window, and the app relaunches itself. Behaviourally unchanged on macOS:MacUpdater.quitAndInstall()takes no such flags.reconcileStartupReceiptnow returns{ settings, failedInstallVersion }. A pending install version that does not match the running version opens the app instatus: 'error'with a message naming both versions; the next check clears it. Success detection is unchanged.nsis.allowElevation: false. Soft hardening: it stops a normally launched installer from offering the all-users path, whose per-machine installs are the only ones that still hitUAC_RunElevatedon update. It is not a guarantee — an installer started as Administrator still offers both modes. A hard per-user policy would need acustomInstallModemacro inbuild/installer.nsh, which is a larger decision and not part of this change.Verified against the pinned
app-builder-lib26.15.3 NSIS templates rather than documentation: the assisted installer honours/Sand only then applies--force-run(installSection.nsh), the directory the user chose is read back from the HKCUInstallLocationso no/D=is needed (multiUser.nsh), and elevation under/Sis gated on an existing per-machine installation (installer.nsi).Not changed:
autoDownloadstaysfalseandautoInstallOnAppQuitstaysfalse. Downloading and installing remain two deliberate user actions, and closing the app is still not consent to install.Checklist
/approve).gen-changesetsskill, or this PR needs no changeset.gen-docsskill, or this PR needs no doc update.Tests
updater.spec.tsasserted onlytoHaveBeenCalledOnce(), which passes with or without the flags. AddedtoHaveBeenCalledWith(true, true)— which also makes the eventual electron-updater v7 migration to{ isSilent, isForceRunAfter }explicit — plus two failure-receipt tests, and flipped the two existingallowElevationexpectations. Reverting all three product values fails exactly 4 tests; 172 pass as shipped.Still to do
A Windows VM pass: install the current release into a non-default directory, then update. The invariant is that the app stays at the previously selected
InstallLocationwith no second installation under the default per-user Programs directory, and relaunches on its own after the installer finishes.