Files
AutistMask/docs/RELEASE.md
T
sneak 69ce8fd6d6
check / check (push) Waiting to run
e2e / e2e-chrome (push) Waiting to run
e2e / e2e-firefox (push) Waiting to run
docs: rewrite the release procedure sentence by sentence against the prose rules
Rewrite docs/RELEASE.md from scratch: a terms list, what is needed, and six
numbered steps, each with its check. Every statement is checked against the
Makefile, script/, build.js, the two workflows and the tests. The step that
described distribution routes not built is cut; installing from the archives
replaces it. The reproducibility claim now names the build date that build.js
compiles in, so it holds only within one UTC day.

Model: fable-5-1
2026-11-02 02:49:45 +00:00

6.9 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.

Nothing in this repository publishes. There is no store upload, no signing and no CRX (a CRX is Chrome's packaged extension file). Anyone can run the build and the packaging. Tagging is the owner's job (@sneak).

Terms

  • The release commit is the commit on main to be released. main receives one pull request per milestone (the tracker's set of issues for one version), from next, and releases are tagged from main (TODO.md, Workflow).
  • A clean checkout is a working tree at the release commit where git status --porcelain prints nothing. The build stamps the commit hash into the extension and appends -dirty when the tree has local changes.
  • A tag is a name that git fixes to one commit. Release tags read vX.Y.Z, such as v0.1.0.
  • A digest is the sha256 hash of a file (a fixed-length fingerprint of its bytes), written as 64 hex characters.
  • A workflow is a list of jobs that the tracker runs on every push, from a file under .gitea/workflows/. The tracker lists the runs on the repository's Actions page.

What you need

  • A clean checkout of the release commit.
  • make.
  • Docker, for make check. make package needs no Docker.
  • Node and yarn. Both come from the system when present. Otherwise make setup installs Node through a pinned install of nvm (a Node version manager) and yarn through corepack (Node's package manager installer).

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 what make check runs: the tests, the lint checks and the formatting check. The lint checks are ESLint (a code checker), the prettier check (a formatting check) and script/check-censored;
  • the Docker image build.

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.

Check: on the Actions page, the check run for the release commit succeeded, and both e2e jobs for that commit succeeded. make check on the clean checkout gives the same answer as the check run.

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 step 5 creates.

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, which writes dist/chrome/ and dist/firefox/, and a build receipt: the list of every file that build.js wrote, with each file's digest. The receipt lives outside the repository and is deleted when the build ends;
  • runs script/verify-build --expect release against 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 DEBUG on;
    • any file carries the fixed recovery phrase that a debug build hands out;
  • runs script/check-censored --require-dist against 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 every file named by the manifest or the popup markup is inside the browser's directory;
  • checks that the version in each manifest.json is the tree's version;
  • writes one archive per browser, reads it back and compares every member (a file inside the archive) with dist/ byte for byte;
  • writes SHA256SUMS: one line per archive with the digest, two spaces and 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. It is not signed. The output line Not shipped names dist/styles.css. build.js writes the compiled stylesheet there and copies it into each browser directory. The copy at the root of dist/ is in neither archive. make clean removes dist/ and release/.

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 and fixes the compression level. dist/ itself carries the build date: build.js compiles the day of the build, in UTC (universal time), into the extension, with the commit hash and the version. 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. On the release commit, run:

git tag -a vX.Y.Z -m "AutistMask X.Y.Z"
git push origin vX.Y.Z

-a makes an annotated tag: a tag object with its own message, author and date. 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 tracker lists the tag under the repository's tags.

6. Hand out the archives

The archives in release/ are the release. This repository has no further step.

Chrome loads the extension from a directory:

  • unzip autistmask-chrome-<version>.zip;
  • open chrome://extensions/ and turn on "Developer mode";
  • click "Load unpacked" and pick the unzipped directory.

Chrome then reports the extension id gipbhkogfopeahplcjhipkgpcimdpkip, which the key in manifest/chrome.json fixes.

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 about:debugging and unloads it when Firefox exits. The section "Installing on Firefox" in README.md has the details.

Check: each archive installs on a fresh browser profile by the route above.