ZDDC/build.sh
ZDDC 408a1a0571 refactor: HTML tools live in website/releases/ as static files + symlink hierarchy
Rolls back the HTML-tool side of the Codeberg-as-canonical refactor
(commits 2dc9ad2, b28c4ae, bdac8dc) in favor of a simpler model:
per-version HTML files committed under website/releases/ as immutable
real files; partial-version pins (<tool>_v<X.Y>.html, <tool>_v<X>.html)
and channel mirrors (<tool>_<channel>.html) are checked-in symlinks.
Docker-tag pattern: :1.2.3 is pinned, :1.2 floats, :1 floats further,
:stable floats furthest.

URL scheme — every URL resolves to actual HTML via the symlink chain;
no JS indirection, no manifest.json, no Caddy regex-rewrite:

  /releases/<tool>_v<X.Y.Z>.html    exact version (real file)
  /releases/<tool>_v<X.Y>.html      latest patch within X.Y.* (symlink)
  /releases/<tool>_v<X>.html        latest within X.*.* (symlink)
  /releases/<tool>_stable.html      current stable (symlink)
  /releases/<tool>_beta.html        current beta (symlink to stable when no
                                    active beta; real file when beta is in flight)
  /releases/<tool>_alpha.html       current alpha (similar — symlink to beta
                                    or stable when no active alpha)

Cascade rule (in shared/build-lib.sh promote_release):
  --release [version] (stable cut) → write per-version file; refresh 5
                                     symlinks (_v<X.Y>, _v<X>, _stable,
                                     _beta, _alpha) → new versioned file;
                                     tag <tool>-v<X.Y.Z>.
  --release beta                   → overwrite <tool>_beta.html with real
                                     bytes; cascade _alpha.html → _beta.html
                                     (symlink). No tag — channel URLs are
                                     stable URLs by design; counters defeat
                                     that.
  --release alpha                  → overwrite <tool>_alpha.html with real
                                     bytes. No tag, no other side-effects.
  Plain `sh tool/build.sh`         → dist/ only. No website/releases/
                                     side-effect, no commit.

Code changes:

