Windows VERSIONINFO and the MSIX package version were two derivations of
one fact, and the sideload one counted minutes since the last stable, which
overflows a 16-bit field after 45 days. Both now come from
storePackageVersionAt's year.hourOfYear.secondOfHour.0, so a later build
always sorts above an earlier one and the cap has nothing left to cap.
Canary and commit builds must not replace stable or share its desktop
state. Package names alone are insufficient because Electron reads the
product name before main initializes its paths. Pin nonstable userData
before the first lookup, and keep the packaged identity independent of
runtime build variables.
Keep release artifact filenames unchanged. Qualify payload CLI names,
route each nonstable MSIX alias to its own entrypoint, and copy the
immutable desktop provenance into the embedded Python checkout. Only
stable releases can use the official Store identity.
Targeted validation: 75 JavaScript tests passed, 2 platform skips;
15 Python tests passed with file retries disabled. Native Windows SDK
manifest proof is tracked separately. Full app install, signing and macOS
launch validation are not claimed.
Keep commit admission on the trusted workflow checkout and reject mixed
release inputs before loading repository code. Stage every built product
under its commit with receipt-bound summary links, never channel writes.
Build both Windows universal bundles through the existing SDK scripts.
Keep Store calendar versions separate from sideload app versions so zero-
major app versions remain packageable. Reject invalid arguments before
modifying bundles. Bind desktop and Termux versions to the source commit,
and record Termux cache provenance without labeling commits as tags.
Verification: 77 Python tests and 36 JS tests passed. Real makeappx packed
and unpacked disposable per-arch and universal packages. Seven official
workflow-expression checks, actionlint, syntax, lint and prose passed.
No signing, installed-app update, Android build, or remote dispatch ran.
The Windows package version ignored an explicit stable tag.
Use the tag for sideload version derivation and reject candidate metadata
that does not match. Keep the separate Store version policy unchanged.
Restrict baseline manifests to the configured release origin and reject
cross-origin manifest responses. Cover the checks with real loopback HTTPS
and extend the version and empty-gate regression tests.
Document retries that reuse the original artifacts. Remove an unreachable
manifest check and an unused test import.
Targeted Python and JavaScript tests, Ruff, ESLint and the shared MSIX
module typecheck passed. Full signed native release acceptance was not run.
Run the entire CI workflow before Docker build and tests. Require Nix,
native payload smoke tests, install/update E2E and signed-package upgrade
acceptance before publishing. Keep Desktop Playwright E2E deferred.
Archive tested Docker images and signed bundle candidates with provenance
and hashes. Publishers consume those exact artifacts without rebuilding.
Advance stable channels only after all required publications succeed.
Keep canaries on their separate path and reject direct stable-builder
publication that bypasses the gate.
Move shared release transport, manifests and gates to Python. Keep native
Electron adapters in JS and share feed/MIME facts as JSON. Replace the
R2/feed JS implementation and move its protocol tests to Python.
Verified targeted Python and JS tests, real loopback transport and CLI
execution, temporary Git admission, workflow graph lint, and typechecks.
No live stable release was run. Native signing, package upgrades and real
registry/Store promotion still need their release-run receipts. Separate
services cannot promote atomically. A promotion failure keeps the run red.
Route packaged macOS bundles and Light through the updater strategy.
Use electron-updater 6.8.9 and wait for native signature acceptance before
backend teardown. Keep checkout and Store ownership separate.
Share Darwin feed paths between packaging, runtime and publication.
Validate both native feeds, verify streamed artifact hashes, prevent
same-tag artifact replacement, and conditionally update the channel
pointer. Protect live feed references during canary retention.
Use one notarization owner. Require publishing credentials and validate
the stapled app. Keep Windows, Linux and Termux jobs unchanged.
Verified with updater/feed unit and transport tests, release-helper tests,
desktop typechecks, the desktop JS build, and workflow lint. No E2E,
native macOS install, release dispatch or public publication was run.
Sync r2-release.mjs to main's client before this fork's first real
R2 writes (the stale pre-divergence copy produced SignatureDoesNotMatch),
and make the post-put size verification robust: HEAD responses can lose
content-length through proxies (reproduced against the live bucket), so
fall back to a signed 1-byte ranged GET whose Content-Range carries the
authoritative size. The apt artifact MIME types (extensionless
InRelease/Release/Packages by exact basename, .deb, .asc) live in
msix-shared.mjs beside every other content type.
The electron tsconfig, renderer, e2e and electron-builder checkJs passes
now all pass cleanly. Three pre-existing issues fixed (the branch is
ahead of upstream/main, where none of these files exist):
- package-process-reap.ts: guard `isUnderInstallRoot` loop members with
`typeof root !== 'string'` so the TS union (string | readonly string[])
narrows before normalize() — the old `if (!root)` did not narrow arrays.
- msix-shared.mjs: add JSDoc param/return types to the four git-tag
helpers and the stamp/XML/Content-Type helpers so the electron-builder
checkJs pass (which walks this module via its import graph) stops
flagging implicit-any.
- electron-builder.config.cjs: bind storeMsix to a local + a
mustStoreMsix() assertion helper — `store` is typed boolean, so checkJs
cannot correlate it with the optional storeMsix; the helper is the
single documented assertion point (checkJs forbids `!`).
The fast-moving desktop prerelease channel is now "canary" everywhere:
the tag shape (vX.Y.Z-canary.<ts>), the electron-updater/R2 feed dirs
(canary.yml / releases/<os>/canary/), the update-channel consts and CLI
choices, the MSIX build-number derivation, the App Installer channel
paths, and the Windows Store flight var (MS_STORE_CANARY_FLIGHT_ID).
Also renames the scheduled workflow to canary-release.yml and the
release test file to test_release_canary.py, and flips the CLI flags
(--canary / --prune-canaries / prune-canaries subcommand).
Unrelated "nightly" mentions are untouched: Brave's own browser channel
(browser_connect), cron scheduling prose (README, i18n, cron/browser/
kanban docs, zh-Hans), upstream skill docs (comfyui/unsloth/torchtitan),
evals fixtures, Node's node-nightly prereleases, and cron job names in
gateway tests.
Note: MS_STORE_NIGHTLY_FLIGHT_ID was renamed to MS_STORE_CANARY_FLIGHT_ID
in the workflow — the matching repo/org variable on GitHub must be
renamed in repo settings for the Store flight ring to keep working.
The msixbundle + finalize jobs were both gated on the entire 6-leg build
matrix, so macos + linux legs blocked the win32 App Installer feed and
everything else downstream.
Restructure desktop-bundled-release.yml into per-OS jobs:
- build-win32 (x64 + arm64) — the only active builder legs; builds the
bundled + Store variants, audits arch, uploads *.msix, stages to R2.
- build-darwin / build-linux — dummy skips for now (no macOS updater arm;
linux unshipped). Matrix kept so downstream jobs stay green; re-enable
by restoring the build body + runners.
- publish-win32-updater (was msixbundle) — needs build-win32 only; stages
the App Installer feed (.appinstaller + universal .msixbundle).
- publish-win32-store (NEW, parallel) — bundles the two Store-*.msix into
one universal Store .msixbundle and submits it to the Windows Store via
the MSStore CLI (microsoft/microsoft-store-apppublisher@v1.4). Stable
tags only; gated on MS_STORE_PRODUCT_ID var so it stays skipped until
the release-signing environment is configured. The store bundle is left
unsigned on purpose — Partner Center re-signs on ingestion.
- publish-darwin-updater (was finalize) — dummy skip; no mac feed to
publish until the darwin electron-updater arm returns.
Shared plumbing: resolveWinSdkTools moves into msix-shared.mjs (single
resolver for both bundle jobs, kills the dead candidates var); new
bundle-store-msixbundle.mjs bundles the store per-arch packages and
prints the bundle path on stdout for the workflow.
Verified: node --check all scripts, yaml parses + needs graph resolves,
107 r2-release tests pass.
Wire the desktop app onto the pm store for real distribution:
- MSIX bundle: electron-builder config, appx assets, manifest, copilot
key + deep-link routing, App Installer + Windows Store variant
(sign only the msix; inner binaries covered by the package block map)
- Rust CLI shim (apps/desktop/shim) — bundled builds run from the store
python + shim, never the venv; payload symlinks relativized so the
relocatable venv survives relocation
- Cloudflare R2 release pipeline: publish binaries + update feeds,
nightly channels/tags, stamp-first version resolution
- Update system: gate, uninstall steward, boot bootstrap, release
channels, update receipts
- install.ps1 reduced to a 361-line stage-protocol bootstrapper (heavy
deps are pm's job); darwin updater + update-channel mirror ripped
- doctor: main's re-landed TCC anchor kept, termux branches removed
Rebuilt from ethie/pm onto the pm-store stack. 22 hot files hand-merged;
uv.lock + package-lock.json keep main's newer dep tree; test_engines
reads the pm/lock.json pin; lazy_deps.py deleted (all 222 importers
migrated to pm in the foundation commit).