Lays foundation for unified release versioning across MLSysBook publishable artifacts. Pure additions — no existing builds, configs, or sources are touched. scripts/version/release.py Python CLI with helpers: - compute-id: semver bump from previous tag (patch/minor/major/none/explicit) - compute-hash: deterministic SHA-256 over input directories with per-file index - emit-release: writes releases/<project>-<id>/release.json (canonical artifact) - emit-manifest: writes the build-time manifest the deployable bundles Tier A (citable) emits per-file Merkle index; Tier B (lite) is flat. scripts/version/schema.json JSON Schema for release.json. Validates project/tier/release_id/release_hash + Tier A's files[] index. Used by validators in CI. shared/release/release-pill.html Footer snippet — fetches deployable manifest at runtime, renders "v0.1.0 · Apr 26, 2026" pill. Configured per-project via <meta name="release-manifest"> tag. Silent on any fetch failure. shared/release/release-card.html About-page snippet — fuller release-identity card with click-to-copy hash. Same fetch + meta-tag conventions. shared/release/README.md Operator-facing contract documentation. .github/workflows/_release-prepare.yml Reusable workflow_call. Validates confirm == "PUBLISH", computes new_release_id from previous tag + bump (delegates to release.py for canonical math). Outputs new_release_id/new_tag/previous_* for caller's downstream build and finalize steps. Refuses to re-tag existing releases (citation integrity). Caller workflows still own their build commands and tag/release creation; this only standardizes the input shape and version math.
5.2 KiB
Shared release infrastructure
Single source of truth for "what release is this?" across every publishable
artifact in the MLSysBook monorepo. Mirrors and generalizes the StaffML
pattern landed in feat/staffml-version. See docs/VERSIONING.md for
contributor-facing usage; this file is for operators and downstream
projects that need to know the contract.
What's here
| File | Purpose |
|---|---|
release-pill.html |
Footer snippet — small "v0.1.0 · Apr 26" identity pill |
release-card.html |
About-page snippet — fuller release-identity card with copyable hash |
README.md |
This file |
The Python helpers and JSON schema live at the repo root under
scripts/version/ so they can be invoked from any workflow without
relative-path gymnastics:
| Path | Purpose |
|---|---|
scripts/version/release.py |
CLI: compute-id, compute-hash, emit-release, emit-manifest |
scripts/version/schema.json |
JSON Schema for releases/<id>/release.json |
The reusable GitHub Actions workflow lives where workflows live:
| Path | Purpose |
|---|---|
.github/workflows/_release-publish.yml |
workflow_call — orchestrates bump + tag + release notes |
Contract
Every project that adopts the pattern produces TWO build-time artifacts on every publish:
-
releases/<project>-<release_id>/release.json— the canonical, commit-ready release artifact. Validates againstscripts/version/schema.json. Containsrelease_id,release_hash(full hex digest over input bytes),git_sha,created_at,input_paths, and ametadataobject with project-specific stats. Tier A also includes afiles: [{path, hash}, ...]array (Merkle-ish per-file hashes) for partial verification. -
<deployable>/release-manifest.json— the build-time projection the deployable bundles. Strict subset:releaseId,releaseHash,schemaVersion,tier,project,buildDate, plus ametadataobject. Static sites deploy this at the site root; the footer pill fetches it via<meta name="release-manifest" content="...">.
A project's site may extend (1) and (2) with project-specific keys
(StaffML's vault-manifest.json adds questionCount, trackDistribution,
etc.) — but those keys live in metadata, never at the top level.
Tiers
- Tier A (citable): full Merkle-style file index in
release.json. Use for academically-cited content (paper hashes, textbook releases, StaffML question bank, TinyTorch framework releases). - Tier B (lite): single SHA-256 over content directory; no per-file index. Use for rapidly-iterating content where citation isn't a primary concern (Kits, Labs, Instructors).
Footer pill setup (Quarto)
Each Quarto project does ~3 lines of config. Example for a project
deployed at https://mlsysbook.ai/<project-base>/:
# _quarto.yml
project:
resources:
- "../shared/release/release-pill.html"
format:
html:
include-in-header:
- text: |
<meta name="release-manifest" content="/<project-base>/release-manifest.json">
include-after-body:
- file: "../shared/release/release-pill.html"
The publish workflow drops release-manifest.json at the site root
(<deploy_path>/release-manifest.json) so the meta-tag URL resolves.
Footer pill setup (Next.js / hand-rolled)
The pill is a React-free static snippet — works in any HTML. Set the
meta tag once in your layout, then drop the snippet wherever you want
the pill (typically the footer). See StaffML's Footer.tsx for an
inline-React equivalent that bakes the manifest at build time instead
of fetching at runtime — that approach is preferred for citation-
critical content and is what StaffML uses.
Reusable workflow setup
Each project's <project>-publish-live.yml becomes a thin wrapper
calling _release-publish.yml. See StaffML's workflow for a full
example. The reusable workflow handles:
- Validates
confirm: PUBLISHsafety gate - Computes
new_release_idfromrelease_type+ previous tag - Calls the project's build with the computed
release_id - Validates the manifest the build emitted (must agree with computed id)
- Tags
<project>-v<release_id> - Generates GitHub Release notes (AI-enhanced if
ai_release_notes=yes) - Uploads the deployable artifact
The project-specific build commands are passed in via with:. The
reusable workflow never assumes a particular build tool.
When NOT to use this
- One-shot scripts or internal tools that don't get cited or deployed.
- Documentation that lives inside another project's repo (use the outer project's release identity).
- Anything that doesn't run through a publish workflow at all (Periodic-Table, currently — needs a publish workflow first).
Validating an existing release
# From repo root, validate a release.json against the schema:
python3 -c "
import json, jsonschema
schema = json.load(open('scripts/version/schema.json'))
release = json.load(open('releases/staffml-0.1.0/release.json'))
jsonschema.validate(release, schema)
print('OK')
"
(jsonschema package required; install via pip install jsonschema.)
See also
docs/VERSIONING.md— contributor-facing how-tointerviews/staffml/src/lib/stats.ts— StaffML's reference reader.github/workflows/_release-publish.yml— reusable workflow source