chore: ./build is dev-only; ./build alpha is the explicit deploy

Reverts the prior CLI simplification. ./build (no arg) now does source
work only — tool dist/ + cross-compiled zddc-server binaries — and
leaves the website worktree alone. Channel/release cuts are explicit:

  ./build                  dev build (source only, no deploy)
  ./build alpha            cut alpha          (cascades nothing)
  ./build beta             cut beta           (cascades alpha → beta)
  ./build release [X.Y.Z]  cut stable         (cascades all)

Rationale: editing source shouldn't have a side-effect on the live
site. The website worktree at ~/src/zddc-website/ is what Caddy serves
in real time, so any write to it is a deploy. Treating dev iteration
as alpha-publish was confusing — the user wanted source builds and
deploys to be distinct verbs.

Mechanically: a `dev` (default) branch is added to the case statement;
the post-build matrix-index regen + channel-link verifier are
conditional on RELEASE_CHANNEL being set; dev builds skip them.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
ZDDC 2026-05-02 08:29:58 -05:00
parent 6167e99f3a
commit 76e1e78c55
10 changed files with 112 additions and 84 deletions

View file

@ -3,25 +3,26 @@
## Commands ## Commands
```bash ```bash
# ── Lockstep release driver: every invocation is a publish action. ──────── # ── ./build subcommands ────────────────────────────────────────────────────
# Alpha is the default since "alpha = active dev iteration" and that's # `./build` (no arg) is a source-side dev build only — assembles tool/dist/
# what running the build IS. Coordinated version (for release): max # + cross-compiles zddc-server. The website worktree is left alone.
# across all six tools' latest tags + 1. Workflow: # Channel + release subcommands deploy artifacts to the website worktree
# alpha = active dev → beta = ready for general testing → stable = ship # under $ZDDC_DEPLOY_RELEASES_DIR (default ~/src/zddc-website/releases).
# Workflow: alpha = active dev → beta = ready for testing → release = ship.
./build # cut alpha (default) — cascades nothing ./build # dev build (no website worktree write)
./build beta # cut beta — cascades alpha → beta ./build alpha # cut alpha (cascades nothing)
./build release # cut stable — coordinated next version, ./build beta # cut beta (cascades alpha → beta)
# cascades all to the new version ./build release # cut stable, coordinated next version
# (cascades alpha + beta → new stable; tags all six)
./build release 1.2.0 # cut stable at explicit version ./build release 1.2.0 # cut stable at explicit version
./build help # usage ./build help
# Build a single HTML tool's dist/ for testing (does NOT touch the # Single-tool dev build for testing (does NOT touch the website worktree):
# website worktree's release artifacts):
sh tool/build.sh # archive|transmittal|classifier|mdedit|landing sh tool/build.sh # archive|transmittal|classifier|mdedit|landing
# Single-tool release (rare; prefer the lockstep ./build above so # Single-tool release (rare; prefer ./build alpha|beta|release so versions
# versions don't drift between tools). Same flag form as before. # don't drift between tools). Same flag form as before.
sh tool/build.sh --release [<version>|alpha|beta] sh tool/build.sh --release [<version>|alpha|beta]
./freshen-channel <tool> <channel> # rebuild one tool's alpha/beta from its current stable tag ./freshen-channel <tool> <channel> # rebuild one tool's alpha/beta from its current stable tag

View file

