ZDDC/build.sh
ZDDC c95f07966d feat(tools,build): in-flight HTML-tool reworks and build-infra updates
Bundles a stretch of in-progress work across the SPA tools so the
tree returns to a coherent shippable state ahead of cutting a new
zddc-server stable image:

- landing: substantial rework of the project picker (sortable/filterable
  table, presets refactor, ?projects= filter, ?v= channel propagation,
  loading/error states)
- archive: presets cleanup, source.js refactor, filtering/url-state
  alignment with the landing page
- mdedit: file-system module split, resizer, file-tree improvements,
  base/toc styling tweaks
- transmittal/classifier: small template touch-ups for shared chrome
- shared: build-lib.sh helpers, new favicon.svg
- bootstrap, build.sh: pick up the channel-aware install/track zip
  generation
- tests: new landing.spec.js, expanded archive/mdedit/build-label specs
- docs: CLAUDE.md picks up the zddc-server section and freshens the
  alpha-build exception note
- regenerated artifacts: install.zip, track-{alpha,beta,stable}.zip,
  *_alpha.html — these are produced by `sh build.sh` and per project
  convention are committed alongside the source changes

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 12:52:27 -05:00

267 lines
11 KiB
Bash
Executable file

#!/bin/sh
set -eu
# Top-level build script — builds all ZDDC HTML tools, the zddc-server
# binaries, and the downloadable bundles (install.zip and track-*.zip).
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
# podman isn't on PATH; the runtime container release path
# (release-image.sh) does its own internal build via Containerfile's
# multi-stage flow and doesn't need these host-side binaries.
echo ""
echo "=== Building zddc-server binaries ==="
if command -v podman >/dev/null 2>&1; then
podman build --target binaries -o "$SCRIPT_DIR/zddc/dist/" "$SCRIPT_DIR/zddc/" 2>&1 | grep -v "^-->"
else
echo "podman not found — skipping cross-compiled binary build."
echo " (Standalone-binary distribution only; the runtime container"
echo " release path builds its own copy via Containerfile.)"
fi
# ─── Bootstrap zips ──────────────────────────────────────────────────────────
# Generated from bootstrap/level{1,2}.html.tmpl on every build so they are
# always in sync with the current bootstrap pattern.
#
# install.zip — drop into deployment root for self-contained install.
# Contains the 5 current-stable HTMLs at root plus a
# _template/ directory with 4 level-1 stubs that
# projects can use as their starting layout.
# track-<channel>.zip — drop the level-2 stubs over deployment root to make
# the whole site track <channel> from zddc.varasys.io.
#
# install.zip needs at least one stable release to exist under
# website/releases/; if none exist yet, that zip is skipped with a warning.
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_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}_stable.html" ] || _missing="$_missing $_tool"
done <<EOF
$TOOL_TABLE
EOF
if [ -n "$_missing" ]; then
echo "Skipping install.zip — no stable release for:$_missing"
return 0
fi
_staging=$(mktemp -d)
while IFS='|' read -r _tool _file _title; do
cp "$RELEASES_DIR/${_tool}_stable.html" "$_staging/$_file"
done <<EOF
$TOOL_TABLE
EOF
# _template/ holds level-1 bootstraps for the four interactive tools
# (landing only lives at deployment root; project directories do not
# have their own landing page).
mkdir -p "$_staging/_template"
while IFS='|' read -r _tool _file _title; do
render_stub "$BOOTSTRAP_DIR/level1.html.tmpl" "$_tool" "$_title" "" "$_staging/_template/$_file"
done <<EOF
archive|archive.html|Archive
transmittal|transmittal.html|Transmittal
classifier|classifier.html|Classifier
mdedit|mdedit.html|Markdown Editor
EOF
cp "$BOOTSTRAP_DIR/README.md" "$_staging/README.md"
rm -f "$WEBSITE_DIR/install.zip"
(cd "$_staging" && zip -qr "$WEBSITE_DIR/install.zip" .)
echo "Wrote $WEBSITE_DIR/install.zip"
rm -rf "$_staging"
}
build_track_zip() {
_channel="$1"
_staging=$(mktemp -d)
while IFS='|' read -r _tool _file _title; do
render_stub "$BOOTSTRAP_DIR/level2.html.tmpl" "$_tool" "$_title" "$_channel" "$_staging/$_file"
done <<EOF
$TOOL_TABLE
EOF
rm -f "$WEBSITE_DIR/track-$_channel.zip"
(cd "$_staging" && zip -qr "$WEBSITE_DIR/track-$_channel.zip" .)
echo "Wrote $WEBSITE_DIR/track-$_channel.zip"
rm -rf "$_staging"
}
# Regenerate website/releases/index.html — a static directory listing of
# all tool releases with channel rows + per-version archive links. Read
# from the filesystem so the page is always consistent with what's there.
build_releases_index() {
_out="$RELEASES_DIR/index.html"
{
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; }
</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
printf '%s\n' "$TOOL_TABLE" | while IFS='|' read -r _tool _file _title; do
_stable_target=""
if [ -L "$RELEASES_DIR/${_tool}_stable.html" ]; then
_stable_target=$(readlink "$RELEASES_DIR/${_tool}_stable.html")
fi
printf ' <section class="rel-tool">\n'
printf ' <h2>%s</h2>\n' "$_title"
printf ' <div class="rel-channels">\n'
[ -e "$RELEASES_DIR/${_tool}_stable.html" ] && printf ' <a class="stable" href="%s_stable.html">stable</a>\n' "$_tool"
[ -e "$RELEASES_DIR/${_tool}_beta.html" ] && printf ' <a class="beta" href="%s_beta.html">beta</a>\n' "$_tool"
[ -e "$RELEASES_DIR/${_tool}_alpha.html" ] && printf ' <a class="alpha" href="%s_alpha.html">alpha</a>\n' "$_tool"
printf ' </div>\n'
_versions=$(ls -1 "$RELEASES_DIR" 2>/dev/null | grep -E "^${_tool}_v[0-9]" | sort -V -r)
if [ -n "$_versions" ]; then
printf ' <div class="rel-versions"><strong>Pin to version:</strong>\n'
printf '%s\n' "$_versions" | while read -r _v; do
_ver=${_v#${_tool}_v}; _ver=${_ver%.html}
printf ' <a href="%s">v%s</a>\n' "$_v" "$_ver"
done
printf ' </div>\n'
fi
if [ -n "$_stable_target" ]; then
printf ' <div class="rel-meta">stable currently → %s</div>\n' "$_stable_target"
fi
printf ' </section>\n'
done
cat <<'TAIL'
<section style="margin-top: var(--spacing-2xl); color: var(--color-text-muted); font-size: 0.9rem;">
<p>Append <code>?v=alpha</code>, <code>?v=beta</code>, <code>?v=stable</code>, or <code>?v=0.0.1</code> 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 install.zip, track-*.zip, releases/index.html ==="
build_install_zip
build_track_zip alpha
build_track_zip beta
build_track_zip stable
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 downloads: website/"
echo " install.zip — self-contained install for deployment root"
echo " track-alpha.zip — level-2 stubs that track the alpha channel"
echo " track-beta.zip — level-2 stubs that track the beta channel"
echo " track-stable.zip — level-2 stubs that track the stable channel"