LevelCode v0.8.0
LevelCode v0.8.0
The release that makes Check for Updates… real.
Until now that dialog always answered "There are currently no updates available." That was correct behaviour rather than a bug: the update feed deliberately refused to hand the built-in updater anything, because there was nothing it could safely install. Squirrel installs a signed .zip of the app, never a .dmg — and LevelCode only ever published dmgs.
v0.8.0 ships the missing artifact and the client-side plumbing around it. This is the first build published with update assets attached, so it is the release the update path starts from.
Highlights
Signed, notarized update assets
scripts/make-dmg.sh now emits LevelCode-<arch>.app.zip alongside the .dmg — on the Developer-ID path only.
- The dmg stays the human download (open, drag to Applications); the zip is update-only. They are not interchangeable.
- Built with
ditto -c -k --sequesterRsrc --keepParent, which preserves the stapled notarization ticket — so an app installed by an update still validates offline, with no Gatekeeper prompt. Re-zipping withzip(1)would drop it. - Emitted only when real Developer ID signing is used. Squirrel refuses an update whose signing identity doesn't match the running app, so an ad-hoc build can never masquerade as an update asset.
- The zip is cut after notarization completes, never before — an unstapled app would otherwise ship.
The Download button can't hand you a raw zip
The notify-only updater's Download action preferred the feed's url. That was correct while url was a release page — but now that the feed serves the signed .app.zip there, the same code would have started a raw zip download instead of opening a page.
Download now resolves product.downloadUrl → this release's notes page → the feed base, with the install artifact dropped from the human path entirely. A future feed change cannot put a zip back behind that button.
Updates roll out behind two independent guards
The server only offers a build to the auto-installing updater when both hold: signed feed assets are declared live, and the resolved release actually has a signed zip for that architecture. Releases cut before this one therefore stay notify-only on their own, rather than relying on the rollout being performed in the right order. Architecture matching is by exact filename, so an Intel install can never be handed the arm64 build.
Under the hood
scripts/make-dmg.sh— emits the Squirrel update asset (plus a local-only.sha256for hand-verifying a publish); header step list corrected to match the script, and the "unnotarized" warning scoped to the ad-hoc path it actually describes.extensions/levelcode-updater/update.js— new puredownloadUrl(feed, product, base)helper.extensions/levelcode-updater/extension.js— the Download action now delegates to it.docs/AUTO-UPDATE.md— new. The full feed contract, rollout order, risks, and the exit test, including which failure modes are enforced in code versus only documented.docs/RELEASING.md— publishing steps updated for the second asset, and an explicit note that the.sha256sidecars stay local (the feed reads GitHub's own asset digest).
Test coverage
extensions/levelcode-updater/test/update.test.js— 9 cases, 2 new, covering the Download path: the helper never returnsfeed.urleven when that is a signed.app.zip, falls back to a page rather than an artifact, and tolerates a null feed or product. Both new cases were mutation-checked — restoring the previous preference order fails them.- The release CI gate continues to run the 13
levelcode-aisuites. Note thelevelcode-updatertests are not part of that gate yet and are currently run manually.
Full changelog: v0.7.2...v0.8.0