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.
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.
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.
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-0.12.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>:
- Reads the
### Summarybullets of the## [<version>]section ofCHANGELOG.md. There is no[Unreleased]; a missing section or a section with no Summary fails before anything is built. - Renders the note exactly as the app will show it — a numbered list, the
channel's note, the character count against
MessageMaxChars— and waits:aapprove,eedit in$EDITOR,ddeny. An approved edit is offered back toCHANGELOG.md.--yesskips the gate;--dry-runwrites nothing. - Builds the channel.
- Writes into
<Catalogue>/<Directory>in the sibling catalogue checkout: the zip;metadata.json, whose top-level fields describe this build and whoseVersionsarray lists the lastHistoryLimitbuilds so a player can roll back; and a per-channelCHANGELOG.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. - Prints what to do next: run the catalogue's own
build-officialscript there and commit.
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.