refactor: distribute via Codeberg release assets, drop the upstream image
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>
This commit is contained in:
parent
6e1777e9c2
commit
2dc9ad240c
31 changed files with 1173 additions and 814 deletions
10
.gitignore
vendored
10
.gitignore
vendored
|
|
@ -20,6 +20,16 @@ test-results/
|
|||
# New tool dist files must be force-added: git add -f tool/dist/tool.html
|
||||
dist/
|
||||
|
||||
# Release artifacts — published to Codeberg release assets, not committed.
|
||||
# website/releases/index.html and website/releases/manifest.json are
|
||||
# regenerated by build.sh and DO get committed (they're tiny indices).
|
||||
# Everything else under website/releases/ — versioned HTML tool files,
|
||||
# zddc-server binaries — lives on Codeberg; the website's Caddy reverse-
|
||||
# proxies download URLs to Codeberg release-asset URLs.
|
||||
website/releases/*.html
|
||||
!website/releases/index.html
|
||||
website/releases/zddc-server-*
|
||||
|
||||
# IDE and project files
|
||||
.opencode/
|
||||
opencode.json
|
||||
|
|
|
|||
111
AGENTS.md
111
AGENTS.md
|
|
@ -162,21 +162,22 @@ Format: `trackingNumber_revision (status) - title.extension`
|
|||
|
||||
### Releasing — channels and layout
|
||||
|
||||
Three channels. Versioning is **pre-release semver**: stable owns clean `vX.Y.Z`; alpha and beta carry `vX.Y.Z-alpha` / `vX.Y.Z-beta` suffixes (with a `.N` counter on the zddc-server image only — see "zddc-server release flow" below). The next-stable target X.Y.Z is patch-bumped from the latest clean `<tool>-vX.Y.Z` tag, so the on-page label always communicates which stable the alpha/beta is working toward.
|
||||
Three channels. Versioning is **pre-release semver**: stable owns clean `vX.Y.Z`; alpha and beta carry `vX.Y.Z-alpha.N` / `vX.Y.Z-beta.N`. The next-stable target X.Y.Z is patch-bumped from the latest clean `<tool>-vX.Y.Z` tag.
|
||||
|
||||
- **Stable**: versioned, immutable. `sh tool/build.sh --release [version]` writes `website/releases/<tool>_v<version>.html`, refreshes the `<tool>_stable.html` symlink, and tags `<tool>-v<version>`. Skips automatically if source has not changed since the latest tag. Pass an explicit version to override auto-increment. Label: `vX.Y.Z` (black).
|
||||
- **Beta**: mutable. `sh tool/build.sh --release beta` overwrites `website/releases/<tool>_beta.html` in place. No tag — alpha and beta artifacts are mutable files, not immutable per-build snapshots. Label: `vX.Y.Z-beta · <date> · <sha>` (red), where `X.Y.Z` is the next-stable target.
|
||||
- **Alpha**: mutable, analogous. `sh tool/build.sh --release alpha`. **Also**: every plain (non-release) `tool/build.sh` invocation copies the just-built dist file into `website/releases/<tool>_alpha.html`, so the alpha hyperlinks on the website always serve the latest dev build. Plain copy (not symlink) so the file is reachable regardless of how the web server mounts the working tree (the canonical Caddy setup at `/etc/containers/systemd/caddy.container` mounts only `website/` read-only and cannot follow `../<tool>/dist/` symlinks). Side-effect: every dev build dirties the corresponding `_alpha.html` in `website/releases/`; commit those alongside the source change or `git checkout` them before pushing. Plain-build alpha pages carry the dev label `vX.Y.Z-alpha · <ts> · <sha>[-dirty]` (red, with the full timestamp + dirty marker so iterative dev builds are distinguishable from formal `--release alpha` cuts, which use `<date>` only and never carry `-dirty`).
|
||||
**Storage model.** Built artifacts live on Codeberg as release assets attached to git tags — *not* committed to this repo. The website at zddc.varasys.io serves them by reverse-proxying `/releases/<tag>/<asset>` to the corresponding Codeberg URL, so consumers (operators' bootstrap stubs, `zddc-use`) only ever talk to zddc.varasys.io. Channel resolution is via `website/releases/manifest.json` — a small file `build.sh` regenerates from the Codeberg API and commits.
|
||||
|
||||
Stable releases do **not** automatically clobber `<tool>_alpha.html` / `<tool>_beta.html` — those keep whatever was last built into them. Use `./freshen-channel <tool> <channel>` (see "Freshen helper" below) to drag a channel forward to current stable; never `git checkout` the main worktree by hand for this.
|
||||
- **Stable**: `sh tool/build.sh --release [version]` (or just `--release` to auto-bump patch from the latest stable tag). Tags `<tool>-v<version>`, uploads `<tool>_v<version>.html` as a release asset on Codeberg. Label: `vX.Y.Z` (black). Skips silently if source has not changed since the latest stable tag (HEAD-vs-tag diff).
|
||||
- **Beta**: `sh tool/build.sh --release beta`. Tags `<tool>-v<next-patch>-beta.N`, uploads `<tool>_v<next-patch>-beta.N.html`. Label: `vX.Y.Z-beta · <date> · <sha>` (red).
|
||||
- **Alpha**: `sh tool/build.sh --release alpha`. Tags `<tool>-v<next-patch>-alpha.N`, uploads. Label: `vX.Y.Z-alpha · <date> · <sha>` (red).
|
||||
- **Plain dev builds** (no `--release`): produce `tool/dist/<tool>.html` only. No website/releases side-effect, no Codeberg upload. To publish, re-run with `--release alpha`.
|
||||
|
||||
After cutting a stable release, run `git push --tags` to publish the tag.
|
||||
After any release run, `sh build.sh` queries the Codeberg API once and rewrites `website/releases/index.html` and `manifest.json`. Commit those alongside the release.
|
||||
|
||||
The "skip if no source change since last tag" guard for stable releases compares **HEAD** to the latest tag — uncommitted working-tree changes are invisible. If you edit a tool and want a stable release to actually fire, commit the change first; otherwise the build prints `no source changes since <tool>-vX.Y.Z — skipping` and exits 0. Alpha and beta channel builds always rebuild (no skip check).
|
||||
After cutting a release, run `git push --tags` to publish the tag.
|
||||
|
||||
Agents must **never** write to `website/releases/<tool>_v*.html`, `website/releases/<tool>_stable.html`, `website/releases/<tool>_beta.html`, or `website/index.html` directly — always go through `--release` or `./freshen-channel`. (The `<tool>_alpha.html` files are an exception: every plain build rewrites them with a copy of the current dist, as described above.)
|
||||
`$CODEBERG_TOKEN` must be exported before any `--release` invocation. The `promote_release` helper calls `publish_codeberg_release` which uses the token to create the release and upload the asset.
|
||||
|
||||
`landing/build.sh --release <version>` additionally writes `website/index.html` (the root URL of zddc.varasys.io).
|
||||
`landing/build.sh --release <version>` additionally writes `website/index.html` (the root URL of zddc.varasys.io) as a regular committed file — that page is hand-edited intro copy, not a release asset.
|
||||
|
||||
### Channel discipline (MUST rules)
|
||||
|
||||
|
|
@ -191,7 +192,7 @@ The build system does not enforce these. Treating channels carelessly defeats th
|
|||
|
||||
### Freshen helper
|
||||
|
||||
`./freshen-channel <tool> <channel>` rebuilds the alpha or beta channel of a tool from its current stable tag. Use it after every stable release (rule 4 above) and any other time alpha/beta has fallen behind stable.
|
||||
`./freshen-channel <tool> <channel>` rebuilds the alpha or beta channel of a tool from its current stable tag, cutting a new pre-release tag (e.g., `<tool>-v<next-patch>-alpha.N`) and uploading the asset to Codeberg. Use it after every stable release (rule 4 above) and any other time alpha/beta has fallen behind stable.
|
||||
|
||||
```sh
|
||||
./freshen-channel archive alpha
|
||||
|
|
@ -200,13 +201,12 @@ The build system does not enforce these. Treating channels carelessly defeats th
|
|||
|
||||
What it does:
|
||||
|
||||
1. Finds the latest `<tool>-v*` tag.
|
||||
1. Finds the latest `<tool>-v*` clean stable tag.
|
||||
2. Creates a temporary git worktree at that tag — does **not** touch the main worktree's HEAD or working tree.
|
||||
3. Runs `<tool>/build.sh --release <channel>` inside the worktree.
|
||||
4. Copies the resulting `<tool>_<channel>.html` into the main repo's `website/releases/`.
|
||||
5. Removes the worktree.
|
||||
3. Runs `<tool>/build.sh --release <channel>` inside the worktree, which tags `<tool>-v<next-patch>-<channel>.N` and uploads to Codeberg.
|
||||
4. Removes the worktree.
|
||||
|
||||
The on-page label of the freshened build is `v<next-stable>-<channel> · <today> · <stable-tag-sha>` — the version reflects the next-stable target (patch+1 from current stable, since the freshen establishes the baseline for the upcoming pre-release window) and the SHA pins which stable was used as the source, recoverable via `git checkout`.
|
||||
The on-page label of the freshened build is `v<next-stable>-<channel> · <today> · <stable-tag-sha>` — the SHA pins which stable was used as the source, recoverable via `git checkout`.
|
||||
|
||||
Note: the build pipeline used is the one **at the tag**, not on `main`. That is intentional (pure reproducibility). If you have made build-system improvements since stable was cut and want the freshen to use them, cut a new stable first.
|
||||
|
||||
|
|
@ -215,9 +215,9 @@ Note: the build pipeline used is the one **at the tag**, not on `main`. That is
|
|||
`build.sh` regenerates `website/bootstrap/` on every invocation:
|
||||
|
||||
- `bootstrap/level1/<tool>.html` — 4 same-origin level-1 stubs (archive, transmittal, classifier, mdedit; landing has no level-1 stub since it only lives at deployment root).
|
||||
- `bootstrap/track-{alpha,beta,stable}/<tool>.html` — 5 level-2 stubs per channel, hardcoded to fetch the named channel from `zddc.varasys.io/releases/`.
|
||||
- `bootstrap/track-{alpha,beta,stable}/<tool>.html` — 5 level-2 stubs per channel that resolve the channel via `zddc.varasys.io/releases/manifest.json` and fetch the asset via `zddc.varasys.io/releases/<tag>/<tool>_v<version>.html` (Caddy proxies to Codeberg).
|
||||
|
||||
End users install via copy-paste shell snippets on the home page's "Install on your server" section — each snippet `curl`s the relevant stubs (or stable HTMLs, for the self-contained option) into the operator's deployment directory.
|
||||
End users install via copy-paste shell snippets on the home page's "Install on your server" section — each snippet `curl`s the relevant stubs (or a one-shot version-pinned HTML, for the self-contained option) into the operator's deployment directory.
|
||||
|
||||
See `bootstrap/README.md` for the install / pin / audit story.
|
||||
|
||||
|
|
@ -278,28 +278,33 @@ ZDDC_DATA_DIR=/path/to/your/archive podman-compose -f zddc/podman-compose.yaml u
|
|||
|
||||
### Release tagging
|
||||
|
||||
`release-image.sh` auto-derives the version (from the latest clean
|
||||
`zddc-server-vX.Y.Z` tag) and creates the git tag itself, so the
|
||||
operator no longer cuts a tag manually before running the script:
|
||||
`zddc/release.sh` is the canonical path. It tags the commit, compiles
|
||||
the binaries (native Go), and uploads them as Codeberg release assets.
|
||||
There's no container image build / push anymore — the chart's
|
||||
`Dockerfile.prod` and `Dockerfile` (dev) compile zddc-server from
|
||||
source at build time, fetching the right tag from Codeberg directly.
|
||||
The upstream `codeberg.org/varasys/zddc-server` registry is frozen
|
||||
(historical tags only).
|
||||
|
||||
```sh
|
||||
sh release-image.sh # alpha cut, version auto-derived
|
||||
sh release-image.sh alpha # same
|
||||
sh release-image.sh beta # beta cut
|
||||
sh release-image.sh stable # stable cut, patch++ from latest stable
|
||||
sh release-image.sh stable 0.1.0 # stable cut, explicit version
|
||||
sh zddc/release.sh # alpha cut, version auto-derived
|
||||
sh zddc/release.sh alpha # same
|
||||
sh zddc/release.sh beta # beta cut
|
||||
sh zddc/release.sh stable # stable cut, patch++ from latest stable
|
||||
sh zddc/release.sh stable 0.1.0 # stable cut, explicit version
|
||||
```
|
||||
|
||||
**Default channel is `alpha`** so `:stable` never advances by accident
|
||||
during active development. Pass `beta` to soak; pass `stable` only when
|
||||
deliberately promoting. The script tags the commit but does NOT push
|
||||
git history — finish with `git push origin <branch>` and `git push --tags` (or push the specific tag).
|
||||
**Default channel is `alpha`** so a stable-equivalent tag never
|
||||
appears by accident during active development. Pass `beta` to soak;
|
||||
pass `stable` only when deliberately promoting. The script tags the
|
||||
commit but does NOT push — finish with `git push origin <branch>` and
|
||||
`git push origin <tag>`.
|
||||
|
||||
**Versioning** — pre-release semver. Stable cuts get clean `vX.Y.Z`
|
||||
tags and `:vX.Y.Z` image tags. Alpha and beta cuts get
|
||||
`vX.Y.Z-alpha.N` / `vX.Y.Z-beta.N` where `X.Y.Z` is the next patch
|
||||
of the latest clean stable, and `N` is a per-channel counter that
|
||||
resets when stable advances. Example sequence (current stable v0.0.7):
|
||||
tags. Alpha and beta cuts get `vX.Y.Z-alpha.N` / `vX.Y.Z-beta.N`
|
||||
where `X.Y.Z` is the next patch of the latest clean stable and `N`
|
||||
is a per-channel counter that resets when stable advances. Example
|
||||
sequence (current stable v0.0.7):
|
||||
|
||||
```
|
||||
alpha → v0.0.8-alpha.1
|
||||
|
|
@ -310,35 +315,29 @@ stable → v0.0.8 (counter resets at next-patch advance)
|
|||
alpha → v0.0.9-alpha.1
|
||||
```
|
||||
|
||||
Cascades (unchanged):
|
||||
|
||||
- `alpha` → `:<version> :alpha`
|
||||
- `beta` → `:<version> :beta :alpha`
|
||||
- `stable` → `:<version> :stable :beta :alpha`
|
||||
|
||||
Pre-release semver ordering (`0.0.8-alpha.1 < 0.0.8-alpha.2 <
|
||||
0.0.8-beta.1 < 0.0.8`) is honored by all standard tooling — registry
|
||||
tag sorting, `git tag --sort=-v:refname`, `sort -V`, npm, cargo —
|
||||
0.0.8-beta.1 < 0.0.8`) is honored by all standard tooling — Codeberg
|
||||
release sorting, `git tag --sort=-v:refname`, `sort -V`, npm, cargo —
|
||||
so consumers can pin or compare versions without surprises.
|
||||
|
||||
**Binary publishing** — `release-image.sh` also mirrors the
|
||||
cross-compiled binaries from `zddc/dist/zddc-server-<os>-<arch>` into
|
||||
`website/releases/zddc-server-<os>-<arch>-<channel>` for every channel
|
||||
in the cascade. These are mutable channel pointers (no immutable
|
||||
per-version files — those live in the container registry to keep the
|
||||
git tree from growing 40MB per release). Plain `sh build.sh` does
|
||||
NOT mirror the binaries; only `release-image.sh` does, deliberately,
|
||||
so the website's binaries always match a published image.
|
||||
**Binary publishing** — release.sh uploads the four cross-compiled
|
||||
binaries (`zddc-server-{linux,darwin,windows}-{amd64,arm64}`) as
|
||||
release assets attached to the new git tag on Codeberg. The website
|
||||
at zddc.varasys.io reverse-proxies `/releases/<tag>/<asset>` URLs to
|
||||
the corresponding Codeberg release-asset URL, so consumers
|
||||
(`zddc-use`, the level-2 bootstrap stubs, the dynamic chart
|
||||
Dockerfiles) only ever talk to zddc.varasys.io.
|
||||
|
||||
The mirrored files at `https://zddc.varasys.io/releases/zddc-server-…`
|
||||
are what the dev shell's `zddc-use` helper fetches by default.
|
||||
After publishing: run `sh build.sh` to refresh
|
||||
`website/releases/index.html` and `manifest.json` against the new
|
||||
release list, and commit those.
|
||||
|
||||
Prerequisite: `podman login codeberg.org` (one-time, with a Codeberg
|
||||
personal token scoped `package:write`).
|
||||
Prerequisites:
|
||||
- Go 1.24+ on PATH (or run from a Go container).
|
||||
- `$CODEBERG_TOKEN` exported, scoped to write the VARASYS/ZDDC repo.
|
||||
|
||||
There is no CI for this — `.woodpecker.yml` was removed in favor of
|
||||
local-build-and-push because solo workflow benefits from one canonical
|
||||
path that fails loudly and visibly on the developer's terminal.
|
||||
There is no CI for this — solo workflow benefits from one canonical
|
||||
local path that fails loudly and visibly on the developer's terminal.
|
||||
|
||||
### Notes
|
||||
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ npx playwright test <tool> # one spec
|
|||
# zddc/ Go server (separate sub-project, not part of sh build.sh)
|
||||
(cd zddc && go test ./...) # unit tests (Go 1.24+)
|
||||
podman build -t zddc-server zddc/ # build container image
|
||||
sh release-image.sh [alpha|beta|stable] [<version>] # canonical image release; alpha/beta auto-derive version (default: alpha)
|
||||
sh zddc/release.sh [alpha|beta|stable] [<version>] # cut + publish zddc-server binaries to Codeberg release assets (default: alpha; auto-derives version)
|
||||
```
|
||||
|
||||
No lint/typecheck/format commands exist for the HTML tools — vanilla JS + POSIX sh by design.
|
||||
|
|
|
|||
|
|
@ -90,6 +90,4 @@ echo "Wrote $output_html"
|
|||
|
||||
if [ "$is_release" = "1" ]; then
|
||||
promote_release "archive"
|
||||
else
|
||||
update_alpha "archive"
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -18,32 +18,51 @@
|
|||
// ?v=0.0.4 (or v0.0.4) pins to that exact stable version
|
||||
// (none) uses the {{CHANNEL}} default
|
||||
//
|
||||
// Requires zddc.varasys.io to serve Access-Control-Allow-Origin: *.
|
||||
// Resolution path:
|
||||
// 1. Channel mode (no ?v= or ?v={alpha,beta,stable}): fetch
|
||||
// manifest.json from the same origin, look up
|
||||
// "<tool>-<channel>" → tag, then fetch the asset.
|
||||
// 2. Version pin: build the URL directly (skip manifest).
|
||||
//
|
||||
// The asset URL is /releases/<tag>/<tool>_v<version>.html. Caddy on
|
||||
// zddc.varasys.io reverse-proxies that to the Codeberg release-asset
|
||||
// URL — neither this stub nor the user's browser hits codeberg.org
|
||||
// directly.
|
||||
(async function () {
|
||||
const params = new URLSearchParams(location.search);
|
||||
const v = params.get('v');
|
||||
const tool = '{{TOOL}}';
|
||||
const defaultChannel = '{{CHANNEL}}';
|
||||
const channels = { alpha: '_alpha', beta: '_beta', stable: '_stable' };
|
||||
const channels = ['alpha', 'beta', 'stable'];
|
||||
const base = 'https://zddc.varasys.io/releases/';
|
||||
|
||||
function suffixFor(value) {
|
||||
if (!value) return '_' + defaultChannel;
|
||||
if (value in channels) return channels[value];
|
||||
const ver = value.startsWith('v') ? value.slice(1) : value;
|
||||
return '_v' + ver;
|
||||
}
|
||||
|
||||
const upstream = 'https://zddc.varasys.io/releases/' + tool + suffixFor(v) + '.html';
|
||||
function isChannel(s) { return channels.indexOf(s) >= 0; }
|
||||
|
||||
try {
|
||||
const resp = await fetch(upstream, { cache: 'no-cache', credentials: 'omit' });
|
||||
let assetUrl;
|
||||
if (v && !isChannel(v)) {
|
||||
// Explicit version pin (e.g. ?v=0.0.4 or ?v=v0.0.4).
|
||||
const ver = v.replace(/^v/, '');
|
||||
const tag = tool + '-v' + ver;
|
||||
assetUrl = base + tag + '/' + tool + '_v' + ver + '.html';
|
||||
} else {
|
||||
// Channel mode: resolve via manifest.
|
||||
const channel = (v && isChannel(v)) ? v : defaultChannel;
|
||||
const manifest = await (await fetch(base + 'manifest.json', { cache: 'no-cache', credentials: 'omit' })).json();
|
||||
const tag = manifest[tool + '-' + channel];
|
||||
if (!tag) throw new Error('manifest has no entry for ' + tool + '-' + channel);
|
||||
const ver = tag.replace(tool + '-v', '');
|
||||
assetUrl = base + tag + '/' + tool + '_v' + ver + '.html';
|
||||
}
|
||||
|
||||
const resp = await fetch(assetUrl, { cache: 'no-cache', credentials: 'omit' });
|
||||
if (!resp.ok) throw new Error(resp.status + ' ' + resp.statusText);
|
||||
const html = await resp.text();
|
||||
document.open();
|
||||
document.write(html);
|
||||
document.close();
|
||||
} catch (err) {
|
||||
document.body.textContent = 'Failed to load from ' + upstream + ': ' + err.message;
|
||||
document.body.textContent = 'Failed to load ' + tool + ': ' + err.message;
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
|
|
|
|||
251
build.sh
251
build.sh
|
|
@ -29,17 +29,25 @@ 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.
|
||||
# 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 podman >/dev/null 2>&1; then
|
||||
podman build --target binaries -o "$SCRIPT_DIR/zddc/dist/" "$SCRIPT_DIR/zddc/" 2>&1 | grep -v "^-->"
|
||||
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 "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.)"
|
||||
echo "go not found — skipping cross-compiled binary build."
|
||||
echo " (Install Go 1.24+ to build standalone binaries.)"
|
||||
fi
|
||||
|
||||
# ─── Bootstrap stubs ─────────────────────────────────────────────────────────
|
||||
|
|
@ -116,11 +124,71 @@ EOF
|
|||
done
|
||||
}
|
||||
|
||||
# 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.
|
||||
# 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>
|
||||
|
|
@ -184,113 +252,104 @@ build_releases_index() {
|
|||
|
||||
<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")
|
||||
# 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"
|
||||
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
|
||||
# zddc-server section — different artifact shape than the HTML tools.
|
||||
# Two artifact families:
|
||||
# 1. Downloadable binaries at website/releases/zddc-server-<os>-<arch>-<channel>
|
||||
# (mutable channel pointers; release-image.sh writes them).
|
||||
# 2. Container images at codeberg.org/varasys/zddc-server:<channel> and
|
||||
# :<version> (the registry; we don't proxy them through the website,
|
||||
# but we list the recent tags and a `podman pull` snippet for each).
|
||||
printf ' <section class="rel-tool">\n'
|
||||
printf ' <h2>zddc-server</h2>\n'
|
||||
printf ' <p style="margin-top:0;color:var(--color-text-muted);font-size:0.95rem;">Go HTTP file server with ACL, .archive index, and a delegated-admin .zddc editor. Distributed as a container image (preferred) or a standalone binary.</p>\n'
|
||||
|
||||
# Binaries table.
|
||||
_platforms="linux-amd64 darwin-amd64 darwin-arm64 windows-amd64"
|
||||
_have_any_bin=0
|
||||
for _ch in stable beta alpha; do
|
||||
for _p in $_platforms; do
|
||||
_ext=""; case "$_p" in windows-*) _ext=".exe" ;; esac
|
||||
if [ -f "$RELEASES_DIR/zddc-server-${_p}-${_ch}${_ext}" ]; then
|
||||
_have_any_bin=1; break 2
|
||||
# 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
|
||||
done
|
||||
if [ "$_have_any_bin" = "1" ]; then
|
||||
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 _ch in stable beta alpha; do
|
||||
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
|
||||
_f="zddc-server-${_p}-${_ch}${_ext}"
|
||||
if [ -f "$RELEASES_DIR/$_f" ]; then
|
||||
printf '<td><a href="%s">download</a></td>' "$_f"
|
||||
else
|
||||
printf '<td class="empty">—</td>'
|
||||
fi
|
||||
_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
|
||||
|
||||
# Container image: pull-snippet box plus the recent git tag list.
|
||||
printf ' <h3 style="font-size:1rem;margin:0.75rem 0 0.4rem;">Container image</h3>\n'
|
||||
printf ' <p style="margin:0 0 0.4rem;font-size:0.9rem;">Pull from <a href="https://codeberg.org/VARASYS/-/packages/container/zddc-server">codeberg.org/varasys/zddc-server</a>:</p>\n'
|
||||
printf ' <div class="rel-channels">\n'
|
||||
printf ' <a class="stable" href="https://codeberg.org/VARASYS/-/packages/container/zddc-server/stable">:stable</a>\n'
|
||||
printf ' <a class="beta" href="https://codeberg.org/VARASYS/-/packages/container/zddc-server/beta">:beta</a>\n'
|
||||
printf ' <a class="alpha" href="https://codeberg.org/VARASYS/-/packages/container/zddc-server/alpha">:alpha</a>\n'
|
||||
# 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'
|
||||
printf ' <code class="rel-pull">podman pull codeberg.org/varasys/zddc-server:stable</code>\n'
|
||||
fi
|
||||
|
||||
# Recent zddc-server tags from git: clean stables first (top), then
|
||||
# the most recent pre-releases. Dropping older pre-releases keeps the
|
||||
# list readable since alpha/beta cuts proliferate.
|
||||
_server_stables=$(git -C "$SCRIPT_DIR" tag --list 'zddc-server-v*' 2>/dev/null \
|
||||
| grep -E '^zddc-server-v[0-9]+\.[0-9]+\.[0-9]+$' \
|
||||
| sed 's|^zddc-server-v||' \
|
||||
| sort -V -r)
|
||||
_server_prereleases=$(git -C "$SCRIPT_DIR" tag --list 'zddc-server-v*' 2>/dev/null \
|
||||
| grep -E '^zddc-server-v[0-9]+\.[0-9]+\.[0-9]+-' \
|
||||
| sed 's|^zddc-server-v||' \
|
||||
| sort -V -r \
|
||||
| head -10)
|
||||
if [ -n "$_server_stables" ] || [ -n "$_server_prereleases" ]; then
|
||||
printf ' <div class="rel-versions" style="margin-top:0.6rem;"><strong>Pin to version:</strong>\n'
|
||||
printf '%s\n' "$_server_stables" | while read -r _v; do
|
||||
[ -n "$_v" ] || continue
|
||||
printf ' <a href="https://codeberg.org/VARASYS/-/packages/container/zddc-server/%s">v%s</a>\n' "$_v" "$_v"
|
||||
done
|
||||
if [ -n "$_server_prereleases" ]; then
|
||||
printf ' <span style="color:var(--color-text-muted);">recent pre-releases:</span>\n'
|
||||
printf '%s\n' "$_server_prereleases" | while read -r _v; do
|
||||
[ -n "$_v" ] || continue
|
||||
printf ' <a href="https://codeberg.org/VARASYS/-/packages/container/zddc-server/%s">v%s</a>\n' "$_v" "$_v"
|
||||
done
|
||||
fi
|
||||
printf ' </div>\n'
|
||||
fi
|
||||
printf ' </section>\n'
|
||||
done
|
||||
|
||||
cat <<'TAIL'
|
||||
|
||||
|
|
|
|||
|
|
@ -88,6 +88,4 @@ echo "Wrote $output_html"
|
|||
|
||||
if [ "$is_release" = "1" ]; then
|
||||
promote_release "classifier"
|
||||
else
|
||||
update_alpha "classifier"
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -72,11 +72,7 @@ if [ "$is_release" = "1" ]; then
|
|||
promote_release "landing"
|
||||
# NOTE: website/index.html is a hand-edited intro page for
|
||||
# zddc.varasys.io, not the landing tool. The landing tool ships
|
||||
# only via website/releases/ — the self-contained install snippet
|
||||
# on the home page copies landing_stable.html to <deployment-root>/
|
||||
# index.html, where the project picker UI is useful (it queries
|
||||
# zddc-server for the project list). See AGENTS.md "Releasing —
|
||||
# channels and layout".
|
||||
else
|
||||
update_alpha "landing"
|
||||
# only via Codeberg release assets (uploaded by promote_release) —
|
||||
# the self-contained install snippet on the home page fetches
|
||||
# landing_v<version>.html via the zddc.varasys.io Caddy proxy.
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -130,6 +130,4 @@ echo "Wrote $output_html ($(wc -c < "$output_html") bytes)"
|
|||
|
||||
if [ "$is_release" = "1" ]; then
|
||||
promote_release "mdedit"
|
||||
else
|
||||
update_alpha "mdedit"
|
||||
fi
|
||||
|
|
|
|||
214
release-image.sh
214
release-image.sh
|
|
@ -1,214 +0,0 @@
|
|||
#!/bin/sh
|
||||
# release-image.sh — build the zddc-server runtime image locally and push it
|
||||
# to codeberg.org/varasys/zddc-server with cascading channel tags.
|
||||
#
|
||||
# Usage:
|
||||
# sh release-image.sh # alpha cut, version auto-derived
|
||||
# sh release-image.sh alpha # same
|
||||
# sh release-image.sh beta # beta cut, version auto-derived
|
||||
# sh release-image.sh stable # stable cut, patch++ from latest stable
|
||||
# sh release-image.sh stable 0.1.0 # stable cut, explicit version
|
||||
#
|
||||
# Channel cascade rules (unchanged from earlier):
|
||||
# alpha → :<version> :alpha
|
||||
# beta → :<version> :beta :alpha
|
||||
# stable → :<version> :stable :beta :alpha
|
||||
#
|
||||
# Versioning: pre-release semver. Stable releases own clean vX.Y.Z; alpha
|
||||
# and beta carry vX.Y.Z-alpha.N / vX.Y.Z-beta.N suffixes, where X.Y.Z is
|
||||
# the next patch of the latest clean stable tag and N is a per-channel
|
||||
# counter that resets when stable advances.
|
||||
#
|
||||
# The script does ONE thing the old version did not: it `git tag`s the
|
||||
# release before pushing the image, since auto-derivation means the
|
||||
# operator can no longer predict the version up-front. The tag is pushed
|
||||
# to origin only if the operator runs `git push --tags` afterwards
|
||||
# (intentional — the script never pushes git history on its own).
|
||||
#
|
||||
# Prerequisites:
|
||||
# - podman (or docker — commands are identical)
|
||||
# - logged in to codeberg.org: `podman login codeberg.org`
|
||||
# (Codeberg username + a token with `package:write` scope, generated at
|
||||
# https://codeberg.org/user/settings/applications)
|
||||
#
|
||||
# What it does:
|
||||
# 1. Compute the version (auto for alpha/beta, auto-or-explicit for stable).
|
||||
# 2. Refresh dist/web by running sh build.sh — the Containerfile's
|
||||
# server stage COPYs those files in.
|
||||
# 3. Tag the current commit zddc-server-v<version>.
|
||||
# 4. Build zddc/Containerfile's `server` stage as a single local image.
|
||||
# 5. Tag and push each cascade tag.
|
||||
|
||||
set -eu
|
||||
|
||||
usage() {
|
||||
cat >&2 <<'EOF'
|
||||
usage: release-image.sh [alpha|beta|stable] [<version>]
|
||||
|
||||
alpha (default) cut alpha, version auto-derived from the latest
|
||||
clean stable tag (vX.Y.Z + patch++ + -alpha.N).
|
||||
beta cut beta, version auto-derived (vX.Y.Z + -beta.N).
|
||||
stable cut stable. Without <version>, patch-bump from the
|
||||
latest clean stable tag. With <version>, use it
|
||||
verbatim (must be a clean X.Y.Z).
|
||||
EOF
|
||||
exit 1
|
||||
}
|
||||
|
||||
CHANNEL="${1:-alpha}"
|
||||
case "$CHANNEL" in
|
||||
alpha | beta | stable) ;;
|
||||
-h | --help) usage ;;
|
||||
*) echo "error: unknown channel '$CHANNEL'" >&2; usage ;;
|
||||
esac
|
||||
|
||||
EXPLICIT_VERSION="${2:-}"
|
||||
if [ -n "$EXPLICIT_VERSION" ] && [ "$CHANNEL" != "stable" ]; then
|
||||
echo "error: an explicit <version> is only valid with the 'stable' channel" >&2
|
||||
echo " alpha and beta versions are auto-derived." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
REPO="codeberg.org/varasys/zddc-server"
|
||||
SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd)
|
||||
TAG_PREFIX="zddc-server-v"
|
||||
|
||||
# Source build-lib.sh so we can call next_prerelease for alpha/beta and
|
||||
# share the validation helpers used by the HTML tools.
|
||||
root_dir="$SCRIPT_DIR"
|
||||
. "$SCRIPT_DIR/shared/build-lib.sh"
|
||||
|
||||
# --- Determine the version --------------------------------------------------
|
||||
case "$CHANNEL" in
|
||||
alpha | beta)
|
||||
VERSION=$(next_prerelease "$CHANNEL" "$TAG_PREFIX")
|
||||
;;
|
||||
stable)
|
||||
if [ -n "$EXPLICIT_VERSION" ]; then
|
||||
_validate_semver "$EXPLICIT_VERSION"
|
||||
VERSION="$EXPLICIT_VERSION"
|
||||
else
|
||||
# Auto-bump patch from the latest clean stable tag.
|
||||
_latest=$(git -C "$SCRIPT_DIR" tag --list "${TAG_PREFIX}*" 2>/dev/null \
|
||||
| grep -E "^${TAG_PREFIX}[0-9]+\.[0-9]+\.[0-9]+\$" \
|
||||
| sed "s|^${TAG_PREFIX}||" \
|
||||
| sort -V \
|
||||
| tail -1)
|
||||
[ -n "$_latest" ] || _latest="0.0.0"
|
||||
_major="${_latest%%.*}"
|
||||
_rest="${_latest#*.}"
|
||||
_minor="${_rest%%.*}"
|
||||
_patch="${_rest#*.}"
|
||||
VERSION="${_major}.${_minor}.$((_patch + 1))"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
case "$CHANNEL" in
|
||||
alpha) TAGS="$VERSION alpha" ;;
|
||||
beta) TAGS="$VERSION beta alpha" ;;
|
||||
stable) TAGS="$VERSION stable beta alpha" ;;
|
||||
esac
|
||||
|
||||
# Pick podman or docker, whichever is on PATH.
|
||||
if command -v podman >/dev/null 2>&1; then
|
||||
OCI=podman
|
||||
elif command -v docker >/dev/null 2>&1; then
|
||||
OCI=docker
|
||||
else
|
||||
echo "error: neither podman nor docker found on PATH" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
GIT_TAG="${TAG_PREFIX}${VERSION}"
|
||||
|
||||
echo "=== Building $REPO ==="
|
||||
echo "Channel: $CHANNEL"
|
||||
echo "Version: $VERSION"
|
||||
echo "Git tag: $GIT_TAG"
|
||||
echo "Image tags: $TAGS"
|
||||
echo "OCI CLI: $OCI"
|
||||
echo
|
||||
|
||||
# --- Refresh HTML dist (Containerfile COPYs from dist/web) ------------------
|
||||
sh "$SCRIPT_DIR/build.sh"
|
||||
|
||||
# --- Mirror cross-compiled binaries to website/releases/ -------------------
|
||||
# Channel pointers, mutable: cascade rules mirror this image's binaries to
|
||||
# every channel in the cascade so a `zddc-use :stable` curl pulls bytes that
|
||||
# match the just-published stable image. Per-version pinning is provided by
|
||||
# the container registry's :vX.Y.Z-alpha.N tag, not by an immutable file
|
||||
# here — keeps the repo from growing 40MB per release. Plain `sh build.sh`
|
||||
# does NOT mirror binaries, deliberately: it would dirty 40MB of files on
|
||||
# every dev iteration. Use release-image.sh as the publish trigger.
|
||||
publish_binary() {
|
||||
_src="$1" # path under zddc/dist/, e.g. zddc-server-linux-amd64
|
||||
_basename=$(basename "$_src")
|
||||
if [ ! -f "$SCRIPT_DIR/zddc/dist/$_src" ]; then
|
||||
echo "warn: $_src not found in zddc/dist/; skipping mirror" >&2
|
||||
return 0
|
||||
fi
|
||||
# The version-style tag (e.g. 0.0.8-alpha.1) is in $TAGS too — skip it;
|
||||
# only iterate the channel-name tags.
|
||||
for _tag in $TAGS; do
|
||||
case "$_tag" in
|
||||
alpha | beta | stable)
|
||||
_dest="$SCRIPT_DIR/website/releases/${_basename%.exe}-${_tag}"
|
||||
case "$_basename" in *.exe) _dest="${_dest}.exe" ;; esac
|
||||
cp -f "$SCRIPT_DIR/zddc/dist/$_src" "$_dest"
|
||||
echo "mirrored $_src → website/releases/$(basename "$_dest")"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
publish_binary zddc-server-linux-amd64
|
||||
publish_binary zddc-server-darwin-amd64
|
||||
publish_binary zddc-server-darwin-arm64
|
||||
publish_binary zddc-server-windows-amd64.exe
|
||||
|
||||
# --- Tag the commit (idempotent: skip if the tag already points here) -------
|
||||
if git -C "$SCRIPT_DIR" rev-parse -q --verify "refs/tags/$GIT_TAG" >/dev/null; then
|
||||
_existing=$(git -C "$SCRIPT_DIR" rev-list -n 1 "$GIT_TAG")
|
||||
_head=$(git -C "$SCRIPT_DIR" rev-parse HEAD)
|
||||
if [ "$_existing" != "$_head" ]; then
|
||||
echo "error: tag $GIT_TAG already exists at $_existing, but HEAD is $_head" >&2
|
||||
echo " refusing to overwrite. Resolve manually." >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "(tag $GIT_TAG already at HEAD)"
|
||||
else
|
||||
git -C "$SCRIPT_DIR" tag "$GIT_TAG"
|
||||
echo "tagged $GIT_TAG (run 'git push --tags' to publish)"
|
||||
fi
|
||||
|
||||
# --- Build + push -----------------------------------------------------------
|
||||
"$OCI" build --target server -t zddc-server:build "$SCRIPT_DIR/zddc/"
|
||||
|
||||
echo
|
||||
echo "=== Pushing tags ==="
|
||||
for tag in $TAGS; do
|
||||
"$OCI" tag zddc-server:build "$REPO:$tag"
|
||||
"$OCI" push "$REPO:$tag"
|
||||
echo "pushed $REPO:$tag"
|
||||
done
|
||||
|
||||
echo
|
||||
echo "=== Done ==="
|
||||
echo "Image: $REPO:$VERSION"
|
||||
echo "Cascading tags: $TAGS"
|
||||
echo "Git tag: $GIT_TAG (publish with: git push origin $GIT_TAG)"
|
||||
echo
|
||||
case "$CHANNEL" in
|
||||
stable)
|
||||
echo "Reminder (channel discipline rule 4): freshen alpha + beta now"
|
||||
echo "so users tracking those channels are not on stale code:"
|
||||
echo " ./freshen-channel <tool> alpha"
|
||||
echo " ./freshen-channel <tool> beta"
|
||||
echo
|
||||
echo "Bump tnd-zddc-chart's ZDDC_SERVER_TAG (push to develop) so the"
|
||||
echo "chart's CI rebuilds the dev-shell image and helm-rolls."
|
||||
;;
|
||||
beta)
|
||||
echo "Beta cut. Soak before promoting to stable."
|
||||
;;
|
||||
esac
|
||||
|
|
@ -21,36 +21,28 @@
|
|||
# — sets globals: build_label, build_version,
|
||||
# is_release, is_red, channel.
|
||||
# See "Channels and release args" below.
|
||||
# promote_release <tool> — write to website/releases/ in the layout
|
||||
# driven by $channel and $build_version. For
|
||||
# stable, also update the _stable.html
|
||||
# symlink and create the git tag.
|
||||
# update_alpha <tool> — copy the just-built dist file to
|
||||
# website/releases/<tool>_alpha.html so the
|
||||
# site's alpha hyperlinks always serve the
|
||||
# latest dev build. Called automatically by
|
||||
# each tool's build.sh on plain (non-release)
|
||||
# builds. Plain copy (not symlink) so it
|
||||
# survives deployments whose web server only
|
||||
# mounts website/. `--release alpha` writes
|
||||
# the same file with the formal
|
||||
# "alpha · <date> · <sha>" label; the next
|
||||
# plain build clobbers it.
|
||||
# promote_release <tool> — for stable / alpha / beta, tag the
|
||||
# commit and upload the dist HTML as a
|
||||
# Codeberg release asset. No local mirror
|
||||
# under website/releases/ — the website
|
||||
# reverse-proxies download URLs to Codeberg
|
||||
# release-asset URLs. Stable cuts skip when
|
||||
# the tool's source is unchanged since the
|
||||
# latest stable tag.
|
||||
#
|
||||
# Channels and release args:
|
||||
# <none> dev build, dist/ + alpha mirror, label
|
||||
# <none> dev build, dist/ only, label
|
||||
# "v<next-stable>-alpha · <ts> · <sha>[-dirty]" (red).
|
||||
# Plain builds ARE alpha builds — every dev build cascades
|
||||
# to the alpha channel via update_alpha. The version embeds
|
||||
# the next-stable target (patch-bump of the latest clean
|
||||
# <tool>-vX.Y.Z tag).
|
||||
# No website/releases/ side-effect. To publish, re-run
|
||||
# with `--release alpha`.
|
||||
# --release stable, auto-bump patch from latest tag (or 0.0.1).
|
||||
# Label "vX.Y.Z" (black).
|
||||
# --release X.Y.Z stable, explicit version.
|
||||
# Label "vX.Y.Z" (black). Tags + uploads.
|
||||
# --release X.Y.Z stable, explicit version. Tags + uploads.
|
||||
# --release alpha alpha channel cut at HEAD;
|
||||
# label "v<next-stable>-alpha · <date> · <sha>" (red).
|
||||
# Tags <tool>-vX.Y.Z-alpha.N + uploads.
|
||||
# --release beta beta channel; label "v<next-stable>-beta · <date> · <sha>".
|
||||
# Opt-in soak channel before stable promotion.
|
||||
# Tags + uploads.
|
||||
# --release <other> error.
|
||||
# =============================================================================
|
||||
|
||||
|
|
@ -60,6 +52,11 @@ if [ -z "${root_dir:-}" ]; then
|
|||
exit 1
|
||||
fi
|
||||
|
||||
# Pull in the Codeberg release-publish helper so promote_release can call
|
||||
# its publish_codeberg_release function. Sourced unconditionally — the
|
||||
# helper has no side effects when sourced (only defines functions).
|
||||
. "$root_dir/../shared/publish-codeberg-release.sh"
|
||||
|
||||
# Fail hard on any missing source file
|
||||
ensure_exists() {
|
||||
_path="$1"
|
||||
|
|
@ -265,80 +262,83 @@ _next_stable_for_tool() {
|
|||
echo "${_major}.${_minor}.$((_patch + 1))"
|
||||
}
|
||||
|
||||
# Promote a built dist file to the appropriate slot under website/releases/.
|
||||
# Promote a built dist file to a Codeberg release.
|
||||
# Reads from caller scope: $channel, $build_version, $output_html, $root_dir.
|
||||
#
|
||||
# Stable releases write website/releases/<tool>_v<version>.html, refresh the
|
||||
# website/releases/<tool>_stable.html symlink, and tag <tool>-v<version> in
|
||||
# git. Skips silently when the source has not changed since the latest tag.
|
||||
# All three channels (alpha, beta, stable) follow the same shape now:
|
||||
# 1. Compute the version (already done by compute_build_label for stable;
|
||||
# for alpha/beta we compute next_prerelease here).
|
||||
# 2. Tag the commit <tool>-v<version> (or <tool>-v<version>-CHANNEL.N).
|
||||
# 3. Upload the built dist HTML as a release asset to Codeberg.
|
||||
#
|
||||
# Alpha and beta channel releases overwrite website/releases/<tool>_<channel>.html
|
||||
# in place with no tag (the embedded label carries date + commit SHA, so the
|
||||
# source is recoverable from git directly).
|
||||
# Idempotent: the publish helper replaces a same-named asset on re-upload,
|
||||
# and the tag step is a no-op if the tag already points at HEAD.
|
||||
#
|
||||
# For stable: the original "skip if no source change since latest stable
|
||||
# tag" guard still applies — pointless re-releases are silently no-op'd.
|
||||
# For alpha/beta: the auto-incrementing counter already differentiates
|
||||
# successive cuts, so no skip check.
|
||||
#
|
||||
# Requires $CODEBERG_TOKEN exported. publish_codeberg_release surfaces a
|
||||
# clear error if it isn't.
|
||||
promote_release() {
|
||||
_tool="$1"
|
||||
_releases_dir="$root_dir/../website/releases"
|
||||
mkdir -p "$_releases_dir"
|
||||
|
||||
if [ "$channel" = "alpha" ] || [ "$channel" = "beta" ]; then
|
||||
_dest="${_releases_dir}/${_tool}_${channel}.html"
|
||||
cp "$output_html" "$_dest"
|
||||
echo "Released $channel to $_dest"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [ "$channel" != "stable" ] || [ -z "$build_version" ]; then
|
||||
echo "promote_release: refusing to promote — channel=$channel build_version=$build_version" >&2
|
||||
case "$channel" in
|
||||
stable)
|
||||
if [ -z "$build_version" ]; then
|
||||
echo "promote_release: stable channel but no build_version" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
_latest=$(git -C "$root_dir" tag --list "${_tool}-v*" --sort=-v:refname 2>/dev/null | head -1)
|
||||
_latest=$(git -C "$root_dir" tag --list "${_tool}-v*" 2>/dev/null \
|
||||
| grep -E "^${_tool}-v[0-9]+\.[0-9]+\.[0-9]+\$" \
|
||||
| sort -V | tail -1)
|
||||
if [ -n "$_latest" ] && git -C "$root_dir" diff --quiet "$_latest" HEAD -- . ../shared 2>/dev/null; then
|
||||
echo "${_tool}: no source changes since $_latest — skipping"
|
||||
return 0
|
||||
fi
|
||||
_version="$build_version"
|
||||
;;
|
||||
alpha | beta)
|
||||
_version=$(next_prerelease "$channel" "${_tool}-v")
|
||||
;;
|
||||
*)
|
||||
echo "promote_release: unknown channel '$channel'" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
_versioned="${_releases_dir}/${_tool}_v${build_version}.html"
|
||||
cp "$output_html" "$_versioned"
|
||||
echo "Released $_versioned"
|
||||
_tag="${_tool}-v${_version}"
|
||||
|
||||
# Symlink target is relative to its own directory so the link survives
|
||||
# path moves and works regardless of where the website is mounted.
|
||||
(cd "$_releases_dir" && ln -sfn "${_tool}_v${build_version}.html" "${_tool}_stable.html")
|
||||
echo "Updated ${_tool}_stable.html -> ${_tool}_v${build_version}.html"
|
||||
# Tag the commit (idempotent: skip if already at HEAD).
|
||||
if git -C "$root_dir" rev-parse -q --verify "refs/tags/$_tag" >/dev/null; then
|
||||
_existing=$(git -C "$root_dir" rev-list -n 1 "$_tag")
|
||||
_head=$(git -C "$root_dir" rev-parse HEAD)
|
||||
if [ "$_existing" != "$_head" ]; then
|
||||
echo "promote_release: tag $_tag already exists at $_existing, but HEAD is $_head" >&2
|
||||
return 1
|
||||
fi
|
||||
echo "(tag $_tag already at HEAD)"
|
||||
else
|
||||
git -C "$root_dir" tag "$_tag"
|
||||
echo "tagged $_tag"
|
||||
fi
|
||||
|
||||
git -C "$root_dir" tag "${_tool}-v${build_version}"
|
||||
echo "Tagged ${_tool}-v${build_version} — run: git push --tags"
|
||||
}
|
||||
|
||||
# Mirror the just-built dist file to website/releases/<tool>_alpha.html so
|
||||
# the website's alpha hyperlinks always serve whatever dist currently holds.
|
||||
# Plain copy (not symlink): symlinks pointing outside website/ break under
|
||||
# deployments whose web server only mounts website/ (notably the canonical
|
||||
# Caddy setup at /etc/containers/systemd/caddy.container, which mounts
|
||||
# /home/user/src/zddc/website read-only and cannot follow ../ paths to
|
||||
# landing/dist or archive/dist on the host filesystem).
|
||||
#
|
||||
# Trade-off: every dev build that touches a tool's source dirties the
|
||||
# corresponding _alpha.html file in git. Commit those alongside the source
|
||||
# change (or `git checkout` them before pushing) since the alpha channel is
|
||||
# explicitly mutable.
|
||||
#
|
||||
# `--release alpha` overwrites the same file with a "alpha · date · sha"
|
||||
# labeled build; the next plain build clobbers it again. That's the alpha-
|
||||
# is-mutable contract.
|
||||
#
|
||||
# Reads $output_html and $root_dir from caller scope.
|
||||
update_alpha() {
|
||||
_tool="$1"
|
||||
_releases_dir="$root_dir/../website/releases"
|
||||
_dest="${_releases_dir}/${_tool}_alpha.html"
|
||||
mkdir -p "$_releases_dir"
|
||||
# rm first: if the dest is currently a symlink to dist (legacy from the
|
||||
# earlier symlink approach), `cp` would follow the symlink and try to
|
||||
# write to the same file it's reading from. Removing first replaces the
|
||||
# symlink with a plain regular file copy.
|
||||
rm -f "$_dest"
|
||||
cp "$output_html" "$_dest"
|
||||
echo "Mirrored to $_dest"
|
||||
# Upload to Codeberg. The asset name embeds the version so consumers
|
||||
# can pin to a specific build (e.g. <tool>_v0.0.3-alpha.1.html).
|
||||
_asset="${_tool}_v${_version}.html"
|
||||
_staged="$root_dir/$_tool/dist/$_asset"
|
||||
cp "$output_html" "$_staged"
|
||||
if ! command -v publish_codeberg_release >/dev/null 2>&1; then
|
||||
# build-lib.sh is sourced before publish-codeberg-release.sh in the
|
||||
# canonical wrapper scripts; if the helper isn't loaded yet, bail
|
||||
# with a clear pointer.
|
||||
echo "promote_release: publish_codeberg_release not available; source shared/publish-codeberg-release.sh first" >&2
|
||||
return 1
|
||||
fi
|
||||
publish_codeberg_release "VARASYS/ZDDC" "$_tag" "$_staged"
|
||||
rm -f "$_staged"
|
||||
|
||||
echo "Released $_tag (channel: $channel, version: $_version)"
|
||||
echo " publish git tag with: git push origin $_tag"
|
||||
}
|
||||
|
|
|
|||
167
shared/publish-codeberg-release.sh
Executable file
167
shared/publish-codeberg-release.sh
Executable file
|
|
@ -0,0 +1,167 @@
|
|||
#!/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
|
||||
|
|
@ -172,6 +172,4 @@ echo "Wrote $output_html"
|
|||
|
||||
if [ "$is_release" = "1" ]; then
|
||||
promote_release "transmittal"
|
||||
else
|
||||
update_alpha "transmittal"
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -18,32 +18,51 @@
|
|||
// ?v=0.0.4 (or v0.0.4) pins to that exact stable version
|
||||
// (none) uses the alpha default
|
||||
//
|
||||
// Requires zddc.varasys.io to serve Access-Control-Allow-Origin: *.
|
||||
// Resolution path:
|
||||
// 1. Channel mode (no ?v= or ?v={alpha,beta,stable}): fetch
|
||||
// manifest.json from the same origin, look up
|
||||
// "<tool>-<channel>" → tag, then fetch the asset.
|
||||
// 2. Version pin: build the URL directly (skip manifest).
|
||||
//
|
||||
// The asset URL is /releases/<tag>/<tool>_v<version>.html. Caddy on
|
||||
// zddc.varasys.io reverse-proxies that to the Codeberg release-asset
|
||||
// URL — neither this stub nor the user's browser hits codeberg.org
|
||||
// directly.
|
||||
(async function () {
|
||||
const params = new URLSearchParams(location.search);
|
||||
const v = params.get('v');
|
||||
const tool = 'archive';
|
||||
const defaultChannel = 'alpha';
|
||||
const channels = { alpha: '_alpha', beta: '_beta', stable: '_stable' };
|
||||
const channels = ['alpha', 'beta', 'stable'];
|
||||
const base = 'https://zddc.varasys.io/releases/';
|
||||
|
||||
function suffixFor(value) {
|
||||
if (!value) return '_' + defaultChannel;
|
||||
if (value in channels) return channels[value];
|
||||
const ver = value.startsWith('v') ? value.slice(1) : value;
|
||||
return '_v' + ver;
|
||||
}
|
||||
|
||||
const upstream = 'https://zddc.varasys.io/releases/' + tool + suffixFor(v) + '.html';
|
||||
function isChannel(s) { return channels.indexOf(s) >= 0; }
|
||||
|
||||
try {
|
||||
const resp = await fetch(upstream, { cache: 'no-cache', credentials: 'omit' });
|
||||
let assetUrl;
|
||||
if (v && !isChannel(v)) {
|
||||
// Explicit version pin (e.g. ?v=0.0.4 or ?v=v0.0.4).
|
||||
const ver = v.replace(/^v/, '');
|
||||
const tag = tool + '-v' + ver;
|
||||
assetUrl = base + tag + '/' + tool + '_v' + ver + '.html';
|
||||
} else {
|
||||
// Channel mode: resolve via manifest.
|
||||
const channel = (v && isChannel(v)) ? v : defaultChannel;
|
||||
const manifest = await (await fetch(base + 'manifest.json', { cache: 'no-cache', credentials: 'omit' })).json();
|
||||
const tag = manifest[tool + '-' + channel];
|
||||
if (!tag) throw new Error('manifest has no entry for ' + tool + '-' + channel);
|
||||
const ver = tag.replace(tool + '-v', '');
|
||||
assetUrl = base + tag + '/' + tool + '_v' + ver + '.html';
|
||||
}
|
||||
|
||||
const resp = await fetch(assetUrl, { cache: 'no-cache', credentials: 'omit' });
|
||||
if (!resp.ok) throw new Error(resp.status + ' ' + resp.statusText);
|
||||
const html = await resp.text();
|
||||
document.open();
|
||||
document.write(html);
|
||||
document.close();
|
||||
} catch (err) {
|
||||
document.body.textContent = 'Failed to load from ' + upstream + ': ' + err.message;
|
||||
document.body.textContent = 'Failed to load ' + tool + ': ' + err.message;
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -18,32 +18,51 @@
|
|||
// ?v=0.0.4 (or v0.0.4) pins to that exact stable version
|
||||
// (none) uses the alpha default
|
||||
//
|
||||
// Requires zddc.varasys.io to serve Access-Control-Allow-Origin: *.
|
||||
// Resolution path:
|
||||
// 1. Channel mode (no ?v= or ?v={alpha,beta,stable}): fetch
|
||||
// manifest.json from the same origin, look up
|
||||
// "<tool>-<channel>" → tag, then fetch the asset.
|
||||
// 2. Version pin: build the URL directly (skip manifest).
|
||||
//
|
||||
// The asset URL is /releases/<tag>/<tool>_v<version>.html. Caddy on
|
||||
// zddc.varasys.io reverse-proxies that to the Codeberg release-asset
|
||||
// URL — neither this stub nor the user's browser hits codeberg.org
|
||||
// directly.
|
||||
(async function () {
|
||||
const params = new URLSearchParams(location.search);
|
||||
const v = params.get('v');
|
||||
const tool = 'classifier';
|
||||
const defaultChannel = 'alpha';
|
||||
const channels = { alpha: '_alpha', beta: '_beta', stable: '_stable' };
|
||||
const channels = ['alpha', 'beta', 'stable'];
|
||||
const base = 'https://zddc.varasys.io/releases/';
|
||||
|
||||
function suffixFor(value) {
|
||||
if (!value) return '_' + defaultChannel;
|
||||
if (value in channels) return channels[value];
|
||||
const ver = value.startsWith('v') ? value.slice(1) : value;
|
||||
return '_v' + ver;
|
||||
}
|
||||
|
||||
const upstream = 'https://zddc.varasys.io/releases/' + tool + suffixFor(v) + '.html';
|
||||
function isChannel(s) { return channels.indexOf(s) >= 0; }
|
||||
|
||||
try {
|
||||
const resp = await fetch(upstream, { cache: 'no-cache', credentials: 'omit' });
|
||||
let assetUrl;
|
||||
if (v && !isChannel(v)) {
|
||||
// Explicit version pin (e.g. ?v=0.0.4 or ?v=v0.0.4).
|
||||
const ver = v.replace(/^v/, '');
|
||||
const tag = tool + '-v' + ver;
|
||||
assetUrl = base + tag + '/' + tool + '_v' + ver + '.html';
|
||||
} else {
|
||||
// Channel mode: resolve via manifest.
|
||||
const channel = (v && isChannel(v)) ? v : defaultChannel;
|
||||
const manifest = await (await fetch(base + 'manifest.json', { cache: 'no-cache', credentials: 'omit' })).json();
|
||||
const tag = manifest[tool + '-' + channel];
|
||||
if (!tag) throw new Error('manifest has no entry for ' + tool + '-' + channel);
|
||||
const ver = tag.replace(tool + '-v', '');
|
||||
assetUrl = base + tag + '/' + tool + '_v' + ver + '.html';
|
||||
}
|
||||
|
||||
const resp = await fetch(assetUrl, { cache: 'no-cache', credentials: 'omit' });
|
||||
if (!resp.ok) throw new Error(resp.status + ' ' + resp.statusText);
|
||||
const html = await resp.text();
|
||||
document.open();
|
||||
document.write(html);
|
||||
document.close();
|
||||
} catch (err) {
|
||||
document.body.textContent = 'Failed to load from ' + upstream + ': ' + err.message;
|
||||
document.body.textContent = 'Failed to load ' + tool + ': ' + err.message;
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -18,32 +18,51 @@
|
|||
// ?v=0.0.4 (or v0.0.4) pins to that exact stable version
|
||||
// (none) uses the alpha default
|
||||
//
|
||||
// Requires zddc.varasys.io to serve Access-Control-Allow-Origin: *.
|
||||
// Resolution path:
|
||||
// 1. Channel mode (no ?v= or ?v={alpha,beta,stable}): fetch
|
||||
// manifest.json from the same origin, look up
|
||||
// "<tool>-<channel>" → tag, then fetch the asset.
|
||||
// 2. Version pin: build the URL directly (skip manifest).
|
||||
//
|
||||
// The asset URL is /releases/<tag>/<tool>_v<version>.html. Caddy on
|
||||
// zddc.varasys.io reverse-proxies that to the Codeberg release-asset
|
||||
// URL — neither this stub nor the user's browser hits codeberg.org
|
||||
// directly.
|
||||
(async function () {
|
||||
const params = new URLSearchParams(location.search);
|
||||
const v = params.get('v');
|
||||
const tool = 'landing';
|
||||
const defaultChannel = 'alpha';
|
||||
const channels = { alpha: '_alpha', beta: '_beta', stable: '_stable' };
|
||||
const channels = ['alpha', 'beta', 'stable'];
|
||||
const base = 'https://zddc.varasys.io/releases/';
|
||||
|
||||
function suffixFor(value) {
|
||||
if (!value) return '_' + defaultChannel;
|
||||
if (value in channels) return channels[value];
|
||||
const ver = value.startsWith('v') ? value.slice(1) : value;
|
||||
return '_v' + ver;
|
||||
}
|
||||
|
||||
const upstream = 'https://zddc.varasys.io/releases/' + tool + suffixFor(v) + '.html';
|
||||
function isChannel(s) { return channels.indexOf(s) >= 0; }
|
||||
|
||||
try {
|
||||
const resp = await fetch(upstream, { cache: 'no-cache', credentials: 'omit' });
|
||||
let assetUrl;
|
||||
if (v && !isChannel(v)) {
|
||||
// Explicit version pin (e.g. ?v=0.0.4 or ?v=v0.0.4).
|
||||
const ver = v.replace(/^v/, '');
|
||||
const tag = tool + '-v' + ver;
|
||||
assetUrl = base + tag + '/' + tool + '_v' + ver + '.html';
|
||||
} else {
|
||||
// Channel mode: resolve via manifest.
|
||||
const channel = (v && isChannel(v)) ? v : defaultChannel;
|
||||
const manifest = await (await fetch(base + 'manifest.json', { cache: 'no-cache', credentials: 'omit' })).json();
|
||||
const tag = manifest[tool + '-' + channel];
|
||||
if (!tag) throw new Error('manifest has no entry for ' + tool + '-' + channel);
|
||||
const ver = tag.replace(tool + '-v', '');
|
||||
assetUrl = base + tag + '/' + tool + '_v' + ver + '.html';
|
||||
}
|
||||
|
||||
const resp = await fetch(assetUrl, { cache: 'no-cache', credentials: 'omit' });
|
||||
if (!resp.ok) throw new Error(resp.status + ' ' + resp.statusText);
|
||||
const html = await resp.text();
|
||||
document.open();
|
||||
document.write(html);
|
||||
document.close();
|
||||
} catch (err) {
|
||||
document.body.textContent = 'Failed to load from ' + upstream + ': ' + err.message;
|
||||
document.body.textContent = 'Failed to load ' + tool + ': ' + err.message;
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -18,32 +18,51 @@
|
|||
// ?v=0.0.4 (or v0.0.4) pins to that exact stable version
|
||||
// (none) uses the alpha default
|
||||
//
|
||||
// Requires zddc.varasys.io to serve Access-Control-Allow-Origin: *.
|
||||
// Resolution path:
|
||||
// 1. Channel mode (no ?v= or ?v={alpha,beta,stable}): fetch
|
||||
// manifest.json from the same origin, look up
|
||||
// "<tool>-<channel>" → tag, then fetch the asset.
|
||||
// 2. Version pin: build the URL directly (skip manifest).
|
||||
//
|
||||
// The asset URL is /releases/<tag>/<tool>_v<version>.html. Caddy on
|
||||
// zddc.varasys.io reverse-proxies that to the Codeberg release-asset
|
||||
// URL — neither this stub nor the user's browser hits codeberg.org
|
||||
// directly.
|
||||
(async function () {
|
||||
const params = new URLSearchParams(location.search);
|
||||
const v = params.get('v');
|
||||
const tool = 'mdedit';
|
||||
const defaultChannel = 'alpha';
|
||||
const channels = { alpha: '_alpha', beta: '_beta', stable: '_stable' };
|
||||
const channels = ['alpha', 'beta', 'stable'];
|
||||
const base = 'https://zddc.varasys.io/releases/';
|
||||
|
||||
function suffixFor(value) {
|
||||
if (!value) return '_' + defaultChannel;
|
||||
if (value in channels) return channels[value];
|
||||
const ver = value.startsWith('v') ? value.slice(1) : value;
|
||||
return '_v' + ver;
|
||||
}
|
||||
|
||||
const upstream = 'https://zddc.varasys.io/releases/' + tool + suffixFor(v) + '.html';
|
||||
function isChannel(s) { return channels.indexOf(s) >= 0; }
|
||||
|
||||
try {
|
||||
const resp = await fetch(upstream, { cache: 'no-cache', credentials: 'omit' });
|
||||
let assetUrl;
|
||||
if (v && !isChannel(v)) {
|
||||
// Explicit version pin (e.g. ?v=0.0.4 or ?v=v0.0.4).
|
||||
const ver = v.replace(/^v/, '');
|
||||
const tag = tool + '-v' + ver;
|
||||
assetUrl = base + tag + '/' + tool + '_v' + ver + '.html';
|
||||
} else {
|
||||
// Channel mode: resolve via manifest.
|
||||
const channel = (v && isChannel(v)) ? v : defaultChannel;
|
||||
const manifest = await (await fetch(base + 'manifest.json', { cache: 'no-cache', credentials: 'omit' })).json();
|
||||
const tag = manifest[tool + '-' + channel];
|
||||
if (!tag) throw new Error('manifest has no entry for ' + tool + '-' + channel);
|
||||
const ver = tag.replace(tool + '-v', '');
|
||||
assetUrl = base + tag + '/' + tool + '_v' + ver + '.html';
|
||||
}
|
||||
|
||||
const resp = await fetch(assetUrl, { cache: 'no-cache', credentials: 'omit' });
|
||||
if (!resp.ok) throw new Error(resp.status + ' ' + resp.statusText);
|
||||
const html = await resp.text();
|
||||
document.open();
|
||||
document.write(html);
|
||||
document.close();
|
||||
} catch (err) {
|
||||
document.body.textContent = 'Failed to load from ' + upstream + ': ' + err.message;
|
||||
document.body.textContent = 'Failed to load ' + tool + ': ' + err.message;
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -18,32 +18,51 @@
|
|||
// ?v=0.0.4 (or v0.0.4) pins to that exact stable version
|
||||
// (none) uses the alpha default
|
||||
//
|
||||
// Requires zddc.varasys.io to serve Access-Control-Allow-Origin: *.
|
||||
// Resolution path:
|
||||
// 1. Channel mode (no ?v= or ?v={alpha,beta,stable}): fetch
|
||||
// manifest.json from the same origin, look up
|
||||
// "<tool>-<channel>" → tag, then fetch the asset.
|
||||
// 2. Version pin: build the URL directly (skip manifest).
|
||||
//
|
||||
// The asset URL is /releases/<tag>/<tool>_v<version>.html. Caddy on
|
||||
// zddc.varasys.io reverse-proxies that to the Codeberg release-asset
|
||||
// URL — neither this stub nor the user's browser hits codeberg.org
|
||||
// directly.
|
||||
(async function () {
|
||||
const params = new URLSearchParams(location.search);
|
||||
const v = params.get('v');
|
||||
const tool = 'transmittal';
|
||||
const defaultChannel = 'alpha';
|
||||
const channels = { alpha: '_alpha', beta: '_beta', stable: '_stable' };
|
||||
const channels = ['alpha', 'beta', 'stable'];
|
||||
const base = 'https://zddc.varasys.io/releases/';
|
||||
|
||||
function suffixFor(value) {
|
||||
if (!value) return '_' + defaultChannel;
|
||||
if (value in channels) return channels[value];
|
||||
const ver = value.startsWith('v') ? value.slice(1) : value;
|
||||
return '_v' + ver;
|
||||
}
|
||||
|
||||
const upstream = 'https://zddc.varasys.io/releases/' + tool + suffixFor(v) + '.html';
|
||||
function isChannel(s) { return channels.indexOf(s) >= 0; }
|
||||
|
||||
try {
|
||||
const resp = await fetch(upstream, { cache: 'no-cache', credentials: 'omit' });
|
||||
let assetUrl;
|
||||
if (v && !isChannel(v)) {
|
||||
// Explicit version pin (e.g. ?v=0.0.4 or ?v=v0.0.4).
|
||||
const ver = v.replace(/^v/, '');
|
||||
const tag = tool + '-v' + ver;
|
||||
assetUrl = base + tag + '/' + tool + '_v' + ver + '.html';
|
||||
} else {
|
||||
// Channel mode: resolve via manifest.
|
||||
const channel = (v && isChannel(v)) ? v : defaultChannel;
|
||||
const manifest = await (await fetch(base + 'manifest.json', { cache: 'no-cache', credentials: 'omit' })).json();
|
||||
const tag = manifest[tool + '-' + channel];
|
||||
if (!tag) throw new Error('manifest has no entry for ' + tool + '-' + channel);
|
||||
const ver = tag.replace(tool + '-v', '');
|
||||
assetUrl = base + tag + '/' + tool + '_v' + ver + '.html';
|
||||
}
|
||||
|
||||
const resp = await fetch(assetUrl, { cache: 'no-cache', credentials: 'omit' });
|
||||
if (!resp.ok) throw new Error(resp.status + ' ' + resp.statusText);
|
||||
const html = await resp.text();
|
||||
document.open();
|
||||
document.write(html);
|
||||
document.close();
|
||||
} catch (err) {
|
||||
document.body.textContent = 'Failed to load from ' + upstream + ': ' + err.message;
|
||||
document.body.textContent = 'Failed to load ' + tool + ': ' + err.message;
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -18,32 +18,51 @@
|
|||
// ?v=0.0.4 (or v0.0.4) pins to that exact stable version
|
||||
// (none) uses the beta default
|
||||
//
|
||||
// Requires zddc.varasys.io to serve Access-Control-Allow-Origin: *.
|
||||
// Resolution path:
|
||||
// 1. Channel mode (no ?v= or ?v={alpha,beta,stable}): fetch
|
||||
// manifest.json from the same origin, look up
|
||||
// "<tool>-<channel>" → tag, then fetch the asset.
|
||||
// 2. Version pin: build the URL directly (skip manifest).
|
||||
//
|
||||
// The asset URL is /releases/<tag>/<tool>_v<version>.html. Caddy on
|
||||
// zddc.varasys.io reverse-proxies that to the Codeberg release-asset
|
||||
// URL — neither this stub nor the user's browser hits codeberg.org
|
||||
// directly.
|
||||
(async function () {
|
||||
const params = new URLSearchParams(location.search);
|
||||
const v = params.get('v');
|
||||
const tool = 'archive';
|
||||
const defaultChannel = 'beta';
|
||||
const channels = { alpha: '_alpha', beta: '_beta', stable: '_stable' };
|
||||
const channels = ['alpha', 'beta', 'stable'];
|
||||
const base = 'https://zddc.varasys.io/releases/';
|
||||
|
||||
function suffixFor(value) {
|
||||
if (!value) return '_' + defaultChannel;
|
||||
if (value in channels) return channels[value];
|
||||
const ver = value.startsWith('v') ? value.slice(1) : value;
|
||||
return '_v' + ver;
|
||||
}
|
||||
|
||||
const upstream = 'https://zddc.varasys.io/releases/' + tool + suffixFor(v) + '.html';
|
||||
function isChannel(s) { return channels.indexOf(s) >= 0; }
|
||||
|
||||
try {
|
||||
const resp = await fetch(upstream, { cache: 'no-cache', credentials: 'omit' });
|
||||
let assetUrl;
|
||||
if (v && !isChannel(v)) {
|
||||
// Explicit version pin (e.g. ?v=0.0.4 or ?v=v0.0.4).
|
||||
const ver = v.replace(/^v/, '');
|
||||
const tag = tool + '-v' + ver;
|
||||
assetUrl = base + tag + '/' + tool + '_v' + ver + '.html';
|
||||
} else {
|
||||
// Channel mode: resolve via manifest.
|
||||
const channel = (v && isChannel(v)) ? v : defaultChannel;
|
||||
const manifest = await (await fetch(base + 'manifest.json', { cache: 'no-cache', credentials: 'omit' })).json();
|
||||
const tag = manifest[tool + '-' + channel];
|
||||
if (!tag) throw new Error('manifest has no entry for ' + tool + '-' + channel);
|
||||
const ver = tag.replace(tool + '-v', '');
|
||||
assetUrl = base + tag + '/' + tool + '_v' + ver + '.html';
|
||||
}
|
||||
|
||||
const resp = await fetch(assetUrl, { cache: 'no-cache', credentials: 'omit' });
|
||||
if (!resp.ok) throw new Error(resp.status + ' ' + resp.statusText);
|
||||
const html = await resp.text();
|
||||
document.open();
|
||||
document.write(html);
|
||||
document.close();
|
||||
} catch (err) {
|
||||
document.body.textContent = 'Failed to load from ' + upstream + ': ' + err.message;
|
||||
document.body.textContent = 'Failed to load ' + tool + ': ' + err.message;
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -18,32 +18,51 @@
|
|||
// ?v=0.0.4 (or v0.0.4) pins to that exact stable version
|
||||
// (none) uses the beta default
|
||||
//
|
||||
// Requires zddc.varasys.io to serve Access-Control-Allow-Origin: *.
|
||||
// Resolution path:
|
||||
// 1. Channel mode (no ?v= or ?v={alpha,beta,stable}): fetch
|
||||
// manifest.json from the same origin, look up
|
||||
// "<tool>-<channel>" → tag, then fetch the asset.
|
||||
// 2. Version pin: build the URL directly (skip manifest).
|
||||
//
|
||||
// The asset URL is /releases/<tag>/<tool>_v<version>.html. Caddy on
|
||||
// zddc.varasys.io reverse-proxies that to the Codeberg release-asset
|
||||
// URL — neither this stub nor the user's browser hits codeberg.org
|
||||
// directly.
|
||||
(async function () {
|
||||
const params = new URLSearchParams(location.search);
|
||||
const v = params.get('v');
|
||||
const tool = 'classifier';
|
||||
const defaultChannel = 'beta';
|
||||
const channels = { alpha: '_alpha', beta: '_beta', stable: '_stable' };
|
||||
const channels = ['alpha', 'beta', 'stable'];
|
||||
const base = 'https://zddc.varasys.io/releases/';
|
||||
|
||||
function suffixFor(value) {
|
||||
if (!value) return '_' + defaultChannel;
|
||||
if (value in channels) return channels[value];
|
||||
const ver = value.startsWith('v') ? value.slice(1) : value;
|
||||
return '_v' + ver;
|
||||
}
|
||||
|
||||
const upstream = 'https://zddc.varasys.io/releases/' + tool + suffixFor(v) + '.html';
|
||||
function isChannel(s) { return channels.indexOf(s) >= 0; }
|
||||
|
||||
try {
|
||||
const resp = await fetch(upstream, { cache: 'no-cache', credentials: 'omit' });
|
||||
let assetUrl;
|
||||
if (v && !isChannel(v)) {
|
||||
// Explicit version pin (e.g. ?v=0.0.4 or ?v=v0.0.4).
|
||||
const ver = v.replace(/^v/, '');
|
||||
const tag = tool + '-v' + ver;
|
||||
assetUrl = base + tag + '/' + tool + '_v' + ver + '.html';
|
||||
} else {
|
||||
// Channel mode: resolve via manifest.
|
||||
const channel = (v && isChannel(v)) ? v : defaultChannel;
|
||||
const manifest = await (await fetch(base + 'manifest.json', { cache: 'no-cache', credentials: 'omit' })).json();
|
||||
const tag = manifest[tool + '-' + channel];
|
||||
if (!tag) throw new Error('manifest has no entry for ' + tool + '-' + channel);
|
||||
const ver = tag.replace(tool + '-v', '');
|
||||
assetUrl = base + tag + '/' + tool + '_v' + ver + '.html';
|
||||
}
|
||||
|
||||
const resp = await fetch(assetUrl, { cache: 'no-cache', credentials: 'omit' });
|
||||
if (!resp.ok) throw new Error(resp.status + ' ' + resp.statusText);
|
||||
const html = await resp.text();
|
||||
document.open();
|
||||
document.write(html);
|
||||
document.close();
|
||||
} catch (err) {
|
||||
document.body.textContent = 'Failed to load from ' + upstream + ': ' + err.message;
|
||||
document.body.textContent = 'Failed to load ' + tool + ': ' + err.message;
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -18,32 +18,51 @@
|
|||
// ?v=0.0.4 (or v0.0.4) pins to that exact stable version
|
||||
// (none) uses the beta default
|
||||
//
|
||||
// Requires zddc.varasys.io to serve Access-Control-Allow-Origin: *.
|
||||
// Resolution path:
|
||||
// 1. Channel mode (no ?v= or ?v={alpha,beta,stable}): fetch
|
||||
// manifest.json from the same origin, look up
|
||||
// "<tool>-<channel>" → tag, then fetch the asset.
|
||||
// 2. Version pin: build the URL directly (skip manifest).
|
||||
//
|
||||
// The asset URL is /releases/<tag>/<tool>_v<version>.html. Caddy on
|
||||
// zddc.varasys.io reverse-proxies that to the Codeberg release-asset
|
||||
// URL — neither this stub nor the user's browser hits codeberg.org
|
||||
// directly.
|
||||
(async function () {
|
||||
const params = new URLSearchParams(location.search);
|
||||
const v = params.get('v');
|
||||
const tool = 'landing';
|
||||
const defaultChannel = 'beta';
|
||||
const channels = { alpha: '_alpha', beta: '_beta', stable: '_stable' };
|
||||
const channels = ['alpha', 'beta', 'stable'];
|
||||
const base = 'https://zddc.varasys.io/releases/';
|
||||
|
||||
function suffixFor(value) {
|
||||
if (!value) return '_' + defaultChannel;
|
||||
if (value in channels) return channels[value];
|
||||
const ver = value.startsWith('v') ? value.slice(1) : value;
|
||||
return '_v' + ver;
|
||||
}
|
||||
|
||||
const upstream = 'https://zddc.varasys.io/releases/' + tool + suffixFor(v) + '.html';
|
||||
function isChannel(s) { return channels.indexOf(s) >= 0; }
|
||||
|
||||
try {
|
||||
const resp = await fetch(upstream, { cache: 'no-cache', credentials: 'omit' });
|
||||
let assetUrl;
|
||||
if (v && !isChannel(v)) {
|
||||
// Explicit version pin (e.g. ?v=0.0.4 or ?v=v0.0.4).
|
||||
const ver = v.replace(/^v/, '');
|
||||
const tag = tool + '-v' + ver;
|
||||
assetUrl = base + tag + '/' + tool + '_v' + ver + '.html';
|
||||
} else {
|
||||
// Channel mode: resolve via manifest.
|
||||
const channel = (v && isChannel(v)) ? v : defaultChannel;
|
||||
const manifest = await (await fetch(base + 'manifest.json', { cache: 'no-cache', credentials: 'omit' })).json();
|
||||
const tag = manifest[tool + '-' + channel];
|
||||
if (!tag) throw new Error('manifest has no entry for ' + tool + '-' + channel);
|
||||
const ver = tag.replace(tool + '-v', '');
|
||||
assetUrl = base + tag + '/' + tool + '_v' + ver + '.html';
|
||||
}
|
||||
|
||||
const resp = await fetch(assetUrl, { cache: 'no-cache', credentials: 'omit' });
|
||||
if (!resp.ok) throw new Error(resp.status + ' ' + resp.statusText);
|
||||
const html = await resp.text();
|
||||
document.open();
|
||||
document.write(html);
|
||||
document.close();
|
||||
} catch (err) {
|
||||
document.body.textContent = 'Failed to load from ' + upstream + ': ' + err.message;
|
||||
document.body.textContent = 'Failed to load ' + tool + ': ' + err.message;
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -18,32 +18,51 @@
|
|||
// ?v=0.0.4 (or v0.0.4) pins to that exact stable version
|
||||
// (none) uses the beta default
|
||||
//
|
||||
// Requires zddc.varasys.io to serve Access-Control-Allow-Origin: *.
|
||||
// Resolution path:
|
||||
// 1. Channel mode (no ?v= or ?v={alpha,beta,stable}): fetch
|
||||
// manifest.json from the same origin, look up
|
||||
// "<tool>-<channel>" → tag, then fetch the asset.
|
||||
// 2. Version pin: build the URL directly (skip manifest).
|
||||
//
|
||||
// The asset URL is /releases/<tag>/<tool>_v<version>.html. Caddy on
|
||||
// zddc.varasys.io reverse-proxies that to the Codeberg release-asset
|
||||
// URL — neither this stub nor the user's browser hits codeberg.org
|
||||
// directly.
|
||||
(async function () {
|
||||
const params = new URLSearchParams(location.search);
|
||||
const v = params.get('v');
|
||||
const tool = 'mdedit';
|
||||
const defaultChannel = 'beta';
|
||||
const channels = { alpha: '_alpha', beta: '_beta', stable: '_stable' };
|
||||
const channels = ['alpha', 'beta', 'stable'];
|
||||
const base = 'https://zddc.varasys.io/releases/';
|
||||
|
||||
function suffixFor(value) {
|
||||
if (!value) return '_' + defaultChannel;
|
||||
if (value in channels) return channels[value];
|
||||
const ver = value.startsWith('v') ? value.slice(1) : value;
|
||||
return '_v' + ver;
|
||||
}
|
||||
|
||||
const upstream = 'https://zddc.varasys.io/releases/' + tool + suffixFor(v) + '.html';
|
||||
function isChannel(s) { return channels.indexOf(s) >= 0; }
|
||||
|
||||
try {
|
||||
const resp = await fetch(upstream, { cache: 'no-cache', credentials: 'omit' });
|
||||
let assetUrl;
|
||||
if (v && !isChannel(v)) {
|
||||
// Explicit version pin (e.g. ?v=0.0.4 or ?v=v0.0.4).
|
||||
const ver = v.replace(/^v/, '');
|
||||
const tag = tool + '-v' + ver;
|
||||
assetUrl = base + tag + '/' + tool + '_v' + ver + '.html';
|
||||
} else {
|
||||
// Channel mode: resolve via manifest.
|
||||
const channel = (v && isChannel(v)) ? v : defaultChannel;
|
||||
const manifest = await (await fetch(base + 'manifest.json', { cache: 'no-cache', credentials: 'omit' })).json();
|
||||
const tag = manifest[tool + '-' + channel];
|
||||
if (!tag) throw new Error('manifest has no entry for ' + tool + '-' + channel);
|
||||
const ver = tag.replace(tool + '-v', '');
|
||||
assetUrl = base + tag + '/' + tool + '_v' + ver + '.html';
|
||||
}
|
||||
|
||||
const resp = await fetch(assetUrl, { cache: 'no-cache', credentials: 'omit' });
|
||||
if (!resp.ok) throw new Error(resp.status + ' ' + resp.statusText);
|
||||
const html = await resp.text();
|
||||
document.open();
|
||||
document.write(html);
|
||||
document.close();
|
||||
} catch (err) {
|
||||
document.body.textContent = 'Failed to load from ' + upstream + ': ' + err.message;
|
||||
document.body.textContent = 'Failed to load ' + tool + ': ' + err.message;
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -18,32 +18,51 @@
|
|||
// ?v=0.0.4 (or v0.0.4) pins to that exact stable version
|
||||
// (none) uses the beta default
|
||||
//
|
||||
// Requires zddc.varasys.io to serve Access-Control-Allow-Origin: *.
|
||||
// Resolution path:
|
||||
// 1. Channel mode (no ?v= or ?v={alpha,beta,stable}): fetch
|
||||
// manifest.json from the same origin, look up
|
||||
// "<tool>-<channel>" → tag, then fetch the asset.
|
||||
// 2. Version pin: build the URL directly (skip manifest).
|
||||
//
|
||||
// The asset URL is /releases/<tag>/<tool>_v<version>.html. Caddy on
|
||||
// zddc.varasys.io reverse-proxies that to the Codeberg release-asset
|
||||
// URL — neither this stub nor the user's browser hits codeberg.org
|
||||
// directly.
|
||||
(async function () {
|
||||
const params = new URLSearchParams(location.search);
|
||||
const v = params.get('v');
|
||||
const tool = 'transmittal';
|
||||
const defaultChannel = 'beta';
|
||||
const channels = { alpha: '_alpha', beta: '_beta', stable: '_stable' };
|
||||
const channels = ['alpha', 'beta', 'stable'];
|
||||
const base = 'https://zddc.varasys.io/releases/';
|
||||
|
||||
function suffixFor(value) {
|
||||
if (!value) return '_' + defaultChannel;
|
||||
if (value in channels) return channels[value];
|
||||
const ver = value.startsWith('v') ? value.slice(1) : value;
|
||||
return '_v' + ver;
|
||||
}
|
||||
|
||||
const upstream = 'https://zddc.varasys.io/releases/' + tool + suffixFor(v) + '.html';
|
||||
function isChannel(s) { return channels.indexOf(s) >= 0; }
|
||||
|
||||
try {
|
||||
const resp = await fetch(upstream, { cache: 'no-cache', credentials: 'omit' });
|
||||
let assetUrl;
|
||||
if (v && !isChannel(v)) {
|
||||
// Explicit version pin (e.g. ?v=0.0.4 or ?v=v0.0.4).
|
||||
const ver = v.replace(/^v/, '');
|
||||
const tag = tool + '-v' + ver;
|
||||
assetUrl = base + tag + '/' + tool + '_v' + ver + '.html';
|
||||
} else {
|
||||
// Channel mode: resolve via manifest.
|
||||
const channel = (v && isChannel(v)) ? v : defaultChannel;
|
||||
const manifest = await (await fetch(base + 'manifest.json', { cache: 'no-cache', credentials: 'omit' })).json();
|
||||
const tag = manifest[tool + '-' + channel];
|
||||
if (!tag) throw new Error('manifest has no entry for ' + tool + '-' + channel);
|
||||
const ver = tag.replace(tool + '-v', '');
|
||||
assetUrl = base + tag + '/' + tool + '_v' + ver + '.html';
|
||||
}
|
||||
|
||||
const resp = await fetch(assetUrl, { cache: 'no-cache', credentials: 'omit' });
|
||||
if (!resp.ok) throw new Error(resp.status + ' ' + resp.statusText);
|
||||
const html = await resp.text();
|
||||
document.open();
|
||||
document.write(html);
|
||||
document.close();
|
||||
} catch (err) {
|
||||
document.body.textContent = 'Failed to load from ' + upstream + ': ' + err.message;
|
||||
document.body.textContent = 'Failed to load ' + tool + ': ' + err.message;
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -18,32 +18,51 @@
|
|||
// ?v=0.0.4 (or v0.0.4) pins to that exact stable version
|
||||
// (none) uses the stable default
|
||||
//
|
||||
// Requires zddc.varasys.io to serve Access-Control-Allow-Origin: *.
|
||||
// Resolution path:
|
||||
// 1. Channel mode (no ?v= or ?v={alpha,beta,stable}): fetch
|
||||
// manifest.json from the same origin, look up
|
||||
// "<tool>-<channel>" → tag, then fetch the asset.
|
||||
// 2. Version pin: build the URL directly (skip manifest).
|
||||
//
|
||||
// The asset URL is /releases/<tag>/<tool>_v<version>.html. Caddy on
|
||||
// zddc.varasys.io reverse-proxies that to the Codeberg release-asset
|
||||
// URL — neither this stub nor the user's browser hits codeberg.org
|
||||
// directly.
|
||||
(async function () {
|
||||
const params = new URLSearchParams(location.search);
|
||||
const v = params.get('v');
|
||||
const tool = 'archive';
|
||||
const defaultChannel = 'stable';
|
||||
const channels = { alpha: '_alpha', beta: '_beta', stable: '_stable' };
|
||||
const channels = ['alpha', 'beta', 'stable'];
|
||||
const base = 'https://zddc.varasys.io/releases/';
|
||||
|
||||
function suffixFor(value) {
|
||||
if (!value) return '_' + defaultChannel;
|
||||
if (value in channels) return channels[value];
|
||||
const ver = value.startsWith('v') ? value.slice(1) : value;
|
||||
return '_v' + ver;
|
||||
}
|
||||
|
||||
const upstream = 'https://zddc.varasys.io/releases/' + tool + suffixFor(v) + '.html';
|
||||
function isChannel(s) { return channels.indexOf(s) >= 0; }
|
||||
|
||||
try {
|
||||
const resp = await fetch(upstream, { cache: 'no-cache', credentials: 'omit' });
|
||||
let assetUrl;
|
||||
if (v && !isChannel(v)) {
|
||||
// Explicit version pin (e.g. ?v=0.0.4 or ?v=v0.0.4).
|
||||
const ver = v.replace(/^v/, '');
|
||||
const tag = tool + '-v' + ver;
|
||||
assetUrl = base + tag + '/' + tool + '_v' + ver + '.html';
|
||||
} else {
|
||||
// Channel mode: resolve via manifest.
|
||||
const channel = (v && isChannel(v)) ? v : defaultChannel;
|
||||
const manifest = await (await fetch(base + 'manifest.json', { cache: 'no-cache', credentials: 'omit' })).json();
|
||||
const tag = manifest[tool + '-' + channel];
|
||||
if (!tag) throw new Error('manifest has no entry for ' + tool + '-' + channel);
|
||||
const ver = tag.replace(tool + '-v', '');
|
||||
assetUrl = base + tag + '/' + tool + '_v' + ver + '.html';
|
||||
}
|
||||
|
||||
const resp = await fetch(assetUrl, { cache: 'no-cache', credentials: 'omit' });
|
||||
if (!resp.ok) throw new Error(resp.status + ' ' + resp.statusText);
|
||||
const html = await resp.text();
|
||||
document.open();
|
||||
document.write(html);
|
||||
document.close();
|
||||
} catch (err) {
|
||||
document.body.textContent = 'Failed to load from ' + upstream + ': ' + err.message;
|
||||
document.body.textContent = 'Failed to load ' + tool + ': ' + err.message;
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -18,32 +18,51 @@
|
|||
// ?v=0.0.4 (or v0.0.4) pins to that exact stable version
|
||||
// (none) uses the stable default
|
||||
//
|
||||
// Requires zddc.varasys.io to serve Access-Control-Allow-Origin: *.
|
||||
// Resolution path:
|
||||
// 1. Channel mode (no ?v= or ?v={alpha,beta,stable}): fetch
|
||||
// manifest.json from the same origin, look up
|
||||
// "<tool>-<channel>" → tag, then fetch the asset.
|
||||
// 2. Version pin: build the URL directly (skip manifest).
|
||||
//
|
||||
// The asset URL is /releases/<tag>/<tool>_v<version>.html. Caddy on
|
||||
// zddc.varasys.io reverse-proxies that to the Codeberg release-asset
|
||||
// URL — neither this stub nor the user's browser hits codeberg.org
|
||||
// directly.
|
||||
(async function () {
|
||||
const params = new URLSearchParams(location.search);
|
||||
const v = params.get('v');
|
||||
const tool = 'classifier';
|
||||
const defaultChannel = 'stable';
|
||||
const channels = { alpha: '_alpha', beta: '_beta', stable: '_stable' };
|
||||
const channels = ['alpha', 'beta', 'stable'];
|
||||
const base = 'https://zddc.varasys.io/releases/';
|
||||
|
||||
function suffixFor(value) {
|
||||
if (!value) return '_' + defaultChannel;
|
||||
if (value in channels) return channels[value];
|
||||
const ver = value.startsWith('v') ? value.slice(1) : value;
|
||||
return '_v' + ver;
|
||||
}
|
||||
|
||||
const upstream = 'https://zddc.varasys.io/releases/' + tool + suffixFor(v) + '.html';
|
||||
function isChannel(s) { return channels.indexOf(s) >= 0; }
|
||||
|
||||
try {
|
||||
const resp = await fetch(upstream, { cache: 'no-cache', credentials: 'omit' });
|
||||
let assetUrl;
|
||||
if (v && !isChannel(v)) {
|
||||
// Explicit version pin (e.g. ?v=0.0.4 or ?v=v0.0.4).
|
||||
const ver = v.replace(/^v/, '');
|
||||
const tag = tool + '-v' + ver;
|
||||
assetUrl = base + tag + '/' + tool + '_v' + ver + '.html';
|
||||
} else {
|
||||
// Channel mode: resolve via manifest.
|
||||
const channel = (v && isChannel(v)) ? v : defaultChannel;
|
||||
const manifest = await (await fetch(base + 'manifest.json', { cache: 'no-cache', credentials: 'omit' })).json();
|
||||
const tag = manifest[tool + '-' + channel];
|
||||
if (!tag) throw new Error('manifest has no entry for ' + tool + '-' + channel);
|
||||
const ver = tag.replace(tool + '-v', '');
|
||||
assetUrl = base + tag + '/' + tool + '_v' + ver + '.html';
|
||||
}
|
||||
|
||||
const resp = await fetch(assetUrl, { cache: 'no-cache', credentials: 'omit' });
|
||||
if (!resp.ok) throw new Error(resp.status + ' ' + resp.statusText);
|
||||
const html = await resp.text();
|
||||
document.open();
|
||||
document.write(html);
|
||||
document.close();
|
||||
} catch (err) {
|
||||
document.body.textContent = 'Failed to load from ' + upstream + ': ' + err.message;
|
||||
document.body.textContent = 'Failed to load ' + tool + ': ' + err.message;
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -18,32 +18,51 @@
|
|||
// ?v=0.0.4 (or v0.0.4) pins to that exact stable version
|
||||
// (none) uses the stable default
|
||||
//
|
||||
// Requires zddc.varasys.io to serve Access-Control-Allow-Origin: *.
|
||||
// Resolution path:
|
||||
// 1. Channel mode (no ?v= or ?v={alpha,beta,stable}): fetch
|
||||
// manifest.json from the same origin, look up
|
||||
// "<tool>-<channel>" → tag, then fetch the asset.
|
||||
// 2. Version pin: build the URL directly (skip manifest).
|
||||
//
|
||||
// The asset URL is /releases/<tag>/<tool>_v<version>.html. Caddy on
|
||||
// zddc.varasys.io reverse-proxies that to the Codeberg release-asset
|
||||
// URL — neither this stub nor the user's browser hits codeberg.org
|
||||
// directly.
|
||||
(async function () {
|
||||
const params = new URLSearchParams(location.search);
|
||||
const v = params.get('v');
|
||||
const tool = 'landing';
|
||||
const defaultChannel = 'stable';
|
||||
const channels = { alpha: '_alpha', beta: '_beta', stable: '_stable' };
|
||||
const channels = ['alpha', 'beta', 'stable'];
|
||||
const base = 'https://zddc.varasys.io/releases/';
|
||||
|
||||
function suffixFor(value) {
|
||||
if (!value) return '_' + defaultChannel;
|
||||
if (value in channels) return channels[value];
|
||||
const ver = value.startsWith('v') ? value.slice(1) : value;
|
||||
return '_v' + ver;
|
||||
}
|
||||
|
||||
const upstream = 'https://zddc.varasys.io/releases/' + tool + suffixFor(v) + '.html';
|
||||
function isChannel(s) { return channels.indexOf(s) >= 0; }
|
||||
|
||||
try {
|
||||
const resp = await fetch(upstream, { cache: 'no-cache', credentials: 'omit' });
|
||||
let assetUrl;
|
||||
if (v && !isChannel(v)) {
|
||||
// Explicit version pin (e.g. ?v=0.0.4 or ?v=v0.0.4).
|
||||
const ver = v.replace(/^v/, '');
|
||||
const tag = tool + '-v' + ver;
|
||||
assetUrl = base + tag + '/' + tool + '_v' + ver + '.html';
|
||||
} else {
|
||||
// Channel mode: resolve via manifest.
|
||||
const channel = (v && isChannel(v)) ? v : defaultChannel;
|
||||
const manifest = await (await fetch(base + 'manifest.json', { cache: 'no-cache', credentials: 'omit' })).json();
|
||||
const tag = manifest[tool + '-' + channel];
|
||||
if (!tag) throw new Error('manifest has no entry for ' + tool + '-' + channel);
|
||||
const ver = tag.replace(tool + '-v', '');
|
||||
assetUrl = base + tag + '/' + tool + '_v' + ver + '.html';
|
||||
}
|
||||
|
||||
const resp = await fetch(assetUrl, { cache: 'no-cache', credentials: 'omit' });
|
||||
if (!resp.ok) throw new Error(resp.status + ' ' + resp.statusText);
|
||||
const html = await resp.text();
|
||||
document.open();
|
||||
document.write(html);
|
||||
document.close();
|
||||
} catch (err) {
|
||||
document.body.textContent = 'Failed to load from ' + upstream + ': ' + err.message;
|
||||
document.body.textContent = 'Failed to load ' + tool + ': ' + err.message;
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -18,32 +18,51 @@
|
|||
// ?v=0.0.4 (or v0.0.4) pins to that exact stable version
|
||||
// (none) uses the stable default
|
||||
//
|
||||
// Requires zddc.varasys.io to serve Access-Control-Allow-Origin: *.
|
||||
// Resolution path:
|
||||
// 1. Channel mode (no ?v= or ?v={alpha,beta,stable}): fetch
|
||||
// manifest.json from the same origin, look up
|
||||
// "<tool>-<channel>" → tag, then fetch the asset.
|
||||
// 2. Version pin: build the URL directly (skip manifest).
|
||||
//
|
||||
// The asset URL is /releases/<tag>/<tool>_v<version>.html. Caddy on
|
||||
// zddc.varasys.io reverse-proxies that to the Codeberg release-asset
|
||||
// URL — neither this stub nor the user's browser hits codeberg.org
|
||||
// directly.
|
||||
(async function () {
|
||||
const params = new URLSearchParams(location.search);
|
||||
const v = params.get('v');
|
||||
const tool = 'mdedit';
|
||||
const defaultChannel = 'stable';
|
||||
const channels = { alpha: '_alpha', beta: '_beta', stable: '_stable' };
|
||||
const channels = ['alpha', 'beta', 'stable'];
|
||||
const base = 'https://zddc.varasys.io/releases/';
|
||||
|
||||
function suffixFor(value) {
|
||||
if (!value) return '_' + defaultChannel;
|
||||
if (value in channels) return channels[value];
|
||||
const ver = value.startsWith('v') ? value.slice(1) : value;
|
||||
return '_v' + ver;
|
||||
}
|
||||
|
||||
const upstream = 'https://zddc.varasys.io/releases/' + tool + suffixFor(v) + '.html';
|
||||
function isChannel(s) { return channels.indexOf(s) >= 0; }
|
||||
|
||||
try {
|
||||
const resp = await fetch(upstream, { cache: 'no-cache', credentials: 'omit' });
|
||||
let assetUrl;
|
||||
if (v && !isChannel(v)) {
|
||||
// Explicit version pin (e.g. ?v=0.0.4 or ?v=v0.0.4).
|
||||
const ver = v.replace(/^v/, '');
|
||||
const tag = tool + '-v' + ver;
|
||||
assetUrl = base + tag + '/' + tool + '_v' + ver + '.html';
|
||||
} else {
|
||||
// Channel mode: resolve via manifest.
|
||||
const channel = (v && isChannel(v)) ? v : defaultChannel;
|
||||
const manifest = await (await fetch(base + 'manifest.json', { cache: 'no-cache', credentials: 'omit' })).json();
|
||||
const tag = manifest[tool + '-' + channel];
|
||||
if (!tag) throw new Error('manifest has no entry for ' + tool + '-' + channel);
|
||||
const ver = tag.replace(tool + '-v', '');
|
||||
assetUrl = base + tag + '/' + tool + '_v' + ver + '.html';
|
||||
}
|
||||
|
||||
const resp = await fetch(assetUrl, { cache: 'no-cache', credentials: 'omit' });
|
||||
if (!resp.ok) throw new Error(resp.status + ' ' + resp.statusText);
|
||||
const html = await resp.text();
|
||||
document.open();
|
||||
document.write(html);
|
||||
document.close();
|
||||
} catch (err) {
|
||||
document.body.textContent = 'Failed to load from ' + upstream + ': ' + err.message;
|
||||
document.body.textContent = 'Failed to load ' + tool + ': ' + err.message;
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -18,32 +18,51 @@
|
|||
// ?v=0.0.4 (or v0.0.4) pins to that exact stable version
|
||||
// (none) uses the stable default
|
||||
//
|
||||
// Requires zddc.varasys.io to serve Access-Control-Allow-Origin: *.
|
||||
// Resolution path:
|
||||
// 1. Channel mode (no ?v= or ?v={alpha,beta,stable}): fetch
|
||||
// manifest.json from the same origin, look up
|
||||
// "<tool>-<channel>" → tag, then fetch the asset.
|
||||
// 2. Version pin: build the URL directly (skip manifest).
|
||||
//
|
||||
// The asset URL is /releases/<tag>/<tool>_v<version>.html. Caddy on
|
||||
// zddc.varasys.io reverse-proxies that to the Codeberg release-asset
|
||||
// URL — neither this stub nor the user's browser hits codeberg.org
|
||||
// directly.
|
||||
(async function () {
|
||||
const params = new URLSearchParams(location.search);
|
||||
const v = params.get('v');
|
||||
const tool = 'transmittal';
|
||||
const defaultChannel = 'stable';
|
||||
const channels = { alpha: '_alpha', beta: '_beta', stable: '_stable' };
|
||||
const channels = ['alpha', 'beta', 'stable'];
|
||||
const base = 'https://zddc.varasys.io/releases/';
|
||||
|
||||
function suffixFor(value) {
|
||||
if (!value) return '_' + defaultChannel;
|
||||
if (value in channels) return channels[value];
|
||||
const ver = value.startsWith('v') ? value.slice(1) : value;
|
||||
return '_v' + ver;
|
||||
}
|
||||
|
||||
const upstream = 'https://zddc.varasys.io/releases/' + tool + suffixFor(v) + '.html';
|
||||
function isChannel(s) { return channels.indexOf(s) >= 0; }
|
||||
|
||||
try {
|
||||
const resp = await fetch(upstream, { cache: 'no-cache', credentials: 'omit' });
|
||||
let assetUrl;
|
||||
if (v && !isChannel(v)) {
|
||||
// Explicit version pin (e.g. ?v=0.0.4 or ?v=v0.0.4).
|
||||
const ver = v.replace(/^v/, '');
|
||||
const tag = tool + '-v' + ver;
|
||||
assetUrl = base + tag + '/' + tool + '_v' + ver + '.html';
|
||||
} else {
|
||||
// Channel mode: resolve via manifest.
|
||||
const channel = (v && isChannel(v)) ? v : defaultChannel;
|
||||
const manifest = await (await fetch(base + 'manifest.json', { cache: 'no-cache', credentials: 'omit' })).json();
|
||||
const tag = manifest[tool + '-' + channel];
|
||||
if (!tag) throw new Error('manifest has no entry for ' + tool + '-' + channel);
|
||||
const ver = tag.replace(tool + '-v', '');
|
||||
assetUrl = base + tag + '/' + tool + '_v' + ver + '.html';
|
||||
}
|
||||
|
||||
const resp = await fetch(assetUrl, { cache: 'no-cache', credentials: 'omit' });
|
||||
if (!resp.ok) throw new Error(resp.status + ' ' + resp.statusText);
|
||||
const html = await resp.text();
|
||||
document.open();
|
||||
document.write(html);
|
||||
document.close();
|
||||
} catch (err) {
|
||||
document.body.textContent = 'Failed to load from ' + upstream + ': ' + err.message;
|
||||
document.body.textContent = 'Failed to load ' + tool + ': ' + err.message;
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -1,107 +0,0 @@
|
|||
# syntax=docker/dockerfile:1
|
||||
#
|
||||
# Multi-stage build with three useful targets:
|
||||
#
|
||||
# --target binaries — scratch image holding cross-compiled binaries.
|
||||
# Use `podman build --target binaries -o dist/ .`
|
||||
# to extract zddc-server-{linux,darwin,windows}-*
|
||||
# to the host. No image published from this stage.
|
||||
#
|
||||
# --target server — alpine-based runtime. Default target. Published
|
||||
# as codeberg.org/varasys/zddc-server:vX.Y.Z.
|
||||
#
|
||||
# Build context expectations (when targeting `server`):
|
||||
# dist/web/index.html and dist/web/archive.html must exist —
|
||||
# produced by `sh build.sh` from the repo root. The release flow
|
||||
# (`release-image.sh` at the repo root) runs build.sh first and
|
||||
# then invokes `podman build --target server`.
|
||||
#
|
||||
|
||||
# ─── Stage 1: build ──────────────────────────────────────────────────────────
|
||||
FROM docker.io/library/golang:1.24-alpine AS builder
|
||||
|
||||
WORKDIR /src
|
||||
|
||||
# git is required by go mod for VCS dependencies
|
||||
RUN apk add --no-cache git
|
||||
|
||||
# Skip sum DB checks (allows building with empty/partial go.sum)
|
||||
ENV GONOSUMDB=* GOPRIVATE=* GOPROXY=direct
|
||||
|
||||
# Copy source
|
||||
COPY . .
|
||||
|
||||
# Build linux/amd64 (used by the runtime image and Linux hosts)
|
||||
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -trimpath -ldflags="-s -w" \
|
||||
-o /out/zddc-server-linux-amd64 ./cmd/zddc-server
|
||||
|
||||
# Cross-compile for macOS (Intel and Apple Silicon)
|
||||
RUN CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -trimpath -ldflags="-s -w" \
|
||||
-o /out/zddc-server-darwin-amd64 ./cmd/zddc-server
|
||||
|
||||
RUN CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -trimpath -ldflags="-s -w" \
|
||||
-o /out/zddc-server-darwin-arm64 ./cmd/zddc-server
|
||||
|
||||
# Cross-compile for Windows
|
||||
RUN CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -trimpath -ldflags="-s -w" \
|
||||
-o /out/zddc-server-windows-amd64.exe ./cmd/zddc-server
|
||||
|
||||
# ─── Stage 2: export binaries ─────────────────────────────────────────────────
|
||||
# Use `podman build --target binaries -o dist/ .` to extract binaries to the host.
|
||||
# No base image needed — this stage only exists to hold the output files.
|
||||
FROM scratch AS binaries
|
||||
COPY --from=builder /out/ /
|
||||
|
||||
# ─── Stage 3: runtime (published image) ─────────────────────────────────────
|
||||
FROM docker.io/library/alpine:3.20 AS server
|
||||
|
||||
LABEL org.opencontainers.image.title="zddc-server" \
|
||||
org.opencontainers.image.description="HTTP server for ZDDC archives — ACL via .zddc files, virtual archive index, audit logging" \
|
||||
org.opencontainers.image.source="https://codeberg.org/VARASYS/ZDDC" \
|
||||
org.opencontainers.image.documentation="https://zddc.varasys.io/zddc-server.html" \
|
||||
org.opencontainers.image.licenses="AGPL-3.0-only" \
|
||||
org.opencontainers.image.vendor="VARASYS"
|
||||
|
||||
# wget is in the base image (busybox); explicitly install ca-certificates
|
||||
# (outbound HTTPS for any future upstream auth check) and tini (PID-1
|
||||
# orphan reaper + signal forwarder). zddc-server itself only spawns
|
||||
# subprocesses transitively — e.g. once a future render path shells out
|
||||
# to pandoc, which in turn shells out to xelatex / lua filters / dot —
|
||||
# and any of those grandchildren orphaned by a mid-run crash get
|
||||
# reparented to PID 1. Without an init that knows to wait(2) on them,
|
||||
# they accumulate as zombies. tini is ~24KB and does exactly this.
|
||||
RUN apk add --no-cache ca-certificates tini && rm -rf /var/cache/apk/*
|
||||
|
||||
# Non-root user. UID/GID are deliberately fixed so volume permissions are
|
||||
# predictable across hosts.
|
||||
RUN addgroup -S -g 1000 zddc && adduser -S -u 1000 -G zddc zddc
|
||||
|
||||
# Binary
|
||||
COPY --from=builder /out/zddc-server-linux-amd64 /usr/local/bin/zddc-server
|
||||
|
||||
# Bundled landing + archive tools — useful for self-contained demos and as
|
||||
# a fallback web root. Set ZDDC_ROOT=/opt/zddc-server/web to serve only
|
||||
# these (no external data). For real archives, mount the data tree at
|
||||
# /srv (the default ZDDC_ROOT below).
|
||||
COPY dist/web/index.html /opt/zddc-server/web/index.html
|
||||
COPY dist/web/archive.html /opt/zddc-server/web/archive.html
|
||||
|
||||
# Conventional mount point for the served archive. Operators mount their
|
||||
# data here (Azure Files, NFS, hostPath, …). Override with ZDDC_ROOT.
|
||||
VOLUME /srv
|
||||
|
||||
USER zddc
|
||||
|
||||
# Default config: data mount at /srv. Override at run time as needed.
|
||||
# Other env vars (ZDDC_TLS_CERT, ZDDC_EMAIL_HEADER, ZDDC_CORS_ORIGIN, …)
|
||||
# are intentionally not defaulted — see zddc/README.md.
|
||||
ENV ZDDC_ROOT=/srv
|
||||
|
||||
EXPOSE 8443
|
||||
|
||||
# Liveness probe for `docker run` users. Kubernetes deployments override
|
||||
# this with their own livenessProbe / readinessProbe.
|
||||
HEALTHCHECK --interval=30s --timeout=5s --start-period=5s --retries=3 \
|
||||
CMD wget --no-check-certificate -q --spider https://localhost:8443/ || exit 1
|
||||
|
||||
ENTRYPOINT ["/sbin/tini", "--", "/usr/local/bin/zddc-server"]
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
# podman-compose.yaml — local development environment
|
||||
# Usage: podman-compose up --build
|
||||
#
|
||||
# Requires a directory to serve. Set ZDDC_DATA_DIR to the path of your
|
||||
# archive root, or mount it manually.
|
||||
#
|
||||
# Note: Use :z volume suffix for SELinux-enabled hosts (RHEL, Fedora, CentOS).
|
||||
|
||||
services:
|
||||
zddc-server:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Containerfile
|
||||
image: zddc-server:dev
|
||||
ports:
|
||||
- "8443:8443"
|
||||
# TLS modes (set via ZDDC_TLS_CERT):
|
||||
# empty (default) — self-signed cert generated in memory at startup
|
||||
# ZDDC_TLS_CERT=none — plain HTTP, no TLS (use when reverse proxy handles TLS)
|
||||
# ZDDC_TLS_CERT=/path — load cert from file (also set ZDDC_TLS_KEY)
|
||||
environment:
|
||||
ZDDC_ROOT: /data
|
||||
ZDDC_ADDR: ":8443"
|
||||
ZDDC_LOG_LEVEL: debug
|
||||
ZDDC_INDEX_PATH: .archive
|
||||
# ZDDC_EMAIL_HEADER defaults to X-Auth-Request-Email — uncomment to override.
|
||||
# ZDDC_EMAIL_HEADER: X-Auth-Request-Email
|
||||
volumes:
|
||||
- type: bind
|
||||
source: ${ZDDC_DATA_DIR:-./testdata}
|
||||
target: /data
|
||||
options: z
|
||||
restart: unless-stopped
|
||||
186
zddc/release.sh
Executable file
186
zddc/release.sh
Executable file
|
|
@ -0,0 +1,186 @@
|
|||
#!/bin/sh
|
||||
# release.sh — cut a zddc-server release: tag, compile binaries,
|
||||
# publish them as assets to a Codeberg release.
|
||||
#
|
||||
# Usage:
|
||||
# sh zddc/release.sh # alpha cut (default), version auto-derived
|
||||
# sh zddc/release.sh alpha # same
|
||||
# sh zddc/release.sh beta # beta cut, version auto-derived
|
||||
# sh zddc/release.sh stable # stable cut, patch++ from latest stable
|
||||
# sh zddc/release.sh stable 0.1.0 # stable cut, explicit version
|
||||
#
|
||||
# What this is NOT: there's no container image build, no registry push.
|
||||
# Those went away when the upstream codeberg.org/varasys/zddc-server
|
||||
# image lost its only consumers (tnd-zddc-chart's two Dockerfiles now
|
||||
# compile from source directly, fetching this Codeberg release tarball
|
||||
# / binary). See AGENTS.md "Releasing" for the full flow.
|
||||
#
|
||||
# Prerequisites:
|
||||
# - Go 1.24+ on PATH.
|
||||
# - $CODEBERG_TOKEN exported, scoped to write the VARASYS/ZDDC repo.
|
||||
# - curl, jq, git.
|
||||
#
|
||||
# What it does:
|
||||
# 1. Determine version per the pre-release semver scheme:
|
||||
# alpha/beta → next_prerelease (vX.Y.Z-CHANNEL.N from latest stable)
|
||||
# stable → operator-supplied or patch-bumped from latest stable
|
||||
# 2. Tag the current commit zddc-server-v<version>.
|
||||
# 3. Cross-compile binaries (linux/darwin/windows × amd64/arm64)
|
||||
# into zddc/dist/zddc-server-<os>-<arch>[.exe]. Native Go preferred.
|
||||
# 4. Upload each binary as an asset to the new Codeberg release.
|
||||
# 5. Print the operator's next steps (push the tag).
|
||||
#
|
||||
# The script does NOT push the tag itself — that's a deliberate `git push`
|
||||
# you do after reviewing.
|
||||
|
||||
set -eu
|
||||
|
||||
usage() {
|
||||
cat >&2 <<'EOF'
|
||||
usage: release.sh [alpha|beta|stable] [<version>]
|
||||
|
||||
alpha (default) cut alpha. Auto-derive version from the latest clean
|
||||
zddc-server-vX.Y.Z tag plus next-patch + -alpha.N.
|
||||
beta cut beta. Auto-derive version (vX.Y.Z + -beta.N).
|
||||
stable cut stable. Without <version>, patch-bump from the
|
||||
latest clean stable tag. With <version>, use it
|
||||
verbatim (must be a clean X.Y.Z).
|
||||
EOF
|
||||
exit 1
|
||||
}
|
||||
|
||||
CHANNEL="${1:-alpha}"
|
||||
case "$CHANNEL" in
|
||||
alpha | beta | stable) ;;
|
||||
-h | --help) usage ;;
|
||||
*) echo "error: unknown channel '$CHANNEL'" >&2; usage ;;
|
||||
esac
|
||||
|
||||
EXPLICIT_VERSION="${2:-}"
|
||||
if [ -n "$EXPLICIT_VERSION" ] && [ "$CHANNEL" != "stable" ]; then
|
||||
echo "error: an explicit <version> is only valid with the 'stable' channel" >&2
|
||||
echo " alpha and beta versions are auto-derived." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "${CODEBERG_TOKEN:-}" ]; then
|
||||
echo "error: CODEBERG_TOKEN must be exported in the environment" >&2
|
||||
echo " (Codeberg user → Settings → Applications → generate a token" >&2
|
||||
echo " with scope sufficient to create releases on VARASYS/ZDDC.)" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
SCRIPT_DIR=$(cd "$(dirname "$0")/.." && pwd)
|
||||
TAG_PREFIX="zddc-server-v"
|
||||
REPO="VARASYS/ZDDC"
|
||||
|
||||
# Source build-lib.sh for next_prerelease + _validate_semver. It
|
||||
# requires root_dir set; pointing at the repo root works.
|
||||
root_dir="$SCRIPT_DIR"
|
||||
. "$SCRIPT_DIR/shared/build-lib.sh"
|
||||
. "$SCRIPT_DIR/shared/publish-codeberg-release.sh"
|
||||
|
||||
# --- Determine version -----------------------------------------------------
|
||||
case "$CHANNEL" in
|
||||
alpha | beta)
|
||||
VERSION=$(next_prerelease "$CHANNEL" "$TAG_PREFIX")
|
||||
;;
|
||||
stable)
|
||||
if [ -n "$EXPLICIT_VERSION" ]; then
|
||||
_validate_semver "$EXPLICIT_VERSION"
|
||||
VERSION="$EXPLICIT_VERSION"
|
||||
else
|
||||
_latest=$(git -C "$SCRIPT_DIR" tag --list "${TAG_PREFIX}*" 2>/dev/null \
|
||||
| grep -E "^${TAG_PREFIX}[0-9]+\.[0-9]+\.[0-9]+\$" \
|
||||
| sed "s|^${TAG_PREFIX}||" \
|
||||
| sort -V \
|
||||
| tail -1)
|
||||
[ -n "$_latest" ] || _latest="0.0.0"
|
||||
_major="${_latest%%.*}"
|
||||
_rest="${_latest#*.}"
|
||||
_minor="${_rest%%.*}"
|
||||
_patch="${_rest#*.}"
|
||||
VERSION="${_major}.${_minor}.$((_patch + 1))"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
GIT_TAG="${TAG_PREFIX}${VERSION}"
|
||||
|
||||
echo "=== zddc-server release ==="
|
||||
echo "Channel: $CHANNEL"
|
||||
echo "Version: $VERSION"
|
||||
echo "Git tag: $GIT_TAG"
|
||||
echo
|
||||
|
||||
# --- Tag the commit (idempotent: skip if the tag already points here) -----
|
||||
if git -C "$SCRIPT_DIR" rev-parse -q --verify "refs/tags/$GIT_TAG" >/dev/null; then
|
||||
_existing=$(git -C "$SCRIPT_DIR" rev-list -n 1 "$GIT_TAG")
|
||||
_head=$(git -C "$SCRIPT_DIR" rev-parse HEAD)
|
||||
if [ "$_existing" != "$_head" ]; then
|
||||
echo "error: tag $GIT_TAG already exists at $_existing, but HEAD is $_head" >&2
|
||||
echo " refusing to overwrite. Resolve manually." >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "(tag $GIT_TAG already at HEAD)"
|
||||
else
|
||||
git -C "$SCRIPT_DIR" tag "$GIT_TAG"
|
||||
echo "tagged $GIT_TAG"
|
||||
fi
|
||||
|
||||
# --- Cross-compile binaries (native Go) ------------------------------------
|
||||
if ! command -v go >/dev/null 2>&1; then
|
||||
echo "error: go not found on PATH" >&2
|
||||
echo " (install Go 1.24+, or run this script from inside a Go" >&2
|
||||
echo " container — there's no podman fallback anymore.)" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
DIST="$SCRIPT_DIR/zddc/dist"
|
||||
mkdir -p "$DIST"
|
||||
|
||||
echo
|
||||
echo "=== Cross-compiling ==="
|
||||
cd "$SCRIPT_DIR/zddc"
|
||||
LDFLAGS="-s -w -X main.version=${VERSION}"
|
||||
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="$LDFLAGS" -o "$DIST/$out" ./cmd/zddc-server
|
||||
done
|
||||
cd "$SCRIPT_DIR"
|
||||
|
||||
# --- Publish to Codeberg ---------------------------------------------------
|
||||
echo
|
||||
echo "=== Publishing to Codeberg release $GIT_TAG ==="
|
||||
publish_codeberg_release "$REPO" "$GIT_TAG" \
|
||||
"$DIST/zddc-server-linux-amd64" \
|
||||
"$DIST/zddc-server-darwin-amd64" \
|
||||
"$DIST/zddc-server-darwin-arm64" \
|
||||
"$DIST/zddc-server-windows-amd64.exe"
|
||||
|
||||
echo
|
||||
echo "=== Done ==="
|
||||
echo "Release: https://codeberg.org/$REPO/releases/tag/$GIT_TAG"
|
||||
echo "Git tag: $GIT_TAG (publish with: git push origin $GIT_TAG)"
|
||||
echo
|
||||
case "$CHANNEL" in
|
||||
stable)
|
||||
echo "Reminder (channel discipline rule 4): freshen alpha + beta now"
|
||||
echo "so the floating channels resolve to at-least-current code:"
|
||||
echo " ./freshen-channel zddc-server alpha"
|
||||
echo " ./freshen-channel zddc-server beta"
|
||||
;;
|
||||
beta)
|
||||
echo "Beta cut. Soak before promoting to stable."
|
||||
;;
|
||||
esac
|
||||
echo
|
||||
echo "Don't forget to also regenerate the website releases page:"
|
||||
echo " sh build.sh"
|
||||
echo " git add website/releases/index.html website/releases/manifest.json"
|
||||
echo " git commit && git push"
|
||||
Loading…
Reference in a new issue