diff --git a/AGENTS.md b/AGENTS.md index 1e2f3a8..7e74f91 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -3,17 +3,17 @@ ## Commands ```bash -# Build all tools (writes to dist/ only; also regenerates website/releases/{index.html,manifest.json}) +# Build all tools (writes to dist/ only; also regenerates website/releases/index.html + bootstrap stubs) sh build.sh # Build single tool sh tool/build.sh # archive | transmittal | classifier | mdedit | landing -# Cut a stable release (auto-increments patch version, tags -vX.Y.Z, uploads _vX.Y.Z.html to Codeberg) +# Cut a stable release (auto-increments patch version, writes website/releases/_v.html, refreshes 5 symlinks, tags -v) sh tool/build.sh --release sh tool/build.sh --release 1.2.0 # explicit version -# Cut an alpha/beta channel build (tags -vX.Y.Z-{alpha,beta}.N, uploads to Codeberg as a prerelease) +# Cut an alpha/beta channel build (overwrites website/releases/_.html in place; on a beta cut, alpha cascades to a symlink → beta. No git tag.) sh tool/build.sh --release alpha sh tool/build.sh --release beta @@ -55,25 +55,37 @@ shared/ sourced by every tool's build.sh via: . "$root_dir/../shared/build-lib.sh" website/ - index.html hand-edited intro page (root URL) + index.html hand-edited intro page (root URL) + install.sh → ../bootstrap/install.sh symlink so the upstream serves /install.sh releases/ - index.html versions index, regenerated by build.sh from the Codeberg release list - manifest.json - → tag map (regenerated by build.sh; consumed by the level-2 stub at runtime) + index.html versions index, regenerated by build.sh from filesystem scan + _v.html real per-version files (committed, immutable) + _v.html -> ... symlink: latest patch within X.Y.* + _v.html -> ... symlink: latest within X.*.* + _stable.html -> ... symlink: current stable + _beta.html -> ... symlink to stable (or real bytes if active beta) + _alpha.html -> ... symlink to beta/stable (or real bytes if active alpha) bootstrap/ - level1/.html same-origin level-1 stubs (4 tools, no landing) - track-stable/.html level-2 stubs that track the current-stable channel - track-alpha/.html level-2 stubs that track the alpha channel - track-beta/.html level-2 stubs that track the beta channel + level1/.html same-origin level-1 stubs (4 tools, no landing) + track-stable/.html level-2 stubs that track the current-stable channel + track-alpha/.html level-2 stubs that track the alpha channel + track-beta/.html level-2 stubs that track the beta channel bootstrap/ level1.html.tmpl per-project bootstrap template (relative ../.html) level2.html.tmpl level-2 channel-tracking bootstrap template + install.sh unified bootstrap script (modes: copy / track; channels: stable/beta/alpha/) README.md install / channel / pin docs + +helm/ + zddc-server-prod/ production-shaped Helm chart (compiles from source via init container) + zddc-server-dev/ dev-shaped variant (tracks main HEAD; debug-level logging; faster probes) + README.md chart design rationale + quick-start ``` -**Critical:** `dist/` files are gitignored. They're the canonical built artifact for testing and the source for `--release` uploads to Codeberg, but they aren't checked in. Never edit them directly. +**Critical:** `dist/` files are gitignored. They're the canonical built artifact for testing and the source for `--release` writes into `website/releases/`, but they aren't checked in. Never edit them directly. -The per-version `_v.html` artifacts and zddc-server binaries also aren't checked in — they live on Codeberg as release assets attached to git tags. `website/releases/` only contains `index.html` (versions index) and `manifest.json` (channel → tag map), both regenerated by `build.sh` from a Codeberg API call. +`website/releases/` IS committed — per-version files as real bytes, partial-version pins (`_v`, `_v`) and channel mirrors (`_stable`, `_beta`, `_alpha`) as symlinks. The build script (`shared/build-lib.sh promote_release`) maintains the symlink chain on each release. zddc-server binaries are NOT in this repo — they're attached as Codeberg release assets to clean `zddc-server-vX.Y.Z` tags by `zddc/release.sh`. ## Shared CSS (`shared/base.css`) @@ -162,37 +174,47 @@ Format: `trackingNumber_revision (status) - title.extension` ### Releasing — channels and layout -Three channels. Versioning is **pre-release semver**: stable owns clean `vX.Y.Z`; alpha and beta carry `vX.Y.Z-alpha.N` / `vX.Y.Z-beta.N`. The next-stable target X.Y.Z is patch-bumped from the latest clean `-vX.Y.Z` tag. +Three channels. Versioning is **per-tool semver**: stable owns clean `vX.Y.Z`; alpha and beta are mutable channel mirrors that get overwritten in place (no counter tags — channel URLs are stable URLs by design). The next-stable target X.Y.Z used in alpha/beta on-page labels is patch-bumped from the latest clean `-vX.Y.Z` tag. -**Storage model.** Built artifacts live on Codeberg as release assets attached to git tags — *not* committed to this repo. The website at zddc.varasys.io serves them by reverse-proxying `/releases//` to the corresponding Codeberg URL, so consumers (operators' bootstrap stubs, `zddc-use`) only ever talk to zddc.varasys.io. Channel resolution is via `website/releases/manifest.json` — a small file `build.sh` regenerates from the Codeberg API and commits. +**Storage model.** All HTML tool artifacts live in this repo under `website/releases/`. Per-version files (`_v.html`) are real, immutable, committed bytes; partial-version pins (`_v.html`, `_v.html`) and channel mirrors (`_.html`) are checked-in symlinks pointing at the appropriate concrete file. No `manifest.json`, no Codeberg indirection, no Caddy proxy magic. Caddy serves these as plain static files. -- **Stable**: `sh tool/build.sh --release [version]` (or just `--release` to auto-bump patch from the latest stable tag). Tags `-v`, uploads `_v.html` as a release asset on Codeberg. Label: `vX.Y.Z` (black). Skips silently if source has not changed since the latest stable tag (HEAD-vs-tag diff). -- **Beta**: `sh tool/build.sh --release beta`. Tags `-v-beta.N`, uploads `_v-beta.N.html`. Label: `vX.Y.Z-beta · · ` (red). -- **Alpha**: `sh tool/build.sh --release alpha`. Tags `-v-alpha.N`, uploads. Label: `vX.Y.Z-alpha · · ` (red). -- **Plain dev builds** (no `--release`): produce `tool/dist/.html` only. No website/releases side-effect, no Codeberg upload. To publish, re-run with `--release alpha`. +zddc-server binaries are a separate concern — they ship as Codeberg release assets attached to clean `zddc-server-vX.Y.Z` tags by `zddc/release.sh`. zddc-server has no alpha/beta channel for binaries; the helm charts under `helm/` build from source. -After any release run, `sh build.sh` queries the Codeberg API once and rewrites `website/releases/index.html` and `manifest.json`. Commit those alongside the release. +`shared/build-lib.sh promote_release` is the single point of truth for HTML-tool releases: -After cutting a release, run `git push --tags` to publish the tag. +- **Stable** (`sh tool/build.sh --release [version]`, or just `--release` to auto-bump patch): Writes `website/releases/_v.html` (immutable real bytes), then refreshes 5 symlinks — `_v.html`, `_v.html`, `_stable.html`, `_beta.html`, `_alpha.html` — all → the new versioned file. Tags `-v`. Cascade rule: stable cut means beta and alpha both reset to stable (no active dev on either downstream channel). Skips silently if source has not changed since the latest stable tag. +- **Beta** (`sh tool/build.sh --release beta`): Overwrites `_beta.html` with the dist HTML bytes (replacing the symlink with a real file if one was there). Cascade: `_alpha.html` → `_beta.html` (symlink). No tag. +- **Alpha** (`sh tool/build.sh --release alpha`): Overwrites `_alpha.html` with the dist HTML bytes. No tag, no other side-effects. +- **Plain dev builds** (no `--release`): produce `tool/dist/.html` only. No `website/releases/` side-effect, no commit. To publish, re-run with `--release alpha`. -`$CODEBERG_TOKEN` must be exported before any `--release` invocation. The `promote_release` helper calls `publish_codeberg_release` which uses the token to create the release and upload the asset. +On-page `{{BUILD_LABEL}}` format: -`website/index.html` (the root URL of zddc.varasys.io) is **hand-edited static content**, not built by `landing/build.sh`. The landing tool ships only as a Codeberg release asset; the self-contained install snippet curls `landing_v.html` to `/index.html` at customer-deployment time. +- Plain dev: `vX.Y.Z-alpha · · [-dirty]` (red), where X.Y.Z is the next-stable target. +- `--release alpha`: `vX.Y.Z-alpha · · ` (red). +- `--release beta`: `vX.Y.Z-beta · · ` (red). +- `--release [version]`: `v` (black). + +After cutting a stable release, `git push origin ` to publish the tag. `git push origin main` publishes the new versioned file + updated symlinks. + +No `$CODEBERG_TOKEN` is needed for HTML-tool releases — they never touch Codeberg. (`zddc/release.sh` for zddc-server stable cuts still requires it for binary uploads.) + +`website/index.html` (the root URL of zddc.varasys.io) is **hand-edited static content**, not built by `landing/build.sh`. The landing tool's release file is `website/releases/landing_v.html`; the unified install script (`bootstrap/install.sh`) curls the chosen channel's `landing_.html` to `/index.html` at customer-deployment time. ### Channel discipline (MUST rules) The build system does not enforce these. Treating channels carelessly defeats the point of having three. Be disciplined. -1. **Stable doesn't regress.** No known-broken features that worked in the previous stable. If you ship `v0.0.5` with a bug, the path forward is `v0.0.6` with a fix — never edit `v0.0.5` in place. Stable files are immutable. -2. **No backports.** Don't try to patch an older stable version. Always cut a new stable at a higher version. Users pinned to the old version stay pinned by their own choice; they can move forward when they want. -3. **Alpha and beta are mutable.** Document this anywhere you invite users to test them. Pinning `?v=alpha` (or `_alpha.html`) in a production deployment is a mistake; it gets rebuilt without notice. -4. **Stale-channel rule.** Users tracking alpha (or beta) MUST never see a build older than current stable. After every stable release, run `./freshen-channel alpha` and `./freshen-channel beta` so each channel is at-least-current. This is not optional. -5. **Hotfix path.** For critical bugs: fix on `main`, cut a new stable (no beta soak required), then freshen alpha + beta. Tag the commit message `fix:` or include "hotfix" so the intent is visible in `git log`. -6. **Beta soak before promoting (recommended).** Give a beta a few days of exposure before cutting the same code as stable. Not enforced; use judgment for trivial changes. +1. **Stable doesn't regress.** No known-broken features that worked in the previous stable. If you ship `v0.0.5` with a bug, the path forward is `v0.0.6` with a fix — never edit a previously-published `_v0.0.5.html` in place. Stable per-version files are immutable. +2. **Coordinated minor/major bumps.** When any tool needs a minor or major bump, all five tools cut at the same time even if patches differ. Per-tool patches stay independent. This is a release-time process rule, not enforced by tooling. +3. **No backports.** Don't try to patch an older stable version. Always cut a new stable at a higher version. Users pinned to the old version stay pinned by choice; they can move forward when they want. +4. **Alpha and beta are mutable.** Document this anywhere you invite users to test them. Pinning a deployment to a channel mirror means it gets rebuilt without notice. For something stable, pin to a per-version URL (`?v=0.0.5` or the `_v0.0.5.html` mirror). +5. **Cascade is automatic.** A stable cut resets beta and alpha symlinks → stable. A beta cut resets alpha → beta. So "no active beta" silently shows current stable and "no active alpha" silently shows current beta. Operators don't need to run a freshen step after a stable release; the cascade handles it. +6. **Hotfix path.** For critical bugs: fix on `main`, cut a new stable. Tag the commit message `fix:` or include "hotfix" so intent is visible in `git log`. +7. **Beta soak before promoting (recommended).** Give a beta a few days of exposure before cutting the same code as stable. Not enforced; use judgment for trivial changes. ### Freshen helper -`./freshen-channel ` rebuilds the alpha or beta channel of a tool from its current stable tag, cutting a new pre-release tag (e.g., `-v-alpha.N`) and uploading the asset to Codeberg. Use it after every stable release (rule 4 above) and any other time alpha/beta has fallen behind stable. +`./freshen-channel ` rebuilds the alpha or beta channel of a tool from its current stable tag — useful when you want a channel to advance to current stable code without doing active dev on it (e.g. after upstream dependency changes). Most of the time you don't need it: the cascade rule (rule 5 above) means a stable cut already resets the downstream channel symlinks. Use this when you specifically want a fresh build with a new on-page label timestamp instead of a symlink. ```sh ./freshen-channel archive alpha @@ -203,21 +225,19 @@ What it does: 1. Finds the latest `-v*` clean stable tag. 2. Creates a temporary git worktree at that tag — does **not** touch the main worktree's HEAD or working tree. -3. Runs `/build.sh --release ` inside the worktree, which tags `-v-.N` and uploads to Codeberg. +3. Runs `/build.sh --release ` inside the worktree, which overwrites `_.html` with the freshly-built bytes. (Note: this is in the worktree, not on main — you'll need to commit the resulting changes back to main afterward.) 4. Removes the worktree. -The on-page label of the freshened build is `v- · · ` — 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. +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. ### Bootstrap stubs `build.sh` regenerates `website/bootstrap/` on every invocation: - `bootstrap/level1/.html` — 4 same-origin level-1 stubs (archive, transmittal, classifier, mdedit; landing has no level-1 stub since it only lives at deployment root). -- `bootstrap/track-{alpha,beta,stable}/.html` — 5 level-2 stubs per channel that resolve the channel via `zddc.varasys.io/releases/manifest.json` and fetch the asset via `zddc.varasys.io/releases//_v.html` (Caddy proxies to Codeberg). +- `bootstrap/track-{alpha,beta,stable}/.html` — 5 level-2 stubs per channel that fetch `/releases/_.html` directly. Static-file resolution end-to-end via the symlink chain — no JS indirection, no manifest lookup. -End users install via copy-paste shell snippets on the home page's "Install on your server" section — each snippet `curl`s the relevant stubs (or a one-shot version-pinned HTML, for the self-contained option) into the operator's deployment directory. +End users install via the unified `bootstrap/install.sh` script (also served at `https://zddc.varasys.io/install.sh`). The home page's "Install on your server" section prints one-liners that invoke the script with the appropriate `--mode` / `--channel` / `--target` flags. See `bootstrap/README.md` for the install / pin / audit story. @@ -290,59 +310,18 @@ ZDDC_ROOT=/path/to/your/archive ZDDC_TLS_CERT=none ZDDC_ADDR=:8080 \ ### Release tagging -`zddc/release.sh` is the canonical path. It tags the commit, compiles -the binaries (native Go), and uploads them as Codeberg release assets. -There's no container image build / push anymore — the chart's -`Dockerfile.prod` and `Dockerfile` (dev) compile zddc-server from -source at build time, fetching the right tag from Codeberg directly. -The upstream `codeberg.org/varasys/zddc-server` registry is frozen -(historical tags only). +`zddc/release.sh` is the canonical path. It tags the commit, cross-compiles binaries (native Go), and uploads them as Codeberg release assets. **Stable cuts only** — zddc-server has no alpha/beta channel for binary distribution. Active dev/soak happens via the `helm/zddc-server-dev/` chart, which builds from source on every pod restart against any commit you point it at. ```sh -sh zddc/release.sh # alpha cut, version auto-derived -sh zddc/release.sh alpha # same -sh zddc/release.sh beta # beta cut -sh zddc/release.sh stable # stable cut, patch++ from latest stable -sh zddc/release.sh stable 0.1.0 # stable cut, explicit version +sh zddc/release.sh # patch-bump from latest clean stable tag +sh zddc/release.sh 0.1.0 # explicit version ``` -**Default channel is `alpha`** so a stable-equivalent tag never -appears by accident during active development. Pass `beta` to soak; -pass `stable` only when deliberately promoting. The script tags the -commit but does NOT push — finish with `git push origin ` and -`git push origin `. +The script tags the commit but does NOT push — finish with `git push origin main` and `git push origin `. -**Versioning** — pre-release semver. Stable cuts get clean `vX.Y.Z` -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): +**Versioning** — clean semver. Stable cuts get `-vX.Y.Z` tags; no `-alpha.N` / `-beta.N` counters. The historical `zddc-server-v0.0.8-alpha.1` and `-alpha.2` tags from the previous scheme stay as artifacts but no new alpha/beta tags get added. -``` -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 -``` - -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 — Codeberg -release sorting, `git tag --sort=-v:refname`, `sort -V`, npm, cargo — -so consumers can pin or compare versions without surprises. - -**Binary publishing** — release.sh uploads the four cross-compiled -binaries (`zddc-server-{linux,darwin,windows}-{amd64,arm64}`) as -release assets attached to the new git tag on Codeberg. The website -at zddc.varasys.io reverse-proxies `/releases//` URLs to -the corresponding Codeberg release-asset URL, so consumers -(`zddc-use`, the level-2 bootstrap stubs, the dynamic chart -Dockerfiles) only ever talk to zddc.varasys.io. - -After publishing: run `sh build.sh` to refresh -`website/releases/index.html` and `manifest.json` against the new -release list, and commit those. +**Binary publishing** — release.sh uploads the four cross-compiled binaries (`zddc-server-{linux,darwin,windows}-{amd64,arm64}`) as release assets attached to the new git tag on Codeberg. Operators download from `https://codeberg.org/VARASYS/ZDDC/releases/download/zddc-server-vX.Y.Z/zddc-server-` directly. Prerequisites: - Go 1.24+ on PATH (or run from a Go container). diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 5f1a661..b2eb1e7 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -33,20 +33,28 @@ tool/ tool.html # Generated output — never edit this manually ``` -Website files (what `zddc.varasys.io` serves) — committed in this repo as static assets, but the actual built tool HTML and zddc-server binaries live on Codeberg as release assets: +Website files (what `zddc.varasys.io` serves) — committed in this repo as static assets, including the per-version HTML tool files: ``` website/ - index.html # hand-edited intro page (root URL) + index.html # hand-edited intro page (root URL) + install.sh → ../bootstrap/install.sh # symlink so the upstream serves /install.sh releases/ - index.html # versions index, regenerated by build.sh from the Codeberg API - manifest.json # - → tag map, regenerated by build.sh; the level-2 stub fetches this + index.html # versions index, regenerated by build.sh from filesystem scan + _v.html # real per-version files (committed, immutable) + _v.html → ... # symlink: latest patch within X.Y.* + _v.html → ... # symlink: latest within X.*.* + _stable.html → ... # symlink: current stable + _beta.html → ... # symlink to stable (or real bytes when active beta dev) + _alpha.html → ... # symlink to beta/stable (or real bytes when active alpha dev) bootstrap/ - level1/.html # same-origin stubs for project subdirectories - track-{alpha,beta,stable}/ # per-channel level-2 stubs (5 tools each) + level1/.html # same-origin stubs for project subdirectories + track-{alpha,beta,stable}/ # per-channel level-2 stubs (5 tools each) ``` -The per-version `_v.html` files and zddc-server binaries are **not** committed — they live on Codeberg as release assets attached to git tags. Caddy at `zddc.varasys.io` reverse-proxies `/releases//` to the corresponding Codeberg URL, so consumers (operators' bootstrap stubs, `zddc-use`, the chart Dockerfiles) only ever talk to `zddc.varasys.io`. +Every URL under `/releases/` resolves directly via the symlink chain — no `manifest.json`, no Caddy regex-rewrite, no JavaScript indirection, no Codeberg proxy. Caddy serves these as plain static files. The Docker-tag pattern: `:1.2.3` is pinned, `:1.2` floats, `:1` floats further, `:stable` floats furthest, and `:beta` / `:alpha` are mutable channel mirrors that overwrite in place. + +zddc-server binaries are a separate concern — they ship as Codeberg release assets attached to clean `zddc-server-vX.Y.Z` tags by `zddc/release.sh`. The `helm/zddc-server-{prod,dev}/` charts build from source via init container instead of fetching binaries. There is no `website/dev/`. To preview a build locally, open `dist/tool.html` directly via the dev server. To publish on `zddc.varasys.io`, cut a release. @@ -69,7 +77,7 @@ Each topic has exactly one authoritative home; everything else links to it. | Architecture & internal patterns | `ARCHITECTURE.md` (this file) | `AGENTS.md` | | Per-tool internal design quirks | `/README.md` | (linked from website intro tool cards) | -`website/index.html` is **hand-edited static content** (analogous to `reference.html`), not the landing-tool output. The landing tool ships only as a Codeberg release asset (`landing-v` tag → `landing_v.html` asset) — the self-contained install snippet curls the current-stable asset through Caddy at `zddc.varasys.io/releases//landing_v.html` and saves it as `/index.html` for customer sites where the project picker UI is actually useful (it queries `zddc-server` for the project list). The public website at `zddc.varasys.io/` has nothing to pick, so its root URL is the introduction page. +`website/index.html` is **hand-edited static content** (analogous to `reference.html`), not the landing-tool output. The landing tool's released bytes live at `website/releases/landing_v.html` (with `landing_.html` symlinks for channel mirrors). The unified install script (`bootstrap/install.sh`, served at `/install.sh`) curls the chosen channel's `landing_.html` to `/index.html` for customer sites where the project picker UI is actually useful (it queries `zddc-server` for the project list). The public website at `zddc.varasys.io/` has nothing to pick, so its root URL is the introduction page. When updating documentation, prefer linking over duplicating. If you find yourself rewriting the file-naming convention in a tool's README, link to `reference.html` instead. @@ -85,21 +93,21 @@ Each tool's `build.sh`: 2. Reads JS files in declaration order, concatenates them 3. Processes `template.html` with `awk`, replacing `{{PLACEHOLDER}}` markers with the concatenated content and stripping CDN `