#!/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 set -e FLAKE_URL="https://kepr.uk/nina/archive/HEAD.tar.gz" RELEASES_URL="https://kepr.uk/api/nina/releases/latest" INSTALL_DIR="${NINA_INSTALL_DIR:-$HOME/.local/bin}" MIN_ZIG_MINOR=16 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: unsupported platform %s-%s\n' "$os" "$arch" >&2 exit 1 ;; esac have() { command -v "$1" >/dev/null 2>&1; } say() { printf 'nina: %s\n' "$1"; } die() { say "error: $1" >&2; exit 1; } blank() { printf '\n'; } is_nixos() { [ -e /etc/NIXOS ]; } fetch_text() { if have curl; then curl -fsSL "$1" elif have wget; then wget -qO- "$1" else die "curl or wget 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 required"; fi } # ── banner ──────────────────────────────────────────────────────────────────── banner() { cat <<'BANNER' _ __ _ _ __ __ _ | '_ \ (_)| '_ \ / _` | | | | || || | | || (_| | |_| |_||_||_| |_| \__,_| NixOS Intuitive Navigation Assistant · kepr.uk/nina BANNER } # ── fetch source archive ────────────────────────────────────────────────────── fetch_source() { srcdir="$(mktemp -d /tmp/nina-src.XXXXXX)" say "fetching source from Kepr..." fetch_file "$FLAKE_URL" "${srcdir}/nina.tar.gz" \ || die "could not download source archive — check your connection" tar -xzf "${srcdir}/nina.tar.gz" -C "$srcdir" --strip-components=1 \ || die "could not extract source archive" printf '%s' "$srcdir" } # ── 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 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 "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 } # ── NixOS: nix profile install (primary) ───────────────────────────────────── nixos_profile_install() { have nix || return 1 say "installing nina to ~/.nix-profile/bin/nina via nix profile..." blank if nix profile install "${FLAKE_URL}#nina"; then return 0 fi blank say "nix profile install failed. Common causes:" say " flakes not enabled — add to /etc/nix/nix.conf or ~/.config/nix/nix.conf:" say " experimental-features = nix-command flakes" say " once nina is installed: nina setup enables this automatically" blank return 1 } # ── NixOS: nix-shell source build (fallback) ────────────────────────────────── nixos_shell_build() { have nix-shell || return 1 say "opening nix-shell with zig_0_16 and building nina from source..." blank srcdir="$(fetch_source)" nix-shell -p zig_0_16 \ --run "cd '${srcdir}' && zig build -Doptimize=ReleaseSafe" \ || die "build failed — see output above" mkdir -p "$INSTALL_DIR" cp "${srcdir}/zig-out/bin/nina" "${INSTALL_DIR}/nina" chmod +x "${INSTALL_DIR}/nina" blank say "installed from source → ${INSTALL_DIR}/nina" say "note: this binary was built outside of nix — adopt ~/nina.nix into your" say "configuration.nix to make it nix-managed." } # ── generate ~/nina.nix ─────────────────────────────────────────────────────── generate_nixos_module() { # Write ~/nina.nix without invoking shell substitution on the nix code inside. cat > "$HOME/nina.nix" <<'NIXEOF' # ~/nina.nix — generated by the nina installer # # Declares nina as a NixOS system package. # # Add to /etc/nixos/configuration.nix: # imports = [ /home/YOUR_USER/nina.nix ]; # Then: sudo nixos-rebuild switch # # To update: nina update (or re-run the installer) # To remove: nina goodbye (or delete this file, then nixos-rebuild switch) # # For flake-based configurations, add nina as a flake input instead: # inputs.nina.url = "https://kepr.uk/nina/archive/HEAD.tar.gz"; # # in your nixosModules list: nina.nixosModules.default # # then set: programs.nina.enable = true; { ... }: { imports = [ "${builtins.fetchTarball "https://kepr.uk/nina/archive/HEAD.tar.gz"}/nixos-module.nix" ]; programs.nina.enable = true; } NIXEOF blank say "generated ~/nina.nix for system-wide installation:" say " 1. add to /etc/nixos/configuration.nix:" say " imports = [ /home/$(id -un)/nina.nix ];" say " 2. sudo nixos-rebuild switch" } # ── Zig version check ───────────────────────────────────────────────────────── check_zig() { if ! have zig; then blank say "Zig not found. Building nina requires Zig 0.${MIN_ZIG_MINOR}+." blank say " Download: https://ziglang.org/download/" if is_nixos; then say " NixOS: nix-shell -p zig_0_16 --run 'sh install.sh'" fi blank die "Zig 0.${MIN_ZIG_MINOR}+ required" 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 blank say "found zig ${zig_ver}, but nina requires Zig 0.${MIN_ZIG_MINOR}+." if is_nixos; then say " run: nix-shell -p zig_0_16 --run 'sh install.sh'" else say " update: https://ziglang.org/download/" fi blank die "Zig 0.${MIN_ZIG_MINOR}+ required; found ${zig_ver}" fi say "zig ${zig_ver} found" } # ── generic source build (non-NixOS) ───────────────────────────────────────── build_from_source() { say "building from source..." check_zig if [ -f "build.zig" ] && [ -f "src/main.zig" ]; then srcdir="." else srcdir="$(fetch_source)" fi (cd "$srcdir" && zig build -Doptimize=ReleaseSafe) \ || die "build failed — see output above" 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}:"*) ;; *) blank say "${INSTALL_DIR} is not in your PATH. Add it:" say " export PATH=\"\${PATH}:${INSTALL_DIR}\"" say " echo 'export PATH=\"\${PATH}:${INSTALL_DIR}\"' >> ~/.bashrc" if is_nixos; then say " NixOS: add to environment.sessionVariables in configuration.nix" fi ;; esac } # ── closing message ────────────────────────────────────────────────────────── farewell() { printf '\n' printf ' thank you!! running nina update occasionally will keep me in tip top shape.\n' printf ' get started by running nina hello\n' printf '\n' printf ' if nina turns out not to be for you, nina goodbye will remove her cleanly.\n' printf '\n' } # ── main ────────────────────────────────────────────────────────────────────── banner if is_nixos; then say "NixOS detected" if nixos_profile_install; then generate_nixos_module farewell exit 0 fi nixos_shell_build generate_nixos_module check_path farewell exit 0 fi # macOS / generic Linux: download binary, fall back to source build if install_binary; then check_path farewell exit 0 fi say "no pre-built binary for ${platform} — falling back to source build" build_from_source check_path farewell