build: auto-download Jerboa release toolchain when jerbuild is missing
ober
7d94d2c51dfc678ec4026487091fcf29f761412a
--- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ gitsafe-macos gitsafe-macos.sha256 *.so *.wpo +.jerboa/ --- a/Makefile +++ b/Makefile @@ -1,26 +1,43 @@ # jerbuild bundles Chez Scheme + the jerboa stdlib, so building gitsafe needs # only `jerbuild` + a C compiler — no jerboa source checkout and no separately -# built Chez. -JERBUILD ?= jerbuild -JH := $(shell $(JERBUILD) --jerboa-home 2>/dev/null) -ifeq ($(JH),) -$(error jerbuild not found on PATH (or '$(JERBUILD) --jerboa-home' failed). Install jerbuild, or set JERBUILD=/path/to/jerbuild) -endif - -LIBDIRS := --libdirs $(CURDIR):$(JH)/lib -JEXEC := $(JERBUILD) exec $(LIBDIRS) +# built Chez. If no jerbuild is found, `make binary` downloads the Jerboa +# release toolchain for this os/arch into .jerboa/bin (support/ensure-jerboa.sh). +JERBOA_VERSION ?= v0.2.0 +JERBOA_TOOL_DIR ?= $(CURDIR)/.jerboa/bin +JERBUILD ?= $(shell if [ -x "$(JERBOA_TOOL_DIR)/jerbuild" ]; then echo "$(JERBOA_TOOL_DIR)/jerbuild"; \ + elif [ -x ../jerboa/dist/jerbuild ] && [ -x ../jerboa/dist/jerboa ]; then echo ../jerboa/dist/jerbuild; \ + elif command -v jerbuild >/dev/null 2>&1; then command -v jerbuild; \ + else echo "$(JERBOA_TOOL_DIR)/jerbuild"; fi) +JH = $(shell "$(JERBUILD)" --jerboa-home 2>/dev/null) + +LIBDIRS = --libdirs $(CURDIR):$(JH)/lib +JEXEC = $(JERBUILD) exec $(LIBDIRS) BIN := gitsafe-bin BIN_DIR := $(HOME)/.local/bin TEMPLATE_DIR := $(HOME)/.git-templates HOOK_DIR := $(TEMPLATE_DIR)/hooks -.PHONY: all build binary run test install clean help +.PHONY: all build binary run test install clean help ensure-jerboa-tools .DEFAULT_GOAL := help all: binary +# Make sure a working jerbuild exists; download the release toolchain for +# this os/arch into $(JERBOA_TOOL_DIR) when none is found. +ensure-jerboa-tools: + @if "$(JERBUILD)" --jerboa-home >/dev/null 2>&1; then \ + echo "=== Using Jerboa toolchain: $(JERBUILD) ==="; \ + else \ + echo "=== Fetching Jerboa $(JERBOA_VERSION) release tools into $(JERBOA_TOOL_DIR) ==="; \ + sh support/ensure-jerboa.sh "$(JERBOA_VERSION)" "$(JERBOA_TOOL_DIR)"; \ + fi + @"$(JERBUILD)" --jerboa-home >/dev/null || { \ + echo "ERROR: Jerboa toolchain is unavailable; set JERBUILD=/path/to/jerbuild or JERBOA_VERSION=<tag>" >&2; \ + exit 1; \ + } + # Standalone native binary via .jerbuild (entry gitsafe/main-binary.ss). -binary: +binary: ensure-jerboa-tools $(JERBUILD) build build: binary @@ -28,7 +45,7 @@ build: binary run: binary ./$(BIN) $(ARGS) -test: +test: ensure-jerboa-tools $(JEXEC) test/test-gitsafe.ss install: binary @@ -49,7 +66,7 @@ clean: help: @echo "gitsafe — secret-scanning git hooks (jerbuild + cc only)" @echo "" - @echo " make binary Build the standalone ./gitsafe-bin" + @echo " make binary Build the standalone ./gitsafe-bin (auto-downloads jerbuild)" @echo " make run ARGS='...' Build + run ./gitsafe-bin" @echo " make test Run the test suite" @echo " make install Build + install to ~/.local/bin + global git hooks" --- a/README.md +++ b/README.md @@ -7,23 +7,19 @@ A fast, zero-dependency git hook that blocks commits and pushes containing leake ```bash git clone https://git.sr.ht/~lisp/jerboa-gitsafe cd jerboa-gitsafe -make install +make binary install ``` -This builds a fully static binary via Docker and installs it to `~/.local/bin/gitsafe`, then configures global git hooks so every new repo is protected automatically. +This builds a native binary and installs it to `~/.local/bin/gitsafe`, then configures global git hooks so every new repo is protected automatically. No Jerboa checkout needed: if `jerbuild` isn't already installed, the build downloads the prebuilt Jerboa toolchain for your os/arch into `.jerboa/bin` (macOS arm64, Linux amd64/arm64, FreeBSD amd64). The only requirement is a C compiler. ## Install -### From Docker (recommended) - -Requires only Docker. No Chez Scheme, no Jerboa, no compiler toolchain. - ```bash make install ``` -This runs `make linux` (Docker build) and then: -- Copies the static binary to `~/.local/bin/gitsafe` +This builds the binary (fetching the Jerboa toolchain on first run if needed) and then: +- Copies the binary to `~/.local/bin/gitsafe` - Creates pre-commit and pre-push hooks in `~/.git-templates/hooks/` - Sets `git config --global init.templateDir ~/.git-templates` @@ -37,10 +33,11 @@ export PATH="$HOME/.local/bin:$PATH" ### Build only (no install) ```bash -make linux # Docker build → ./gitsafe-musl -make linux-local # Local build (requires musl-gcc + musl Chez) +make binary # Native build → ./gitsafe-bin ``` +To pin a toolchain explicitly: `make binary JERBUILD=/path/to/jerbuild` or `JERBOA_VERSION=<tag>`. + ## Setting Up Existing Repos `make install` configures git's global template directory, which applies to all **new** repos (`git init` / `git clone`). To add gitsafe hooks to repos that already exist, you have two options: @@ -296,12 +293,9 @@ Options: ## Make Targets ``` -make linux Docker build (canonical, reproducible) -make linux-local Local build (requires musl-gcc + musl Chez) -make install Docker build + install to ~/.local/bin + global hooks -make verify-harden Verify binary hardening (stripped, no path leaks) -make binary Native build (requires local Chez + Jerboa) -make run ARGS='...' Run in interpreter mode (development) +make binary Native build → ./gitsafe-bin (auto-downloads jerbuild) +make install Build + install to ~/.local/bin + global hooks +make run ARGS='...' Build + run ./gitsafe-bin make test Run test suite make clean Remove all build artifacts ``` new file mode 100755 --- /dev/null +++ b/support/ensure-jerboa.sh @@ -0,0 +1,121 @@ +#!/bin/sh +# Bootstrap a project-local Jerboa toolchain from SourceHut release artifacts. +# +# Usage: +# support/ensure-jerboa.sh v0.2.0 .jerboa/bin +# +# Override the artifact location with: +# JERBOA_RELEASE_BASE=https://example.org/releases/v0.2.0 +# or the SourceHut repo with: +# JERBOA_RELEASE_REPO=~lisp/jerboa +# For testing or unusual hosts, override target detection with: +# JERBOA_RELEASE_TARGET=macos-arm64 + +set -eu + +usage() { + echo "Usage: $0 VERSION [BINDIR]" >&2 +} + +[ "${1:-}" ] || { usage; exit 2; } + +version=$1 +bindir=${2:-.jerboa/bin} +repo=${JERBOA_RELEASE_REPO:-~lisp/jerboa} +origin=${JERBOA_RELEASE_ORIGIN:-https://git.sr.ht} + +if [ "${JERBOA_RELEASE_TARGET:-}" ]; then + target=$JERBOA_RELEASE_TARGET +else + os=$(uname -s) + arch=$(uname -m) + case "$os-$arch" in + Darwin-arm64) target=macos-arm64 ;; + Linux-x86_64|Linux-amd64) target=linux-amd64 ;; + Linux-aarch64|Linux-arm64) target=linux-arm64 ;; + FreeBSD-amd64|FreeBSD-x86_64) target=freebsd-amd64 ;; + *) + echo "ERROR: unsupported platform for Jerboa release artifacts: $os $arch" >&2 + echo " set JERBUILD=/path/to/jerbuild or build Jerboa locally for this host" >&2 + exit 1 + ;; + esac +fi + +case "$target" in + macos-arm64|linux-amd64|linux-arm64|freebsd-amd64) ;; + *) + echo "ERROR: unsupported release target: $target" >&2 + exit 1 + ;; +esac + +file="jerboa-${version}-${target}.tar.gz" +base=${JERBOA_RELEASE_BASE:-${origin}/${repo}/refs/download/${version}} +url="${base%/}/${file}" +sum_url="${url}.sha256" + +download() { + src=$1 + dst=$2 + if command -v curl >/dev/null 2>&1; then + curl -fsSL "$src" -o "$dst" + elif command -v fetch >/dev/null 2>&1; then + fetch -q -o "$dst" "$src" + elif command -v wget >/dev/null 2>&1; then + wget -q -O "$dst" "$src" + else + echo "ERROR: need curl, fetch, or wget to download Jerboa" >&2 + exit 1 + fi +} + +sha256_file() { + path=$1 + if command -v sha256sum >/dev/null 2>&1; then + sha256sum "$path" | awk '{print $1}' + elif command -v shasum >/dev/null 2>&1; then + shasum -a 256 "$path" | awk '{print $1}' + elif command -v sha256 >/dev/null 2>&1; then + sha256 -q "$path" + else + echo "ERROR: need sha256sum, shasum, or sha256" >&2 + exit 1 + fi +} + +tmp=$(mktemp -d "${TMPDIR:-/tmp}/jerboa-bootstrap.XXXXXX") +trap 'rm -rf "$tmp"' EXIT HUP INT TERM + +archive="$tmp/$file" +sum_file="$tmp/$file.sha256" + +echo "fetching $url" +download "$url" "$archive" +download "$sum_url" "$sum_file" + +expected=$(awk '{print $1; exit}' "$sum_file") +actual=$(sha256_file "$archive") +if [ "$expected" != "$actual" ]; then + echo "ERROR: checksum mismatch for $file" >&2 + echo "expected: $expected" >&2 + echo "actual: $actual" >&2 + exit 1 +fi + +mkdir -p "$tmp/extract" +tar -xzf "$archive" -C "$tmp/extract" +root="$tmp/extract/jerboa-${version}-${target}" +[ -x "$root/bin/jerboa" ] || { + echo "ERROR: archive did not contain bin/jerboa" >&2 + exit 1 +} + +mkdir -p "$bindir" +cp "$root/bin/jerboa" "$bindir/jerboa" +chmod 0755 "$bindir/jerboa" +for link in jmcp jlsp jerbuild jpkg; do + ln -sf jerboa "$bindir/$link" +done + +echo "installed $bindir/{jerboa,jmcp,jlsp,jerbuild,jpkg}"