From 3a6c06aa0e4e42fd0c0be77660ba3ac3a757b2e2 Mon Sep 17 00:00:00 2001 From: Sergii Demianchuk Date: Thu, 23 Jul 2026 23:02:18 -0400 Subject: [PATCH] docs(releasing): commit the version-stamp check and the auto-update warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These 38 lines had been sitting uncommitted in the working tree across several releases, surviving only because nobody ran `git checkout -- .`. They document two things the runbook needs and neither is guessable from the scripts: 1. VERIFY THE VERSION STAMP (§3). The app must report levelcodeVersion, not the Code-OSS base — and this fails SILENTLY, so it needs an explicit per-release check. Includes the one-liner, what each field must be, and a table mapping the two build log lines to "good" vs "fix before publishing". 2. PUBLISHING IS DEPLOYING (§5). With LEVELCODE_UPDATE_FEED_SIGNED=1, publishing a release makes every existing install download and apply it at its next check. No staged rollout, and the rollback lever stops propagation but cannot un-update anyone who already took it. Plus a curl to confirm the feed actually picked the release up. I did NOT write this prose — it is pre-existing work by the repo owner. My part was verifying it before enshrining it, since committing an inaccurate runbook is worse than leaving it uncommitted: - both quoted log lines exist verbatim (build-macos.sh:101 and :105) - stamp-levelcode-version.mjs does write levelcodeVersion and deliberately leaves `version` at the Code-OSS base for engines.vscode, exactly as the note claims - the feed path and the "LevelCode Updater" user-agent match what production nginx actually logs Co-Authored-By: Claude Opus 4.8 --- docs/RELEASING.md | 39 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/docs/RELEASING.md b/docs/RELEASING.md index 850db4a..c643d7b 100644 --- a/docs/RELEASING.md +++ b/docs/RELEASING.md @@ -53,7 +53,7 @@ export NOTARY_PROFILE="levelcode-notary" ```bash ./scripts/bootstrap.sh # first time / after an upstream bump (clone + brand + patch + npm ci) -./scripts/build-macos.sh # → VSCode-darwin-/LevelCode.app, then auto-strips proprietary MS/Copilot code from the app +./scripts/build-macos.sh # → VSCode-darwin-/LevelCode.app; strips proprietary MS/Copilot code, then stamps the release version (§3) ./scripts/make-dmg.sh # de-Microsoft (defensive) → sign (Developer ID) → dmg → notarize → staple → verify ``` With `CODESIGN_IDENTITY` + `NOTARY_PROFILE` set, `make-dmg.sh` runs the whole signed+notarized pipeline; @@ -75,6 +75,26 @@ find VSCode-darwin-arm64/LevelCode.app \( -path "*@github/copilot*" -o -path "*m \( -name "*.node" -o -name "*.dylib" -o -name "mxc-exec-mac" \) -print # → prints NOTHING ``` +Confirm the app reports **its own** version rather than the Code-OSS base — this **fails silently**, so +check it every release: +```bash +python3 -c "import json;d=json.load(open('VSCode-darwin-arm64/LevelCode.app/Contents/Resources/app/product.json'));print(d.get('levelcodeVersion'),'|',d.get('version'),'|',d.get('commit','')[:7])" +# → 0.8.1 | 1.126.0 | 07b7341 +# ^ the release version ^ MUST stay 1.126.0 (extensions' engines.vscode is checked against it) +``` +A **missing `levelcodeVersion`** means `build-macos.sh` could not resolve a tag (`git describe --tags` +failed) and fell back — the update tooltip and About will then report `1.126.0`, the upstream base, which +is exactly the confusion this stamp exists to prevent. The build log distinguishes the two: + +| Log line | Meaning | +| --- | --- | +| `[build] Stamping the LevelCode release version (v0.8.1) …` | Good. | +| `[build] WARN: no reachable git tag — skipping the release-version stamp.` | **No stamp** — fix before publishing. | + +An off-tag build is stamped `0.8.1-3-g1a2b3c4` on purpose (see `scripts/stamp-levelcode-version.mjs`): a +build that isn't a release must not impersonate one. A release build sits on the tag and gets a clean +`0.8.1`, so a suffix here means you are not building what you think you are. + Build **both architectures** on matching hardware (Apple silicon → `arm64`, Intel → `x64`) to ship both dmgs. ## 4. Publish @@ -92,10 +112,27 @@ from **levelcode.ai/download**. ## 5. The update feed +> ⚠️ **Auto-update is LIVE — publishing is deploying.** `LEVELCODE_UPDATE_FEED_SIGNED=1` is set in +> production, so a published release is no longer just *announced*: the built-in Squirrel updater +> **downloads and installs it on every existing install** at that install's next check. There is no +> staged rollout and no percentage gate. The rollback lever (below) stops it propagating further but +> **cannot un-update anyone who already took it**. Finish §3 on a real Mac *before* `--draft=false`. +> +> This changed with v0.8.0 — the first release to carry signed `.app.zip` assets. Everything published +> before that stayed notify-only regardless of the flag, because the feed refuses to hand Squirrel a +> release that has no installable asset for its arch. + Publishing the GitHub release **is** the announcement — `Levelcode::EditorReleaseFeed` (thin.ly) reads `releases/latest` and serves `/api/update/{target}/{quality}/{commit}`. There is no feed file to hand-maintain per release. +Confirm the feed picked the release up (it caches for 5 minutes): +```bash +curl -s -H "User-Agent: LevelCode Updater" \ + https://levelcode.ai/api/update/darwin-arm64/stable/deadbeef | python3 -m json.tool +# url must end in LevelCode-arm64.app.zip (NOT a /releases/tag/ page), and productVersion must be the new one +``` + Two assets, **not** interchangeable: | Asset | Consumer |