@ -25,15 +25,19 @@ This is a **monorepo of independent tools**, not one application:
## Most-used commands ## Most-used commands
```bash ```bash
# Lockstep release driver. Every invocation is a publish action; # Source-side dev build only — assembles tool/dist/ + cross-compiles
# alpha is the default since "alpha = active dev iteration". # zddc-server. Does NOT write to the website worktree.
./build # cut alpha (default) ./build
./build beta # cut beta — cascades alpha → beta
./build release # cut stable — coordinated next version, cascades all # Channel/release cuts — write into ~/src/zddc-website/releases/
./build alpha # cut alpha (cascades nothing)
./build beta # cut beta (cascades alpha → beta)
./build release # cut stable, coordinated next version
# (cascades alpha + beta → new stable; tags all six tools)
./build release X.Y.Z # cut stable at explicit version ./build release X.Y.Z # cut stable at explicit version
./build help # usage ./build help # usage
sh tool/build.sh # build one HTML tool's dist/ (no channel write) sh tool/build.sh # iterate on one HTML tool's dist/
sh tool/build.sh --release [...] # single-tool release (rare; prefer the lockstep ./build) sh tool/build.sh --release [...] # single-tool release (rare; prefer the lockstep ./build)
./freshen-channel <tool> <channel> # rebuild one tool's alpha/beta from its current stable tag ./freshen-channel <tool> <channel> # rebuild one tool's alpha/beta from its current stable tag
@ -55,7 +59,7 @@ No lint/typecheck/format commands exist for the HTML tools — vanilla JS + POSI
- **No tags for alpha/beta.** Channel URLs are stable URLs by design — appending counter tags would defeat the purpose. The on-page label encodes `<date> · <sha>` for traceability. Stable cuts get clean `<tool>-vX.Y.Z` tags for every tool (six tags per cut, all sharing the same X.Y.Z). - **No tags for alpha/beta.** Channel URLs are stable URLs by design — appending counter tags would defeat the purpose. The on-page label encodes `<date> · <sha>` for traceability. Stable cuts get clean `<tool>-vX.Y.Z` tags for every tool (six tags per cut, all sharing the same X.Y.Z).
- **Pre-release semver in the on-page label.** Plain dev builds and `--release alpha|beta` cuts embed `vX.Y.Z-{alpha,beta}` in `{{BUILD_LABEL}}` where X.Y.Z is the next-stable target. Plain dev adds a full timestamp + `-dirty` marker; `--release alpha|beta` is date-only. - **Pre-release semver in the on-page label.** Plain dev builds and `--release alpha|beta` cuts embed `vX.Y.Z-{alpha,beta}` in `{{BUILD_LABEL}}` where X.Y.Z is the next-stable target. Plain dev adds a full timestamp + `-dirty` marker; `--release alpha|beta` is date-only.
- **Channel-link verifier.** Every `./build` ends with a check that every `<tool>_{stable,beta,alpha}.html` (and zddc-server's per-platform binary mirrors + stub pages) resolves. Bootstrap-friendly: skips zddc-server checks until the first `--release` cut materializes the binaries. - **Channel-link verifier.** Every `./build` ends with a check that every `<tool>_{stable,beta,alpha}.html` (and zddc-server's per-platform binary mirrors + stub pages) resolves. Bootstrap-friendly: skips zddc-server checks until the first `--release` cut materializes the binaries.
- **`sh tool/build.sh` is a single-tool dev build.** Writes `dist/<tool>.html` only; no new per-version files / symlink updates in the website worktree. Useful for iterating on one tool without cutting alpha for the whole suite. To publish, run `./build` (cuts alpha for everything together). Release artifacts always land in `${ZDDC_DEPLOY_RELEASES_DIR:-~/src/zddc-website/releases}`; nothing is pushed automatically — commit + push the website branch when you want to publish. - **`./build` (no arg) is a source-side dev build.** Assembles `tool/dist/` + cross-compiled binaries; does NOT write to the website worktree. Use it to iterate without affecting the live site. To deploy, run `./build alpha|beta|release` — those promote to `${ZDDC_DEPLOY_RELEASES_DIR:-~/src/zddc-website/releases}`. Nothing is pushed to Codeberg automatically; commit + push the website branch when you want to publish.
- **Always build before running tests** — Playwright opens `dist/tool.html` via `file://`. - **Always build before running tests** — Playwright opens `dist/tool.html` via `file://`.
- **`</` in JS string/template literals breaks inline `<script>`** embedding. `shared/build-lib.sh` provides `escape_js_close_tags`; every tool's `build.sh` runs JS through it before inlining. - **`</` in JS string/template literals breaks inline `<script>`** embedding. `shared/build-lib.sh` provides `escape_js_close_tags`; every tool's `build.sh` runs JS through it before inlining.
- **All ZDDC parsing/formatting/hashing goes through `window.zddc`** (from `shared/zddc.js` + `shared/hash.js` + `shared/zddc-filter.js`). API: `parseFilename`, `parseFolder`, `parseRevision`, `formatFilename`, `formatFolder`, `compareRevisions`, `isValidStatus`, `splitExtension`, `joinExtension`, `crypto.{sha256Hex, sha256String, sha256File, bytesToHex}`, `filter.{parse, matches}`. File objects across tools use `trackingNumber` (string) and `extension` (string, **no leading dot** — use `zddc.joinExtension(name, ext)` to build a filename). Add edge cases to `tests/zddc.spec.js`, not per-tool tests. - **All ZDDC parsing/formatting/hashing goes through `window.zddc`** (from `shared/zddc.js` + `shared/hash.js` + `shared/zddc-filter.js`). API: `parseFilename`, `parseFolder`, `parseRevision`, `formatFilename`, `formatFolder`, `compareRevisions`, `isValidStatus`, `splitExtension`, `joinExtension`, `crypto.{sha256Hex, sha256String, sha256File, bytesToHex}`, `filter.{parse, matches}`. File objects across tools use `trackingNumber` (string) and `extension` (string, **no leading dot** — use `zddc.joinExtension(name, ext)` to build a filename). Add edge cases to `tests/zddc.spec.js`, not per-tool tests.

