Skip to content

feat(release): verify Windows automatic updates end to end - #3240

Merged
Astro-Han merged 7 commits into
apache:mainfrom
liugddx:feat/windows-autoupdate-verification
Aug 19, 2026
Merged

feat(release): verify Windows automatic updates end to end#3240
Astro-Han merged 7 commits into
apache:mainfrom
liugddx:feat/windows-autoupdate-verification

Conversation

@liugddx

@liugddx liugddx commented Aug 19, 2026

Copy link
Copy Markdown
Member

What & why

Closes the automatic updates item of #2142 Phase 3 (distribution): the update service, latest.yml production, and the closed-app NSIS upgrade gate all existed, but no evidence ever exercised the actual in-app path — a running packaged app discovering, downloading, and installing a newer build through its own electron-updater. This PR adds that evidence end to end on the unsigned Windows x64 preview channel.

What this does

1. Loopback-only test feed override (apps/desktop/src/main/app-update-service.ts). MAKA_UPDATE_TEST_FEED is resolved by an exported pure function that accepts exactly http://127.0.0.1:<port>[/path] and maps it to a generic provider. Any other value — remote host, localhost, another loopback alias, HTTPS, userinfo, query, malformed — throws at construction: a mistyped override must never silently fall back to the production GitHub feed. The override changes the feed URL and nothing else; with the variable unset the configuration is byte-identical to production (pinned by the existing unit test). Security posture: setting an env var on the app's process already requires same-user code execution, which under the per-user NSIS install model can rewrite the install directory directly — the hook crosses no privilege boundary, and update signature verification (once a certificate exists) applies to overridden feeds unchanged. Unit tests pin two accepted forms and eleven rejected confusion shapes.

2. Version-bumped installer builder (scripts/package-windows-autoupdate-next.mjs). Reuses the workspace build from a full package:windows-x64 run and repackages NSIS-only with extraMetadata.version = patch+1, so the upgraded app genuinely reports the new version via app.getVersion(). Output goes to apps/desktop/release-autoupdate-next, outside the release upload globs, so fake-versioned artifacts can never reach a draft release. Asserts properties, not exit codes: exe/latest.yml/blockmap exist, latest.yml advertises exactly the bumped version and installer name, and its sha512 matches the installer byte for byte.

3. End-to-end harness (scripts/verify-windows-autoupdate.mjs). Installs the candidate, launches it against a loopback feed serving the bumped build, and asserts stage by stage with individual deadlines and named failures:

  • renderer-driven checkForUpdates via CDP (window.maka.app), polled to the downloaded status with the exact current → latest version pair;
  • feed request log: latest.yml and the installer were actually fetched (200/206), zero unexpected paths;
  • installUpdate handoff (an evaluation dropped by the app quitting is the expected success shape; a structured refusal fails);
  • old process exits; NSIS upgrades; automatic relaunch is observed and the on-disk image carries the new ProductVersion;
  • the relaunched instance (spawned by NSIS without our CDP/user-data flags) is stopped promptly, then the full packaged smoke runs against the upgraded install with an added renderer-bridge assertion that app.getVersion() reports the bumped version;
  • clean silent uninstall; cleanup mirrors the installer-lifecycle verifier (straggler kill, best-effort uninstall, AggregateError cause chaining).

verify-packaged-app.mjs generalizes its CDP internals into exported evaluateInRenderer / findRendererTarget / reserveTcpPort / stopChild; the existing smoke's assertions are unchanged.

4. CI wiring. Both release-windows-check.yml (PR gate; paths filter extended to the new scripts and app-update-service.ts) and release-desktop.yml (before asset upload, so a failure blocks the release) build the bumped installer and run the verification; timeouts raised 60→75 and 45→60 to absorb the ~8–10 minute addition.

5. Docs. docs/windows-support.md (EN + zh) now records what the gate proves and keeps the unproven remainder explicit: signature verification awaits an Authenticode certificate, business-data migration and mid-install rollback stay uncovered.

Verification

  • npm --workspace @maka/desktop run typecheck clean; desktop unit tests: the update-service suites pass 19/19 including the new feed-override tests (the workspace run shows 22 pre-existing local-environment failures — EBUSY SQLite cleanup and EPERM symlink — unrelated to this change and present on main locally).
  • Pure pieces exercised directly: version-bump function (accept/reject shapes), platform guard, export surface, biome clean.
  • The full packaged end-to-end run could not be executed on the development machine — it lacks Visual Studio Build Tools, which electron-builder needs to rebuild node-pty for Electron (and a pre-existing local EBUSY flake in release-cli-eval-support.test.mjs blocks check:release there as well). The authoritative end-to-end evidence is this PR's own windows_sandbox-style gate: release-windows-check runs the complete package → verify → autoupdate chain on windows-2025. Treat that lane's result as the verification for this PR; I will iterate on it if it surfaces harness issues.

AI use

Select exactly one:

  • No generative tool made a substantive contribution
  • Generative tooling made a substantive contribution

Tool(s) and scope: Claude Fable 5 (Anthropic), driven by the human contributor of record, authored the feed-override hook and tests, the bumped-installer builder, the end-to-end harness, the CI wiring, and the documentation updates, all under human review. Commits carry Generated-by: Claude Fable 5 trailers; the final squash commit must retain them.

