Skip to content

Commit 52f5330

Browse files
Auto-install pinned Zig toolchain in setup + devcontainer (#33)
The Zig FFI bridge is half of the ABI-FFI standard, but nothing installed Zig: .tool-versions only lists it (commented), setup.sh stops at `just`, and the devcontainer's `postCreateCommand: just deps` referenced a `deps` recipe that did not exist. Unlike the other toolchain pieces, Zig is not distributed via GitHub releases, so it must come from ziglang.org. Add scripts/install-zig.sh: an idempotent, fail-soft installer for the pinned Zig 0.14.0 (arch/OS-aware, uses the system CA store the agent proxy populates, never --insecure). If ziglang.org is not on the session's egress allowlist the download 403s and the script exits 0 with an actionable message, so it never blocks setup or a session. Wire it in via the two paths the project already uses: a "Step 1b" in setup.sh (where the template exposes that step), and a new `deps` Justfile recipe backing the devcontainer postCreateCommand. Once ziglang.org is allowlisted, future setups and dev containers install Zig automatically. Claude-Session: https://claude.ai/code/session_019xMKB3T4Vo5FYC7Czx3JSH Co-authored-by: Claude <noreply@anthropic.com>
1 parent be1d62d commit 52f5330

3 files changed

Lines changed: 74 additions & 0 deletions

File tree

Justfile

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,3 +128,9 @@ crg-badge:
128128
D) color="orange" ;; E) color="red" ;; F) color="critical" ;; \
129129
*) color="lightgrey" ;; esac; \
130130
echo "[![CRG $$grade](https://img.shields.io/badge/CRG-$$grade-$$color?style=flat-square)](https://github.com/hyperpolymath/standards/tree/main/component-readiness-grades)"
131+
132+
# Install dev dependencies (invoked by the devcontainer postCreateCommand).
133+
# Installs the pinned Zig FFI toolchain, then warms the Cargo cache.
134+
deps:
135+
./scripts/install-zig.sh
136+
cargo fetch

scripts/install-zig.sh

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
#!/bin/sh
2+
# SPDX-License-Identifier: MPL-2.0
3+
# Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) <j.d.a.jewell@open.ac.uk>
4+
#
5+
# install-zig.sh — install the pinned Zig toolchain (the Zig FFI bridge half of
6+
# the ABI-FFI standard). Idempotent and fail-soft: it never aborts the caller.
7+
#
8+
# Egress note: Zig is NOT distributed via GitHub releases, so it is fetched from
9+
# ziglang.org. Inside a Claude Code session, outbound HTTPS goes through the
10+
# policy-enforcing agent proxy; github.com is allowlisted by default but
11+
# ziglang.org must be added explicitly, or this download returns 403. We use the
12+
# system CA store the proxy already populated — never pass --insecure.
13+
set -eu
14+
15+
ZIG_VERSION="${ZIG_VERSION:-0.14.0}"
16+
PREFIX="${ZIG_PREFIX:-/usr/local}"
17+
18+
# Already at the pinned version? Done.
19+
if command -v zig >/dev/null 2>&1 && [ "$(zig version 2>/dev/null)" = "$ZIG_VERSION" ]; then
20+
echo "install-zig: zig $ZIG_VERSION already installed"
21+
exit 0
22+
fi
23+
24+
# Map host arch/OS to Zig's release naming.
25+
case "$(uname -m)" in
26+
x86_64|amd64) zarch="x86_64" ;;
27+
aarch64|arm64) zarch="aarch64" ;;
28+
*) echo "install-zig: unsupported arch $(uname -m); install Zig $ZIG_VERSION manually" >&2; exit 0 ;;
29+
esac
30+
case "$(uname -s)" in
31+
Linux) zos="linux" ;;
32+
Darwin) zos="macos" ;;
33+
*) echo "install-zig: unsupported OS $(uname -s); install Zig $ZIG_VERSION manually" >&2; exit 0 ;;
34+
esac
35+
36+
tarball="zig-${zos}-${zarch}-${ZIG_VERSION}.tar.xz"
37+
url="https://ziglang.org/download/${ZIG_VERSION}/${tarball}"
38+
dest="${PREFIX}/lib/zig-${ZIG_VERSION}"
39+
40+
tmp="$(mktemp -d)"
41+
trap 'rm -rf "$tmp"' EXIT
42+
43+
echo "install-zig: fetching $url"
44+
if ! curl -fsSL --retry 2 -o "$tmp/$tarball" "$url"; then
45+
echo "install-zig: download failed (HTTP error or blocked host)." >&2
46+
echo "install-zig: if this is a Claude Code session, add 'ziglang.org' to the" >&2
47+
echo " egress allowlist — github.com is allowed but ziglang.org is not." >&2
48+
exit 0 # fail-soft: a missing Zig must not block setup or session start
49+
fi
50+
51+
mkdir -p "$dest" "${PREFIX}/bin"
52+
tar -xJf "$tmp/$tarball" -C "$dest" --strip-components=1
53+
ln -sf "$dest/zig" "${PREFIX}/bin/zig"
54+
55+
if command -v zig >/dev/null 2>&1 && [ "$(zig version 2>/dev/null)" = "$ZIG_VERSION" ]; then
56+
echo "install-zig: installed zig $(zig version)"
57+
else
58+
echo "install-zig: installed to ${PREFIX}/bin/zig — ensure ${PREFIX}/bin is on PATH" >&2
59+
fi

setup.sh

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,15 @@ main() {
194194
printf "%sStep 1: Install task runner%s\n" "$BOLD" "$RESET"
195195
install_just || { fail "Cannot proceed without just"; exit 1; }
196196
printf "\n"
197+
# Step 1b: Install the pinned Zig toolchain (the Zig FFI bridge half of the
198+
# ABI-FFI standard). Best-effort — see scripts/install-zig.sh.
199+
printf "%sStep 1b: Install Zig toolchain%s\n" "$BOLD" "$RESET"
200+
if [ -x ./scripts/install-zig.sh ]; then
201+
./scripts/install-zig.sh || warn "Zig install skipped (see scripts/install-zig.sh)"
202+
else
203+
warn "scripts/install-zig.sh not found — skipping Zig install"
204+
fi
205+
printf "\n"
197206

198207
# Step 2: Check if we're in the repo directory
199208
if [ ! -f "Justfile" ] && [ ! -f "justfile" ]; then

0 commit comments

Comments
 (0)