Files
sow-tools/wrappers/crucible.sh
T
archvillainette d3ee546e33
build-binaries / build-binaries (pull_request) Successful in 2m10s
test-image / build-image (pull_request) Successful in 37s
test / test (pull_request) Successful in 1m20s
command and help ux pass
2026-06-25 11:17:29 +02:00

108 lines
3.9 KiB
Bash
Executable File

#!/usr/bin/env bash
# crucible — bootstrap wrapper (POSIX/bash). Prefers a crucible already on PATH
# (Nix devshell / installed); otherwise downloads the latest released binary
# from Gitea into a per-version cache and execs it.
#
# CANONICAL SOURCE: sow-tools/wrappers/crucible.sh. Do not hand-edit copies in
# consumer repos — they are kept in sync by sow-tools CI (see drift-check).
#
# Requires: bash, curl, and sha256sum (Linux) or shasum (macOS).
# Env: CRUCIBLE_HOME (cache dir), CRUCIBLE_TOKEN (private releases),
# CRUCIBLE_GITEA (default https://git.westgate.pw),
# CRUCIBLE_REPO (default ShadowsOverWestgate/sow-tools).
set -euo pipefail
# 0. Prefer an existing crucible (Nix users, or already installed).
if command -v crucible >/dev/null 2>&1; then
exec crucible "$@"
fi
gitea="${CRUCIBLE_GITEA:-https://git.westgate.pw}"
repo="${CRUCIBLE_REPO:-ShadowsOverWestgate/sow-tools}"
# 1. --repo-local materializes into <repo>/.crucible instead of the user cache.
repo_local=0
if [ "${1:-}" = "--repo-local" ]; then repo_local=1; shift; fi
# 2. OS/arch -> asset name.
os="$(uname -s)"; arch="$(uname -m)"
case "$os" in
Linux) os=linux ;;
Darwin) os=darwin ;;
*) echo "crucible: unsupported OS '$os' — install crucible manually" >&2; exit 1 ;;
esac
case "$arch" in
x86_64|amd64) arch=amd64 ;;
aarch64|arm64) arch=arm64 ;;
*) echo "crucible: unsupported arch '$arch'" >&2; exit 1 ;;
esac
asset="crucible-${os}-${arch}"
# 3. Auth: anonymous first, token fallback.
token="${CRUCIBLE_TOKEN:-}"
if [ -z "$token" ] && [ -f "$HOME/.config/crucible/token" ]; then
token="$(cat "$HOME/.config/crucible/token")"
fi
fetch() { # fetch URL DEST ; tries anon then token
if curl -fsSL "$1" -o "$2" 2>/dev/null; then return 0; fi
if [ -n "$token" ]; then curl -fsSL -H "Authorization: token $token" "$1" -o "$2"; return $?; fi
return 1
}
fetch_stdout() { # prints body; anon then token
if curl -fsSL "$1" 2>/dev/null; then return 0; fi
if [ -n "$token" ]; then curl -fsSL -H "Authorization: token $token" "$1"; return $?; fi
return 1
}
# 4. Resolve the latest release tag (no jq dependency).
api="${gitea}/api/v1/repos/${repo}/releases/latest"
tag="$(fetch_stdout "$api" 2>/dev/null | grep -o '"tag_name":"[^"]*"' | head -1 | cut -d'"' -f4 || true)"
# 5. Cache dir.
if [ "$repo_local" = 1 ]; then
cache_root="$(cd "$(dirname "$0")" && pwd)/.crucible"
else
cache_root="${CRUCIBLE_HOME:-${XDG_CACHE_HOME:-$HOME/.cache}/crucible}"
fi
# 6. Offline fallback: if tag lookup failed, use the newest cached version.
if [ -z "$tag" ]; then
if [ -d "$cache_root" ]; then
cached="$(ls -1 "$cache_root" 2>/dev/null | sort -V | tail -1 || true)"
if [ -n "$cached" ] && [ -x "$cache_root/$cached/crucible" ]; then
echo "crucible: offline — using cached $cached" >&2
exec "$cache_root/$cached/crucible" "$@"
fi
fi
echo "crucible: cannot reach $gitea and no cached binary found." >&2
echo " set CRUCIBLE_TOKEN if releases are private, or check the network." >&2
exit 1
fi
dest_dir="$cache_root/$tag"
bin="$dest_dir/crucible"
# 7. Download + verify on cache miss.
if [ ! -x "$bin" ]; then
mkdir -p "$dest_dir"
base="${gitea}/${repo}/releases/download/${tag}"
tmp="$(mktemp -d)"; trap 'rm -rf "$tmp"' EXIT
echo "crucible: fetching ${asset} ${tag}..." >&2
fetch "${base}/${asset}" "$tmp/crucible" || { echo "crucible: download failed for ${asset}" >&2; exit 1; }
if fetch "${base}/SHA256SUMS" "$tmp/SHA256SUMS"; then
want="$(grep " ${asset}\$" "$tmp/SHA256SUMS" | awk '{print $1}')"
if command -v sha256sum >/dev/null 2>&1; then
got="$(sha256sum "$tmp/crucible" | awk '{print $1}')"
else
got="$(shasum -a 256 "$tmp/crucible" | awk '{print $1}')"
fi
if [ -n "$want" ] && [ "$want" != "$got" ]; then
echo "crucible: checksum mismatch for ${asset} (want $want got $got)" >&2; exit 1
fi
fi
chmod +x "$tmp/crucible"
mv "$tmp/crucible" "$bin"
fi
exec "$bin" "$@"