liugddx and others added 3 commits August 19, 2026 15:08
The updater feed was hardcoded to the production GitHub repository, so
no harness could drive a real electron-updater download/install cycle
on a packaged Windows build -- the Windows auto-update path shipped
without end-to-end evidence.

Add MAKA_UPDATE_TEST_FEED, resolved by an exported pure function that
accepts exactly http://127.0.0.1:<port>[/path] and maps it to a generic
provider. Anything else set -- a remote host, localhost, another
loopback alias, HTTPS, userinfo, a query string, or a malformed URL --
throws at construction: a mistyped override must never silently fall
back to the production feed, because a test run quietly installing a
real release is exactly the failure this shape exists to prevent.

This is not an update-hijack vector: setting an environment variable on
the app's process already requires code execution as the same user, and
the per-user NSIS install model means that user can rewrite the
installation directory directly -- the override grants no capability
across any privilege boundary. With the variable unset the feed
configuration is byte-identical to production, and the override changes
the feed URL and nothing else: every other updater setting and the
whole status machine behave identically under it. Update signature
verification (once a certificate exists) applies to overridden feeds
exactly as to the GitHub feed.

Unit tests pin the accepted shape (two positive forms, eleven rejected
confusion shapes), the generic-provider mapping, the unchanged
production feed without the variable, and the fail-closed constructor.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Generated-by: Claude Fable 5
verify-windows-autoupdate installs the candidate build, points its
packaged updater at a loopback feed serving the version-bumped
installer, and asserts the complete in-app path stage by stage:
renderer-driven check, downloaded status with the exact version pair,
feed request log (latest.yml and installer actually fetched, zero
unexpected paths), installUpdate handoff, old-process exit, NSIS
upgrade, automatic relaunch whose on-disk image carries the new
ProductVersion, a full packaged smoke of the upgraded install that also
asserts app.getVersion() via the renderer bridge, and a clean silent
uninstall. Every stage has its own deadline and named failure; cleanup
mirrors the installer-lifecycle verifier (kill stragglers, best-effort
uninstall, AggregateError cause chaining).

The relaunched instance is observed and then stopped promptly: NSIS
isForceRunAfter restarts without our CDP/user-data arguments, so its
existence and image version are the assertions, and the inherited
environment still points at the isolated home.

verify-packaged-app generalizes its CDP internals into exported
evaluateInRenderer/findRendererTarget/reserveTcpPort/stopChild with the
smoke's assertions unchanged. Both Windows release workflows build the
bumped installer and run the verification before uploads, with
timeouts raised to absorb the ~8-10 minute addition.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Generated-by: Claude Fable 5
The support baseline now states what the new gate proves -- a running
installed candidate discovers, downloads, and installs a newer build
through its packaged electron-updater against a loopback feed,
relaunches as the new version, and passes the full packaged smoke, with
feed requests, updater states, and the final version asserted
individually -- and keeps the unproven remainder explicit: update
signature verification awaits an Authenticode certificate (the
production GitHub feed configuration is pinned by unit tests and
exercised on real releases), business-data migration, and mid-install
rollback stay uncovered. EN and zh sections updated together.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Generated-by: Claude Fable 5
@coderabbitai

coderabbitai Bot commented Aug 19, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 9c483f84-ceba-4b31-aa8a-a6af833d4b25

📥 Commits

Reviewing files that changed from the base of the PR and between 7a14d84 and 72c409e.

📒 Files selected for processing (1)
  • scripts/verify-windows-autoupdate.mjs

Included review availability: Your plan provides up to 3 included reviews per hour; 0 remain after this review.


📝 Walkthrough

Summary

This PR adds end-to-end verification for Windows automatic updates on the unsigned x64 preview channel. It verifies discovery, download, installation, NSIS upgrade, relaunch, version validation, packaged smoke testing, and uninstall.

The PR extends the existing update service and packaged-app verification path. It does not replace the production update path. The loopback-only MAKA_UPDATE_TEST_FEED override fails closed for invalid values and preserves the production GitHub feed when unset.

The solution is the smallest coherent path shown by the diff. Shared verifier exports avoid duplicate process and CDP logic. The patch-version-bumped installer is required to test the upgrade flow. No deletion or simplification is apparent without weakening coverage.

Complexity delta

  • Adds one optional testFeedUrl dependency and one environment variable.
  • Adds validation branches for unset, valid loopback, and invalid feed URLs.
  • Adds a dedicated patch-version-bumped NSIS packaging path.
  • Adds a Windows E2E harness with loopback feed serving, range handling, request auditing, blockmap probing, process control, installer handling, relaunch, and cleanup.
  • Adds packaged-verifier options and reusable verifier exports.
  • Adds release and pull request workflow steps.
  • Adds unit tests for feed routing and rejection.
  • Increases CI and test-maintenance burden.
  • Does not add a production update provider or change the default GitHub feed.

The added complexity is necessary to verify the previously untested upgrade path. Total maintenance complexity increases, but the increase has direct verification value.

No code or tests can be deleted or simplified based on the available diff without weakening update-flow coverage or regression protection.

