docs: second rework of the release procedure after review
check / check (push) Canceled after 0s
e2e / e2e-chrome (push) Canceled after 0s
e2e / e2e-firefox (push) Canceled after 0s

Apply the five wording findings of the second review verbatim: the owner's
rule as one lead-in with three acts, the yarn source without a pronoun, the
workflow definition with its file first, the two e2e jobs named with their
scripts, and the manifest version tied to step 2. The same three faults
were fixed elsewhere: pronouns with two candidates, phrases that arrive
after the words they belong to, and terms used before they were defined.

Model: fable-5-1
This commit is contained in:
2026-11-02 03:34:38 +00:00
parent c45cf628a9
commit fd53737403
+41 -34
View File
@@ -1,13 +1,13 @@
# 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.
A release is a tagged commit on the `main` branch and the two archives that
`make package` writes from that commit. This file is the procedure, in order.
The owner ([@sneak](https://sneak.berlin)) alone decides three things:
Only the owner ([@sneak](https://sneak.berlin)):
- whether and when a release is tagged;
- the tags: only the owner creates, moves or deletes a tag;
- where the archives are published.
- decides whether and when a release is tagged;
- creates, moves or deletes a tag;
- decides 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
@@ -16,16 +16,17 @@ 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 repository): https://git.eeqj.de/sneak/AutistMask. The tracker's Actions
page, https://git.eeqj.de/sneak/AutistMask/actions, lists the workflow runs.
- A workflow is a file under `.gitea/workflows/` that lists jobs. The tracker
runs every workflow on each push. 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 from `main` only
(`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 clean checkout is a working tree (the checked-out files) at the release
commit where `git status --porcelain` prints nothing. The build stamps the
commit hash (the commit's id) into the extension and appends `-dirty` when the
tree has local changes.
- A tag is a name that git attaches 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
@@ -39,8 +40,9 @@ file).
step 1. `make package` needs no Docker.
- Node and yarn. Both come from the system when present. Otherwise
`script/bootstrap` installs 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, through `npm` (Node's package manager).
nvm, a Node version manager. `script/bootstrap` installs yarn through corepack
(Node's package manager installer). When no corepack is found,
`script/bootstrap` installs yarn through `npm` (Node's package manager).
## Procedure
@@ -51,23 +53,25 @@ every push. `script/cibuild` runs:
- `script/bootstrap`, which installs the dependencies;
- `script/check`, which is what `make check` runs;
- the Docker image build.
- the Docker image build (an image is Docker's packaged form of a program).
`script/check` runs:
- `script/test`: the tests, in Docker;
- `script/lint`, in Docker, which runs three checks:
- `script/lint`, which runs three checks in Docker:
- ESLint, a code checker;
- `prettier --check .`, the formatting check;
- `script/check-censored`, which fails when the name that `RULES.md` bars
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.
The `e2e` workflow (`.gitea/workflows/e2e.yml`) has two jobs. One runs
`script/test-e2e`, the browser tests in Chrome. The other runs
`script/test-e2e-firefox`, the browser tests in Firefox. 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.
Run `script/cibuild` on the clean checkout. `script/cibuild` runs the same steps
as the `check` run, and 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
@@ -108,7 +112,7 @@ make package
`make build`:
- runs `build.js`. It writes `dist/chrome/`, `dist/firefox/` and a build
- runs `build.js`. `build.js` writes `dist/chrome/`, `dist/firefox/` and a build
receipt. The receipt lists every file that `build.js` wrote, with each file's
digest. The receipt lives outside the repository and is deleted when the build
ends;
@@ -127,9 +131,11 @@ make package
- 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 `version` in each `manifest.json` is the tree's version;
popup's HTML page names is inside the browser's directory (`dist/chrome/` or
`dist/firefox/`). The manifest is `manifest.json`, the file that describes the
extension to the browser. The popup is the window that the toolbar button
opens;
- checks that the `version` in each `manifest.json` is the version from step 2;
- writes one archive per browser;
- reads each archive back;
- compares every member (a file inside the archive) with `dist/` byte for byte;
@@ -147,7 +153,7 @@ 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.
extension). The `.xpi` 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.
@@ -159,18 +165,19 @@ version from step 2.
From `release/`, run `sha256sum -c SHA256SUMS`.
The archives are deterministic for a given `dist/`. `script/lib/zip.js`:
The archives are deterministic for a given `dist/`: the same `dist/` always
gives the same bytes. `script/lib/zip.js`:
- sorts the members;
- sets every timestamp to 1980-01-01;
- sets every member's timestamp (its recorded date) 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
Check: `sha256sum -c` prints `OK` for both archives. On the same UTC day, a
second `make package` from another clean checkout of the same commit gives the
same `SHA256SUMS`.
### 5. Tag the release commit
@@ -185,7 +192,7 @@ 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.
the tag 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
@@ -210,10 +217,10 @@ Chrome loads the extension from a 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.
`manifest/chrome.json` sets the id.
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
115 or newer, in any edition, loads the `.xpi` as a temporary add-on from the
`about:debugging` page. Firefox unloads a temporary add-on when Firefox exits.
The section "Installing on Firefox" in `README.md` has the details.