Skip to main content

Build and release

The build is a dependency graph​

tools/build/build.js is not a script that runs steps in order. Each step declares what it needs, and anything whose needs are met runs, up to one job per core — about 16 s of sequential steps down to about 4 s, the length of the longest chain. Output is buffered and printed in declaration order, so the log still reads top to bottom.

Steps marked ⁽ᵒ⁾ are optional: a stale document, a drifted code map or a changed trace row prints its findings and never blocks a build. Their gating forms are npm run pages:docs:check, events:docs:check, dispatch:eval and map:check, run on their own. check:live is the one check in the build that is not optional, because what it catches is a setting that silently does nothing on a running script.

app.gap.Tsum/tools/build/build.js
loading...

build.sh and build.ps1 translate flags into this and do nothing else. They used to hold the same recipe twice, in two dialects, and had drifted.

The sign step runs only when the build is given the maintainers' signing key. It comes after every dist: step and before the archive and the adb push, so the signature covers exactly the files that ship. Without the key the build is complete and unsigned, and the release tool warns loudly, because an unsigned release gets no network on a device linked for remote management. Why that is the right trade is Trust and access.

Channels​

config.json names three channels — Alpha, Beta, Production — each with a display name, an archive base name, a catalogue directory, a note appended to every release message, and a Status: the lowest ReleaseStatus that channel offers. A skill or setting marked Alpha is listed on Alpha builds only; promoting it later is a one-word edit.

app.gap.Tsum/config.json
loading...

The version is not there. It is package.json's version, so npm version and the release cannot disagree. The archive is named from both: TsumTsum-Alpha-5.0a2.zip. The settings page carries a $VERSION placeholder substituted at build time, and so does the game bundle (ScriptVersion in data.ts), so the stats CSV can say which build played a round.

What ships is compacted only in ways that cannot change it​

terser runs with compress: false and mangle: false — the same program, minus whitespace. Top-level names are the API: the settings page evaluates start({...}) and stop() by name, and the host calls onEvent and onLog, so toplevel mangling could never be turned on. --verify bundle evaluates the shipped file under the Node host shim and fails the build if a name the bridge needs has gone.

The archive is written by tools/build/zip.js, a small deterministic writer, because zip and Compress-Archive disagree about entry order and metadata and the same dist/ used to produce two different digests. The .sha256 sidecar holds the bare 64-character digest and nothing else, so a copy that has travelled to a device can be checked back against the build it came from.

The release pipeline​

npm run release:<channel>:

  1. Reads the ### Summary bullets of the ## [<version>] section of CHANGELOG.md. There is no [Unreleased]; a missing section or a section with no Summary fails before anything is built.
  2. Renders the note exactly as the app and Discord will show it — the Summary's **Additions** / *Area* / **Fixes** groups (a numbered list when it has no headings), the channel's note, the character count against MessageMaxChars — and waits: a approve, e edit in $EDITOR, d deny. An approved edit is offered back to CHANGELOG.md. --yes skips the gate; --dry-run writes nothing.
  3. Builds the channel.
  4. Writes into <Catalogue>/<Directory> in the sibling catalogue checkout: the zip; metadata.json, whose top-level fields describe this build and whose Versions array lists the last HistoryLimit builds so a player can roll back; and a per-channel CHANGELOG.md. Archives past the limit are deleted. The hash is taken from the bytes just written, so an entry can never describe a build other than the one beside it.
  5. Prints what to do next: check the entry with the catalogue's own build-catalogue script, then commit and push there.
app.gap.Tsum/tools/release/release.js
loading...

Release to the catalogue is the step-by-step; Your own library source explains the format the catalogue publishes and how to host one yourself.