ci(publish): wire Scoop bucket + Homebrew tap jobs (D3 §3b/§3c)
Add sibling publish.yaml jobs (scoop-bucket, homebrew-tap) that render a manifest from the release .sha256 sidecars and idempotently push it to the org-level lazyeval/scoop-bucket and lazyeval/homebrew-tap repos, using the scoped lazyeval-ci bot token (LAZYEVAL_PKG_TOKEN). Render logic lives in dependency-free bash (the CI image has no jq/ruby): scripts/render-scoop-manifest.sh and scripts/render-homebrew-formula.sh. scripts/test-package-renders.sh exercises both: it validates the Scoop JSON with node and asserts fields on both manifests, and additionally runs `ruby -c` on the formula where ruby is present (dev box), skipping it gracefully otherwise. A new ci.yaml `manifests` job runs that test on every push so a render regression surfaces immediately, not at the next manual publish dispatch. The CI image has no ruby, so in CI the gate covers the Scoop JSON (node) and field assertions for both manifests; the formula's Ruby syntax is checked dev-side only (the static heredoc's variable parts cannot introduce syntax errors). - Scoop: x64 (gnu) + arm64 (gnullvm); #/-rename fragment so the bin shim is version-stable; checkver, no autoupdate (the pipeline is the updater). - Homebrew: on_macos/on_linux x arch bare-binary formula; no Windows. Docs: ADR-0056 Amendment 2 (+ README index, requirements D3). Unverified pending real use: scoop/brew install, the HEAD:main branch assumption, macOS Gatekeeper-via-brew on the ad-hoc-signed binary.
This commit is contained in:
Executable
+86
@@ -0,0 +1,86 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Render the Homebrew formula for rdbms-playground to stdout.
|
||||
#
|
||||
# Pure function of its inputs — NO network, NO jq/ruby — so it runs unchanged in
|
||||
# the CI job container (node:22-bookworm-slim: bash + coreutils only). Given a
|
||||
# version and the four macOS/Linux asset SHA-256 hashes it prints a complete
|
||||
# formula. The publish.yaml `homebrew-tap` job fetches the hashes from the
|
||||
# release .sha256 sidecars and commits the result into lazyeval/homebrew-tap as
|
||||
# Formula/rdbms-playground.rb.
|
||||
#
|
||||
# The release assets are bare binaries (no archive), so Homebrew stages the
|
||||
# single downloaded file in the build dir and `install` drops it under a stable
|
||||
# name. Windows is intentionally absent — Homebrew has no Windows port (Scoop /
|
||||
# winget cover Windows).
|
||||
#
|
||||
# Usage: render-homebrew-formula.sh <version> <mac-arm> <mac-intel> <linux-arm> <linux-intel>
|
||||
# <version> version, with or without a leading 'v'
|
||||
# <mac-arm> sha256 of aarch64-apple-darwin
|
||||
# <mac-intel> sha256 of x86_64-apple-darwin
|
||||
# <linux-arm> sha256 of aarch64-unknown-linux-musl
|
||||
# <linux-intel> sha256 of x86_64-unknown-linux-musl
|
||||
set -euo pipefail
|
||||
|
||||
if [ "$#" -ne 5 ]; then
|
||||
echo "usage: $0 <version> <mac-arm> <mac-intel> <linux-arm> <linux-intel>" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
version=${1#v}
|
||||
mac_arm=$2
|
||||
mac_intel=$3
|
||||
linux_arm=$4
|
||||
linux_intel=$5
|
||||
|
||||
base="https://git.lazyeval.net/oli/rdbms-playground/releases/download/v$version"
|
||||
|
||||
# Ruby interpolations (#{version}, #{bin}) must survive verbatim into the
|
||||
# formula; they contain no '$', so this unquoted heredoc leaves them untouched
|
||||
# and only expands the shell variables below.
|
||||
cat <<EOF
|
||||
# typed: false
|
||||
# frozen_string_literal: true
|
||||
|
||||
# rdbms-playground — installs the prebuilt release binary for the host
|
||||
# platform. Regenerated for each release by scripts/render-homebrew-formula.sh;
|
||||
# do not edit by hand.
|
||||
class RdbmsPlayground < Formula
|
||||
desc "Cross-platform TUI playground for learning relational databases"
|
||||
homepage "https://relplay.org"
|
||||
version "$version"
|
||||
license any_of: ["MIT", "Apache-2.0"]
|
||||
|
||||
on_macos do
|
||||
on_arm do
|
||||
url "$base/rdbms-playground-v$version-aarch64-apple-darwin"
|
||||
sha256 "$mac_arm"
|
||||
end
|
||||
on_intel do
|
||||
url "$base/rdbms-playground-v$version-x86_64-apple-darwin"
|
||||
sha256 "$mac_intel"
|
||||
end
|
||||
end
|
||||
|
||||
on_linux do
|
||||
on_arm do
|
||||
url "$base/rdbms-playground-v$version-aarch64-unknown-linux-musl"
|
||||
sha256 "$linux_arm"
|
||||
end
|
||||
on_intel do
|
||||
url "$base/rdbms-playground-v$version-x86_64-unknown-linux-musl"
|
||||
sha256 "$linux_intel"
|
||||
end
|
||||
end
|
||||
|
||||
def install
|
||||
# The release asset is a single bare binary; Homebrew stages it in the
|
||||
# build dir under its (versioned) basename. Install it as a stable name.
|
||||
bin.install Dir["*"].first => "rdbms-playground"
|
||||
end
|
||||
|
||||
test do
|
||||
assert_match "rdbms-playground #{version}", shell_output("#{bin}/rdbms-playground --version")
|
||||
end
|
||||
end
|
||||
EOF
|
||||
Executable
+66
@@ -0,0 +1,66 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Render the Scoop manifest for rdbms-playground to stdout.
|
||||
#
|
||||
# Pure function of its inputs — NO network, NO jq/ruby — so it runs unchanged in
|
||||
# the CI job container (node:22-bookworm-slim: bash + coreutils only). Given a
|
||||
# version and the two Windows asset SHA-256 hashes it prints a complete,
|
||||
# schema-valid Scoop manifest. The publish.yaml `scoop-bucket` job fetches the
|
||||
# hashes from the release's .sha256 sidecars and commits the result into the
|
||||
# lazyeval/scoop-bucket repository as rdbms-playground.json.
|
||||
#
|
||||
# Manifest updates are CI-driven (this script, per release), so the manifest
|
||||
# carries `checkver` (so `scoop status` / the community excavator can see when
|
||||
# the bucket lags upstream) but deliberately NO `autoupdate` — our pipeline is
|
||||
# the updater, not Scoop's maintainer tooling.
|
||||
#
|
||||
# Usage: render-scoop-manifest.sh <version> <hash-x64> <hash-arm64>
|
||||
# <version> version, with or without a leading 'v' (e.g. 0.2.0 or v0.2.0)
|
||||
# <hash-x64> sha256 of the x86_64-pc-windows-gnu.exe asset
|
||||
# <hash-arm64> sha256 of the aarch64-pc-windows-gnullvm.exe asset
|
||||
set -euo pipefail
|
||||
|
||||
if [ "$#" -ne 3 ]; then
|
||||
echo "usage: $0 <version> <hash-x64> <hash-arm64>" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
# Accept either 0.2.0 or v0.2.0; the manifest 'version' field is bare.
|
||||
version=${1#v}
|
||||
hash_x64=$2
|
||||
hash_arm64=$3
|
||||
|
||||
repo="https://git.lazyeval.net/oli/rdbms-playground"
|
||||
base="$repo/releases/download/v$version"
|
||||
|
||||
# The `#/rdbms-playground.exe` fragment tells Scoop to save the versioned asset
|
||||
# under a stable filename, so the `bin` shim resolves regardless of version.
|
||||
url_x64="$base/rdbms-playground-v$version-x86_64-pc-windows-gnu.exe#/rdbms-playground.exe"
|
||||
url_arm64="$base/rdbms-playground-v$version-aarch64-pc-windows-gnullvm.exe#/rdbms-playground.exe"
|
||||
|
||||
# Note: \$.tag_name emits a literal $ (Scoop's JSONPath); the regex uses [0-9.]
|
||||
# rather than \d so the manifest contains no backslashes to escape.
|
||||
cat <<EOF
|
||||
{
|
||||
"version": "$version",
|
||||
"description": "A cross-platform TUI playground for learning relational databases.",
|
||||
"homepage": "https://relplay.org",
|
||||
"license": "MIT OR Apache-2.0",
|
||||
"architecture": {
|
||||
"64bit": {
|
||||
"url": "$url_x64",
|
||||
"hash": "$hash_x64"
|
||||
},
|
||||
"arm64": {
|
||||
"url": "$url_arm64",
|
||||
"hash": "$hash_arm64"
|
||||
}
|
||||
},
|
||||
"bin": "rdbms-playground.exe",
|
||||
"checkver": {
|
||||
"url": "https://git.lazyeval.net/api/v1/repos/oli/rdbms-playground/releases/latest",
|
||||
"jsonpath": "\$.tag_name",
|
||||
"regex": "v([0-9.]+)"
|
||||
}
|
||||
}
|
||||
EOF
|
||||
Executable
+92
@@ -0,0 +1,92 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Tests for the package-manifest render scripts (Scoop + Homebrew).
|
||||
#
|
||||
# Validates that each render script emits a well-formed manifest for given
|
||||
# inputs, and that the inputs land in the right places. Dependency-light:
|
||||
# requires node (always in the CI image) for JSON parsing; uses jq and ruby for
|
||||
# extra checks when present (both available on the dev box). No network.
|
||||
#
|
||||
# Run: scripts/test-package-renders.sh
|
||||
set -euo pipefail
|
||||
|
||||
here=$(cd "$(dirname "$0")" && pwd)
|
||||
tmp=$(mktemp -d)
|
||||
trap 'rm -rf "$tmp"' EXIT
|
||||
|
||||
fail() { echo "FAIL: $*" >&2; exit 1; }
|
||||
pass() { echo "ok: $*"; }
|
||||
|
||||
# Distinct dummy hashes so we can assert each lands in the right slot.
|
||||
VER=9.9.9
|
||||
H_WIN_X64=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa01
|
||||
H_WIN_ARM=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa02
|
||||
H_MAC_ARM=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa03
|
||||
H_MAC_X64=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa04
|
||||
H_LIN_ARM=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa05
|
||||
H_LIN_X64=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa06
|
||||
|
||||
# ---- Scoop ----------------------------------------------------------------
|
||||
scoop=$tmp/rdbms-playground.json
|
||||
# Pass a leading 'v' to confirm it gets stripped.
|
||||
"$here/render-scoop-manifest.sh" "v$VER" "$H_WIN_X64" "$H_WIN_ARM" > "$scoop"
|
||||
|
||||
node -e 'JSON.parse(require("fs").readFileSync(process.argv[1],"utf8"))' "$scoop" \
|
||||
|| fail "scoop manifest is not valid JSON"
|
||||
pass "scoop manifest parses as JSON"
|
||||
|
||||
# Field-level assertions via node (no jq dependency).
|
||||
check_json() { # <jsexpr> <expected>
|
||||
local got
|
||||
got=$(node -e '
|
||||
const m = JSON.parse(require("fs").readFileSync(process.argv[1],"utf8"));
|
||||
process.stdout.write(String(eval(process.argv[2])));
|
||||
' "$scoop" "$1")
|
||||
[ "$got" = "$2" ] || fail "scoop: $1 = '$got', expected '$2'"
|
||||
}
|
||||
check_json 'm.version' "$VER"
|
||||
check_json 'm.bin' "rdbms-playground.exe"
|
||||
check_json 'm.architecture["64bit"].hash' "$H_WIN_X64"
|
||||
check_json 'm.architecture["arm64"].hash' "$H_WIN_ARM"
|
||||
check_json 'm.checkver.jsonpath' '$.tag_name'
|
||||
grep -q "rdbms-playground-v$VER-x86_64-pc-windows-gnu.exe#/rdbms-playground.exe" "$scoop" \
|
||||
|| fail "scoop: x64 url/fragment missing"
|
||||
grep -q "rdbms-playground-v$VER-aarch64-pc-windows-gnullvm.exe#/rdbms-playground.exe" "$scoop" \
|
||||
|| fail "scoop: arm64 url/fragment missing"
|
||||
pass "scoop manifest fields correct"
|
||||
|
||||
if command -v jq >/dev/null 2>&1; then
|
||||
jq -e . "$scoop" >/dev/null || fail "scoop: jq rejected the manifest"
|
||||
pass "scoop manifest valid per jq"
|
||||
fi
|
||||
|
||||
# ---- Homebrew -------------------------------------------------------------
|
||||
formula=$tmp/rdbms-playground.rb
|
||||
"$here/render-homebrew-formula.sh" "$VER" "$H_MAC_ARM" "$H_MAC_X64" "$H_LIN_ARM" "$H_LIN_X64" > "$formula"
|
||||
|
||||
if command -v ruby >/dev/null 2>&1; then
|
||||
ruby -c "$formula" >/dev/null || fail "homebrew formula is not valid Ruby"
|
||||
pass "homebrew formula parses as Ruby"
|
||||
else
|
||||
echo "warn: ruby not present — skipping formula syntax check" >&2
|
||||
fi
|
||||
|
||||
# Each hash must appear exactly once (right asset → right slot), the version
|
||||
# must be present, and #{version} must survive verbatim for brew's test block.
|
||||
for pair in \
|
||||
"aarch64-apple-darwin:$H_MAC_ARM" \
|
||||
"x86_64-apple-darwin:$H_MAC_X64" \
|
||||
"aarch64-unknown-linux-musl:$H_LIN_ARM" \
|
||||
"x86_64-unknown-linux-musl:$H_LIN_X64"; do
|
||||
target=${pair%%:*}; hash=${pair#*:}
|
||||
grep -q "rdbms-playground-v$VER-$target\"" "$formula" || fail "homebrew: url for $target missing"
|
||||
grep -q "sha256 \"$hash\"" "$formula" || fail "homebrew: sha256 for $target missing"
|
||||
done
|
||||
grep -q 'version "'"$VER"'"' "$formula" || fail "homebrew: version line missing"
|
||||
grep -q 'assert_match "rdbms-playground #{version}"' "$formula" \
|
||||
|| fail "homebrew: test block #{version} interpolation was mangled"
|
||||
grep -q 'rdbms-playground-v9.9.9-x86_64-pc-windows' "$formula" \
|
||||
&& fail "homebrew: formula unexpectedly references a Windows asset"
|
||||
pass "homebrew formula fields correct"
|
||||
|
||||
echo "all render tests passed"
|
||||
Reference in New Issue
Block a user