- .gitignore — drop website/releases/*.html and website/releases/zddc-server-*
  exclusions; HTML tool files are tracked again. Replace the comment with
  the new model description.
- shared/build-lib.sh — drop next_prerelease (no -alpha.N / -beta.N counter
  tags). Drop the Codeberg-upload path for HTML tools (no longer sourcing
  publish-codeberg-release.sh from build-lib). promote_release rewritten
  with two helpers: _promote_stable (per-version file + 5 symlinks + tag)
  and _promote_channel (overwrite mirror + cascade alpha→beta on beta cut).
- zddc/release.sh — drop alpha/beta channel path entirely; binaries publish
  only on stable cuts. zddc-server's beta/alpha builds-from-source via the
  helm charts (next phase) — no binary distribution needed for those channels.
- bootstrap/level2.html.tmpl — drop manifest.json fetch; resolve ?v= to a
  static URL via the symlink chain. New suffixFor() handles channel names,
  exact versions, and partial-version pins (?v=0.0, ?v=0). Same logic in
  level1.html.tmpl already works because the local-staging files (e.g.
  ../<tool>_v0.0.html) exist via the same symlink scheme.
- build.sh build_releases_index — revert to filesystem scan of
  website/releases/ instead of Codeberg API call. Drop manifest.json
  generation. Per-tool sections list channel chips + per-version pin links;
  zddc-server section links to Codeberg release pages directly.
- tests/build-label.spec.js — fix the channel-label regex to match the
  pre-release-semver format introduced in commit 9459139 ("v0.0.3-alpha · ...").
  Pre-existing test failure that wasn't caught at the time.

Storage:

- 30 new committed files under website/releases/ — 10 real (per-version) +
  20 symlinks (5 tools × 4 partial/channel variants, plus alpha as a real
  file by default).
- Initial state: stable v0.0.2 across all 5 tools; alpha/beta/v0.0/v0
  symlinks all point at <tool>_v0.0.2.html.
- manifest.json deleted (no longer needed).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 09:40:16 -05:00

296 lines
13 KiB
Bash
Executable file
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/bin/sh
set -eu
# Top-level build script — builds all ZDDC HTML tools, the zddc-server
# binaries, and the bootstrap stubs published under website/bootstrap/.
SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd)
echo "=== Building ZDDC tools ==="
sh "$SCRIPT_DIR/transmittal/build.sh" "${1:-}" "${2:-}"
sh "$SCRIPT_DIR/archive/build.sh" "${1:-}" "${2:-}"
sh "$SCRIPT_DIR/classifier/build.sh" "${1:-}" "${2:-}"
sh "$SCRIPT_DIR/mdedit/build.sh" "${1:-}" "${2:-}"
sh "$SCRIPT_DIR/landing/build.sh" "${1:-}" "${2:-}"
echo ""
echo "=== Assembling zddc/dist/web/ ==="
# Only landing and archive ship inside the server bundle: they call the
# server's JSON API (GET / for the project list, directory listings for the
# archive) and are useless without it. transmittal, classifier, and mdedit
# are pure client-side tools that work from file:// or any static host;
# they are released to website/ for download but not bundled with the server.
mkdir -p "$SCRIPT_DIR/zddc/dist/web"
cp "$SCRIPT_DIR/landing/dist/index.html" "$SCRIPT_DIR/zddc/dist/web/index.html"
cp "$SCRIPT_DIR/archive/dist/archive.html" "$SCRIPT_DIR/zddc/dist/web/archive.html"
echo "Wrote zddc/dist/web/index.html"
echo "Wrote zddc/dist/web/archive.html"
# Cross-compiled zddc-server binaries — only relevant if you're shipping
# standalone Linux/macOS/Windows binaries to users. Skipped silently when
# Go isn't on PATH. (zddc/release.sh handles the publish flow that
# uploads these to Codeberg release assets.)
echo ""
echo "=== Building zddc-server binaries ==="
if command -v go >/dev/null 2>&1; then
cd "$SCRIPT_DIR/zddc"
mkdir -p dist
for target in linux/amd64 darwin/amd64 darwin/arm64 windows/amd64; do
os="${target%/*}"; arch="${target#*/}"
out="zddc-server-${os}-${arch}"
case "$os" in windows) out="${out}.exe" ;; esac
echo " building $out"
CGO_ENABLED=0 GOOS="$os" GOARCH="$arch" \
go build -trimpath -ldflags="-s -w" -o "dist/$out" ./cmd/zddc-server
done
cd "$SCRIPT_DIR"
else
echo "go not found — skipping cross-compiled binary build."
echo " (Install Go 1.24+ to build standalone binaries.)"
fi
# ─── Bootstrap stubs ─────────────────────────────────────────────────────────
# Generated from bootstrap/level{1,2}.html.tmpl on every build and published
# as standalone files under website/bootstrap/. The website's "Install on
# your server" section prints copy-pasteable shell snippets that curl these
# files into the operator's deployment directory.
#
# bootstrap/level1/<tool>.html — same-origin stubs for
# <project>/<tool>.html (4 tools;
# landing only lives at root)
# bootstrap/track-<channel>/<tool>.html — level-2 stubs that fetch the
# named channel from upstream
# (5 tools × 3 channels = 15)
WEBSITE_DIR="$SCRIPT_DIR/website"
RELEASES_DIR="$WEBSITE_DIR/releases"
BOOTSTRAP_DIR="$SCRIPT_DIR/bootstrap"
mkdir -p "$WEBSITE_DIR"
# tool|filename|title
TOOL_TABLE='archive|archive.html|Archive
transmittal|transmittal.html|Transmittal
classifier|classifier.html|Classifier
mdedit|mdedit.html|Markdown Editor
landing|index.html|ZDDC'
# Substitute {{TOOL}}, {{TOOL_TITLE}}, {{CHANNEL}}, {{FAVICON}} in a template.
# The favicon is a base64-encoded data URI built once from shared/favicon.svg.
_favicon_data_uri=""
if [ -f "$SCRIPT_DIR/shared/favicon.svg" ]; then
_favicon_data_uri="data:image/svg+xml;base64,$(base64 -w 0 "$SCRIPT_DIR/shared/favicon.svg")"
fi
render_stub() {
sed \
-e "s|{{TOOL_TITLE}}|$3|g" \
-e "s|{{TOOL}}|$2|g" \
-e "s|{{CHANNEL}}|${4:-}|g" \
-e "s|{{FAVICON}}|$_favicon_data_uri|g" \
"$1" > "$5"
}
build_bootstrap_stubs() {
_stubs="$WEBSITE_DIR/bootstrap"
rm -rf "$_stubs"
mkdir -p "$_stubs/level1"
# Level-1 stubs (same-origin, channel-agnostic). Drop into a project
# subdirectory so <project>/<tool>.html fetches ../<tool>.html.
# Landing has no level-1 stub — landing only lives at deployment root.
while IFS='|' read -r _tool _file _title; do
render_stub "$BOOTSTRAP_DIR/level1.html.tmpl" "$_tool" "$_title" "" \
"$_stubs/level1/$_file"
done <<EOF
archive|archive.html|Archive
transmittal|transmittal.html|Transmittal
classifier|classifier.html|Classifier
mdedit|mdedit.html|Markdown Editor
EOF
echo "Wrote $_stubs/level1/{archive,transmittal,classifier,mdedit}.html"
# Level-2 stubs, one set per channel. Each fetches its named channel
# from upstream on every page load.
for _channel in alpha beta stable; do
mkdir -p "$_stubs/track-$_channel"
while IFS='|' read -r _tool _file _title; do
render_stub "$BOOTSTRAP_DIR/level2.html.tmpl" "$_tool" "$_title" "$_channel" \
"$_stubs/track-$_channel/$_file"
done <<EOF
$TOOL_TABLE
EOF
echo "Wrote $_stubs/track-$_channel/ (5 stubs)"
done
}
# Regenerate website/releases/index.html from a filesystem scan of
# website/releases/. Lists per-version files (real .html files, immutable
# archives) plus channel mirrors and partial-version pins (symlinks).
#
# All URLs in the page resolve directly under <upstream>/releases/<file>
# — no Codeberg API call, no manifest, no proxy magic. Page is static
# and current as of the last `sh build.sh` run.
#
# zddc-server's section links to its Codeberg release pages directly
# (different distribution model — per-platform binaries).
build_releases_index() {
_out="$RELEASES_DIR/index.html"
mkdir -p "$RELEASES_DIR"
{
cat <<'HEAD'
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Releases — ZDDC</title>
<meta name="description" content="All released versions and channel builds of every ZDDC tool.">
<meta name="theme-color" content="#2a5a8a">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
<link rel="stylesheet" href="../css/style.css">
<style>
.rel-tool { margin-top: var(--spacing-xl); padding: var(--spacing-md); border: 1px solid var(--color-border); border-radius: 8px; }
.rel-tool h2 { margin-top: 0; }
.rel-channels { display: flex; flex-wrap: wrap; gap: 0.5rem; margin: 0.75rem 0 1.25rem 0; }
.rel-channels a { padding: 0.25rem 0.625rem; border-radius: 999px; text-decoration: none; border: 1px solid var(--color-border); color: var(--color-text); font-size: 0.9rem; }
.rel-channels a.stable { border-color: var(--color-primary); color: var(--color-primary); font-weight: 600; }
.rel-channels a.beta, .rel-channels a.alpha { color: var(--color-text-muted); }
.rel-channels a:hover { background: var(--color-bg-subtle); }
.rel-versions { font-size: 0.875rem; color: var(--color-text-muted); }
.rel-versions a { margin-right: 0.5rem; color: var(--color-text); text-decoration: none; padding: 0.1rem 0.4rem; border-radius: 4px; }
.rel-versions a:hover { background: var(--color-bg-subtle); text-decoration: underline; }
.rel-meta { font-size: 0.85rem; color: var(--color-text-muted); margin-top: 0.5rem; }
.rel-bin-table { width: 100%; border-collapse: collapse; margin: 0.5rem 0 1rem; font-size: 0.9rem; }
.rel-bin-table th, .rel-bin-table td { text-align: left; padding: 0.4rem 0.6rem; border-bottom: 1px solid var(--color-border); }
.rel-bin-table th { font-weight: 600; color: var(--color-text-muted); }
.rel-bin-table td.ch-stable { color: var(--color-primary); font-weight: 600; }
.rel-bin-table td.ch-beta, .rel-bin-table td.ch-alpha { color: var(--color-text-muted); }
.rel-bin-table a { color: var(--color-text); text-decoration: none; padding: 0.1rem 0.35rem; border-radius: 4px; }
.rel-bin-table a:hover { background: var(--color-bg-subtle); text-decoration: underline; }
.rel-bin-table td.empty { color: var(--color-text-muted); font-style: italic; }
.rel-pull { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 0.85rem; background: var(--color-bg-subtle); padding: 0.25rem 0.5rem; border-radius: 4px; display: inline-block; margin: 0.2rem 0; }
</style>
</head>
<body>
<header class="site-header">
<div class="container header-content">
<a href="/" class="brand">
<div class="brand-logo">
<svg viewBox="0 0 24 24"><path d="M3 5h18v2H3V5zm0 6h12v2H3v-2zm0 6h6v2H3v-2z" /></svg>
</div>
<span class="brand-name">ZDDC</span>
</a>
<nav class="header-nav">
<a href="/" class="nav-link">Home</a>
<a href="../reference.html" class="nav-link">Docs</a>
<a href="index.html" class="nav-link active">Releases</a>
</nav>
</div>
</header>
<section class="hero">
<div class="container">
<h1>Releases</h1>
<p class="hero-subtitle">All published versions and channel builds of every ZDDC tool. Stable releases are immutable; alpha and beta channels are rebuilt without notice.</p>
</div>
</section>
<main class="container" style="margin-bottom: var(--spacing-2xl);">
HEAD
# Render one section per HTML tool, scanning website/releases/ for
# <tool>_v*.html real files (per-version archives) and resolving the
# current channel symlinks.
for _tool_entry in 'archive|Archive' \
'transmittal|Transmittal' \
'classifier|Classifier' \
'mdedit|Markdown Editor' \
'landing|Landing (project picker)'; do
_tool="${_tool_entry%%|*}"
_title="${_tool_entry#*|}"
# All per-version real files for this tool, semver-descending.
# Use find to filter out symlinks; grep + sort -Vr for ordering.
_versioned=$(find "$RELEASES_DIR" -maxdepth 1 -type f -name "${_tool}_v*.html" 2>/dev/null \
| sed "s|^${RELEASES_DIR}/||" \
| sort -Vr)
if [ -z "$_versioned" ]; then
continue
fi
printf ' <section class="rel-tool">\n'
printf ' <h2>%s</h2>\n' "$_title"
# Channel chips — only render if the symlink exists.
printf ' <div class="rel-channels">\n'
for _ch in stable beta alpha; do
_link="${_tool}_${_ch}.html"
if [ -e "$RELEASES_DIR/$_link" ]; then
printf ' <a class="%s" href="%s">%s</a>\n' "$_ch" "$_link" "$_ch"
fi
done
printf ' </div>\n'
# Pin-to-version row: every per-version real file, descending.
printf ' <div class="rel-versions"><strong>Pin to version:</strong>\n'
printf '%s\n' "$_versioned" | while read -r _f; do
[ -n "$_f" ] || continue
# Display as v<X.Y.Z> stripped of <tool>_v prefix and .html suffix.
_v="${_f#${_tool}_v}"
_v="${_v%.html}"
printf ' <a href="%s">v%s</a>\n' "$_f" "$_v"
done
printf ' </div>\n'
printf ' </section>\n'
done
# zddc-server section — links to Codeberg release pages directly,
# since binaries don't live under website/releases/.
printf ' <section class="rel-tool">\n'
printf ' <h2>zddc-server (Go file server)</h2>\n'
printf ' <p>Binaries are published as Codeberg release assets. Pick a platform from the release page; or build from source via the helm charts under <code>helm/</code>.</p>\n'
printf ' <p><a href="https://codeberg.org/VARASYS/ZDDC/releases">Browse zddc-server releases on Codeberg →</a></p>\n'
printf ' </section>\n'
cat <<'TAIL'
<section style="margin-top: var(--spacing-2xl); color: var(--color-text-muted); font-size: 0.9rem;">
<p>Append <code>?v=stable</code>, <code>?v=beta</code>, <code>?v=alpha</code>, <code>?v=0.0</code> (latest 0.0.x), or <code>?v=0.0.1</code> (exact) to any deployment URL to switch versions for a single request — see <a href="../">the home page</a>.</p>
</section>
</main>
<footer class="site-footer">
<div class="container footer-content">
<span>ZDDC is open source — <a href="https://codeberg.org/VARASYS/ZDDC">codeberg.org/VARASYS/ZDDC</a></span>
</div>
</footer>
</body>
</html>
TAIL
} > "$_out"
echo "Wrote $_out"
}
echo ""
echo "=== Building bootstrap stubs and releases/index.html ==="
build_bootstrap_stubs
build_releases_index
echo ""
echo "=== All tools built successfully ==="
echo ""
echo "Server deployment package: zddc/dist/"
echo " Binaries: zddc-server-{linux,darwin,windows}-*"
echo " Web files: web/ (copy contents to ZDDC_ROOT)"
echo ""
echo "Bootstrap stubs: website/bootstrap/"
echo " level1/<tool>.html — same-origin stubs for project subdirs"
echo " track-{alpha,beta,stable}/ — level-2 stubs for each channel"
echo ""
echo "The home page's 'Install on your server' section prints copy-pasteable"
echo "shell snippets that curl these files into the operator's deployment dir."