77
build
View file

@ -1,27 +1,26 @@
#!/bin/sh #!/bin/sh
set -eu set -eu
# build — lockstep release driver for ZDDC. # build — ZDDC source build + lockstep release driver.
# #
# Three subcommands. The first arg names a channel and every invocation # ./build dev build: assemble tool dist/, cross-compile
# is a publish action — alpha is the default since "alpha = active dev # zddc-server binaries. NO write to the website
# iteration" and that's what running the build IS. # worktree; nothing gets deployed.
# ./build alpha dev build + copy alpha mirrors to the website
# worktree (cascades nothing).
# ./build beta dev build + copy beta mirrors (cascades alpha → beta).
# ./build release dev build + cut coordinated stable
# (cascades alpha + beta → new stable; tags all six tools).
# ./build release X.Y.Z same, explicit version.
# ./build help this message.
# #
# ./build cut alpha (default; active dev iteration) # Lockstep: every channel/release cut bumps all six tools (5 HTML +
# ./build beta cut beta (cascades alpha → beta) # zddc-server) together. Coordinated next-stable = max(latest tag) + 1.
# ./build release cut stable (coordinated next version;
# cascades alpha + beta → stable)
# ./build release X.Y.Z cut stable at explicit version
# ./build help this message
# #
# Lockstep: every cut bumps all six tools (5 HTML + zddc-server) # Channel/release cuts write to the website worktree at
# together. The coordinated next-stable version is # ${ZDDC_DEPLOY_RELEASES_DIR:-$HOME/src/zddc-website/releases}. Caddy serves
# max(latest tag across all tools) + 1. # that path live; nothing is pushed to Codeberg automatically. To publish:
# # cd ~/src/zddc-website && git add -A && git commit && git push origin website
# Output goes to ${ZDDC_DEPLOY_RELEASES_DIR:-$HOME/src/zddc-website/releases}
# — the website branch's worktree, which is what Caddy serves. Nothing
# is pushed automatically; commit + push the website branch yourself
# when you want the changes mirrored to Codeberg.
SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd) SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd)
@ -33,10 +32,17 @@ root_dir="$SCRIPT_DIR"
. "$SCRIPT_DIR/shared/build-lib.sh" . "$SCRIPT_DIR/shared/build-lib.sh"
# --- Parse subcommand ------------------------------------------------------ # --- Parse subcommand ------------------------------------------------------
# RELEASE_CHANNEL empty means dev mode (build only, no website worktree
# writes); set means a channel/release cut that promotes to the website
# worktree under $ZDDC_DEPLOY_RELEASES_DIR.
RELEASE_CHANNEL="" RELEASE_CHANNEL=""
RELEASE_VERSION="" RELEASE_VERSION=""
case "${1:-alpha}" in case "${1:-dev}" in
dev|build)
# Dev build: tool dist/ + zddc-server binaries only. Touches
# nothing in the website worktree.
;;
alpha) alpha)
RELEASE_CHANNEL="alpha" RELEASE_CHANNEL="alpha"
;; ;;
@ -64,9 +70,11 @@ case "${1:-alpha}" in
;; ;;
esac esac
# Per-tool argument list. The single-tool build.sh scripts still take # Per-tool argument list. Pass --release flag only when we're cutting
# the legacy `--release [channel|version]` form, so translate. # a channel/release; dev builds invoke each tool with no args.
if [ "$RELEASE_CHANNEL" = "stable" ]; then if [ -z "$RELEASE_CHANNEL" ]; then
TOOL_RELEASE_ARGS=""
elif [ "$RELEASE_CHANNEL" = "stable" ]; then
TOOL_RELEASE_ARGS="--release $RELEASE_VERSION" TOOL_RELEASE_ARGS="--release $RELEASE_VERSION"
else else
TOOL_RELEASE_ARGS="--release $RELEASE_CHANNEL" TOOL_RELEASE_ARGS="--release $RELEASE_CHANNEL"
@ -218,13 +226,16 @@ echo " binary version: $ZDDC_BINARY_VERSION"
' '
# --- Promote zddc-server release artifacts --------------------------------- # --- Promote zddc-server release artifacts ---------------------------------
# Copy the freshly cross-compiled binaries to the website worktree's # On a channel/release cut, copy the freshly cross-compiled binaries to
# releases/ under their canonical names + symlinks. promote_zddc_server # the website worktree's releases/ under their canonical names +
# also re-runs write_zddc_server_stubs_all internally, so the matrix-cell # symlinks. promote_zddc_server also re-runs write_zddc_server_stubs_all
# stub pages get regenerated in the same call. # internally, so the matrix-cell stub pages get regenerated in the same
# call. On a plain dev build, skip — we don't touch the worktree.
if [ -n "$RELEASE_CHANNEL" ]; then
echo "" echo ""
echo "=== Promoting zddc-server $RELEASE_CHANNEL release ===" echo "=== Promoting zddc-server $RELEASE_CHANNEL release ==="
promote_zddc_server "$RELEASE_CHANNEL" "$RELEASE_VERSION" "$RELEASES_DIR" "$SCRIPT_DIR/zddc/dist" promote_zddc_server "$RELEASE_CHANNEL" "$RELEASE_VERSION" "$RELEASES_DIR" "$SCRIPT_DIR/zddc/dist"
fi
# Latest stable version, by following archive_stable.html → versioned target. # Latest stable version, by following archive_stable.html → versioned target.
# Returns "" if no stable cut exists yet (bootstrap state). All HTML tools # Returns "" if no stable cut exists yet (bootstrap state). All HTML tools
@ -623,6 +634,9 @@ PIN_END
echo "Wrote $_out" echo "Wrote $_out"
} }
# Matrix index + verifier only run when we touched the website
# worktree. Dev builds leave the worktree alone.
if [ -n "$RELEASE_CHANNEL" ]; then
echo "" echo ""
echo "=== Building releases/index.html ===" echo "=== Building releases/index.html ==="
build_releases_index build_releases_index
@ -630,10 +644,18 @@ build_releases_index
echo "" echo ""
echo "=== Verifying channel links ===" echo "=== Verifying channel links ==="
verify_channel_links "$RELEASES_DIR" verify_channel_links "$RELEASES_DIR"
fi
echo "" echo ""
echo "=== All tools built successfully ===" echo "=== Build done ==="
echo "" echo ""
if [ -z "$RELEASE_CHANNEL" ]; then
echo "Mode: dev (source-only build, website worktree untouched)"
echo " tool/dist/*.html ready"
echo " zddc/dist/zddc-server-* binaries ready"
echo ""
echo "To deploy alpha mirrors to the website: ./build alpha"
else
echo "Cut: $RELEASE_CHANNEL" echo "Cut: $RELEASE_CHANNEL"
if [ -n "$RELEASE_VERSION" ]; then if [ -n "$RELEASE_VERSION" ]; then
echo "Version: v$RELEASE_VERSION" echo "Version: v$RELEASE_VERSION"
@ -649,3 +671,4 @@ echo "Artifacts written to $RELEASES_DIR/"
echo " cd $(dirname "$RELEASES_DIR") && git status # to review the deploy" echo " cd $(dirname "$RELEASES_DIR") && git status # to review the deploy"
echo " cd $(dirname "$RELEASES_DIR") && git add -A && git commit && git push origin website" echo " cd $(dirname "$RELEASES_DIR") && git add -A && git commit && git push origin website"
echo " ↑ commits + pushes the website branch when you're ready to publish" echo " ↑ commits + pushes the website branch when you're ready to publish"
fi

