Workspace rules, typography, and commits: see ../CLAUDE.md.
- The release train (
train.yml,scripts/train.py): after any release it releases what lies downstream ingraph.yml. - The composite actions every
release.ymlcalls:.github/actions/prepare,central,finish. Callers use them at@master, so a change here reaches every repository's next release at once. Keep inputs and outputs backward compatible. meta.json(meta.yml,scripts/meta.py): the component list the launcher downloads, at R2 keylauncher/meta.json, served ashttps://ancaria.dev/files/launcher/meta.json.- Lines-of-code badges (
cloc.yml,scripts/cloc.py): R2 keysbadges/<repo>.json. - Never put application code, version files, or pins here. A version exists only as a tag
v<version>with a GitHub Release.
graph.ymlreposlists every repository the train may release;edgessay who must follow whom. An edge'sinputis therelease.ymlinput the downstream reads the upstream version from. An edge withpathspropagates only when a file matching one of them (**crosses directories) changed between the upstream's previous tag and its new one; with no previous tag it always propagates.- A downstream repository is released once, after all its released upstreams, at its own highest tag plus a patch (
1.3.0-rc.1becomes1.3.0) unlessversionsnames it. - To add a repository: give it a
release.ymlthat follows the contract, then add it torepos. To add an edge: add the matching input to the downstream'srelease.ymlfirst, then the edge. Add ameta.componentsentry only for something the launcher downloads. scripts/cloc.pykeeps its own list of code repositories (REPOS); add new code repositories there too.python -m unittestmust pass. Planning (walk,topo_order,preview) is pure; keep GitHub calls inGitHubandTrain.
- Every step must survive a rerun of a finished or half-finished job.
preparesaysdone=trueonly for a published (not draft) release.finishcreates a draft, uploads with--clobber, then publishes it, so a failed upload leaves a draft the rerun completes.- The train skips a repository whose target release is already published. A rerun keeps
GITHUB_RUN_ID, so it finds its earlier dispatches by the run titleRelease <version> · train <id>and reuses their versions instead of bumping again. Never change that title format without changingtrain_run_version. meta.pyuploads only whenmeta.jsondiffers from the live file. Badges are always overwritten.
- Org variable
DEVOPS_APP_IDand org secretDEVOPS_APP_KEY: a GitHub App with Contents read and write and Actions read and write, installed on every ancaria-dev repository.finishuses it to start the train; the train uses it to dispatch and watch releases;clocuses it to read private repositories. - In devops:
CLOUDFLARE_API_TOKEN(R2 write onancaria-files) andCLOUDFLARE_ACCOUNT_ID. - Pin
wranglerto an exact version in both upload steps.
python scripts/train.py --dry-run --versions "coderpack=2.1.0"prints the plan with your ownghlogin and dispatches nothing.actionlintlints the workflows; it does not read the composite actions, so runshellcheckon their scripts after changing them.