Validation and risks

Reported validation includes clean typechecking and update-service tests. The full local Windows E2E run was unavailable because Visual Studio Build Tools were missing. Windows CI is the authoritative verification path. The final status of required checks is unverified from the available evidence.

The verifier validates candidate and upgraded artifacts, exact raw feed paths, query support, differential-download probing, installer transfer responses, update state, installer handoff, automatic relaunch, final version, packaged smoke checks, silent uninstall, and failure-safe cleanup.

Remaining coverage gaps include Authenticode signature verification, persisted-data migration, and rollback behavior.

Review-relevant risks

  • The workflows build and execute Windows installers during release and pull request checks. This affects release behavior and requires independent human review under repository policy.
  • The update service adds the exported resolveUpdateFeedOverride function and optional testFeedUrl dependency. This changes the source-level contract and requires independent human review under repository policy.
  • The loopback feed override changes test-time update routing. Although validation is fail-closed, security-sensitive behavior requires independent human review under repository policy.
  • The verifier installs, relaunches, and uninstalls the application in CI. This affects test-host state and release confidence and requires independent human review under repository policy.
  • The package remains unsigned, and signature verification is not covered. Changes to signing or update security require independent human review under repository policy.

The person performing the merge reviews the final diff. A maintainer makes the final determination.

Walkthrough

The PR adds validated loopback feeds, Windows installer packaging, end-to-end autoupdate verification, CI release gates, and updated Windows support documentation.

Changes

Windows automatic update verification

Layer / File(s) Summary
Loopback feed override
apps/desktop/src/main/app-update-service.ts, apps/desktop/src/main/runtime-host-boot.ts, apps/desktop/src/main/__tests__/app-update-service.test.ts
The update service validates loopback HTTP feed URLs and selects a generic feed when configured. Tests cover valid, invalid, and fail-closed cases.
Version-bumped packaging and renderer helpers
scripts/package-windows-autoupdate-next.mjs, scripts/verify-packaged-app.mjs, scripts/verify-windows-x64.mjs, package.json
The packaging script creates and validates the next Windows installer and update metadata. Renderer evaluation, process cleanup, and packaged-app checks support verifier use.
End-to-end update verifier
scripts/verify-windows-autoupdate.mjs
The verifier serves artifacts from a loopback feed, installs and launches the candidate, checks download and installer handoff, validates relaunch and packaged smoke tests, and cleans up resources.
CI gates and support documentation
.github/workflows/release-desktop.yml, .github/workflows/release-windows-check.yml, docs/windows-support.md
Release workflows run packaging and verification steps. Windows documentation records the verified update flow and remaining signing, migration, and rollback limitations.

Estimated code review effort: 4 (Complex) | ~45 minutes

Merge Risk: 🔵 Low · up to 72c40

The PR adds an end-to-end Windows update verification gate, but the verifier may accept non-canonical artifact paths and therefore fail to enforce its exact feed-path contract; this is a bounded merge-readiness risk that should receive explicit owner follow-up.

Sequence Diagram(s)

sequenceDiagram
  participant CI
  participant Packager
  participant LoopbackFeed
  participant CandidateApp
  participant NSISInstaller
  participant UpgradedApp
  CI->>Packager: build version-bumped installer
  Packager->>CI: return installer and update metadata
  CI->>LoopbackFeed: serve update artifacts
  CI->>CandidateApp: install and launch candidate
  CandidateApp->>LoopbackFeed: fetch latest.yml and installer
  CandidateApp->>NSISInstaller: hand off update
  NSISInstaller->>UpgradedApp: install and relaunch
  CI->>UpgradedApp: run packaged smoke checks
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: end-to-end verification of Windows automatic updates.
Description check ✅ Passed The description explains the implementation, verification results, limitations, AI use, and CI impact in sufficient detail.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Ai Use Disclosure ✅ Passed The PR selects substantive generative use, names Claude Fable 5 and its scope, and all seven introduced commits contain standalone Generated-by: Claude Fable 5 trailers.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Verify Windows automatic updates end to end

✨ Enhancement 🧪 Tests 📝 Documentation ⚙️ Configuration changes 🕐 40+ Minutes

Grey Divider

AI Description

• Adds a loopback-only updater feed override that fails closed on unsafe URLs.
• Builds a version-bumped NSIS artifact and verifies the complete running-app upgrade lifecycle.
• Gates Windows CI releases and documents the unsigned automatic-update guarantee.
Diagram

sequenceDiagram
  participant CI as Windows CI
  participant Build as Next Builder
  participant App as Candidate App
  participant Updater as Update Service
  participant Feed as Loopback Feed
  participant NSIS as NSIS Installer
  participant NewApp as Upgraded App
  CI->>Build: Build patch version
  Build-->>CI: Installer and metadata
  CI->>App: Install and launch
  App->>Updater: Check for updates
  Updater->>Feed: Fetch metadata
  Feed-->>Updater: Installer bytes
  Updater-->>App: Downloaded status
  App->>NSIS: Install update
  NSIS->>NewApp: Upgrade and relaunch
  CI->>NewApp: Verify and smoke
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Use a hosted preview feed
  • ➕ More closely resembles production network topology
  • ➕ Could reuse artifacts across multiple test runners
  • ➖ Introduces external availability and credential dependencies
  • ➖ Risks channel contamination or installing unintended releases
  • ➖ Makes request-level assertions and deterministic cleanup harder
