c10740f41f1a

add install.sh — binary from Kepr with source fallback, Zig 0.16 guidance

time
2026-06-04 15:57 UTC · 4 months ago
machine
koh2
agent
claude-sonnet-4-6
session
install script and kepr setup
parent
2aa7f7247657
3 files install.sh+202 −0kiln/fleet/nina4.json+11 −0kiln/fleet/test1.json+8 −0
install.sh+202 −0
@@ -0,0 +1,202 @@1+#!/usr/bin/env sh2+# nina install script3+# Run:  curl -fsSL kepr.uk/nina/install.sh | sh4+# Or:   NINA_INSTALL_DIR=/usr/local/bin sh install.sh5+#6+# Downloads the pre-built binary from Kepr for your platform.7+# Falls back to building from source (requires Zig 0.16+) if unavailable.8+9+set -e10+11+RELEASES_URL="https://kepr.uk/api/nina/releases/latest"12+SOURCE_URL="https://kepr.uk/nina/archive/HEAD.tar.gz"13+INSTALL_DIR="${NINA_INSTALL_DIR:-$HOME/.local/bin}"14+MIN_ZIG_MINOR=1615+16+# Detect platform17+os="$(uname -s)"18+arch="$(uname -m)"19+case "${os}-${arch}" in20+  Linux-x86_64)               platform="linux-x86_64"  ;;21+  Linux-aarch64|Linux-arm64)  platform="linux-aarch64" ;;22+  Darwin-arm64)               platform="macos-aarch64" ;;23+  Darwin-x86_64)              platform="macos-x86_64"  ;;24+  *)25+    printf 'nina install: unsupported platform %s-%s\n' "$os" "$arch" >&226+    printf '  build from source with Zig 0.%s+:\n' "$MIN_ZIG_MINOR" >&227+    printf '    zig build -Doptimize=ReleaseSafe\n' >&228+    exit 1 ;;29+esac30+31+have() { command -v "$1" >/dev/null 2>&1; }32+say()  { printf 'nina install: %s\n' "$1"; }33+die()  { say "error: $1" >&2; exit 1; }34+35+fetch_text() {36+  if have curl;   then curl -fsSL "$1"37+  elif have wget; then wget -qO- "$1"38+  else die "curl or wget is required"; fi39+}40+41+fetch_file() {42+  if have curl;   then curl -fsSL "$1" -o "$2"43+  elif have wget; then wget -qO  "$2" "$1"44+  else die "curl or wget is required"; fi45+}46+47+# ── binary install from Kepr ─────────────────────────────────────────────────48+49+install_binary() {50+  say "fetching release metadata..."51+  meta="$(fetch_text "$RELEASES_URL" 2>/dev/null)" || return 152+  [ -n "$meta" ] || return 153+54+  if have python3; then55+    version="$(printf '%s' "$meta" | python3 -c "56+import sys, json57+d = json.load(sys.stdin)58+print(d.get('version') or '')59+" 2>/dev/null)"60+    dl_url="$(printf '%s' "$meta" | python3 -c "61+import sys, json62+d = json.load(sys.stdin)63+print((d.get('platforms') or {}).get('${platform}', {}).get('url') or '')64+" 2>/dev/null)"65+    cksum="$(printf '%s' "$meta" | python3 -c "66+import sys, json67+d = json.load(sys.stdin)68+p = (d.get('platforms') or {}).get('${platform}', {})69+raw = p.get('blake3') or p.get('checksum') or ''70+print(raw[len('blake3:'):] if raw.startswith('blake3:') else raw)71+" 2>/dev/null)"72+  elif have jq; then73+    version="$(printf '%s' "$meta" | jq -r '.version // empty')"74+    dl_url="$(printf '%s' "$meta" | jq -r ".platforms.\"${platform}\".url // empty")"75+    cksum_raw="$(printf '%s' "$meta" | jq -r ".platforms.\"${platform}\".blake3 // .platforms.\"${platform}\".checksum // empty")"76+    cksum="${cksum_raw#blake3:}"77+  else78+    # Minimal grep/sed fallback for compact JSON79+    version="$(printf '%s' "$meta" | grep -o '"version":"[^"]*"' | sed 's/"version":"//;s/"//')"80+    plat_block="$(printf '%s' "$meta" | grep -o "\"${platform}\":{[^}]*}" | head -1)"81+    dl_url="$(printf '%s' "$plat_block" | grep -o '"url":"[^"]*"' | sed 's/"url":"//;s/"//')"82+    cksum=""83+  fi84+85+  [ -n "$dl_url" ] || return 186+  [ -n "$version" ] || version="latest"87+88+  say "downloading nina ${version} for ${platform}..."89+  tmpfile="$(mktemp /tmp/nina.XXXXXX)"90+  fetch_file "$dl_url" "$tmpfile" || { rm -f "$tmpfile"; return 1; }91+92+  if [ -n "$cksum" ] && have b3sum; then93+    actual="$(b3sum --no-names "$tmpfile" 2>/dev/null | awk '{print $1}')"94+    if [ "$actual" != "$cksum" ]; then95+      say "blake3 checksum mismatch — aborting"96+      rm -f "$tmpfile"97+      return 198+    fi99+    say "checksum verified"100+  fi101+102+  chmod +x "$tmpfile"103+  mkdir -p "$INSTALL_DIR"104+  mv "$tmpfile" "${INSTALL_DIR}/nina"105+  say "installed nina ${version} → ${INSTALL_DIR}/nina"106+  return 0107+}108+109+# ── Zig version check ─────────────────────────────────────────────────────────110+111+check_zig() {112+  if ! have zig; then113+    say ""114+    say "Zig not found. Building nina from source requires Zig 0.${MIN_ZIG_MINOR} or later."115+    say ""116+    say "  NixOS:"117+    say "    nix profile install 'nixpkgs#zig_0_16'   # if your channel has it"118+    say "    nix-shell -p zig_0_16 --run 'sh install.sh'"119+    say "    # or add 'pkgs.zig_0_16' to environment.systemPackages and rebuild"120+    say ""121+    say "  Direct download: https://ziglang.org/download/"122+    say ""123+    die "Zig 0.${MIN_ZIG_MINOR}+ required for source build"124+  fi125+126+  zig_ver="$(zig version 2>/dev/null)"127+  major="$(printf '%s' "$zig_ver" | cut -d. -f1)"128+  minor="$(printf '%s' "$zig_ver" | cut -d. -f2)"129+130+  ok=0131+  [ "$major" -ge 1 ] 2>/dev/null && ok=1132+  [ "$major" -eq 0 ] 2>/dev/null && [ "$minor" -ge "$MIN_ZIG_MINOR" ] 2>/dev/null && ok=1133+134+  if [ "$ok" -ne 1 ]; then135+    say ""136+    say "Found zig ${zig_ver}, but nina requires Zig 0.${MIN_ZIG_MINOR} or later."137+    say ""138+    say "  NixOS: the system 'zig' package is ${zig_ver}. To get 0.16:"139+    say "    nix profile install 'nixpkgs#zig_0_16'"140+    say "    nix-shell -p zig_0_16 --run 'sh install.sh'"141+    say "    # or check nixpkgs-unstable if your stable channel lacks zig_0_16"142+    say ""143+    say "  Direct download: https://ziglang.org/download/"144+    say ""145+    die "Zig 0.${MIN_ZIG_MINOR}+ required; found ${zig_ver}"146+  fi147+148+  say "zig ${zig_ver} found"149+}150+151+# ── source build fallback ─────────────────────────────────────────────────────152+153+build_from_source() {154+  say "falling back to build from source..."155+  check_zig156+157+  if [ -f "build.zig" ] && [ -f "src/main.zig" ]; then158+    srcdir="."159+  else160+    say "fetching source archive from Kepr..."161+    srcdir="$(mktemp -d /tmp/nina-src.XXXXXX)"162+    fetch_file "$SOURCE_URL" "${srcdir}/nina.tar.gz" \163+      || die "could not download source archive"164+    tar -xzf "${srcdir}/nina.tar.gz" -C "$srcdir" --strip-components=1 \165+      || die "could not extract source archive"166+  fi167+168+  say "building (this may take a moment)..."169+  (cd "$srcdir" && zig build -Doptimize=ReleaseSafe) \170+    || die "build failed — see output above for details"171+172+  mkdir -p "$INSTALL_DIR"173+  cp "${srcdir}/zig-out/bin/nina" "${INSTALL_DIR}/nina"174+  chmod +x "${INSTALL_DIR}/nina"175+  say "installed from source → ${INSTALL_DIR}/nina"176+}177+178+# ── PATH reminder ─────────────────────────────────────────────────────────────179+180+check_path() {181+  case ":${PATH}:" in182+    *":${INSTALL_DIR}:"*) ;;183+    *)184+      say ""185+      say "${INSTALL_DIR} is not in your PATH. Add it:"186+      say "  export PATH=\"\${PATH}:${INSTALL_DIR}\""187+      say "  echo 'export PATH=\"\${PATH}:${INSTALL_DIR}\"' >> ~/.bashrc"188+      say "  # NixOS: add '${INSTALL_DIR}' to environment.sessionVariables in configuration.nix"189+      ;;190+  esac191+}192+193+# ── main ──────────────────────────────────────────────────────────────────────194+195+if install_binary; then196+  check_path197+  exit 0198+fi199+200+say "pre-built binary unavailable for ${platform} — trying source build"201+build_from_source202+check_path

