build: pre-release semver for alpha/beta channels
Replace the build-counter version scheme (every alpha push monotonically
bumps the patch number, producing immutable :0.0.X tags that look
indistinguishable from stable releases) with proper semver pre-release
suffixes. Stable owns clean vX.Y.Z; alpha and beta carry
vX.Y.Z-{alpha,beta}[.N] indicating the next-stable target.
The next-stable target is the patch-bump of the latest clean
<prefix>-vX.Y.Z tag. Counter N is per-channel (alpha and beta count
separately) and resets when a new stable advances next-patch. Used
only for zddc-server image tags, where every release is git-tagged;
HTML tools omit the counter since alpha/beta cuts there don't tag.
release-image.sh:
- New CLI: sh release-image.sh [alpha|beta|stable] [<version>].
- Default channel alpha. Version arg only valid (and only optional)
for stable.
- Auto-derives the version via next_prerelease for alpha/beta, and
patch-bump for unspecified stable.
- Now creates the git tag itself (the auto-derived version is no
longer something the operator can predict in advance), but does
not push — operator finishes with `git push --tags`.
shared/build-lib.sh:
- Add next_prerelease(channel, tag_prefix) helper.
- compute_build_label embeds v<next-stable>-{alpha,beta} in the
on-page label for plain and --release alpha|beta builds.
- Plain builds: v<next-stable>-alpha · <ts> · <sha>[-dirty]
--release alpha: v<next-stable>-alpha · <date> · <sha>
--release beta: v<next-stable>-beta · <date> · <sha>
--release [<version>]: v<X.Y.Z> (clean stable, unchanged shape).
Pre-release semver ordering (vX.Y.Z-alpha.1 < vX.Y.Z-alpha.2 <
vX.Y.Z-beta.1 < vX.Y.Z) is honored by registry tag sorting,
git tag --sort=-v:refname, sort -V, npm, cargo — so consumers can
pin or compare versions without surprises.
Existing zddc-server-v0.0.{3..7} git tags and registry tags are
audit history; not rewritten. Going forward, alpha/beta cuts produce
v0.0.8-{alpha,beta}.N format, and clean v0.0.8 is reserved for a
deliberate stable promotion.
freshen-channel needs no code change. It runs --release <channel>
inside a worktree at the latest stable tag, where the build-lib.sh
at that tag is still the old version producing old-format labels;
the first stable cut after this commit will propagate the new format
to subsequent freshens (per the existing "build pipeline at the tag"
reproducibility policy).
AGENTS.md and CLAUDE.md updated.
This commit is contained in:
parent
42da562cb1
commit
94591397cf
10 changed files with 272 additions and 86 deletions
49
AGENTS.md
49
AGENTS.md
|
|
@ -162,11 +162,11 @@ Format: `trackingNumber_revision (status) - title.extension`
|
||||||
|
|
||||||
### Releasing — channels and layout
|
### 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 `<tool>-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/<tool>_v<version>.html`, refreshes the `<tool>_stable.html` symlink, and tags `<tool>-v<version>`. Skips automatically if source has not changed since the latest tag. Pass an explicit version to override auto-increment.
|
- **Stable**: versioned, immutable. `sh tool/build.sh --release [version]` writes `website/releases/<tool>_v<version>.html`, refreshes the `<tool>_stable.html` symlink, and tags `<tool>-v<version>`. 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/<tool>_beta.html` in place. No tag. The on-page label is `beta · <date> · <sha>` so the source is recoverable from git via the SHA.
|
- **Beta**: mutable. `sh tool/build.sh --release beta` overwrites `website/releases/<tool>_beta.html` in place. No tag — alpha and beta artifacts are mutable files, not immutable per-build snapshots. Label: `vX.Y.Z-beta · <date> · <sha>` (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/<tool>_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 `../<tool>/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: <ts> BETA` (red) since the dist file does; `--release alpha` overwrites with the formal `alpha · <date> · <sha>` label.
|
- **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/<tool>_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 `../<tool>/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 · <ts> · <sha>[-dirty]` (red, with the full timestamp + dirty marker so iterative dev builds are distinguishable from formal `--release alpha` cuts, which use `<date>` only and never carry `-dirty`).
|
||||||
|
|
||||||
Stable releases do **not** automatically clobber `<tool>_alpha.html` / `<tool>_beta.html` — those keep whatever was last built into them. Use `./freshen-channel <tool> <channel>` (see "Freshen helper" below) to drag a channel forward to current stable; never `git checkout` the main worktree by hand for this.
|
Stable releases do **not** automatically clobber `<tool>_alpha.html` / `<tool>_beta.html` — those keep whatever was last built into them. Use `./freshen-channel <tool> <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 `<tool>_<channel>.html` into the main repo's `website/releases/`.
|
4. Copies the resulting `<tool>_<channel>.html` into the main repo's `website/releases/`.
|
||||||
5. Removes the worktree.
|
5. Removes the worktree.
|
||||||
|
|
||||||
The on-page label of the freshened build is `<channel> · <today> · <stable-tag-sha>` — the SHA pins which stable was used as the source, recoverable via `git checkout`.
|
The on-page label of the freshened build is `v<next-stable>-<channel> · <today> · <stable-tag-sha>` — 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.
|
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
|
### 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
|
```sh
|
||||||
git tag zddc-server-v0.0.4
|
sh release-image.sh # alpha cut, version auto-derived
|
||||||
git push origin zddc-server-v0.0.4
|
sh release-image.sh alpha # same
|
||||||
sh release-image.sh 0.0.4 # default: alpha → :0.0.4 :alpha
|
sh release-image.sh beta # beta cut
|
||||||
sh release-image.sh 0.0.4 beta # beta cascade → :0.0.4 :beta :alpha
|
sh release-image.sh stable # stable cut, patch++ from latest stable
|
||||||
sh release-image.sh 0.0.4 stable # stable cascade → :0.0.4 :stable :beta :alpha
|
sh release-image.sh stable 0.1.0 # stable cut, explicit version
|
||||||
```
|
```
|
||||||
|
|
||||||
**Default channel is `alpha`** so `:stable` never advances by accident
|
**Default channel is `alpha`** so `:stable` never advances by accident
|
||||||
during active development. Pass `beta` to soak; pass `stable` only when
|
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 <branch>` 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` → `:<version> :alpha`
|
- `alpha` → `:<version> :alpha`
|
||||||
- `beta` → `:<version> :beta :alpha`
|
- `beta` → `:<version> :beta :alpha`
|
||||||
- `stable` → `:<version> :stable :beta :alpha`
|
- `stable` → `:<version> :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
|
Prerequisite: `podman login codeberg.org` (one-time, with a Codeberg
|
||||||
personal token scoped `package:write`).
|
personal token scoped `package:write`).
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ npx playwright test <tool> # one spec
|
||||||
# zddc/ Go server (separate sub-project, not part of sh build.sh)
|
# zddc/ Go server (separate sub-project, not part of sh build.sh)
|
||||||
(cd zddc && go test ./...) # unit tests (Go 1.24+)
|
(cd zddc && go test ./...) # unit tests (Go 1.24+)
|
||||||
podman build -t zddc-server zddc/ # build container image
|
podman build -t zddc-server zddc/ # build container image
|
||||||
sh release-image.sh <version> [alpha|beta|stable] # canonical image release (default: alpha; cascades down channels)
|
sh release-image.sh [alpha|beta|stable] [<version>] # 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.
|
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.
|
- **`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/<tool>_v*.html`, `website/releases/<tool>_stable.html`, or `website/releases/<tool>_beta.html` directly** — promote via `sh tool/build.sh --release [version|alpha|beta]`. Stable releases write `website/releases/<tool>_v<ver>.html` (immutable) and refresh `<tool>_stable.html`; alpha/beta overwrite `<tool>_<channel>.html` in place.
|
- **Never write to `website/index.html`, `website/releases/<tool>_v*.html`, `website/releases/<tool>_stable.html`, or `website/releases/<tool>_beta.html` directly** — promote via `sh tool/build.sh --release [version|alpha|beta]`. Stable releases write `website/releases/<tool>_v<ver>.html` (immutable) and refresh `<tool>_stable.html`; alpha/beta overwrite `<tool>_<channel>.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 `<tool>-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/<tool>_alpha.html`.** Every `tool/build.sh` (no flags) mirrors the just-built dist file into `<tool>_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.
|
- **Alpha exception — every plain build dirties `website/releases/<tool>_alpha.html`.** Every `tool/build.sh` (no flags) mirrors the just-built dist file into `<tool>_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://`.
|
- **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.
|
||||||
|
|
|
||||||
167
release-image.sh
167
release-image.sh
|
|
@ -3,62 +3,113 @@
|
||||||
# to codeberg.org/varasys/zddc-server with cascading channel tags.
|
# to codeberg.org/varasys/zddc-server with cascading channel tags.
|
||||||
#
|
#
|
||||||
# Usage:
|
# Usage:
|
||||||
# sh release-image.sh <version> [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 → :<version> :alpha (default — for active dev)
|
# Channel cascade rules (unchanged from earlier):
|
||||||
|
# alpha → :<version> :alpha
|
||||||
# beta → :<version> :beta :alpha
|
# beta → :<version> :beta :alpha
|
||||||
# stable → :<version> :stable :beta :alpha
|
# stable → :<version> :stable :beta :alpha
|
||||||
#
|
#
|
||||||
# Examples:
|
# Versioning: pre-release semver. Stable releases own clean vX.Y.Z; alpha
|
||||||
# sh release-image.sh 0.0.4 # → :0.0.4 :alpha (default)
|
# and beta carry vX.Y.Z-alpha.N / vX.Y.Z-beta.N suffixes, where X.Y.Z is
|
||||||
# sh release-image.sh 0.0.4 beta # → :0.0.4 :beta :alpha
|
# the next patch of the latest clean stable tag and N is a per-channel
|
||||||
# sh release-image.sh 0.0.4 stable # → :0.0.4 :stable :beta :alpha
|
# counter that resets when stable advances.
|
||||||
#
|
#
|
||||||
# Why alpha is the default: `:stable` should only advance on a deliberate
|
# The script does ONE thing the old version did not: it `git tag`s the
|
||||||
# promotion, not as a side-effect of every dev release. With alpha as
|
# release before pushing the image, since auto-derivation means the
|
||||||
# default, `sh release-image.sh <version>` always lands on the alpha
|
# operator can no longer predict the version up-front. The tag is pushed
|
||||||
# channel; the `:stable` tag stays put until you explicitly pass `stable`.
|
# to origin only if the operator runs `git push --tags` afterwards
|
||||||
#
|
# (intentional — the script never pushes git history on its own).
|
||||||
# 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.
|
|
||||||
#
|
#
|
||||||
# Prerequisites:
|
# 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`
|
# - logged in to codeberg.org: `podman login codeberg.org`
|
||||||
# (your Codeberg username + a personal token with `package:write` scope,
|
# (Codeberg username + a token with `package:write` scope, generated at
|
||||||
# generated at https://codeberg.org/user/settings/applications)
|
# https://codeberg.org/user/settings/applications)
|
||||||
#
|
#
|
||||||
# What it does:
|
# What it does:
|
||||||
# 1. Runs `sh build.sh` so zddc/dist/web/{index.html,archive.html} reflect
|
# 1. Compute the version (auto for alpha/beta, auto-or-explicit for stable).
|
||||||
# current source — those get COPYd into the runtime image.
|
# 2. Refresh dist/web by running sh build.sh — the Containerfile's
|
||||||
# 2. Builds zddc/Containerfile's `server` stage as a single local image.
|
# server stage COPYs those files in.
|
||||||
# 3. Tags that image with each cascade tag and pushes them in turn.
|
# 3. Tag the current commit zddc-server-v<version>.
|
||||||
|
# 4. Build zddc/Containerfile's `server` stage as a single local image.
|
||||||
|
# 5. Tag and push each cascade tag.
|
||||||
|
|
||||||
set -eu
|
set -eu
|
||||||
|
|
||||||
usage() {
|
usage() {
|
||||||
echo "usage: $0 <version> [alpha|beta|stable]" >&2
|
cat >&2 <<'EOF'
|
||||||
echo " channel default: alpha (active dev)" >&2
|
usage: release-image.sh [alpha|beta|stable] [<version>]
|
||||||
echo " version: e.g. 0.0.4, 0.0.4-beta.1, 0.0.4-alpha.2" >&2
|
|
||||||
|
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 <version>, patch-bump from the
|
||||||
|
latest clean stable tag. With <version>, use it
|
||||||
|
verbatim (must be a clean X.Y.Z).
|
||||||
|
EOF
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
[ $# -ge 1 ] || usage
|
CHANNEL="${1:-alpha}"
|
||||||
VERSION="$1"
|
case "$CHANNEL" in
|
||||||
CHANNEL="${2:-alpha}"
|
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 <version> 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
|
case "$CHANNEL" in
|
||||||
alpha) TAGS="$VERSION alpha" ;;
|
alpha) TAGS="$VERSION alpha" ;;
|
||||||
beta) TAGS="$VERSION beta alpha" ;;
|
beta) TAGS="$VERSION beta alpha" ;;
|
||||||
stable) TAGS="$VERSION stable beta alpha" ;;
|
stable) TAGS="$VERSION stable beta alpha" ;;
|
||||||
*) echo "error: unknown channel: $CHANNEL (expected alpha|beta|stable)" >&2; exit 1 ;;
|
|
||||||
esac
|
esac
|
||||||
|
|
||||||
REPO="codeberg.org/varasys/zddc-server"
|
|
||||||
SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd)
|
|
||||||
|
|
||||||
# Pick podman or docker, whichever is on PATH.
|
# Pick podman or docker, whichever is on PATH.
|
||||||
if command -v podman >/dev/null 2>&1; then
|
if command -v podman >/dev/null 2>&1; then
|
||||||
OCI=podman
|
OCI=podman
|
||||||
|
|
@ -69,18 +120,35 @@ else
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
GIT_TAG="${TAG_PREFIX}${VERSION}"
|
||||||
|
|
||||||
echo "=== Building $REPO ==="
|
echo "=== Building $REPO ==="
|
||||||
echo "Version: $VERSION"
|
echo "Channel: $CHANNEL"
|
||||||
echo "Channel: $CHANNEL"
|
echo "Version: $VERSION"
|
||||||
echo "Tags: $TAGS"
|
echo "Git tag: $GIT_TAG"
|
||||||
echo "OCI CLI: $OCI"
|
echo "Image tags: $TAGS"
|
||||||
|
echo "OCI CLI: $OCI"
|
||||||
echo
|
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"
|
sh "$SCRIPT_DIR/build.sh"
|
||||||
|
|
||||||
# Build the runtime image as a single local tag, then re-tag for each cascade
|
# --- Tag the commit (idempotent: skip if the tag already points here) -------
|
||||||
# entry so the build runs once and the pushes are cheap layer reuses.
|
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/"
|
"$OCI" build --target server -t zddc-server:build "$SCRIPT_DIR/zddc/"
|
||||||
|
|
||||||
echo
|
echo
|
||||||
|
|
@ -93,8 +161,21 @@ done
|
||||||
|
|
||||||
echo
|
echo
|
||||||
echo "=== Done ==="
|
echo "=== Done ==="
|
||||||
echo "Image is live at $REPO:$VERSION"
|
echo "Image: $REPO:$VERSION"
|
||||||
echo "Tags published: $TAGS"
|
echo "Cascading tags: $TAGS"
|
||||||
|
echo "Git tag: $GIT_TAG (publish with: git push origin $GIT_TAG)"
|
||||||
echo
|
echo
|
||||||
echo "Next: bump tnd-zddc-chart (push to develop) so the chart's CI rebuilds"
|
case "$CHANNEL" in
|
||||||
echo "the dev-shell image against the new :beta and helm-rolls the cluster."
|
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 <tool> alpha"
|
||||||
|
echo " ./freshen-channel <tool> 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
|
||||||
|
|
|
||||||
|
|
@ -38,13 +38,19 @@
|
||||||
# plain build clobbers it.
|
# plain build clobbers it.
|
||||||
#
|
#
|
||||||
# Channels and release args:
|
# Channels and release args:
|
||||||
# <none> dev build, dist/ + alpha mirror, label "alpha · <ts> · <sha>[-dirty]" (red).
|
# <none> dev build, dist/ + alpha mirror, label
|
||||||
# Plain builds ARE alpha builds — every dev build cascades to the
|
# "v<next-stable>-alpha · <ts> · <sha>[-dirty]" (red).
|
||||||
# alpha channel via update_alpha. Label reflects that.
|
# 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
|
||||||
|
# <tool>-vX.Y.Z tag).
|
||||||
# --release stable, auto-bump patch from latest tag (or 0.0.1).
|
# --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 X.Y.Z stable, explicit version.
|
||||||
# --release alpha alpha channel snapshot at HEAD; label "alpha · <date> · <sha>" (red).
|
# --release alpha alpha channel cut at HEAD;
|
||||||
# --release beta beta channel; label "beta · <date> · <sha>" (red); opt-in feedback channel.
|
# label "v<next-stable>-alpha · <date> · <sha>" (red).
|
||||||
|
# --release beta beta channel; label "v<next-stable>-beta · <date> · <sha>".
|
||||||
|
# Opt-in soak channel before stable promotion.
|
||||||
# --release <other> error.
|
# --release <other> error.
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
|
|
||||||
|
|
@ -98,6 +104,58 @@ escape_js_close_tags() {
|
||||||
sed 's#</\([sS][cC][rR][iI][pP][tT]\)#<\\/\1#g' "$1" > "$2"
|
sed 's#</\([sS][cC][rR][iI][pP][tT]\)#<\\/\1#g' "$1" > "$2"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Echo the next pre-release version for a given channel + tag prefix.
|
||||||
|
# next_prerelease <channel> <tag_prefix>
|
||||||
|
#
|
||||||
|
# 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 <prefix>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 <prefix><next_patch>-<channel>.*
|
||||||
|
# and emit <next_patch>-<channel>.<count+1>.
|
||||||
|
#
|
||||||
|
# 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
|
# 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.
|
# is a non-empty numeric string. Exits with an error if not.
|
||||||
_validate_semver() {
|
_validate_semver() {
|
||||||
|
|
@ -125,6 +183,19 @@ _validate_semver() {
|
||||||
# is_release — "1" for any --release invocation, else "0"
|
# is_release — "1" for any --release invocation, else "0"
|
||||||
# is_red — "1" if the label should render red+bold (dev/alpha/beta), else "0"
|
# is_red — "1" if the label should render red+bold (dev/alpha/beta), else "0"
|
||||||
# channel — "stable" / "alpha" / "beta" / "" (dev)
|
# 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() {
|
compute_build_label() {
|
||||||
_tool="$1"
|
_tool="$1"
|
||||||
_flag="${2:-}"
|
_flag="${2:-}"
|
||||||
|
|
@ -135,18 +206,19 @@ compute_build_label() {
|
||||||
channel=""
|
channel=""
|
||||||
build_version=""
|
build_version=""
|
||||||
|
|
||||||
|
# Compute the next-stable target once for label inclusion.
|
||||||
|
_next_stable=$(_next_stable_for_tool "$_tool")
|
||||||
|
|
||||||
if [ "$_flag" != "--release" ]; then
|
if [ "$_flag" != "--release" ]; then
|
||||||
# Plain builds mirror to website/releases/<tool>_alpha.html, so they ARE
|
# Plain builds mirror to website/releases/<tool>_alpha.html, so they ARE
|
||||||
# alpha builds. Label format matches `--release alpha` but includes the
|
# alpha builds. Full timestamp (granular than date) and -dirty marker
|
||||||
# full timestamp (more granular than date) and a -dirty marker when the
|
# distinguish iterative dev builds from formal `--release alpha` cuts.
|
||||||
# working tree has uncommitted changes — useful when iterating before
|
|
||||||
# commit.
|
|
||||||
_sha=$(git -C "$root_dir" rev-parse --short=7 HEAD 2>/dev/null || echo "unknown")
|
_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
|
if ! git -C "$root_dir" diff --quiet HEAD 2>/dev/null; then
|
||||||
_sha="${_sha}-dirty"
|
_sha="${_sha}-dirty"
|
||||||
fi
|
fi
|
||||||
channel="alpha"
|
channel="alpha"
|
||||||
build_label="alpha · ${build_timestamp} · ${_sha}"
|
build_label="v${_next_stable}-alpha · ${build_timestamp} · ${_sha}"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
@ -157,23 +229,12 @@ compute_build_label() {
|
||||||
channel="$_arg"
|
channel="$_arg"
|
||||||
_date=$(date -u +"%Y-%m-%d")
|
_date=$(date -u +"%Y-%m-%d")
|
||||||
_sha=$(git -C "$root_dir" rev-parse --short=7 HEAD 2>/dev/null || echo "unknown")
|
_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
|
return 0
|
||||||
;;
|
;;
|
||||||
'')
|
'')
|
||||||
# Auto-bump patch from latest stable tag for this tool.
|
# Stable cut, auto-bump patch.
|
||||||
_latest=$(git -C "$root_dir" tag --list "${_tool}-v*" --sort=-v:refname 2>/dev/null | head -1)
|
build_version="$_next_stable"
|
||||||
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
|
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
_validate_semver "$_arg"
|
_validate_semver "$_arg"
|
||||||
|
|
@ -186,6 +247,24 @@ compute_build_label() {
|
||||||
build_label="v${build_version}"
|
build_label="v${build_version}"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Compute the next-stable target version for a tool — i.e., the patch-bump
|
||||||
|
# of the latest clean <tool>-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/.
|
# Promote a built dist file to the appropriate slot under website/releases/.
|
||||||
# Reads from caller scope: $channel, $build_version, $output_html, $root_dir.
|
# Reads from caller scope: $channel, $build_version, $output_html, $root_dir.
|
||||||
#
|
#
|
||||||
|
|
|
||||||
|
|
@ -2095,7 +2095,7 @@ td[data-field="trackingNumber"] {
|
||||||
<div class="header-left">
|
<div class="header-left">
|
||||||
<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">alpha · 2026-04-29 21:35:07 · 65cd0ea-dirty</span></span>
|
<span class="build-timestamp"><span style="color:red;font-weight:bold">v0.0.3-alpha · 2026-04-29 22:31:52 · 42da562-dirty</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>
|
||||||
|
|
|
||||||
|
|
@ -2095,7 +2095,7 @@ td[data-field="trackingNumber"] {
|
||||||
<div class="header-left">
|
<div class="header-left">
|
||||||
<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">beta · 2026-04-29 · c95f079</span></span>
|
<span class="build-timestamp"><span style="color:red;font-weight:bold">v0.0.3-beta · 2026-04-29 · 42da562</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>
|
||||||
|
|
|
||||||
|
|
@ -1358,7 +1358,7 @@ body.help-open .app-header {
|
||||||
<div class="header-left">
|
<div class="header-left">
|
||||||
<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">alpha · 2026-04-29 21:35:07 · 65cd0ea-dirty</span></span>
|
<span class="build-timestamp"><span style="color:red;font-weight:bold">v0.0.3-alpha · 2026-04-29 22:30:39 · 42da562-dirty</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>
|
||||||
|
|
|
||||||
|
|
@ -884,7 +884,7 @@ body {
|
||||||
<header class="app-header">
|
<header class="app-header">
|
||||||
<div class="header-left">
|
<div class="header-left">
|
||||||
<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">alpha · 2026-04-29 21:35:07 · 65cd0ea-dirty</span></span>
|
<span class="build-timestamp"><span style="color:red;font-weight:bold">v0.0.3-alpha · 2026-04-29 22:30:39 · 42da562-dirty</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>
|
||||||
|
|
|
||||||
|
|
@ -1650,7 +1650,7 @@ body.help-open .app-header {
|
||||||
<div class="header-left">
|
<div class="header-left">
|
||||||
<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">alpha · 2026-04-29 21:35:07 · 65cd0ea-dirty</span></span>
|
<span class="build-timestamp"><span style="color:red;font-weight:bold">v0.0.3-alpha · 2026-04-29 22:30:39 · 42da562-dirty</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>
|
||||||
|
|
|
||||||
|
|
@ -2192,7 +2192,7 @@ dialog.modal--narrow {
|
||||||
<span id="no-js-notice" class="text-gray-400 text-xs italic">JavaScript not available</span>
|
<span id="no-js-notice" class="text-gray-400 text-xs italic">JavaScript not available</span>
|
||||||
<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">alpha · 2026-04-29 21:35:07 · 65cd0ea</span></span>
|
<span class="build-timestamp"><span style="color:red;font-weight:bold">v0.0.3-alpha · 2026-04-29 22:30:39 · 42da562-dirty</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">
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue