diff --git a/.woodpecker.yml b/.woodpecker.yml index 6a2294b..8a7a961 100644 --- a/.woodpecker.yml +++ b/.woodpecker.yml @@ -1,22 +1,30 @@ # Woodpecker CI for ZDDC. # -# This pipeline only runs on `zddc-server-v*` tag pushes — it builds the -# zddc-server runtime container image and publishes it to Codeberg's -# container registry. Other tags (archive-v*, transmittal-v*, etc.) and -# regular pushes are ignored here; the HTML tool releases happen by -# pushing static files to the website (no image involved). +# Triggers on `zddc-server-v*` tag pushes. Builds the runtime container +# image and publishes it to Codeberg's container registry with channel +# tags that cascade downward, so that pinning to `:stable` always gets +# the most recent stable, `:beta` always tracks the most recent beta-or- +# stable, and `:alpha` always tracks the most recent alpha-or-newer. # -# To enable: in Codeberg → repo Settings → Woodpecker → set the secrets -# codeberg_user = your Codeberg username (e.g. VARASYS) -# codeberg_token = a personal token with package:write scope -# Generate the token at https://codeberg.org/user/settings/applications. +# Tag conventions +# --------------- +# zddc-server-vX.Y.Z → stable release +# zddc-server-vX.Y.Z-beta.N → beta release +# zddc-server-vX.Y.Z-alpha.N → alpha release # -# After setup, cut a release with: -# git tag zddc-server-v0.0.1 -# git push --tags -# and the pipeline will publish: -# codeberg.org/varasys/zddc-server:0.0.1 -# codeberg.org/varasys/zddc-server:latest +# Image tags applied (cascading) +# ------------------------------ +# stable release: :X.Y.Z + :stable, :beta, :alpha, :latest +# beta release: :X.Y.Z-beta.N + :beta, :alpha +# alpha release: :X.Y.Z-alpha.N + :alpha +# +# `:latest` is kept as an alias for `:stable` (Docker convention). +# +# To enable: in the Woodpecker dashboard at https://ci.codeberg.org → +# repo → Settings → Secrets, add codeberg_user (your Codeberg username) +# and codeberg_token (a personal token with package:write scope from +# https://codeberg.org/user/settings/applications). Restrict both +# secrets to the `tag` event for safety. when: - event: tag @@ -26,17 +34,32 @@ steps: prepare-bundle: image: docker.io/alpine:3.20 commands: - # build.sh assembles zddc/dist/web/ from landing and archive - # built outputs (which are committed force-tracked dist files). - # Falls back gracefully when podman isn't present — we don't - # need the cross-compiled binaries here, the runtime container - # builds its own linux/amd64 binary internally. + # 1. Assemble zddc/dist/web/ from the landing + archive built outputs. + # build.sh now skips the (host-side) podman binary build when + # podman is absent, which is the case in CI — the runtime + # container image's own builder stage produces linux/amd64. - sh build.sh - # Image tag = the bare semver after the "zddc-server-v" prefix. - # Plus a "latest" tag for convenience. - - VERSION="${CI_COMMIT_TAG#zddc-server-v}" - - printf '%s\nlatest\n' "$VERSION" > .image-tags - - echo "Will tag image with: $(cat .image-tags | tr '\n' ' ')" + # 2. Compute the image tags. Grammar: + # zddc-server-vX.Y.Z → stable + # zddc-server-vX.Y.Z-alpha.N → alpha (cascade: alpha) + # zddc-server-vX.Y.Z-beta.N → beta (cascade: beta, alpha) + # Anything else is rejected by the `when:` filter; nothing to + # handle here. + - | + VERSION="${CI_COMMIT_TAG#zddc-server-v}" + case "$VERSION" in + *-alpha.*) CHANNEL=alpha ;; + *-beta.*) CHANNEL=beta ;; + *) CHANNEL=stable ;; + esac + case "$CHANNEL" in + stable) TAGS="$VERSION stable beta alpha latest" ;; + beta) TAGS="$VERSION beta alpha" ;; + alpha) TAGS="$VERSION alpha" ;; + esac + printf '%s\n' $TAGS > .image-tags + echo "Channel: $CHANNEL" + echo "Tags: $TAGS" publish-image: image: woodpeckerci/plugin-docker-buildx diff --git a/AGENTS.md b/AGENTS.md index 19831d9..9f9f145 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -58,11 +58,11 @@ website/ index.html current stable landing (root URL) releases/ _v...html immutable stable release archives - _latest.html -> ... symlink to current stable (highest semver) + _stable.html -> ... symlink to current stable (highest semver) _alpha.html mutable; overwritten by --release alpha _beta.html mutable; overwritten by --release beta install.zip drop-in self-contained install (5 stable HTMLs + _template/ stubs) - track-latest.zip level-2 stubs that track the current-stable channel + track-stable.zip level-2 stubs that track the current-stable channel track-alpha.zip level-2 stubs that track the alpha channel track-beta.zip level-2 stubs that track the beta channel @@ -163,7 +163,7 @@ Format: `trackingNumber_revision (status) - title.extension` Three channels: -- **Stable**: versioned, immutable. `sh tool/build.sh --release [version]` writes `website/releases/_v.html`, refreshes the `_latest.html` symlink, and tags `-v`. 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/_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`. @@ -214,7 +214,7 @@ Note: the build pipeline used is the one **at the tag**, not on `main`. That is `build.sh` regenerates three downloadable zips into `website/` on every invocation: - `install.zip` — 5 current-stable HTMLs at root + `_template/` directory containing 4 level-1 bootstrap stubs (per-project use). Skipped if any tool has no stable release yet. -- `track-{alpha,beta,latest}.zip` — 5 level-2 stubs each, hardcoded to fetch the named channel from `zddc.varasys.io/releases/`. Drop one over a deployment root to switch the whole site to that channel. +- `track-{alpha,beta,stable}.zip` — 5 level-2 stubs each, hardcoded to fetch the named channel from `zddc.varasys.io/releases/`. Drop one over a deployment root to switch the whole site to that channel. See `bootstrap/README.md` for the install / pin / audit story. diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index dcc6f8f..6a2aa6e 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -40,11 +40,11 @@ website/ index.html # current stable landing tool (root URL) releases/ _v...html # immutable stable release archives - _latest.html -> ... # symlink to the highest-versioned stable + _stable.html -> ... # symlink to the highest-versioned stable _alpha.html # mutable: overwritten on every --release alpha _beta.html # mutable: overwritten on every --release beta install.zip # current-stable HTMLs + project bootstrap stubs - track-{alpha,beta,latest}.zip # level-2 channel-tracking stubs + track-{alpha,beta,stable}.zip # level-2 channel-tracking stubs ``` 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. @@ -68,7 +68,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 via `website/releases/landing_v.html` and `install.zip` — `install.zip` copies `landing_latest.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. +`website/index.html` is **hand-edited static content** (analogous to `reference.html`), not the landing-tool output. The landing tool ships only via `website/releases/landing_v.html` and `install.zip` — `install.zip` copies `landing_stable.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. @@ -86,17 +86,17 @@ Each tool's `build.sh`: 4. Writes the result to `dist/tool.html` 5. If `--release ` was passed, calls `promote_release` to write the appropriate file under `website/releases/` -The top-level `build.sh` at the repository root calls all five tool build scripts in sequence and then regenerates the bootstrap zips (`install.zip`, `track-{alpha,beta,latest}.zip`) so they always match what's in `releases/`. +The top-level `build.sh` at the repository root calls all five tool build scripts in sequence and then regenerates the bootstrap zips (`install.zip`, `track-{alpha,beta,stable}.zip`) so they always match what's in `releases/`. ### Channels Three release channels: -- **Stable** — versioned, immutable. `--release [version]` writes `website/releases/_v.html`, refreshes the `_latest.html` symlink, and tags `-v` in git. Skips automatically when there is no source change since the last tag. +- **Stable** — versioned, immutable. `--release [version]` writes `website/releases/_v.html`, refreshes the `_stable.html` symlink, and tags `-v` in git. Skips automatically when there is no source change since the last tag. - **Beta** — mutable. `--release beta` overwrites `website/releases/_beta.html` in place. No git tag; the on-page label is `beta · · ` so the source is recoverable from git history via the SHA. - **Alpha** — mutable, analogous to beta. -Stable releases do not automatically clobber `_alpha.html` / `_beta.html` — those keep whatever was last built into them. To freshen alpha to current stable, `git checkout v.. && sh tool/build.sh --release alpha`. +Stable releases do not automatically clobber `_alpha.html` / `_beta.html` — those keep whatever was last built into them. Use `./freshen-channel ` (worktree-based, no manual `git checkout`) to drag a channel forward to current stable. The on-page `{{BUILD_LABEL}}` is rendered red+bold for dev/alpha/beta builds (`is_red=1`) and black for stable releases. The label format is: diff --git a/CLAUDE.md b/CLAUDE.md index 87a9b29..f933c35 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -18,7 +18,7 @@ This is a **monorepo of independent tools**, not one application: - `archive/`, `transmittal/`, `classifier/`, `mdedit/`, `landing/` — five self-contained HTML tools, each compiled to a single inlined HTML file in its own `dist/`. Naming: the first four output `dist/tool.html`; **`landing/` outputs `dist/index.html`** (it's the project picker served at the root of `zddc-server`). - `zddc/` — Go HTTP server (separate sub-project, podman/podman-compose; Go 1.24+). Serves `ZDDC_ROOT/index.html` at `GET /` as the landing page; `Accept: application/json` on `/` returns the ACL-filtered project list. - `shared/` — `base.css` plus shared JS modules (`zddc.js`, `hash.js`, `zddc-filter.js`, `theme.js`, `help.js`) included by every tool's build, and `build-lib.sh` (POSIX sh helpers sourced by every tool's `build.sh`) -- `website/` — published artifacts: `index.html` (root URL), `releases/_v.html` (immutable stable archives), `releases/_latest.html` (symlink to current stable), `releases/_{alpha,beta}.html` (mutable channel files), plus bootstrap zips (`install.zip`, `track-{alpha,beta,latest}.zip`). `--release` is the only path to publishing — there is no `website/dev/`. +- `website/` — published artifacts: `index.html` (root URL), `releases/_v.html` (immutable stable archives), `releases/_stable.html` (symlink to current stable), `releases/_{alpha,beta}.html` (mutable channel files), plus bootstrap zips (`install.zip`, `track-{alpha,beta,stable}.zip`). `--release` is the only path to publishing — there is no `website/dev/`. - `tests/` — Playwright specs (Chromium only, requires File System Access API). `tests/schema.spec.js` validates `transmittal.schema.json` against canonical fixtures via `ajv` (only dev dep besides Playwright) ## Most-used commands @@ -38,7 +38,7 @@ No lint/typecheck/format commands exist — vanilla JS + POSIX sh by design. ## Things that bite if you forget - **`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` or `website/releases/*` directly** — promote via `sh tool/build.sh --release [version|alpha|beta]`. Stable releases write `website/releases/_v.html` (immutable) and refresh `_latest.html`; alpha/beta overwrite `_.html` in place. +- **Never write to `website/index.html` or `website/releases/*` 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. - **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. - **All ZDDC parsing/formatting/hashing goes through `window.zddc`** (from `shared/zddc.js` + `shared/hash.js` + `shared/zddc-filter.js`). API: `parseFilename`, `parseFolder`, `parseRevision`, `formatFilename`, `formatFolder`, `compareRevisions`, `isValidStatus`, `splitExtension`, `joinExtension`, `crypto.{sha256Hex, sha256String, sha256File, bytesToHex}`, `filter.{parse, matches}`. File objects across tools use `trackingNumber` (string) and `extension` (string, **no leading dot** — use `zddc.joinExtension(name, ext)` to build a filename). Add edge cases to `tests/zddc.spec.js`, not per-tool tests. diff --git a/README.md b/README.md index f06595e..3a58f0b 100644 --- a/README.md +++ b/README.md @@ -12,10 +12,10 @@ The name "Zero Day Document Control" comes from the convention itself — adopt | Tool | What it does | |------|--------------| -| **[Archive Browser](https://zddc.varasys.io/releases/archive_latest.html)** | Browse, search, and filter a project archive folder. Group by transmittal, export selections as ZIP. | -| **[Transmittal Creator](https://zddc.varasys.io/releases/transmittal_latest.html)** | Self-contained HTML transmittal records with SHA-256 checksums and optional digital signatures. | -| **[Document Classifier](https://zddc.varasys.io/releases/classifier_latest.html)** | Spreadsheet-like bulk-renamer that copy/pastes with Excel and writes back to disk. | -| **[Markdown Editor](https://zddc.varasys.io/releases/mdedit_latest.html)** | Browser-based markdown editor with YAML front matter, TOC, and direct local file access. | +| **[Archive Browser](https://zddc.varasys.io/releases/archive_stable.html)** | Browse, search, and filter a project archive folder. Group by transmittal, export selections as ZIP. | +| **[Transmittal Creator](https://zddc.varasys.io/releases/transmittal_stable.html)** | Self-contained HTML transmittal records with SHA-256 checksums and optional digital signatures. | +| **[Document Classifier](https://zddc.varasys.io/releases/classifier_stable.html)** | Spreadsheet-like bulk-renamer that copy/pastes with Excel and writes back to disk. | +| **[Markdown Editor](https://zddc.varasys.io/releases/mdedit_stable.html)** | Browser-based markdown editor with YAML front matter, TOC, and direct local file access. | Each tool is published in three channels (stable, beta, alpha) at `https://zddc.varasys.io/releases/_.html`. Append `?v=alpha` (or `?v=0.0.4`, etc.) to any URL to switch versions for one request. See [`bootstrap/README.md`](bootstrap/README.md) for the install / pin / audit story. diff --git a/bootstrap/README.md b/bootstrap/README.md index 7c041d6..2a91813 100644 --- a/bootstrap/README.md +++ b/bootstrap/README.md @@ -65,7 +65,7 @@ choice sticks for everyone using that file. | URL | Behavior | |------------------------------------------------------------------|-----------------------------------------| -| `https://zddc.varasys.io/releases/_latest.html` | current stable; auto-updates within stable | +| `https://zddc.varasys.io/releases/_stable.html` | current stable; auto-updates within stable | | `https://zddc.varasys.io/releases/_beta.html` | latest beta build (mutable) | | `https://zddc.varasys.io/releases/_alpha.html` | latest alpha build (mutable) | | `https://zddc.varasys.io/releases/_v1.2.3.html` | pinned to exact stable version | @@ -112,7 +112,7 @@ upstream must serve `Access-Control-Allow-Origin: *` (or a list including your deployment origin) on the released HTML files. Verify with: ```sh -curl -I https://zddc.varasys.io/releases/archive_latest.html | grep -i access-control +curl -I https://zddc.varasys.io/releases/archive_stable.html | grep -i access-control ``` Level-1 fetches are same-origin so no CORS is involved. diff --git a/bootstrap/level1.html.tmpl b/bootstrap/level1.html.tmpl index 9fb3824..bae9309 100644 --- a/bootstrap/level1.html.tmpl +++ b/bootstrap/level1.html.tmpl @@ -15,7 +15,7 @@ // // URL parameter ?v= selects a specific version or channel: // ?v=0.0.4 (or v0.0.4) tries ../{{TOOL}}_v0.0.4.html locally - // ?v=alpha|beta|latest tries ../{{TOOL}}_.html locally + // ?v=alpha|beta|stable tries ../{{TOOL}}_.html locally // (none) fetches ../{{TOOL}}.html (default) // // If the requested version is not staged locally, falls back to the @@ -30,7 +30,7 @@ const params = new URLSearchParams(location.search); const v = params.get('v'); const tool = '{{TOOL}}'; - const channels = { alpha: '_alpha', beta: '_beta', latest: '_latest', stable: '_latest' }; + const channels = { alpha: '_alpha', beta: '_beta', stable: '_stable' }; function suffixFor(value) { if (!value) return ''; diff --git a/bootstrap/level2.html.tmpl b/bootstrap/level2.html.tmpl index 3333967..f2ee061 100644 --- a/bootstrap/level2.html.tmpl +++ b/bootstrap/level2.html.tmpl @@ -13,7 +13,7 @@ // document.write()s it in place. The default upstream is the // {{CHANNEL}} channel; the URL parameter ?v= overrides it: // - // ?v=alpha|beta|latest switches to that channel + // ?v=alpha|beta|stable switches to that channel // ?v=0.0.4 (or v0.0.4) pins to that exact stable version // (none) uses the {{CHANNEL}} default // @@ -23,7 +23,7 @@ const v = params.get('v'); const tool = '{{TOOL}}'; const defaultChannel = '{{CHANNEL}}'; - const channels = { alpha: '_alpha', beta: '_beta', latest: '_latest', stable: '_latest' }; + const channels = { alpha: '_alpha', beta: '_beta', stable: '_stable' }; function suffixFor(value) { if (!value) return '_' + defaultChannel; diff --git a/build.sh b/build.sh index c4c5aa3..c00a20e 100755 --- a/build.sh +++ b/build.sh @@ -81,7 +81,7 @@ build_install_zip() { # Verify a stable release exists for every tool before staging. _missing="" while IFS='|' read -r _tool _file _title; do - [ -e "$RELEASES_DIR/${_tool}_latest.html" ] || _missing="$_missing $_tool" + [ -e "$RELEASES_DIR/${_tool}_stable.html" ] || _missing="$_missing $_tool" done < — write to website/releases/ in the layout # driven by $channel and $build_version. For -# stable, also update the _latest.html +# stable, also update the _stable.html # symlink and create the git tag. # # Channels and release args: @@ -155,7 +155,7 @@ compute_build_label() { # Reads from caller scope: $channel, $build_version, $output_html, $root_dir. # # Stable releases write website/releases/_v.html, refresh the -# website/releases/_latest.html symlink, and tag -v in +# website/releases/_stable.html symlink, and tag -v in # git. Skips silently when the source has not changed since the latest tag. # # Alpha and beta channel releases overwrite website/releases/_.html @@ -190,8 +190,8 @@ promote_release() { # Symlink target is relative to its own directory so the link survives # path moves and works regardless of where the website is mounted. - (cd "$_releases_dir" && ln -sfn "${_tool}_v${build_version}.html" "${_tool}_latest.html") - echo "Updated ${_tool}_latest.html -> ${_tool}_v${build_version}.html" + (cd "$_releases_dir" && ln -sfn "${_tool}_v${build_version}.html" "${_tool}_stable.html") + echo "Updated ${_tool}_stable.html -> ${_tool}_v${build_version}.html" git -C "$root_dir" tag "${_tool}-v${build_version}" echo "Tagged ${_tool}-v${build_version} — run: git push --tags" diff --git a/transmittal/README.md b/transmittal/README.md index f6e757b..8b64a27 100644 --- a/transmittal/README.md +++ b/transmittal/README.md @@ -56,7 +56,7 @@ When you open a transmittal, it may display "✓ Signature Valid" - but **this d **For Document Controllers / Official Verification:** -1. **Use a trusted tool instance** - Download the official transmittal tool from a trusted source (e.g., your organization's approved version or https://zddc.varasys.io/releases/transmittal_latest.html) +1. **Use a trusted tool instance** - Download the official transmittal tool from a trusted source (e.g., your organization's approved version or https://zddc.varasys.io/releases/transmittal_stable.html) 2. **Export JSON from the transmittal** - Open the transmittal → Click "Download Data" 3. **Import JSON into trusted tool** - Open your trusted tool → Click "Load JSON" → Paste the exported data 4. **Verify file hashes** - Click "Select Directory" and point to the actual files @@ -285,7 +285,7 @@ When viewing a published transmittal, signature status is displayed above the fi **Verification Actions**: - **Verify Externally** — Opens a trusted tool instance for independent verification - Copies JSON data to clipboard - - Opens https://zddc.varasys.io/releases/transmittal_latest.html in validation mode + - Opens https://zddc.varasys.io/releases/transmittal_stable.html in validation mode - Paste JSON and select directory to verify file hashes independently - Only trust verification results from the trusted tool, not this document diff --git a/transmittal/template.html b/transmittal/template.html index 09b4626..9faf8e0 100644 --- a/transmittal/template.html +++ b/transmittal/template.html @@ -353,7 +353,7 @@ conventions at https://codeberg.org/VARASYS/ZDDC#file-naming-convention.
  • Digital signatures — ECDSA signatures bind the digest to a signer’s private key. Any change invalidates the signature, preventing undetected tampering. Add signatures after publishing via Add Signature.
  • Independent verification — A tampered HTML file could ship modified JavaScript that always says “Verified.” To rule this out, the recipient opens the sender’s file in their own trusted copy of the tool using Import HTML. This extracts the raw data and re-verifies it with the recipient’s own code. Use this level for anything that matters.
  • -

    Level 4 assumes the recipient’s tool is trustworthy (downloaded from a known source or built from source). A reference instance is at zddc.varasys.io.

    +

    Level 4 assumes the recipient’s tool is trustworthy (downloaded from a known source or built from source). A reference instance is at zddc.varasys.io.

    Menu Actions

    Actions available from the dropdown button. draft items appear only while editing. published items appear only after publishing. Unmarked items appear in both modes.

    diff --git a/website/index.html b/website/index.html index bb5c316..dbad183 100644 --- a/website/index.html +++ b/website/index.html @@ -49,19 +49,19 @@