Fix the eight findings of the first review: step 1 now runs script/cibuild on the clean checkout instead of make check; the owner's rule on tagging and publishing is stated in the opening and in steps 5 and 6; the Chrome unzip goes into a new directory; yarn comes from corepack or npm; the comma lists are bullets; the prettier check is named once with its two places; the re-read sentences are rewritten; the tracker and its Actions page are named with their links. A fact pass ran every command of steps 2 to 4 and the unzip on a fresh clone, and a definition pass glossed each technical word. Model: fable-5-1
8.2 KiB
Releasing AutistMask
A release is a tagged commit on main and the two archives that make package
writes from that commit. This file is the procedure, in order.
The owner (@sneak) alone decides three things:
- whether and when a release is tagged;
- the tags: only the owner creates, moves or deletes a tag;
- where the archives are published.
Anyone can run steps 1 to 4. No script in this repository uploads or publishes the archives. None signs them, and none packs a CRX (Chrome's packaged extension file).
Terms
- The tracker is the repository's Gitea site (Gitea is the software that hosts the repository): https://git.eeqj.de/sneak/AutistMask. Its Actions page, https://git.eeqj.de/sneak/AutistMask/actions, lists the workflow runs.
- A workflow is a list of jobs that the tracker runs on every push, from a file
under
.gitea/workflows/. Each job runs one script on the tracker's runner (the machine that runs jobs). - The release commit is a commit on
main. Releases are tagged frommainonly (TODO.md, Workflow). - A clean checkout is a working tree at the release commit where
git status --porcelainprints nothing. The build stamps the commit hash into the extension and appends-dirtywhen the tree has local changes. - A tag is a name that git attaches to one commit. Release tags read
vX.Y.Z, such asv0.1.0. - A digest is the sha256 hash of a file (a fixed-length fingerprint of its bytes), written as 64 hexadecimal characters.
What you need
- A clean checkout of the release commit.
make.- Docker (a tool that runs programs in containers), for
script/cibuildin step 1.make packageneeds no Docker. - Node and yarn. Both come from the system when present. Otherwise
script/bootstrapinstalls Node through a pinned (fixed-version) install of nvm, a Node version manager. It installs yarn through corepack (Node's package manager installer) or, failing that, throughnpm(Node's package manager).
Procedure
1. Confirm the release commit is green
The check workflow (.gitea/workflows/check.yml) runs script/cibuild on
every push. script/cibuild runs:
script/bootstrap, which installs the dependencies;script/check, which is whatmake checkruns;- the Docker image build.
script/check runs:
script/test: the tests, in Docker;script/lint, in Docker, which runs three checks:- ESLint, a code checker;
prettier --check ., the formatting check;script/check-censored, which fails when the name thatRULES.mdbars appears outside its allowed places;
script/fmt-check:prettier --check .again, on the host.
The e2e workflow (.gitea/workflows/e2e.yml) runs the two browser suites on
every push, as two jobs. A red e2e job does not fail the check run.
Run script/cibuild on the clean checkout. It runs the same steps as the
check run, and it must exit 0.
Check: on the Actions page, the check run for the release commit succeeded,
and both e2e jobs for that commit succeeded. script/cibuild exited 0 on the
clean checkout.
2. Confirm the version
Three files declare the version:
package.json;manifest/chrome.json;manifest/firefox.json.
script/lib/version.js fails the build when the three disagree.
tests/version.test.js fails when the version is not three numbers separated by
dots. Nothing compares the version with a tag. That comparison is this step.
Check: all three files read the same X.Y.Z, and vX.Y.Z is the tag that the
owner creates in step 5.
3. Build and package
On the clean checkout, run:
make setup
make package
make setup installs two things:
- the dependencies, from the lockfile
yarn.lock, which pins each one's version; - the git pre-commit hook, a script that git runs before each commit. Here the
hook runs
script/check.
make package runs make build, then script/lib/package.js.
make build:
- runs
build.js. It writesdist/chrome/,dist/firefox/and a build receipt. The receipt lists every file thatbuild.jswrote, with each file's digest. The receipt lives outside the repository and is deleted when the build ends; - runs
script/verify-build --expect releaseagainst the receipt. The check fails when:dist/differs from the receipt;- a bundle (one JavaScript file built from many source files) was compiled
with the build-time constant
DEBUGon; - any file carries the test recovery phrase, which a debug build gives every new wallet;
- runs
script/check-censored --require-distagainst the built files; - removes
dist/when any of those steps fails.
script/lib/package.js:
- empties
release/; - refuses a symbolic link (a file that points at another file) under
dist/; - checks that the archive is self-contained: every file that the manifest or the
popup's HTML page names is inside the browser's directory. The manifest is
manifest.json, the file that describes the extension to the browser; - checks that the
versionin eachmanifest.jsonis the tree's version; - writes one archive per browser;
- reads each archive back;
- compares every member (a file inside the archive) with
dist/byte for byte; - writes
SHA256SUMS, with one line per archive:- the digest;
- two spaces;
- the file name.
make package writes three files:
release/autistmask-chrome-<version>.zip
release/autistmask-firefox-<version>.xpi
release/SHA256SUMS
The .xpi is Firefox's add-on package file (an add-on is Firefox's word for an
extension). It is not signed.
make package prints a line that starts Not shipped and names styles.css.
This is expected: each archive holds its own copy of the stylesheet.
Check: release/ holds those three files, and <version> in their names is the
version from step 2.
4. Verify the digests
From release/, run sha256sum -c SHA256SUMS.
The archives are deterministic for a given dist/. script/lib/zip.js:
- sorts the members;
- sets every timestamp to 1980-01-01;
- compresses each member the same way every time.
dist/ itself carries the build date. build.js compiles the build date, a UTC
(universal time) day, into the extension. So two builds of the same commit on
the same UTC day give byte-identical archives. Builds on different days differ.
Check: sha256sum -c prints OK for both archives. A second make package
from another clean checkout of the same commit, on the same UTC day, gives the
same SHA256SUMS.
5. Tag the release commit
Owner only. Only the owner decides whether and when a release is tagged. Only the owner creates, moves or deletes a tag. On the release commit, the owner runs:
git tag -a vX.Y.Z -m "AutistMask X.Y.Z"
git push origin vX.Y.Z
-a makes an annotated tag: a tag that carries a message and records who made
it and when. v0.1.0 is one.
Once the tag exists, git describe --tags on the release commit prints
vX.Y.Z. script/cibuild and script/docker pass that value to the Docker
image build as VERSION.
Check: git rev-parse vX.Y.Z^{commit} prints the release commit's hash, and the
tag is listed at https://git.eeqj.de/sneak/AutistMask/tags.
6. Publish the archives
Owner only. The owner decides where the archives are published. No script in this repository uploads or publishes them.
Whoever receives an archive installs it as follows.
Chrome loads the extension from a directory:
- unzip the archive into a new, empty directory, for example
unzip autistmask-chrome-0.1.0.zip -d autistmask-chrome; - open
chrome://extensions/and turn on "Developer mode"; - click "Load unpacked" and pick that directory.
Chrome then reports the extension id gipbhkogfopeahplcjhipkgpcimdpkip. The id
is the fixed string that identifies the extension to Chrome; the key in
manifest/chrome.json sets it.
Release builds of Firefox refuse to install an unsigned add-on. Desktop Firefox
115 or newer, in any edition, loads the .xpi as a temporary add-on from its
about:debugging page. Firefox unloads a temporary add-on when Firefox exits.
The section "Installing on Firefox" in README.md has the details.
Check: each archive installs on a fresh browser profile (a new, empty set of browser data) by the route above.