#!/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/.html — same-origin stubs for # /.html (4 tools; # landing only lives at root) # bootstrap/track-/.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 /.html fetches ../.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 </ URLs which the website's Caddy # reverse-proxies to codeberg.org/.../releases/download//. # Operators see one origin (zddc.varasys.io); Codeberg is the storage # backend. # # Skips silently if curl/jq aren't on PATH (offline dev shouldn't blow # up). Skips with a warning if the API call fails (network down, rate- # limited, etc.) — the existing index.html stays as-is. build_releases_index() { _out="$RELEASES_DIR/index.html" _manifest="$RELEASES_DIR/manifest.json" mkdir -p "$RELEASES_DIR" if ! command -v curl >/dev/null 2>&1 || ! command -v jq >/dev/null 2>&1; then echo " (curl or jq missing — skipping releases index regeneration)" return 0 fi _api_resp=$(curl -fsSL --max-time 30 \ 'https://codeberg.org/api/v1/repos/VARASYS/ZDDC/releases?limit=100' \ 2>/dev/null) || _api_resp="" if [ -z "$_api_resp" ]; then echo " (Codeberg API unreachable — leaving releases/index.html as-is)" return 0 fi # Build manifest.json: for each tool/channel, find the latest matching # release and emit "-": "". Channel resolution: # alpha = latest tag matching -vX.Y.Z-alpha.N # beta = latest tag matching -vX.Y.Z-beta.N # stable = latest tag matching -vX.Y.Z (no suffix) # "Latest" via sort -V on the version part. _tools="archive transmittal classifier mdedit landing zddc-server" { printf '{\n' _first=1 for _tool in $_tools; do for _ch in stable beta alpha; do if [ "$_ch" = "stable" ]; then _re="^${_tool}-v[0-9]+\\.[0-9]+\\.[0-9]+\$" else _re="^${_tool}-v[0-9]+\\.[0-9]+\\.[0-9]+-${_ch}\\.[0-9]+\$" fi _tag=$(printf '%s' "$_api_resp" \ | jq -r --arg re "$_re" ' [.[] | select(.tag_name | test($re)) | .tag_name] | sort | last // empty ') if [ -n "$_tag" ]; then [ "$_first" = "1" ] || printf ',\n' printf ' "%s-%s": "%s"' "$_tool" "$_ch" "$_tag" _first=0 fi done done printf '\n}\n' } > "$_manifest" echo "Wrote $_manifest" { cat <<'HEAD' Releases — ZDDC

Releases

All published versions and channel builds of every ZDDC tool. Stable releases are immutable; alpha and beta channels are rebuilt without notice.

HEAD # Render one section per tool. The HTML tool releases publish a # single asset per tag (the inlined HTML); zddc-server publishes # per-platform binaries. The renderer handles both. for _tool_entry in 'archive|Archive' \ 'transmittal|Transmittal' \ 'classifier|Classifier' \ 'mdedit|Markdown Editor' \ 'landing|Landing (project picker)' \ 'zddc-server|zddc-server (Go file server)'; do _tool="${_tool_entry%%|*}" _title="${_tool_entry#*|}" _re_stable="^${_tool}-v[0-9]+\\.[0-9]+\\.[0-9]+\$" _re_alpha="^${_tool}-v[0-9]+\\.[0-9]+\\.[0-9]+-alpha\\.[0-9]+\$" _re_beta="^${_tool}-v[0-9]+\\.[0-9]+\\.[0-9]+-beta\\.[0-9]+\$" _latest_stable=$(printf '%s' "$_api_resp" | jq -r --arg re "$_re_stable" \ '[.[] | select(.tag_name | test($re)) | .tag_name] | sort | last // empty') _latest_beta=$(printf '%s' "$_api_resp" | jq -r --arg re "$_re_beta" \ '[.[] | select(.tag_name | test($re)) | .tag_name] | sort | last // empty') _latest_alpha=$(printf '%s' "$_api_resp" | jq -r --arg re "$_re_alpha" \ '[.[] | select(.tag_name | test($re)) | .tag_name] | sort | last // empty') _all_stables=$(printf '%s' "$_api_resp" | jq -r --arg re "$_re_stable" \ '[.[] | select(.tag_name | test($re)) | .tag_name] | sort | reverse | .[]') # Skip the section entirely if no releases exist for this tool. if [ -z "$_latest_stable$_latest_beta$_latest_alpha" ]; then continue fi printf '
\n' printf '

%s

\n' "$_title" # Channel chips. Each link uses /releases// # which Caddy proxies to the Codeberg release-asset URL. # Asset name is _v.html for HTML tools; for # zddc-server we link to the Codeberg release page since # the asset is per-platform (operator picks one). printf '
\n' for _row in "stable|$_latest_stable" "beta|$_latest_beta" "alpha|$_latest_alpha"; do _ch="${_row%%|*}" _tag="${_row#*|}" [ -n "$_tag" ] || continue if [ "$_tool" = "zddc-server" ]; then printf ' %s\n' \ "$_ch" "$_tag" "$_ch" else _ver="${_tag#${_tool}-v}" _asset="${_tool}_v${_ver}.html" printf ' %s\n' \ "$_ch" "$_tag" "$_asset" "$_ch" fi done printf '
\n' # zddc-server: per-platform binary table, one row per channel. if [ "$_tool" = "zddc-server" ]; then _platforms="linux-amd64 darwin-amd64 darwin-arm64 windows-amd64" printf '

Standalone binaries

\n' printf ' ' for _p in $_platforms; do printf '' "$_p"; done printf '\n' for _row in "stable|$_latest_stable" "beta|$_latest_beta" "alpha|$_latest_alpha"; do _ch="${_row%%|*}" _tag="${_row#*|}" printf ' ' "$_ch" "$_ch" if [ -z "$_tag" ]; then for _p in $_platforms; do printf ''; done else for _p in $_platforms; do _ext=""; case "$_p" in windows-*) _ext=".exe" ;; esac _asset="zddc-server-${_p}${_ext}" printf '' "$_tag" "$_asset" done fi printf '\n' done printf '
Channel%s
%sdownload
\n' fi # Pin-to-version row. All stables (semver-descending). HTML # tools link to the asset directly; zddc-server links to the # Codeberg release page (per-platform asset choice). if [ -n "$_all_stables" ]; then printf '
Pin to version:\n' printf '%s\n' "$_all_stables" | while read -r _t; do [ -n "$_t" ] || continue _v="${_t#${_tool}-v}" if [ "$_tool" = "zddc-server" ]; then printf ' v%s\n' "$_t" "$_v" else printf ' v%s\n' "$_t" "$_tool" "$_v" "$_v" fi done printf '
\n' fi printf '
\n' done cat <<'TAIL'

Append ?v=alpha, ?v=beta, ?v=stable, or ?v=0.0.1 to any deployment URL to switch versions for a single request — see the home page.

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/.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."