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>
167 lines
5.8 KiB
Bash
Executable file
167 lines
5.8 KiB
Bash
Executable file
#!/bin/sh
|
|
# publish-codeberg-release.sh — upload assets to a Codeberg release.
|
|
#
|
|
# Usage:
|
|
# publish_codeberg_release <repo> <tag> <asset-path>...
|
|
#
|
|
# Where:
|
|
# <repo> e.g. VARASYS/ZDDC
|
|
# <tag> e.g. zddc-server-v0.0.8-alpha.3 or archive-v0.0.3
|
|
# <asset-path> one or more files to attach to the release
|
|
#
|
|
# Prerequisites:
|
|
# - $CODEBERG_TOKEN exported in the environment, with scope sufficient
|
|
# to create/update releases on the target repo. (Codeberg/Gitea
|
|
# terminology: "Application token with `write:repository` access".)
|
|
# - curl, jq.
|
|
#
|
|
# Behavior:
|
|
# - If a release for $tag does not exist on Codeberg, create it. The
|
|
# prerelease flag is derived from the tag itself: if the version
|
|
# part (text after the last 'v') contains a '-', it is a pre-release
|
|
# (e.g. 'zddc-server-v0.0.8-alpha.2' → prerelease=true);
|
|
# 'zddc-server-v0.0.7' → prerelease=false. Codeberg orders releases
|
|
# by published-at and sets a "Latest" badge against the latest
|
|
# non-prerelease, so this matters.
|
|
# - For each asset: if a same-named asset already exists, delete it
|
|
# first (Codeberg/Gitea API doesn't support in-place replacement).
|
|
# Then upload the new bytes.
|
|
#
|
|
# Idempotent: re-running with the same args leaves the release with the
|
|
# same set of assets.
|
|
#
|
|
# This file is meant to be sourced and invoked via the function name, but
|
|
# it's also runnable directly as a script for quick testing — when run
|
|
# directly (i.e., $0 ends in publish-codeberg-release.sh), the function
|
|
# is called with the script's argv.
|
|
#
|
|
# NOTE: We do NOT `set -eu` at the top, because that would leak into any
|
|
# caller that sources this file. The direct-run dispatch at the bottom
|
|
# turns -eu on for that path only.
|
|
|
|
CODEBERG_API="${CODEBERG_API:-https://codeberg.org/api/v1}"
|
|
|
|
# True iff $1's "version part" (text after last 'v') contains '-'.
|
|
# Tags with a '-' in the version part are pre-releases per the
|
|
# pre-release-semver scheme (see AGENTS.md "Releasing").
|
|
_is_prerelease() {
|
|
_ver="${1##*v}"
|
|
case "$_ver" in *-*) return 0 ;; *) return 1 ;; esac
|
|
}
|
|
|
|
# Fetch a release by tag. Echoes the numeric release ID, or empty on 404.
|
|
# Suppresses 404-on-stderr; other errors propagate.
|
|
_get_release_id() {
|
|
_repo="$1"
|
|
_tag="$2"
|
|
_resp=$(curl -fsS -H "Authorization: token $CODEBERG_TOKEN" \
|
|
"$CODEBERG_API/repos/$_repo/releases/tags/$_tag" 2>/dev/null) || _resp=""
|
|
[ -z "$_resp" ] && return 0
|
|
printf '%s' "$_resp" | jq -r '.id // empty'
|
|
}
|
|
|
|
# Create a release for the given tag. Echoes the new release ID. Bombs
|
|
# out on any error (the caller relies on stable behavior — releases
|
|
# don't get half-created).
|
|
_create_release() {
|
|
_repo="$1"
|
|
_tag="$2"
|
|
if _is_prerelease "$_tag"; then
|
|
_prerelease=true
|
|
else
|
|
_prerelease=false
|
|
fi
|
|
# Inline JSON; tag/name don't contain quotes per our naming rules.
|
|
_body=$(printf '{"tag_name":"%s","name":"%s","prerelease":%s,"draft":false}' \
|
|
"$_tag" "$_tag" "$_prerelease")
|
|
curl -fsS \
|
|
-X POST \
|
|
-H "Authorization: token $CODEBERG_TOKEN" \
|
|
-H "Content-Type: application/json" \
|
|
-d "$_body" \
|
|
"$CODEBERG_API/repos/$_repo/releases" \
|
|
| jq -r '.id'
|
|
}
|
|
|
|
# Echo the asset ID for an asset of the given filename in the given
|
|
# release, or empty if no such asset.
|
|
_find_asset_id() {
|
|
_repo="$1"
|
|
_release_id="$2"
|
|
_name="$3"
|
|
curl -fsS -H "Authorization: token $CODEBERG_TOKEN" \
|
|
"$CODEBERG_API/repos/$_repo/releases/$_release_id" \
|
|
| jq -r --arg n "$_name" '.assets[] | select(.name == $n) | .id' \
|
|
| head -1
|
|
}
|
|
|
|
_delete_asset() {
|
|
_repo="$1"
|
|
_asset_id="$2"
|
|
curl -fsS -X DELETE \
|
|
-H "Authorization: token $CODEBERG_TOKEN" \
|
|
"$CODEBERG_API/repos/$_repo/releases/assets/$_asset_id" >/dev/null
|
|
}
|
|
|
|
_upload_asset() {
|
|
_repo="$1"
|
|
_release_id="$2"
|
|
_asset_path="$3"
|
|
_name=$(basename "$_asset_path")
|
|
# Codeberg/Gitea expects the file under field name "attachment", and
|
|
# the desired display name as the ?name= query parameter (otherwise
|
|
# the original filename is used; we set both for clarity).
|
|
curl -fsS -X POST \
|
|
-H "Authorization: token $CODEBERG_TOKEN" \
|
|
-F "attachment=@${_asset_path}" \
|
|
"$CODEBERG_API/repos/$_repo/releases/$_release_id/assets?name=$(printf '%s' "$_name" | jq -sRr @uri)" \
|
|
>/dev/null
|
|
}
|
|
|
|
publish_codeberg_release() {
|
|
if [ $# -lt 3 ]; then
|
|
echo "usage: publish_codeberg_release <repo> <tag> <asset-path>..." >&2
|
|
return 2
|
|
fi
|
|
if [ -z "${CODEBERG_TOKEN:-}" ]; then
|
|
echo "publish_codeberg_release: CODEBERG_TOKEN not set" >&2
|
|
return 2
|
|
fi
|
|
_repo="$1"
|
|
_tag="$2"
|
|
shift 2
|
|
|
|
_release_id=$(_get_release_id "$_repo" "$_tag")
|
|
if [ -z "$_release_id" ]; then
|
|
echo " creating release for $_tag"
|
|
_release_id=$(_create_release "$_repo" "$_tag")
|
|
if [ -z "$_release_id" ] || [ "$_release_id" = "null" ]; then
|
|
echo "publish_codeberg_release: failed to create release for $_tag" >&2
|
|
return 1
|
|
fi
|
|
fi
|
|
echo " release id: $_release_id"
|
|
|
|
for _asset_path do
|
|
if [ ! -f "$_asset_path" ]; then
|
|
echo "publish_codeberg_release: asset not readable: $_asset_path" >&2
|
|
return 1
|
|
fi
|
|
_name=$(basename "$_asset_path")
|
|
_existing=$(_find_asset_id "$_repo" "$_release_id" "$_name")
|
|
if [ -n "$_existing" ]; then
|
|
echo " replacing existing asset $_name (id $_existing)"
|
|
_delete_asset "$_repo" "$_existing"
|
|
fi
|
|
echo " uploading $_name"
|
|
_upload_asset "$_repo" "$_release_id" "$_asset_path"
|
|
done
|
|
}
|
|
|
|
# When invoked directly (not sourced), call the function with argv.
|
|
case "${0##*/}" in
|
|
publish-codeberg-release.sh)
|
|
set -eu
|
|
publish_codeberg_release "$@"
|
|
;;
|
|
esac
|