diff --git a/AGENTS.md b/AGENTS.md index e4169f1..7865625 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -162,11 +162,11 @@ Format: `trackingNumber_revision (status) - title.extension` ### Releasing — channels and layout -Three channels: +Three channels. Versioning is **pre-release semver**: stable owns clean `vX.Y.Z`; alpha and beta carry `vX.Y.Z-alpha` / `vX.Y.Z-beta` suffixes (with a `.N` counter on the zddc-server image only — see "zddc-server release flow" below). The next-stable target X.Y.Z is patch-bumped from the latest clean `-vX.Y.Z` tag, so the on-page label always communicates which stable the alpha/beta is working toward. -- **Stable**: versioned, immutable. `sh tool/build.sh --release [version]` writes `website/releases/_v.html`, refreshes the `_stable.html` symlink, and tags `-v`. Skips automatically if source has not changed since the latest tag. Pass an explicit version to override auto-increment. -- **Beta**: mutable. `sh tool/build.sh --release beta` overwrites `website/releases/_beta.html` in place. No tag. The on-page label is `beta · · ` so the source is recoverable from git via the SHA. -- **Alpha**: mutable, analogous. `sh tool/build.sh --release alpha`. **Also**: every plain (non-release) `tool/build.sh` invocation copies the just-built dist file into `website/releases/_alpha.html`, so the alpha hyperlinks on the website always serve the latest dev build. Plain copy (not symlink) so the file is reachable regardless of how the web server mounts the working tree (the canonical Caddy setup at `/etc/containers/systemd/caddy.container` mounts only `website/` read-only and cannot follow `..//dist/` symlinks). Side-effect: every dev build dirties the corresponding `_alpha.html` in `website/releases/`; commit those alongside the source change or `git checkout` them before pushing. Copied alpha pages carry the dev label `Built: BETA` (red) since the dist file does; `--release alpha` overwrites with the formal `alpha · · ` label. +- **Stable**: versioned, immutable. `sh tool/build.sh --release [version]` writes `website/releases/_v.html`, refreshes the `_stable.html` symlink, and tags `-v`. Skips automatically if source has not changed since the latest tag. Pass an explicit version to override auto-increment. Label: `vX.Y.Z` (black). +- **Beta**: mutable. `sh tool/build.sh --release beta` overwrites `website/releases/_beta.html` in place. No tag — alpha and beta artifacts are mutable files, not immutable per-build snapshots. Label: `vX.Y.Z-beta · · ` (red), where `X.Y.Z` is the next-stable target. +- **Alpha**: mutable, analogous. `sh tool/build.sh --release alpha`. **Also**: every plain (non-release) `tool/build.sh` invocation copies the just-built dist file into `website/releases/_alpha.html`, so the alpha hyperlinks on the website always serve the latest dev build. Plain copy (not symlink) so the file is reachable regardless of how the web server mounts the working tree (the canonical Caddy setup at `/etc/containers/systemd/caddy.container` mounts only `website/` read-only and cannot follow `..//dist/` symlinks). Side-effect: every dev build dirties the corresponding `_alpha.html` in `website/releases/`; commit those alongside the source change or `git checkout` them before pushing. Plain-build alpha pages carry the dev label `vX.Y.Z-alpha · · [-dirty]` (red, with the full timestamp + dirty marker so iterative dev builds are distinguishable from formal `--release alpha` cuts, which use `` only and never carry `-dirty`). Stable releases do **not** automatically clobber `_alpha.html` / `_beta.html` — those keep whatever was last built into them. Use `./freshen-channel ` (see "Freshen helper" below) to drag a channel forward to current stable; never `git checkout` the main worktree by hand for this. @@ -206,7 +206,7 @@ What it does: 4. Copies the resulting `_.html` into the main repo's `website/releases/`. 5. Removes the worktree. -The on-page label of the freshened build is ` · · ` — the SHA pins which stable was used as the source, recoverable via `git checkout`. +The on-page label of the freshened build is `v- · · ` — the version reflects the next-stable target (patch+1 from current stable, since the freshen establishes the baseline for the upcoming pre-release window) and the SHA pins which stable was used as the source, recoverable via `git checkout`. Note: the build pipeline used is the one **at the tag**, not on `main`. That is intentional (pure reproducibility). If you have made build-system improvements since stable was cut and want the freshen to use them, cut a new stable first. @@ -278,24 +278,49 @@ ZDDC_DATA_DIR=/path/to/your/archive podman-compose -f zddc/podman-compose.yaml u ### Release tagging -Two coordinated steps — git tag for auditability, then local image build: +`release-image.sh` auto-derives the version (from the latest clean +`zddc-server-vX.Y.Z` tag) and creates the git tag itself, so the +operator no longer cuts a tag manually before running the script: ```sh -git tag zddc-server-v0.0.4 -git push origin zddc-server-v0.0.4 -sh release-image.sh 0.0.4 # default: alpha → :0.0.4 :alpha -sh release-image.sh 0.0.4 beta # beta cascade → :0.0.4 :beta :alpha -sh release-image.sh 0.0.4 stable # stable cascade → :0.0.4 :stable :beta :alpha +sh release-image.sh # alpha cut, version auto-derived +sh release-image.sh alpha # same +sh release-image.sh beta # beta cut +sh release-image.sh stable # stable cut, patch++ from latest stable +sh release-image.sh stable 0.1.0 # stable cut, explicit version ``` **Default channel is `alpha`** so `:stable` never advances by accident during active development. Pass `beta` to soak; pass `stable` only when -deliberately promoting. Cascades: +deliberately promoting. The script tags the commit but does NOT push +git history — finish with `git push origin ` and `git push --tags` (or push the specific tag). + +**Versioning** — pre-release semver. Stable cuts get clean `vX.Y.Z` +tags and `:vX.Y.Z` image tags. Alpha and beta cuts get +`vX.Y.Z-alpha.N` / `vX.Y.Z-beta.N` where `X.Y.Z` is the next patch +of the latest clean stable, and `N` is a per-channel counter that +resets when stable advances. Example sequence (current stable v0.0.7): + +``` +alpha → v0.0.8-alpha.1 +alpha → v0.0.8-alpha.2 +beta → v0.0.8-beta.1 +alpha → v0.0.8-alpha.3 (alpha and beta count separately) +stable → v0.0.8 (counter resets at next-patch advance) +alpha → v0.0.9-alpha.1 +``` + +Cascades (unchanged): - `alpha` → `: :alpha` - `beta` → `: :beta :alpha` - `stable` → `: :stable :beta :alpha` +Pre-release semver ordering (`0.0.8-alpha.1 < 0.0.8-alpha.2 < +0.0.8-beta.1 < 0.0.8`) is honored by all standard tooling — registry +tag sorting, `git tag --sort=-v:refname`, `sort -V`, npm, cargo — +so consumers can pin or compare versions without surprises. + Prerequisite: `podman login codeberg.org` (one-time, with a Codeberg personal token scoped `package:write`). diff --git a/CLAUDE.md b/CLAUDE.md index 2c5764a..67b7478 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -36,7 +36,7 @@ npx playwright test # one spec # zddc/ Go server (separate sub-project, not part of sh build.sh) (cd zddc && go test ./...) # unit tests (Go 1.24+) podman build -t zddc-server zddc/ # build container image -sh release-image.sh [alpha|beta|stable] # canonical image release (default: alpha; cascades down channels) +sh release-image.sh [alpha|beta|stable] [] # canonical image release; alpha/beta auto-derive version (default: alpha) ``` No lint/typecheck/format commands exist for the HTML tools — vanilla JS + POSIX sh by design. @@ -45,6 +45,7 @@ No lint/typecheck/format commands exist for the HTML tools — vanilla JS + POSI - **`dist/` is gitignored but force-committed** (`git add -f tool/dist/tool.html`). Never hand-edit a `dist/` file. - **Never write to `website/index.html`, `website/releases/_v*.html`, `website/releases/_stable.html`, or `website/releases/_beta.html` directly** — promote via `sh tool/build.sh --release [version|alpha|beta]`. Stable releases write `website/releases/_v.html` (immutable) and refresh `_stable.html`; alpha/beta overwrite `_.html` in place. +- **Pre-release semver for alpha/beta.** On-page label embeds `vX.Y.Z-{alpha,beta}` where X.Y.Z is the next-stable target (patch+1 from latest clean `-vX.Y.Z` tag). zddc-server image tags additionally carry a `.N` counter (`v0.0.8-alpha.1`, `.2`, …) since every release is tagged; HTML tools omit the counter (alpha/beta cuts aren't tagged). Stable tags are clean `vX.Y.Z`. - **Alpha exception — every plain build dirties `website/releases/_alpha.html`.** Every `tool/build.sh` (no flags) mirrors the just-built dist file into `_alpha.html` as a real copy (not symlink — the canonical Caddy serves only `website/` and can't follow `../` paths). Side-effect: dev builds dirty those files; commit alongside the source change or `git checkout` to discard. - **Always build before running tests** — Playwright opens `dist/tool.html` via `file://`. - **``** embedding. `shared/build-lib.sh` provides `escape_js_close_tags`; every tool's `build.sh` runs JS through it before inlining. diff --git a/release-image.sh b/release-image.sh index 49df0bd..92994d5 100755 --- a/release-image.sh +++ b/release-image.sh @@ -3,62 +3,113 @@ # to codeberg.org/varasys/zddc-server with cascading channel tags. # # Usage: -# sh release-image.sh [alpha|beta|stable] +# sh release-image.sh # alpha cut, version auto-derived +# sh release-image.sh alpha # same +# sh release-image.sh beta # beta cut, version auto-derived +# sh release-image.sh stable # stable cut, patch++ from latest stable +# sh release-image.sh stable 0.1.0 # stable cut, explicit version # -# alpha → : :alpha (default — for active dev) +# Channel cascade rules (unchanged from earlier): +# alpha → : :alpha # beta → : :beta :alpha # stable → : :stable :beta :alpha # -# Examples: -# sh release-image.sh 0.0.4 # → :0.0.4 :alpha (default) -# sh release-image.sh 0.0.4 beta # → :0.0.4 :beta :alpha -# sh release-image.sh 0.0.4 stable # → :0.0.4 :stable :beta :alpha +# Versioning: pre-release semver. Stable releases own clean vX.Y.Z; alpha +# and beta carry vX.Y.Z-alpha.N / vX.Y.Z-beta.N suffixes, where X.Y.Z is +# the next patch of the latest clean stable tag and N is a per-channel +# counter that resets when stable advances. # -# Why alpha is the default: `:stable` should only advance on a deliberate -# promotion, not as a side-effect of every dev release. With alpha as -# default, `sh release-image.sh ` always lands on the alpha -# channel; the `:stable` tag stays put until you explicitly pass `stable`. -# -# Convention: cut a git tag first (`git tag zddc-server-v0.0.4 && git push -# origin zddc-server-v0.0.4`) so version history is auditable, then run this -# to publish the image. The two are coordinated by hand, deliberately — -# `.woodpecker.yml` is gone; this script is the canonical path. +# The script does ONE thing the old version did not: it `git tag`s the +# release before pushing the image, since auto-derivation means the +# operator can no longer predict the version up-front. The tag is pushed +# to origin only if the operator runs `git push --tags` afterwards +# (intentional — the script never pushes git history on its own). # # Prerequisites: -# - podman (or substitute docker — the commands below are identical) +# - podman (or docker — commands are identical) # - logged in to codeberg.org: `podman login codeberg.org` -# (your Codeberg username + a personal token with `package:write` scope, -# generated at https://codeberg.org/user/settings/applications) +# (Codeberg username + a token with `package:write` scope, generated at +# https://codeberg.org/user/settings/applications) # # What it does: -# 1. Runs `sh build.sh` so zddc/dist/web/{index.html,archive.html} reflect -# current source — those get COPYd into the runtime image. -# 2. Builds zddc/Containerfile's `server` stage as a single local image. -# 3. Tags that image with each cascade tag and pushes them in turn. +# 1. Compute the version (auto for alpha/beta, auto-or-explicit for stable). +# 2. Refresh dist/web by running sh build.sh — the Containerfile's +# server stage COPYs those files in. +# 3. Tag the current commit zddc-server-v. +# 4. Build zddc/Containerfile's `server` stage as a single local image. +# 5. Tag and push each cascade tag. set -eu usage() { - echo "usage: $0 [alpha|beta|stable]" >&2 - echo " channel default: alpha (active dev)" >&2 - echo " version: e.g. 0.0.4, 0.0.4-beta.1, 0.0.4-alpha.2" >&2 + cat >&2 <<'EOF' +usage: release-image.sh [alpha|beta|stable] [] + + alpha (default) cut alpha, version auto-derived from the latest + clean stable tag (vX.Y.Z + patch++ + -alpha.N). + beta cut beta, version auto-derived (vX.Y.Z + -beta.N). + stable cut stable. Without , patch-bump from the + latest clean stable tag. With , use it + verbatim (must be a clean X.Y.Z). +EOF exit 1 } -[ $# -ge 1 ] || usage -VERSION="$1" -CHANNEL="${2:-alpha}" +CHANNEL="${1:-alpha}" +case "$CHANNEL" in + alpha | beta | stable) ;; + -h | --help) usage ;; + *) echo "error: unknown channel '$CHANNEL'" >&2; usage ;; +esac + +EXPLICIT_VERSION="${2:-}" +if [ -n "$EXPLICIT_VERSION" ] && [ "$CHANNEL" != "stable" ]; then + echo "error: an explicit is only valid with the 'stable' channel" >&2 + echo " alpha and beta versions are auto-derived." >&2 + exit 1 +fi + +REPO="codeberg.org/varasys/zddc-server" +SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd) +TAG_PREFIX="zddc-server-v" + +# Source build-lib.sh so we can call next_prerelease for alpha/beta and +# share the validation helpers used by the HTML tools. +root_dir="$SCRIPT_DIR" +. "$SCRIPT_DIR/shared/build-lib.sh" + +# --- Determine the version -------------------------------------------------- +case "$CHANNEL" in + alpha | beta) + VERSION=$(next_prerelease "$CHANNEL" "$TAG_PREFIX") + ;; + stable) + if [ -n "$EXPLICIT_VERSION" ]; then + _validate_semver "$EXPLICIT_VERSION" + VERSION="$EXPLICIT_VERSION" + else + # Auto-bump patch from the latest clean stable tag. + _latest=$(git -C "$SCRIPT_DIR" tag --list "${TAG_PREFIX}*" 2>/dev/null \ + | grep -E "^${TAG_PREFIX}[0-9]+\.[0-9]+\.[0-9]+\$" \ + | sed "s|^${TAG_PREFIX}||" \ + | sort -V \ + | tail -1) + [ -n "$_latest" ] || _latest="0.0.0" + _major="${_latest%%.*}" + _rest="${_latest#*.}" + _minor="${_rest%%.*}" + _patch="${_rest#*.}" + VERSION="${_major}.${_minor}.$((_patch + 1))" + fi + ;; +esac case "$CHANNEL" in alpha) TAGS="$VERSION alpha" ;; beta) TAGS="$VERSION beta alpha" ;; stable) TAGS="$VERSION stable beta alpha" ;; - *) echo "error: unknown channel: $CHANNEL (expected alpha|beta|stable)" >&2; exit 1 ;; esac -REPO="codeberg.org/varasys/zddc-server" -SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd) - # Pick podman or docker, whichever is on PATH. if command -v podman >/dev/null 2>&1; then OCI=podman @@ -69,18 +120,35 @@ else exit 1 fi +GIT_TAG="${TAG_PREFIX}${VERSION}" + echo "=== Building $REPO ===" -echo "Version: $VERSION" -echo "Channel: $CHANNEL" -echo "Tags: $TAGS" -echo "OCI CLI: $OCI" +echo "Channel: $CHANNEL" +echo "Version: $VERSION" +echo "Git tag: $GIT_TAG" +echo "Image tags: $TAGS" +echo "OCI CLI: $OCI" echo -# Refresh dist/web — the Containerfile's server stage COPYs them in. +# --- Refresh HTML dist (Containerfile COPYs from dist/web) ------------------ sh "$SCRIPT_DIR/build.sh" -# Build the runtime image as a single local tag, then re-tag for each cascade -# entry so the build runs once and the pushes are cheap layer reuses. +# --- Tag the commit (idempotent: skip if the tag already points here) ------- +if git -C "$SCRIPT_DIR" rev-parse -q --verify "refs/tags/$GIT_TAG" >/dev/null; then + _existing=$(git -C "$SCRIPT_DIR" rev-list -n 1 "$GIT_TAG") + _head=$(git -C "$SCRIPT_DIR" rev-parse HEAD) + if [ "$_existing" != "$_head" ]; then + echo "error: tag $GIT_TAG already exists at $_existing, but HEAD is $_head" >&2 + echo " refusing to overwrite. Resolve manually." >&2 + exit 1 + fi + echo "(tag $GIT_TAG already at HEAD)" +else + git -C "$SCRIPT_DIR" tag "$GIT_TAG" + echo "tagged $GIT_TAG (run 'git push --tags' to publish)" +fi + +# --- Build + push ----------------------------------------------------------- "$OCI" build --target server -t zddc-server:build "$SCRIPT_DIR/zddc/" echo @@ -93,8 +161,21 @@ done echo echo "=== Done ===" -echo "Image is live at $REPO:$VERSION" -echo "Tags published: $TAGS" +echo "Image: $REPO:$VERSION" +echo "Cascading tags: $TAGS" +echo "Git tag: $GIT_TAG (publish with: git push origin $GIT_TAG)" echo -echo "Next: bump tnd-zddc-chart (push to develop) so the chart's CI rebuilds" -echo "the dev-shell image against the new :beta and helm-rolls the cluster." +case "$CHANNEL" in + stable) + echo "Reminder (channel discipline rule 4): freshen alpha + beta now" + echo "so users tracking those channels are not on stale code:" + echo " ./freshen-channel alpha" + echo " ./freshen-channel beta" + echo + echo "Bump tnd-zddc-chart's ZDDC_SERVER_TAG (push to develop) so the" + echo "chart's CI rebuilds the dev-shell image and helm-rolls." + ;; + beta) + echo "Beta cut. Soak before promoting to stable." + ;; +esac diff --git a/shared/build-lib.sh b/shared/build-lib.sh index fe6e04e..be8feab 100755 --- a/shared/build-lib.sh +++ b/shared/build-lib.sh @@ -38,13 +38,19 @@ # plain build clobbers it. # # Channels and release args: -# dev build, dist/ + alpha mirror, label "alpha · · [-dirty]" (red). -# Plain builds ARE alpha builds — every dev build cascades to the -# alpha channel via update_alpha. Label reflects that. +# dev build, dist/ + alpha mirror, label +# "v-alpha · · [-dirty]" (red). +# Plain builds ARE alpha builds — every dev build cascades +# to the alpha channel via update_alpha. The version embeds +# the next-stable target (patch-bump of the latest clean +# -vX.Y.Z tag). # --release stable, auto-bump patch from latest tag (or 0.0.1). +# Label "vX.Y.Z" (black). # --release X.Y.Z stable, explicit version. -# --release alpha alpha channel snapshot at HEAD; label "alpha · · " (red). -# --release beta beta channel; label "beta · · " (red); opt-in feedback channel. +# --release alpha alpha channel cut at HEAD; +# label "v-alpha · · " (red). +# --release beta beta channel; label "v-beta · · ". +# Opt-in soak channel before stable promotion. # --release error. # ============================================================================= @@ -98,6 +104,58 @@ escape_js_close_tags() { sed 's# "$2" } +# Echo the next pre-release version for a given channel + tag prefix. +# next_prerelease +# +# Channel must be alpha or beta. Tag prefix is the leading text on this +# tool's stable git tags — e.g. "zddc-server-v" or "archive-v" — so the +# function can be called from either the server release script or any +# HTML tool's build.sh against the same monorepo tag namespace. +# +# Algorithm: +# 1. Walk tags matching X.Y.Z (clean stable, no suffix); pick the +# semver-highest. Default 0.0.0 if no stable tag exists yet. +# 2. Bump the patch component → next_patch. +# 3. Count existing tags of the form -.* +# and emit -.. +# +# The patch-bump assumption: every active pre-release window targets the +# next patch of the latest stable. Cutting a real stable resets the +# counter naturally because next_patch advances. Operators wanting a +# minor or major bump cut stable explicitly with a version arg, then the +# subsequent alphas auto-derive against the new stable. +next_prerelease() { + _channel="$1" + _prefix="$2" + case "$_channel" in + alpha | beta) ;; + *) echo "next_prerelease: channel must be alpha or beta (got '$_channel')" >&2; return 1 ;; + esac + if [ -z "$_prefix" ]; then + echo "next_prerelease: tag prefix is required" >&2 + return 1 + fi + + _latest=$(git -C "$root_dir" tag --list "${_prefix}*" 2>/dev/null \ + | grep -E "^${_prefix}[0-9]+\.[0-9]+\.[0-9]+\$" \ + | sed "s|^${_prefix}||" \ + | sort -V \ + | tail -1) + [ -n "$_latest" ] || _latest="0.0.0" + + _major="${_latest%%.*}" + _rest="${_latest#*.}" + _minor="${_rest%%.*}" + _patch="${_rest#*.}" + _patch=$((_patch + 1)) + _next_patch="${_major}.${_minor}.${_patch}" + + _count=$(git -C "$root_dir" tag --list "${_prefix}${_next_patch}-${_channel}.*" 2>/dev/null | wc -l | tr -d ' ') + _count=$((_count + 1)) + + echo "${_next_patch}-${_channel}.${_count}" +} + # Validate that $1 is a strict X.Y.Z numeric version, where each component # is a non-empty numeric string. Exits with an error if not. _validate_semver() { @@ -125,6 +183,19 @@ _validate_semver() { # is_release — "1" for any --release invocation, else "0" # is_red — "1" if the label should render red+bold (dev/alpha/beta), else "0" # channel — "stable" / "alpha" / "beta" / "" (dev) +# +# Versioning: pre-release semver. The next-stable target is computed from +# the latest clean tool-vX.Y.Z tag (patch-bump). Plain builds and +# `--release alpha`/`--release beta` carry the next-stable target as a +# pre-release suffix in the on-page label so users can see which stable +# the alpha/beta is working toward. Stable releases write a clean +# vX.Y.Z label and tag. +# +# HTML tools do NOT tag alpha/beta cuts (consistent with current +# behavior — alpha and beta artifacts are mutable files, not immutable +# per-build snapshots). The label distinguishes plain dev builds from +# explicit channel cuts via the timestamp granularity (full ts + dirty +# marker for plain builds vs. date-only for `--release alpha|beta`). compute_build_label() { _tool="$1" _flag="${2:-}" @@ -135,18 +206,19 @@ compute_build_label() { channel="" build_version="" + # Compute the next-stable target once for label inclusion. + _next_stable=$(_next_stable_for_tool "$_tool") + if [ "$_flag" != "--release" ]; then # Plain builds mirror to website/releases/_alpha.html, so they ARE - # alpha builds. Label format matches `--release alpha` but includes the - # full timestamp (more granular than date) and a -dirty marker when the - # working tree has uncommitted changes — useful when iterating before - # commit. + # alpha builds. Full timestamp (granular than date) and -dirty marker + # distinguish iterative dev builds from formal `--release alpha` cuts. _sha=$(git -C "$root_dir" rev-parse --short=7 HEAD 2>/dev/null || echo "unknown") if ! git -C "$root_dir" diff --quiet HEAD 2>/dev/null; then _sha="${_sha}-dirty" fi channel="alpha" - build_label="alpha · ${build_timestamp} · ${_sha}" + build_label="v${_next_stable}-alpha · ${build_timestamp} · ${_sha}" return 0 fi @@ -157,23 +229,12 @@ compute_build_label() { channel="$_arg" _date=$(date -u +"%Y-%m-%d") _sha=$(git -C "$root_dir" rev-parse --short=7 HEAD 2>/dev/null || echo "unknown") - build_label="${channel} · ${_date} · ${_sha}" + build_label="v${_next_stable}-${channel} · ${_date} · ${_sha}" return 0 ;; '') - # Auto-bump patch from latest stable tag for this tool. - _latest=$(git -C "$root_dir" tag --list "${_tool}-v*" --sort=-v:refname 2>/dev/null | head -1) - if [ -z "$_latest" ]; then - build_version="0.0.1" - else - _ver="${_latest#${_tool}-v}" - _major="${_ver%%.*}" - _rest="${_ver#*.}" - _minor="${_rest%%.*}" - _patch="${_rest#*.}" - _patch=$((_patch + 1)) - build_version="${_major}.${_minor}.${_patch}" - fi + # Stable cut, auto-bump patch. + build_version="$_next_stable" ;; *) _validate_semver "$_arg" @@ -186,6 +247,24 @@ compute_build_label() { build_label="v${build_version}" } +# Compute the next-stable target version for a tool — i.e., the patch-bump +# of the latest clean -vX.Y.Z tag. Used by compute_build_label to +# embed the target version in alpha/beta labels. +_next_stable_for_tool() { + _t="$1" + _latest=$(git -C "$root_dir" tag --list "${_t}-v*" 2>/dev/null \ + | grep -E "^${_t}-v[0-9]+\.[0-9]+\.[0-9]+\$" \ + | sed "s|^${_t}-v||" \ + | sort -V \ + | tail -1) + [ -n "$_latest" ] || _latest="0.0.0" + _major="${_latest%%.*}" + _rest="${_latest#*.}" + _minor="${_rest%%.*}" + _patch="${_rest#*.}" + echo "${_major}.${_minor}.$((_patch + 1))" +} + # Promote a built dist file to the appropriate slot under website/releases/. # Reads from caller scope: $channel, $build_version, $output_html, $root_dir. # diff --git a/website/releases/archive_alpha.html b/website/releases/archive_alpha.html index 1699daf..9598ec2 100644 --- a/website/releases/archive_alpha.html +++ b/website/releases/archive_alpha.html @@ -2095,7 +2095,7 @@ td[data-field="trackingNumber"] {
ZDDC Archive - alpha · 2026-04-29 21:35:07 · 65cd0ea-dirty + v0.0.3-alpha · 2026-04-29 22:31:52 · 42da562-dirty
diff --git a/website/releases/archive_beta.html b/website/releases/archive_beta.html index e773409..89b35d7 100644 --- a/website/releases/archive_beta.html +++ b/website/releases/archive_beta.html @@ -2095,7 +2095,7 @@ td[data-field="trackingNumber"] {
ZDDC Archive - beta · 2026-04-29 · c95f079 + v0.0.3-beta · 2026-04-29 · 42da562
diff --git a/website/releases/classifier_alpha.html b/website/releases/classifier_alpha.html index 13df718..e1488cf 100644 --- a/website/releases/classifier_alpha.html +++ b/website/releases/classifier_alpha.html @@ -1358,7 +1358,7 @@ body.help-open .app-header {
ZDDC Classifier - alpha · 2026-04-29 21:35:07 · 65cd0ea-dirty + v0.0.3-alpha · 2026-04-29 22:30:39 · 42da562-dirty
diff --git a/website/releases/landing_alpha.html b/website/releases/landing_alpha.html index 929aa9d..f87d74b 100644 --- a/website/releases/landing_alpha.html +++ b/website/releases/landing_alpha.html @@ -884,7 +884,7 @@ body {
ZDDC Archive - alpha · 2026-04-29 21:35:07 · 65cd0ea-dirty + v0.0.3-alpha · 2026-04-29 22:30:39 · 42da562-dirty
diff --git a/website/releases/mdedit_alpha.html b/website/releases/mdedit_alpha.html index 6971c02..64cecc2 100644 --- a/website/releases/mdedit_alpha.html +++ b/website/releases/mdedit_alpha.html @@ -1650,7 +1650,7 @@ body.help-open .app-header {
ZDDC Markdown - alpha · 2026-04-29 21:35:07 · 65cd0ea-dirty + v0.0.3-alpha · 2026-04-29 22:30:39 · 42da562-dirty
diff --git a/website/releases/transmittal_alpha.html b/website/releases/transmittal_alpha.html index 652d1cc..bcbea6e 100644 --- a/website/releases/transmittal_alpha.html +++ b/website/releases/transmittal_alpha.html @@ -2192,7 +2192,7 @@ dialog.modal--narrow { JavaScript not available
ZDDC Transmittal - alpha · 2026-04-29 21:35:07 · 65cd0ea + v0.0.3-alpha · 2026-04-29 22:30:39 · 42da562-dirty