2. Mock electron-updater
  • ➕ Runs faster and on non-Windows hosts
  • ➕ Avoids installer construction and process orchestration
  • ➖ Cannot prove downloads, NSIS handoff, relaunch, or installed version
  • ➖ Duplicates existing unit-level coverage without closing the release evidence gap

Recommendation: Keep the PR's loopback-feed approach. It exercises the real packaged updater and NSIS lifecycle while remaining deterministic, isolated from production releases, and fail-closed; hosted feeds add operational risk, while mocks cannot establish the required end-to-end evidence.

Files changed (10) +845 / -27

Enhancement (1) +67 / -5
app-update-service.tsAdd a fail-closed loopback test feed override +67/-5

Add a fail-closed loopback test feed override

• Adds a pure resolver for MAKA_UPDATE_TEST_FEED that accepts only explicit HTTP 127.0.0.1 URLs with ports. The service otherwise retains the production GitHub provider and all existing updater behavior.

apps/desktop/src/main/app-update-service.ts

Refactor (1) +40 / -13
verify-packaged-app.mjsExpose reusable CDP and process verification utilities +40/-13

Expose reusable CDP and process verification utilities

• Exports port reservation, renderer discovery, renderer usability, and child shutdown helpers for the update harness. Generalizes CDP evaluation with arbitrary expressions, promise awaiting, configurable timeouts, and renderer exception reporting.

scripts/verify-packaged-app.mjs

Tests (2) +563 / -0
app-update-service.test.tsTest strict loopback update-feed validation +53/-0

Test strict loopback update-feed validation

• Covers generic-provider routing for accepted loopback URLs and fail-closed behavior for malformed or unsafe overrides. Tests reject remote hosts, aliases, HTTPS, missing ports, userinfo, queries, and fragments.

apps/desktop/src/main/tests/app-update-service.test.ts

verify-windows-autoupdate.mjsVerify the complete packaged Windows update lifecycle +510/-0

Verify the complete packaged Windows update lifecycle

• Adds a Windows harness that installs the candidate, serves a newer release locally, drives updater APIs through CDP, and validates download requests and version transitions. It verifies NSIS handoff, automatic relaunch, upgraded smoke behavior, uninstall, deadlines, diagnostics, and failure cleanup.

scripts/verify-windows-autoupdate.mjs

Documentation (1) +19 / -7
windows-support.mdDocument CI-verified unsigned automatic updates +19/-7

Document CI-verified unsigned automatic updates

• Updates English and Chinese support guidance to describe the verified running-app update path. Clarifies that Authenticode verification, migration, rollback, and broader Windows support remain incomplete.

docs/windows-support.md

Other (5) +156 / -2
release-desktop.ymlGate Windows releases on automatic-update verification +14/-1

Gate Windows releases on automatic-update verification

• Extends the release timeout and adds Windows-only steps to build the patch-bumped installer and run the full automatic-update verifier before assets are uploaded. The synthetic artifacts remain outside release upload globs.

.github/workflows/release-desktop.yml

release-windows-check.ymlRun automatic-update verification in Windows PR checks +15/-1

Run automatic-update verification in Windows PR checks

• Routes updater, builder, and verifier changes into the Windows release check. Increases the timeout and executes the version-bumped build and end-to-end update gate.

.github/workflows/release-windows-check.yml

runtime-host-boot.tsPass the test feed into the update service +1/-0

Pass the test feed into the update service

• Wires MAKA_UPDATE_TEST_FEED from the packaged application's environment into AppUpdateService construction.

apps/desktop/src/main/runtime-host-boot.ts

package.jsonExpose Windows auto-update build and verification commands +2/-0

Expose Windows auto-update build and verification commands

• Adds root npm scripts for constructing the synthetic next-version installer and running the end-to-end verifier.

package.json

package-windows-autoupdate-next.mjsBuild and validate a patch-bumped NSIS installer +124/-0

Build and validate a patch-bumped NSIS installer

• Reuses the existing Windows workspace build to produce an isolated patch-bumped NSIS release. Validates the installer, blockmap, metadata version and path, and SHA-512 integrity before returning artifacts.

scripts/package-windows-autoupdate-next.mjs

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
scripts/verify-windows-autoupdate.mjs (1)

383-418: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Extract the duplicated launch-and-wait-for-renderer block.

Lines 383-418 repeat lines 204-253 almost exactly: the same four mkdir calls on isolatedUserEnv, the same three spawn flags, and the same poll on RENDERER_STATE_EXPRESSION until isPackagedRendererUsable. The two copies differ only in stdio and the stderr capture.

Extract one local helper in this file that spawns the executable with an isolated home and returns the CDP target after the renderer becomes usable. Both call sites then pass only what differs. The helper stays local because the stderr-capture variant is specific to this verifier; promoting it to scripts/verify-packaged-app.mjs would widen that module's public surface for one consumer.