View file

@ -1774,7 +1774,7 @@ body.help-open .app-header {
</svg> </svg>
<div class="header-title-group"> <div class="header-title-group">
<span class="app-header__title">ZDDC Markdown</span> <span class="app-header__title">ZDDC Markdown</span>
<span class="build-timestamp"><span style="color:red;font-weight:bold">v0.0.9-alpha · 2026-05-02 · 76820fa</span></span> <span class="build-timestamp"><span style="color:red;font-weight:bold">v0.0.9-alpha · 2026-05-02 · 6167e99</span></span>
</div> </div>
<button id="select-directory" class="btn btn-primary" title="Select a Directory">Select Directory</button> <button id="select-directory" class="btn btn-primary" title="Select a Directory">Select Directory</button>
</div> </div>

View file

@ -2113,7 +2113,7 @@ td[data-field="trackingNumber"] {
</svg> </svg>
<div class="header-title-group"> <div class="header-title-group">
<span class="app-header__title">ZDDC Archive</span> <span class="app-header__title">ZDDC Archive</span>
<span class="build-timestamp"><span style="color:red;font-weight:bold">v0.0.9-alpha · 2026-05-02 · 76820fa</span></span> <span class="build-timestamp"><span style="color:red;font-weight:bold">v0.0.9-alpha · 2026-05-02 · 6167e99</span></span>
</div> </div>
<button id="addDirectoryBtn" class="btn btn-primary">Add Local Directory</button> <button id="addDirectoryBtn" class="btn btn-primary">Add Local Directory</button>
<button id="refreshHeaderBtn" class="btn btn-secondary hidden" title="Refresh Data" style="font-size:1.1rem;"></button> <button id="refreshHeaderBtn" class="btn btn-secondary hidden" title="Refresh Data" style="font-size:1.1rem;"></button>

View file

@ -1376,7 +1376,7 @@ body.help-open .app-header {
</svg> </svg>
<div class="header-title-group"> <div class="header-title-group">
<span class="app-header__title">ZDDC Classifier</span> <span class="app-header__title">ZDDC Classifier</span>
<span class="build-timestamp"><span style="color:red;font-weight:bold">v0.0.9-alpha · 2026-05-02 · 76820fa</span></span> <span class="build-timestamp"><span style="color:red;font-weight:bold">v0.0.9-alpha · 2026-05-02 · 6167e99</span></span>
</div> </div>
<button id="selectDirectoryBtn" class="btn btn-primary">Select Directory</button> <button id="selectDirectoryBtn" class="btn btn-primary">Select Directory</button>
<button id="refreshBtn" class="btn btn-secondary hidden" title="Refresh and rescan directory">Refresh</button> <button id="refreshBtn" class="btn btn-secondary hidden" title="Refresh and rescan directory">Refresh</button>

View file

@ -866,7 +866,7 @@ body {
</g> </g>
</svg> </svg>
<span class="app-header__title">ZDDC Archive</span> <span class="app-header__title">ZDDC Archive</span>
<span class="build-timestamp"><span style="color:red;font-weight:bold">v0.0.9-alpha · 2026-05-02 · 76820fa</span></span> <span class="build-timestamp"><span style="color:red;font-weight:bold">v0.0.9-alpha · 2026-05-02 · 6167e99</span></span>
</div> </div>
<div class="header-right"> <div class="header-right">
<button id="theme-btn" class="btn btn-secondary" title="Theme: auto (follows OS)" aria-label="Theme: auto (follows OS)"></button> <button id="theme-btn" class="btn btn-secondary" title="Theme: auto (follows OS)" aria-label="Theme: auto (follows OS)"></button>

View file

@ -1774,7 +1774,7 @@ body.help-open .app-header {
</svg> </svg>
<div class="header-title-group"> <div class="header-title-group">
<span class="app-header__title">ZDDC Markdown</span> <span class="app-header__title">ZDDC Markdown</span>
<span class="build-timestamp"><span style="color:red;font-weight:bold">v0.0.9-alpha · 2026-05-02 · 76820fa</span></span> <span class="build-timestamp"><span style="color:red;font-weight:bold">v0.0.9-alpha · 2026-05-02 · 6167e99</span></span>
</div> </div>
<button id="select-directory" class="btn btn-primary" title="Select a Directory">Select Directory</button> <button id="select-directory" class="btn btn-primary" title="Select a Directory">Select Directory</button>
</div> </div>

View file

@ -2210,7 +2210,7 @@ dialog.modal--narrow {
</svg> </svg>
<div class="header-title-group"> <div class="header-title-group">
<span class="app-header__title">ZDDC Transmittal</span> <span class="app-header__title">ZDDC Transmittal</span>
<span class="build-timestamp"><span style="color:red;font-weight:bold">v0.0.9-alpha · 2026-05-02 · 76820fa</span></span> <span class="build-timestamp"><span style="color:red;font-weight:bold">v0.0.9-alpha · 2026-05-02 · 6167e99</span></span>
</div> </div>
<div class="app-header__spacer"></div> <div class="app-header__spacer"></div>
<div class="app-header__icons"> <div class="app-header__icons">

View file

@ -1,6 +1,6 @@
# Generated by build.sh — do not edit. One <app>=<build label> per line. # Generated by build.sh — do not edit. One <app>=<build label> per line.
archive=v0.0.9-alpha · 2026-05-02 · 76820fa archive=v0.0.9-alpha · 2026-05-02 · 6167e99
transmittal=v0.0.9-alpha · 2026-05-02 · 76820fa transmittal=v0.0.9-alpha · 2026-05-02 · 6167e99
classifier=v0.0.9-alpha · 2026-05-02 · 76820fa classifier=v0.0.9-alpha · 2026-05-02 · 6167e99
mdedit=v0.0.9-alpha · 2026-05-02 · 76820fa mdedit=v0.0.9-alpha · 2026-05-02 · 6167e99
landing=v0.0.9-alpha · 2026-05-02 · 76820fa landing=v0.0.9-alpha · 2026-05-02 · 6167e99