feat(wrappers): canonical bash bootstrap wrapper
This commit is contained in:
Executable
+107
@@ -0,0 +1,107 @@
|
|||||||
|
#!/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 ffmpeg-free 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" "$@"
|
||||||
Reference in New Issue
Block a user