#!/usr/bin/env sh # nina install script # Run: curl -fsSL kepr.uk/nina/install.sh | sh # Or: NINA_INSTALL_DIR=/usr/local/bin sh install.sh # # Downloads the pre-built binary from Kepr for your platform. # Falls back to building from source (requires Zig 0.16+) if unavailable. set -e RELEASES_URL="https://kepr.uk/api/nina/releases/latest" SOURCE_URL="https://kepr.uk/nina/archive/HEAD.tar.gz" INSTALL_DIR="${NINA_INSTALL_DIR:-$HOME/.local/bin}" MIN_ZIG_MINOR=16 # Detect platform os="$(uname -s)" arch="$(uname -m)" case "${os}-${arch}" in Linux-x86_64) platform="linux-x86_64" ;; Linux-aarch64|Linux-arm64) platform="linux-aarch64" ;; Darwin-arm64) platform="macos-aarch64" ;; Darwin-x86_64) platform="macos-x86_64" ;; *) printf 'nina install: unsupported platform %s-%s\n' "$os" "$arch" >&2 printf ' build from source with Zig 0.%s+:\n' "$MIN_ZIG_MINOR" >&2 printf ' zig build -Doptimize=ReleaseSafe\n' >&2 exit 1 ;; esac have() { command -v "$1" >/dev/null 2>&1; } say() { printf 'nina install: %s\n' "$1"; } die() { say "error: $1" >&2; exit 1; } fetch_text() { if have curl; then curl -fsSL "$1" elif have wget; then wget -qO- "$1" else die "curl or wget is required"; fi } fetch_file() { if have curl; then curl -fsSL "$1" -o "$2" elif have wget; then wget -qO "$2" "$1" else die "curl or wget is required"; fi } # ── binary install from Kepr ───────────────────────────────────────────────── install_binary() { say "fetching release metadata..." meta="$(fetch_text "$RELEASES_URL" 2>/dev/null)" || return 1 [ -n "$meta" ] || return 1 if have python3; then version="$(printf '%s' "$meta" | python3 -c " import sys, json d = json.load(sys.stdin) print(d.get('version') or '') " 2>/dev/null)" dl_url="$(printf '%s' "$meta" | python3 -c " import sys, json d = json.load(sys.stdin) print((d.get('platforms') or {}).get('${platform}', {}).get('url') or '') " 2>/dev/null)" cksum="$(printf '%s' "$meta" | python3 -c " import sys, json d = json.load(sys.stdin) p = (d.get('platforms') or {}).get('${platform}', {}) raw = p.get('blake3') or p.get('checksum') or '' print(raw[len('blake3:'):] if raw.startswith('blake3:') else raw) " 2>/dev/null)" elif have jq; then version="$(printf '%s' "$meta" | jq -r '.version // empty')" dl_url="$(printf '%s' "$meta" | jq -r ".platforms.\"${platform}\".url // empty")" cksum_raw="$(printf '%s' "$meta" | jq -r ".platforms.\"${platform}\".blake3 // .platforms.\"${platform}\".checksum // empty")" cksum="${cksum_raw#blake3:}" else # Minimal grep/sed fallback for compact JSON version="$(printf '%s' "$meta" | grep -o '"version":"[^"]*"' | sed 's/"version":"//;s/"//')" plat_block="$(printf '%s' "$meta" | grep -o "\"${platform}\":{[^}]*}" | head -1)" dl_url="$(printf '%s' "$plat_block" | grep -o '"url":"[^"]*"' | sed 's/"url":"//;s/"//')" cksum="" fi [ -n "$dl_url" ] || return 1 [ -n "$version" ] || version="latest" say "downloading nina ${version} for ${platform}..." tmpfile="$(mktemp /tmp/nina.XXXXXX)" fetch_file "$dl_url" "$tmpfile" || { rm -f "$tmpfile"; return 1; } if [ -n "$cksum" ] && have b3sum; then actual="$(b3sum --no-names "$tmpfile" 2>/dev/null | awk '{print $1}')" if [ "$actual" != "$cksum" ]; then say "blake3 checksum mismatch — aborting" rm -f "$tmpfile" return 1 fi say "checksum verified" fi chmod +x "$tmpfile" mkdir -p "$INSTALL_DIR" mv "$tmpfile" "${INSTALL_DIR}/nina" say "installed nina ${version} → ${INSTALL_DIR}/nina" return 0 } # ── Zig version check ───────────────────────────────────────────────────────── check_zig() { if ! have zig; then say "" say "Zig not found. Building nina from source requires Zig 0.${MIN_ZIG_MINOR} or later." say "" say " NixOS:" say " nix profile install 'nixpkgs#zig_0_16' # if your channel has it" say " nix-shell -p zig_0_16 --run 'sh install.sh'" say " # or add 'pkgs.zig_0_16' to environment.systemPackages and rebuild" say "" say " Direct download: https://ziglang.org/download/" say "" die "Zig 0.${MIN_ZIG_MINOR}+ required for source build" fi zig_ver="$(zig version 2>/dev/null)" major="$(printf '%s' "$zig_ver" | cut -d. -f1)" minor="$(printf '%s' "$zig_ver" | cut -d. -f2)" ok=0 [ "$major" -ge 1 ] 2>/dev/null && ok=1 [ "$major" -eq 0 ] 2>/dev/null && [ "$minor" -ge "$MIN_ZIG_MINOR" ] 2>/dev/null && ok=1 if [ "$ok" -ne 1 ]; then say "" say "Found zig ${zig_ver}, but nina requires Zig 0.${MIN_ZIG_MINOR} or later." say "" say " NixOS: the system 'zig' package is ${zig_ver}. To get 0.16:" say " nix profile install 'nixpkgs#zig_0_16'" say " nix-shell -p zig_0_16 --run 'sh install.sh'" say " # or check nixpkgs-unstable if your stable channel lacks zig_0_16" say "" say " Direct download: https://ziglang.org/download/" say "" die "Zig 0.${MIN_ZIG_MINOR}+ required; found ${zig_ver}" fi say "zig ${zig_ver} found" } # ── source build fallback ───────────────────────────────────────────────────── build_from_source() { say "falling back to build from source..." check_zig if [ -f "build.zig" ] && [ -f "src/main.zig" ]; then srcdir="." else say "fetching source archive from Kepr..." srcdir="$(mktemp -d /tmp/nina-src.XXXXXX)" fetch_file "$SOURCE_URL" "${srcdir}/nina.tar.gz" \ || die "could not download source archive" tar -xzf "${srcdir}/nina.tar.gz" -C "$srcdir" --strip-components=1 \ || die "could not extract source archive" fi say "building (this may take a moment)..." (cd "$srcdir" && zig build -Doptimize=ReleaseSafe) \ || die "build failed — see output above for details" mkdir -p "$INSTALL_DIR" cp "${srcdir}/zig-out/bin/nina" "${INSTALL_DIR}/nina" chmod +x "${INSTALL_DIR}/nina" say "installed from source → ${INSTALL_DIR}/nina" } # ── PATH reminder ───────────────────────────────────────────────────────────── check_path() { case ":${PATH}:" in *":${INSTALL_DIR}:"*) ;; *) say "" say "${INSTALL_DIR} is not in your PATH. Add it:" say " export PATH=\"\${PATH}:${INSTALL_DIR}\"" say " echo 'export PATH=\"\${PATH}:${INSTALL_DIR}\"' >> ~/.bashrc" say " # NixOS: add '${INSTALL_DIR}' to environment.sessionVariables in configuration.nix" ;; esac } # ── main ────────────────────────────────────────────────────────────────────── if install_binary; then check_path exit 0 fi say "pre-built binary unavailable for ${platform} — trying source build" build_from_source check_path