Disposition: optional. The duplication is correct today, and the risk is that a future change to the launch shape is applied to only one copy.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: e6b083c3-60f6-4683-82b7-41c1757dea62

📥 Commits

Reviewing files that changed from the base of the PR and between dc65776 and 315a289.

📒 Files selected for processing (10)
  • .github/workflows/release-desktop.yml
  • .github/workflows/release-windows-check.yml
  • apps/desktop/src/main/__tests__/app-update-service.test.ts
  • apps/desktop/src/main/app-update-service.ts
  • apps/desktop/src/main/runtime-host-boot.ts
  • docs/windows-support.md
  • package.json
  • scripts/package-windows-autoupdate-next.mjs
  • scripts/verify-packaged-app.mjs
  • scripts/verify-windows-autoupdate.mjs

Included review availability: Your plan provides up to 3 included reviews per hour; 2 remain after this review.

Comment thread docs/windows-support.md
Comment thread scripts/verify-windows-autoupdate.mjs Outdated
@qodo-code-review

qodo-code-review Bot commented Aug 19, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (1) 📘 Rule violations (0) 📜 Skill insights (0)

Grey Divider


Action required

1. Boot wiring misses Windows gate ✓ Resolved 🐞 Bug ☼ Reliability
Description
Disposition: fix-now. A future change limited to the new runtime-host-boot.ts propagation of
MAKA_UPDATE_TEST_FEED will not trigger release-windows-check.yml, so CI can no longer establish
that the loopback variable reaches the packaged updater.
Code

apps/desktop/src/main/runtime-host-boot.ts[423]

+  testFeedUrl: process.env.MAKA_UPDATE_TEST_FEED,
Relevance

●●● Strong

Recent Windows precedent accepted adding runtime-owned files to blocking workflow triggers; this
propagation is directly test-critical.

PR-#2961

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The E2E child receives the environment variable, its only production propagation is in the new boot
line, and the workflow trigger list omits that file.

scripts/verify-windows-autoupdate.mjs[213-223]
apps/desktop/src/main/runtime-host-boot.ts[420-425]
.github/workflows/release-windows-check.yml[15-41]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The Windows path-filtered workflow runs the end-to-end updater check but omits the boot module that passes the test-feed environment value into the update service.

## Issue Context
This propagation is required because the harness only sets `MAKA_UPDATE_TEST_FEED` in the packaged application's environment.

## Fix Focus Areas
- .github/workflows/release-windows-check.yml[15-30]
- apps/desktop/src/main/runtime-host-boot.ts[420-425]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Nested feed paths accepted ✓ Resolved 🐞 Bug ≡ Correctness
Description
The feed server validates and later asserts only each request's basename, so
/unexpected/latest.yml and nested installer URLs are served and counted as expected instead of
incrementing the unexpected-path count. This violates the harness's stated exact-path assertion and
can let a wrong updater request shape pass the release gate.
Code

scripts/verify-windows-autoupdate.mjs[R62-65]

