Removes the codeberg.org/varasys/zddc-server registry image, which had no remaining consumer outside this shop. The two chart Dockerfiles (tnd-zddc-chart) now compile zddc-server from source at build time, fetching the right tag from a Codeberg release. release-image.sh, zddc/Containerfile, and zddc/podman-compose.yaml are gone. Build artifacts (HTML tools + zddc-server binaries) move from website/releases/ in this repo to Codeberg release assets attached to git tags. The website at zddc.varasys.io serves them by reverse- proxying /releases/<tag>/<asset> to the corresponding Codeberg URL, so consumers (zddc-use, level-2 bootstrap stubs, the chart Dockerfiles) only ever talk to zddc.varasys.io. Releases page becomes server-rendered static HTML regenerated on each build via a single Codeberg API call. A small website/releases/manifest.json maps <tool>-<channel> → tag for runtime channel resolution by zddc-use and the level-2 stubs. Files added: - shared/publish-codeberg-release.sh — POSIX-sh helper that creates a Codeberg release for a tag (sets prerelease flag from tag suffix) and uploads/replaces release assets idempotently. Sourced by build-lib.sh and zddc/release.sh. - zddc/release.sh — replaces release-image.sh. Tags + cross-compiles binaries via native Go (no podman needed; install Go) + uploads to Codeberg release assets. No image build, no registry push. Files modified: - shared/build-lib.sh — promote_release tags + uploads via the helper for stable AND alpha/beta now (alpha/beta were untagged before). update_alpha removed; per-tool build.sh files no longer mirror to website/releases/<tool>_alpha.html on plain dev builds. - build.sh — prefers native go build over the old podman-based cross-compile (which is gone with Containerfile). build_releases_index queries the Codeberg API once and writes static HTML + manifest.json, with graceful fallback when the API is unreachable. - bootstrap/level2.html.tmpl — fetches manifest.json to resolve channel → tag, then fetches the asset from /releases/<tag>/<asset> (Caddy proxy). Replaces the old /releases/<tool>_<channel>.html flat URL pattern. Operators with curl'd level-2 stubs need to re-issue them — this is a breaking change. - AGENTS.md, CLAUDE.md — rewritten to describe the new flow. - .gitignore — releases/ artifacts now expected to be on Codeberg, not committed locally. NOT in this commit (deferred until $CODEBERG_TOKEN is provisioned): - Backfilling existing tags as Codeberg releases. - Cleanup commit: git rm-ing the existing artifacts in website/releases/. Until backfill happens, those files are how operators with old bootstrap stubs still get content. Once Codeberg has the assets, drop them. - The Caddy reverse-proxy config on zddc.varasys.io. Operator-side changes (not in this repo): - tnd-zddc-chart Dockerfile.prod and Dockerfile (dev) need updating to compile from source rather than `FROM codeberg.org/...:stable`. Done in a separate commit on that repo. - Caddyfile rule for the /releases/<tag>/<asset> reverse-proxy. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
390 lines
18 KiB
Bash
Executable file
390 lines
18 KiB
Bash
Executable file
#!/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 and manifest.json from the
|
||
# Codeberg release list. Single API call at build time, no runtime
|
||
# dependency on Codeberg from the page (it's static HTML when served).
|
||
#
|
||
# Page links use /releases/<tag>/<asset> URLs which the website's Caddy
|
||
# reverse-proxies to codeberg.org/.../releases/download/<tag>/<asset>.
|
||
# 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 "<tool>-<channel>": "<tag>". Channel resolution:
|
||
# alpha = latest tag matching <tool>-vX.Y.Z-alpha.N
|
||
# beta = latest tag matching <tool>-vX.Y.Z-beta.N
|
||
# stable = latest tag matching <tool>-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'
|
||
<!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 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 ' <section class="rel-tool">\n'
|
||
printf ' <h2>%s</h2>\n' "$_title"
|
||
|
||
# Channel chips. Each link uses /releases/<tag>/<asset>
|
||
# which Caddy proxies to the Codeberg release-asset URL.
|
||
# Asset name is <tool>_v<version>.html for HTML tools; for
|
||
# zddc-server we link to the Codeberg release page since
|
||
# the asset is per-platform (operator picks one).
|
||
printf ' <div class="rel-channels">\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 ' <a class="%s" href="https://codeberg.org/VARASYS/ZDDC/releases/tag/%s">%s</a>\n' \
|
||
"$_ch" "$_tag" "$_ch"
|
||
else
|
||
_ver="${_tag#${_tool}-v}"
|
||
_asset="${_tool}_v${_ver}.html"
|
||
printf ' <a class="%s" href="/releases/%s/%s">%s</a>\n' \
|
||
"$_ch" "$_tag" "$_asset" "$_ch"
|
||
fi
|
||
done
|
||
printf ' </div>\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 ' <h3 style="font-size:1rem;margin:0.75rem 0 0.4rem;">Standalone binaries</h3>\n'
|
||
printf ' <table class="rel-bin-table"><thead><tr><th>Channel</th>'
|
||
for _p in $_platforms; do printf '<th>%s</th>' "$_p"; done
|
||
printf '</tr></thead><tbody>\n'
|
||
for _row in "stable|$_latest_stable" "beta|$_latest_beta" "alpha|$_latest_alpha"; do
|
||
_ch="${_row%%|*}"
|
||
_tag="${_row#*|}"
|
||
printf ' <tr><td class="ch-%s">%s</td>' "$_ch" "$_ch"
|
||
if [ -z "$_tag" ]; then
|
||
for _p in $_platforms; do printf '<td class="empty">—</td>'; done
|
||
else
|
||
for _p in $_platforms; do
|
||
_ext=""; case "$_p" in windows-*) _ext=".exe" ;; esac
|
||
_asset="zddc-server-${_p}${_ext}"
|
||
printf '<td><a href="/releases/%s/%s">download</a></td>' "$_tag" "$_asset"
|
||
done
|
||
fi
|
||
printf '</tr>\n'
|
||
done
|
||
printf ' </tbody></table>\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 ' <div class="rel-versions"><strong>Pin to version:</strong>\n'
|
||
printf '%s\n' "$_all_stables" | while read -r _t; do
|
||
[ -n "$_t" ] || continue
|
||
_v="${_t#${_tool}-v}"
|
||
if [ "$_tool" = "zddc-server" ]; then
|
||
printf ' <a href="https://codeberg.org/VARASYS/ZDDC/releases/tag/%s">v%s</a>\n' "$_t" "$_v"
|
||
else
|
||
printf ' <a href="/releases/%s/%s_v%s.html">v%s</a>\n' "$_t" "$_tool" "$_v" "$_v"
|
||
fi
|
||
done
|
||
printf ' </div>\n'
|
||
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 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."
|