c30a6114b9
Cargo.toml version is the single source of truth, surfaced by a --version/-V CLI flag and an in-app `version` command (both via cli::version_text -> cli.version_line). release.yaml gains a guard that fails the release unless the v* tag equals v<CARGO_PKG_VERSION>, keeping --version, the release name, and the asset in lockstep. New app command wired across grammar/REGISTRY/dispatch/usage/help/hint-corpus/keys; 6 test-first tests. Also fixes a stale "macOS deferred" comment in release.yaml. ADR-0054 + README index + plan-doc step 1.
113 lines
4.7 KiB
YAML
113 lines
4.7 KiB
YAML
# Release: on a version tag, build the cross-platform binaries and publish them
|
|
# to a Gitea release with checksums. Runs in the prebuilt CI image, so the
|
|
# pinned toolchain + the release targets + cargo-zigbuild/zig are already warm.
|
|
#
|
|
# Matrix (D1, cross-built from Linux x86_64 via cargo-zigbuild):
|
|
# x86_64-unknown-linux-musl aarch64-unknown-linux-musl (static, D2)
|
|
# x86_64-pc-windows-gnu aarch64-pc-windows-gnullvm (standalone .exe)
|
|
# The two macOS targets are built separately by the dispatched
|
|
# release-macos.yaml (native Tart runner; ADR-ci-003 amendment), uploading to
|
|
# the same release. D3 package-manager manifests layer on later.
|
|
#
|
|
# Tests run once (host) before the matrix, so a tag can never publish untested
|
|
# code, even one pointing at a commit that was never gated on a branch. The
|
|
# version guard (ADR-0054) refuses to publish a tag whose vX.Y.Z disagrees with
|
|
# Cargo.toml's version, keeping `--version`, the release name, and the asset in
|
|
# lockstep.
|
|
name: release
|
|
on:
|
|
push:
|
|
tags:
|
|
- 'v*'
|
|
|
|
jobs:
|
|
test:
|
|
runs-on: ci-public
|
|
container:
|
|
image: git.lazyeval.net/oli/rdbms-playground-ci:latest
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- name: version guard — tag must equal Cargo.toml version (ADR-0054)
|
|
shell: bash
|
|
env:
|
|
TAG: ${{ github.ref_name }}
|
|
run: |
|
|
set -euo pipefail
|
|
# CARGO_PKG_VERSION is the single source of truth; the binary reports
|
|
# it via --version / the `version` command. Parse it from cargo
|
|
# metadata (node is in the CI image; avoids assuming jq).
|
|
VER=$(nix develop -c cargo metadata --no-deps --format-version 1 \
|
|
| node -e 'let s="";process.stdin.on("data",d=>s+=d).on("end",()=>process.stdout.write(JSON.parse(s).packages[0].version))')
|
|
echo "tag=$TAG cargo=$VER"
|
|
if [ "$TAG" != "v$VER" ]; then
|
|
echo "ERROR: release tag '$TAG' != 'v$VER' (Cargo.toml). Bump Cargo.toml to the release version, commit, then retag (ADR-0054)." >&2
|
|
exit 1
|
|
fi
|
|
- name: test
|
|
run: nix develop -c cargo test --no-fail-fast
|
|
|
|
build:
|
|
needs: test
|
|
runs-on: ci-public
|
|
container:
|
|
image: git.lazyeval.net/oli/rdbms-playground-ci:latest
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
target:
|
|
- x86_64-unknown-linux-musl
|
|
- aarch64-unknown-linux-musl
|
|
- x86_64-pc-windows-gnu
|
|
- aarch64-pc-windows-gnullvm
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- name: build
|
|
run: nix develop -c cargo zigbuild --release --target ${{ matrix.target }}
|
|
|
|
- name: package + publish
|
|
# Pin bash: the runner defaults scripted steps to dash, which rejects
|
|
# `set -o pipefail`. bash is in the CI image.
|
|
shell: bash
|
|
env:
|
|
TARGET: ${{ matrix.target }}
|
|
# GITEA_TOKEN is auto-provided with repo write (release) scope.
|
|
TOKEN: ${{ secrets.GITEA_TOKEN }}
|
|
API: ${{ github.server_url }}/api/v1
|
|
REPO: ${{ github.repository }}
|
|
TAG: ${{ github.ref_name }}
|
|
run: |
|
|
set -euo pipefail
|
|
|
|
# Windows targets produce a .exe; the rest a bare binary.
|
|
case "$TARGET" in *windows*) EXT=.exe ;; *) EXT= ;; esac
|
|
BIN="target/$TARGET/release/rdbms-playground$EXT"
|
|
OUT="rdbms-playground-$TAG-$TARGET$EXT"
|
|
mkdir -p dist
|
|
cp "$BIN" "dist/$OUT"
|
|
( cd dist && sha256sum "$OUT" > "$OUT.sha256" )
|
|
ls -l dist
|
|
|
|
# Create the release for this tag; if a sibling matrix job already
|
|
# created it, look it up instead (idempotent + race-tolerant).
|
|
created=$(curl -sS -X POST "$API/repos/$REPO/releases" \
|
|
-H "Authorization: token $TOKEN" \
|
|
-H "Content-Type: application/json" \
|
|
-d "{\"tag_name\":\"$TAG\",\"name\":\"$TAG\",\"body\":\"Automated release for $TAG.\"}")
|
|
id=$(printf '%s' "$created" | node -e 'let s="";process.stdin.on("data",d=>s+=d).on("end",()=>{try{const o=JSON.parse(s);process.stdout.write(String(o.id||""))}catch(e){}})')
|
|
if [ -z "$id" ]; then
|
|
id=$(curl -sS "$API/repos/$REPO/releases/tags/$TAG" \
|
|
-H "Authorization: token $TOKEN" \
|
|
| node -e 'let s="";process.stdin.on("data",d=>s+=d).on("end",()=>{process.stdout.write(String(JSON.parse(s).id))})')
|
|
fi
|
|
echo "release id: $id"
|
|
|
|
for f in dist/*; do
|
|
name=$(basename "$f")
|
|
echo "uploading $name"
|
|
curl -sS -X POST "$API/repos/$REPO/releases/$id/assets?name=$name" \
|
|
-H "Authorization: token $TOKEN" \
|
|
-F "attachment=@$f" > /dev/null
|
|
done
|
|
echo "published $TARGET assets for $TAG"
|