view file

kiln/fleet/nina4.json+11 −0
@@ -0,0 +1,11 @@1+{2+  "id": "nina4",3+  "created_at": 1780231128,4+  "slot_count": 4,5+  "slots": [6+    {"index": 0, "chamber_id": "fleet-nina4-0", "pid": 63251, "ssh_port": 52200, "serial_port": 52201, "overlay": "/var/folders/d2/jb8d6y8x39d97rypxddjrbn00000gn/T//kiln-chambers/overlay-fleet-nina4-0.qcow2", "state": "ready", "last_activity": 1780231129, "snapshots": []},7+    {"index": 1, "chamber_id": "fleet-nina4-1", "pid": 63253, "ssh_port": 52202, "serial_port": 52203, "overlay": "/var/folders/d2/jb8d6y8x39d97rypxddjrbn00000gn/T//kiln-chambers/overlay-fleet-nina4-1.qcow2", "state": "ready", "last_activity": 1780231129, "snapshots": []},8+    {"index": 2, "chamber_id": "fleet-nina4-2", "pid": 63250, "ssh_port": 52204, "serial_port": 52205, "overlay": "/var/folders/d2/jb8d6y8x39d97rypxddjrbn00000gn/T//kiln-chambers/overlay-fleet-nina4-2.qcow2", "state": "ready", "last_activity": 1780231129, "snapshots": []},9+    {"index": 3, "chamber_id": "fleet-nina4-3", "pid": 63252, "ssh_port": 52206, "serial_port": 52207, "overlay": "/var/folders/d2/jb8d6y8x39d97rypxddjrbn00000gn/T//kiln-chambers/overlay-fleet-nina4-3.qcow2", "state": "ready", "last_activity": 1780231129, "snapshots": []}10+  ]11+}

view file

kiln/fleet/test1.json+8 −0
@@ -0,0 +1,8 @@1+{2+  "id": "test1",3+  "created_at": 1780231121,4+  "slot_count": 1,5+  "slots": [6+    {"index": 0, "chamber_id": "fleet-test1-0", "pid": 63244, "ssh_port": 52200, "serial_port": 52201, "overlay": "/var/folders/d2/jb8d6y8x39d97rypxddjrbn00000gn/T//kiln-chambers/overlay-fleet-test1-0.qcow2", "state": "ready", "last_activity": 1780231122, "snapshots": []}7+  ]8+}

view file