+    const name = basename(pathName);
+    const record = { method, path: pathName, status: 0 };
+    requests.push(record);
+    if ((method !== 'GET' && method !== 'HEAD') || !allowedNames.has(name)) {
Relevance

●●● Strong

The basename-only validation is a concrete deterministic path-integrity bug, and no contrary
rejection precedent applies.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The server retains the full decoded pathname but checks only basename(pathName) against
allowedNames; the final evidence check repeats the basename comparison and relies on
unexpectedCount(), which is never incremented for nested paths with an allowed basename.

scripts/verify-windows-autoupdate.mjs[59-69]
scripts/verify-windows-autoupdate.mjs[293-313]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The loopback feed allowlist compares only URL basenames, allowing unexpected nested paths to pass the E2E gate.

## Issue Context
Reuse one exact-path allowlist for both serving requests and asserting observed downloads; no new configuration or public surface is needed.

## Fix Focus Areas
- scripts/verify-windows-autoupdate.mjs[61-66]
- scripts/verify-windows-autoupdate.mjs[293-312]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Upgraded sandbox smoke skipped ✓ Resolved 🐞 Bug ≡ Correctness
Description
Disposition: fix-now. Passing expectedVersion: nextVersion disables the sandbox and disclaimer
checks inside verifyPackagedWindowsApp, so the upgraded installation can pass this new end-to-end
gate even if its packaged sandbox executable or its broker/worker path is broken.
Code

scripts/verify-windows-autoupdate.mjs[R380-383]

+    await verifyPackagedWindowsApp(installDirectory, {
+      workingDirectory: join(temporaryDirectory, 'smoke'),
+      expectedVersion: nextVersion,
+      smokeRenderer: async (executable, { workingDirectory }) => {
Relevance

●●● Strong

This deterministically bypasses existing sandbox checks in a newly claimed full smoke gate; no
contrary precedent applies.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The new verifier passes an expected version, and the existing verifier derives both resource
requirements from whether that option is absent.

scripts/verify-windows-autoupdate.mjs[378-383]
scripts/verify-windows-x64.mjs[99-110]
scripts/verify-windows-x64.mjs[120-190]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The upgraded-install verification supplies an expected version, which currently also suppresses Windows sandbox and disclaimer verification intended only for an older baseline installer.

## Issue Context
The update target is newly built by this PR and should receive the same packaged sandbox verification as the candidate; only the legacy pinned baseline needs resource exemptions.

## Fix Focus Areas
- scripts/verify-windows-autoupdate.mjs[378-383]
- scripts/verify-windows-x64.mjs[82-190]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

4. Release gate changes skip CI 🐞 Bug ⚙ Maintainability
Description
The Windows check's expanded path filter includes the new scripts and updater service but omits
.github/workflows/release-desktop.yml, which now owns the actual release autoupdate commands. A
later PR can break or remove those commands without running the Windows packaging and verification
lane that is intended to validate this release path.
Code

.github/workflows/release-windows-check.yml[R24-27]

+      - 'scripts/verify-windows-autoupdate.mjs'
+      - 'scripts/package-windows-autoupdate-next.mjs'
+      # The packaged updater's feed behavior is only observable on this path.
+      - 'apps/desktop/src/main/app-update-service.ts'
Relevance

●●● Strong

Recent Windows precedent accepted expanding path filters to cover every contract-owning release
path.

PR-#2961

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The check is path-filtered and claims to run whenever the Windows release path changes, while the
new build and verification commands are defined in release-desktop.yml; that workflow file is
absent from the enumerated paths.

.github/workflows/release-windows-check.yml[3-16]
.github/workflows/release-windows-check.yml[16-41]
.github/workflows/release-desktop.yml[156-167]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Changes to the release workflow's new Windows autoupdate gate do not trigger the Windows PR check.

## Issue Context
Extend the existing `pull_request.paths` authority with the workflow file itself; deletion or consolidation cannot preserve validation when this separate release workflow changes, and this adds no new state or public surface.

## Fix Focus Areas
- .github/workflows/release-windows-check.yml[15-41]
- .github/workflows/release-desktop.yml[156-167]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


5. State assertions are overstated ✓ Resolved 🐞 Bug ⚙ Maintainability
Description
Disposition: fix-now. The documentation says every updater state is individually asserted, but the
harness only polls a snapshot until downloaded; fast checking/available/downloading
transitions and the installing transition can occur without being observed.
Code

docs/windows-support.md[R31-32]

+passes the full packaged smoke — with the feed requests, every updater state, and the final version
+asserted individually. What is still not proven: update signature verification (no Authenticode
Relevance

●●● Strong

Recent documentation precedent accepted correcting overstated technical guarantees and aligning
documentation with actual invariants.

PR-#3028

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The harness invokes a check and repeatedly reads current status until it sees only downloaded;
after installUpdate it waits for process exit rather than asserting the installing status.

scripts/verify-windows-autoupdate.mjs[255-291]
scripts/verify-windows-autoupdate.mjs[315-348]
docs/windows-support.md[28-35]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The support documentation claims state-by-state updater assertions that the test does not perform.

## Issue Context
The harness checks the final downloaded state and later app exit/relaunch, but it does not subscribe to or record the intermediate status-change events.

## Fix Focus Areas
- docs/windows-support.md[28-35]
- docs/windows-support.md[60-64]
- scripts/verify-windows-autoupdate.mjs[255-291]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Context sources
Review mode: 🧠 Deep: This is a high-density behavioral change spanning updater configuration, packaging metadata, CI workflows, CDP orchestration, installer handoff, process cleanup, and multiple independent Windows paths; redundant review could catch subtle integration defects.

Grey Divider

Tip of the day
💡 Did you know, you can show, collapse, or hide each part of a finding: code, evidence, and all

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread scripts/verify-windows-autoupdate.mjs Outdated
Comment thread scripts/verify-windows-autoupdate.mjs
Comment thread apps/desktop/src/main/runtime-host-boot.ts

@Astro-Han Astro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The overall E2E shape is good: the production feed remains unchanged without the test override, the override is loopback-only and fail-closed, fake next-version artifacts stay outside release upload globs, metadata hashes are verified, and install/home/process cleanup is isolated.\n\nThe current head is not runnable as a release gate because of the deterministic old-blockmap failure below. Existing Qodo threads also correctly cover three additional gaps, which I am not duplicating inline: basename-only path validation, expectedVersion accidentally disabling current sandbox/disclaimer checks, and incomplete workflow path filters for the production feed wiring. Those must be resolved before this can serve as release evidence.\n\nCurrent CI has the Windows package failure, a cancelled Windows CLI validation, and one check still running.\n\nReviewed with Codex using three independent reviewer agents and OpenCode Go DeepSeek V4 Flash (high); I verified the exact head, live Windows request trace, updater fallback behavior, workflow permissions/artifacts, package-verifier semantics, existing threads, and live CI.\n\n

中文\n\n整体 E2E 结构不错:没有 test override 时 production feed 不变;override 仅允许 loopback 且 fail closed;假的 next-version artifact 不会进入 release upload glob;metadata hash 会校验;install/home/process cleanup 也相互隔离。\n\n但当前 head 不能作为 release gate 运行,因为下面的旧 blockmap 失败是确定性的。已有 Qodo 线程还正确指出三个缺口,我不重复发布行内问题:只按 basename 校验路径;expectedVersion 意外关闭当前 sandbox/disclaimer 检查;以及 production feed wiring 的 workflow path filter 不完整。这些都需修复后才能作为 release evidence。\n\n当前 CI 有 Windows package failure、一项 Windows CLI validation cancelled,另有一项仍在运行。\n\n本次由 Codex 配合三个独立 reviewer agent,以及 OpenCode Go DeepSeek V4 Flash(high)审查;我核验了精确 head、实时 Windows request trace、updater fallback、workflow permissions/artifacts、package verifier 语义、已有线程和实时 CI。\n\n

Comment thread scripts/verify-windows-autoupdate.mjs
The first CI run proved the whole in-app path (latest.yml fetch, new
blockmap, full installer download, downloaded state) and failed only on
the harness's own too-strict feed assertion: electron-updater probes
the *previous* version's blockmap to attempt a differential download,
and the 404 for that legitimate probe was counted as an unexpected
request.

Serve the candidate's own blockmap exactly as a GitHub release hosts
the previous version's assets, so the differential path is real; when
the file is absent next to the candidate installer, the mapped-but-
absent 404 no longer counts as unexpected and the updater's documented
fallback to the full download proceeds. Truly unknown paths still fail
the run.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Generated-by: Claude Fable 5

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
scripts/verify-windows-autoupdate.mjs (1)

64-73: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Reject non-canonical feed paths.

basename(pathName) accepts arbitrary paths that end in an allowed filename. For example, GET /unexpected/latest.yml returns latest.yml and passes the later served('latest.yml') assertion.

Require the exact expected path, such as pathName === \/${name}``, before serving the mapped file. This preserves the stated invariant that unknown paths return 404.

Disposition: fix-now.

Source: Path instructions


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 2efaeaf9-3774-4d2e-910e-0213220328f4

📥 Commits

Reviewing files that changed from the base of the PR and between 315a289 and df30ccb.

📒 Files selected for processing (1)
  • scripts/verify-windows-autoupdate.mjs

Included review availability: Your plan provides up to 3 included reviews per hour; 2 remain after this review.

@Astro-Han Astro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The old-installer blockmap behavior is now modeled correctly, so the earlier deterministic 404 failure is resolved. The loopback feed, next-version artifact, hash checks, isolated install/home, and process scoping remain good foundations.

I’m keeping COMMENT because the existing release-evidence gaps still apply on this head:

  • exact feed request shape is still reduced to basename, so nested/unexpected paths can be served and accepted;
  • passing expectedVersion for the freshly built update still disables sandbox/disclaimer resource checks;
  • the Windows PR path filter still misses the production feed wiring and release workflow;
  • the docs still overstate the updater states actually observed.

Those issues are already covered by existing review threads, so I have not duplicated them inline. I added one new P2 inline for repeatedly buffering the full installer when the differential downloader makes range requests. The current Windows release check is also still running.

The simplest final state is one exact pathname allowlist shared by serving and assertions, version checking separated from explicit resource requirements, and range-aware streaming (or one bounded immutable file cache) for the large installer.

AI-assisted review disclosure: two Codex reviewers independently re-reviewed exact head df30ccb, the 34-line delta, current Windows CI, release artifacts, and existing feedback; no external model was used.

中文说明

旧 installer blockmap 的 404 已正确处理。当前仍需解决已有线程中的几个 release evidence 缺口:feed 仍按 basename 而非精确 pathname 校验;新构建版本因为传入 expectedVersion 而关闭 sandbox/disclaimer 检查;Windows path filter 仍漏掉生产 wiring/release workflow;文档仍夸大实际断言的 updater state。另新增一个 P2 行内评论:range 请求会反复把完整 installer 读入内存。最简终态是共享精确路径 allowlist、把版本校验与资源豁免拆开,并对大 installer 做 range-aware streaming 或有界缓存。

Comment thread scripts/verify-windows-autoupdate.mjs Outdated
Four review findings, each accepted:

- Exact-path feed matching (Qodo): the allowlist compared basenames, so
  a nested /x/latest.yml would have been served as expected. The server
  and the evidence assertions now match full root-level request paths;
  anything else counts as unexpected and fails the run.
- In-memory feed bodies (CodeRabbit): ranged installer requests
  re-read a multi-hundred-megabyte file per request, risking the 180s
  download deadline on slow runners. Bodies are read once at startup.
- Full verification of the upgraded install (Qodo): expectedVersion
  historically marks a released baseline and silently exempted the
  sandbox and disclaimer checks. Those exemptions are now explicit
  options defaulting to the old behavior, and the auto-update gate
  passes requireWindowsSandbox/requireDisclaimer: true -- the upgraded
  install is a current build and gets the full packaged verification.
- Boot wiring in the Windows gate paths (Qodo): a change limited to the
  runtime-host-boot.ts propagation of MAKA_UPDATE_TEST_FEED would not
  have triggered release-windows-check; the filter now includes it.

Also per Astro-Han's P1 (the differential probe, fixed in the previous
commit): the probe is now asserted explicitly -- the run fails if the
updater never asked for the previous blockmap, and the log records
whether the differential or the full-download path was exercised. Docs
narrowed to match: transient updater states are not individually
asserted.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Generated-by: Claude Fable 5

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 789ecaed-4363-427d-acbb-ed313915f86b

📥 Commits

Reviewing files that changed from the base of the PR and between df30ccb and 055ec02.

📒 Files selected for processing (4)
  • .github/workflows/release-windows-check.yml
  • docs/windows-support.md
  • scripts/verify-windows-autoupdate.mjs
  • scripts/verify-windows-x64.mjs

Included review availability: Your plan provides up to 3 included reviews per hour; 1 remains after this review.

Comment thread scripts/verify-windows-autoupdate.mjs Outdated
Comment thread scripts/verify-windows-autoupdate.mjs
Comment thread scripts/verify-windows-x64.mjs Outdated

@Astro-Han Astro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This head fixes the prior full-installer re-read, adds the missing boot workflow trigger, and explicitly verifies the upgraded sandbox/disclaimer resources. One new deterministic blocker remains, and the three current CodeRabbit threads still need disposition; I did not duplicate those inline.

The dedicated Windows package job is still running, so the new release gate has not completed successfully yet.

AI-assisted review disclosure: Codex reviewed exact head 055ec02, including the delta from the last Astro-Han review, current CI, and all live review threads, with two independent reviewer passes.

中文说明

当前 head 已修复重复读取完整安装包、补上 boot wiring 的 workflow 触发,并显式验证升级后安装包中的 sandbox/disclaimer 资源。但新增的 smoke 工作目录没有在首次写文件前创建,会让自动更新门禁确定性失败;另外 3 条当前 CodeRabbit inline 仍需处理,这里没有重复发布。专用 Windows package job 仍在运行。

Comment thread scripts/verify-windows-autoupdate.mjs Outdated
liugddx and others added 2 commits August 19, 2026 18:54
Four defects in the gate's own previous round of fixes, each confirmed
before changing anything:

- Create the smoke directory before verifyPackagedWindowsApp: with
  requireWindowsSandbox now true, the sandbox probe writes its manifest
  into workingDirectory before any renderer smoke mkdir would have
  created it, so the upgraded-install smoke deterministically threw
  ENOENT (Astro-Han P1).
- Match the raw request target: decoding and query-stripping before the
  allowlist meant /%6catest.yml and /latest.yml?cache=1 passed an
  assertion that claims exact root-level paths (CodeRabbit).
- Report the download mode from the installer transfer responses: a 200
  on the previous blockmap proves the feed served it, not that the
  updater downloaded differentially. The log now records the blockmap
  status and the actual installer response sequence (CodeRabbit).
- Treat expectedVersion: null like undefined for the sandbox/disclaimer
  defaults, matching the version fallback's nullish semantics
  (CodeRabbit).

Astro-Han's range re-buffering P2 was already fixed in the previous
commit: bodies are read once at startup and ranged responses serve
subarrays.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Generated-by: Claude Fable 5
…aths

The previous round matched the raw request target including the query,
per review -- and the live gate immediately proved that shape wrong:
electron-updater cache-busts its channel request as latest.yml?noCache=<id>,
so the exact-target match 404'd the updater's own documented request and
the check failed with "Cannot find channel latest.yml".

Match the raw, undecoded path segment (so /%6catest.yml still cannot
alias /latest.yml) and allow the query, recording the full target in the
request log as evidence. Truly unknown paths still count as unexpected
and fail the run.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Generated-by: Claude Fable 5

@Astro-Han Astro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved on exact head 72c409ee445432adf0ba99651b848f2189ba2aa1.

The Windows automatic-update gate is end-to-end rather than a packaging proxy: it exercises 0.1.11 → 0.1.12 through the loopback feed, metadata and blockmap probe, installer download, installUpdate, NSIS relaunch, ProductVersion validation, packaged sandbox/disclaimer smoke, and uninstall cleanup. Feed override handling stays test-only and fail-closed for non-loopback URLs.

No remaining P0-P2 findings. Current CI is fully green, mergeability is CLEAN, and there are no unresolved review threads. This changes release verification rather than production UI/UX, so a screenshot is not applicable. The PR body names Claude Fable 5 and all seven commits carry matching Generated-by: trailers.

AI-assisted review disclosure: Codex reviewed the exact-head release workflow, feed isolation, artifact/update lifecycle, tests, current CI, threads, and provenance metadata. No external model was used. Astro-Han authorized this review campaign.

中文说明

Windows 自动更新门禁验证的是完整真实链路,而不只是打包结果:覆盖旧版到新版更新、feed/metadata/blockmap、下载安装、NSIS 自动重启、版本检查、packaged smoke 与卸载清理。

当前没有剩余 P0-P2,CI 全绿、CLEAN、无未解决线程;该 PR 不改变生产 UI/UX,无需截图。PR body 与 7 个 commit 的 AI disclosure 也完整一致。

@Astro-Han

Copy link
Copy Markdown
Contributor

Human merge confirmation: Astro-Han reviewed the final diff at 72c409ee44 and explicitly authorized this squash merge. This satisfies the independent human-review gate for user-visible update behavior and release security evidence. All required checks are green, the current head is approved, and there are no unresolved review threads. The squash commit will retain Generated-by: Claude Fable 5.

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