install.sh

10.5 KB · raw

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.sh56set -e78FLAKE_URL="https://kepr.uk/nina/archive/HEAD.tar.gz"9RELEASES_URL="https://kepr.uk/api/nina/releases/latest"10INSTALL_DIR="${NINA_INSTALL_DIR:-$HOME/.local/bin}"11MIN_ZIG_MINOR=161213os="$(uname -s)"14arch="$(uname -m)"15case "${os}-${arch}" in16  Linux-x86_64)               platform="linux-x86_64"  ;;17  Linux-aarch64|Linux-arm64)  platform="linux-aarch64" ;;18  Darwin-arm64)               platform="macos-aarch64" ;;19  Darwin-x86_64)              platform="macos-x86_64"  ;;20  *)21    printf 'nina: unsupported platform %s-%s\n' "$os" "$arch" >&222    exit 1 ;;23esac2425have()  { command -v "$1" >/dev/null 2>&1; }26say()   { printf 'nina: %s\n' "$1"; }27die()   { say "error: $1" >&2; exit 1; }28blank() { printf '\n'; }29is_nixos() { [ -e /etc/NIXOS ]; }3031fetch_text() {32  if have curl;   then curl -fsSL "$1"33  elif have wget; then wget -qO- "$1"34  else die "curl or wget required"; fi35}3637fetch_file() {38  if have curl;   then curl -fsSL "$1" -o "$2"39  elif have wget; then wget -qO  "$2" "$1"40  else die "curl or wget required"; fi41}4243# ── banner ────────────────────────────────────────────────────────────────────4445banner() {46  cat <<'BANNER'4748   _ __   _  _ __    __ _49  | '_ \ (_)| '_ \  / _` |50  | | | || || | | || (_| |51  |_| |_||_||_| |_| \__,_|5253  NixOS Intuitive Navigation Assistant  ·  kepr.uk/nina5455BANNER56}5758# ── fetch source archive ──────────────────────────────────────────────────────5960fetch_source() {61  srcdir="$(mktemp -d /tmp/nina-src.XXXXXX)"62  say "fetching source from Kepr..."63  fetch_file "$FLAKE_URL" "${srcdir}/nina.tar.gz" \64    || die "could not download source archive — check your connection"65  tar -xzf "${srcdir}/nina.tar.gz" -C "$srcdir" --strip-components=1 \66    || die "could not extract source archive"67  printf '%s' "$srcdir"68}6970# ── binary install from Kepr ─────────────────────────────────────────────────7172install_binary() {73  say "fetching release metadata..."74  meta="$(fetch_text "$RELEASES_URL" 2>/dev/null)" || return 175  [ -n "$meta" ] || return 17677  if have python3; then78    version="$(printf '%s' "$meta" | python3 -c "79import sys, json80d = json.load(sys.stdin)81print(d.get('version') or '')82" 2>/dev/null)"83    dl_url="$(printf '%s' "$meta" | python3 -c "84import sys, json85d = json.load(sys.stdin)86print((d.get('platforms') or {}).get('${platform}', {}).get('url') or '')87" 2>/dev/null)"88    cksum="$(printf '%s' "$meta" | python3 -c "89import sys, json90d = json.load(sys.stdin)91p = (d.get('platforms') or {}).get('${platform}', {})92raw = p.get('blake3') or p.get('checksum') or ''93print(raw[len('blake3:'):] if raw.startswith('blake3:') else raw)94" 2>/dev/null)"95  elif have jq; then96    version="$(printf '%s' "$meta" | jq -r '.version // empty')"97    dl_url="$(printf '%s' "$meta" | jq -r ".platforms.\"${platform}\".url // empty")"98    cksum_raw="$(printf '%s' "$meta" | jq -r ".platforms.\"${platform}\".blake3 // .platforms.\"${platform}\".checksum // empty")"99    cksum="${cksum_raw#blake3:}"100  else101    version="$(printf '%s' "$meta" | grep -o '"version":"[^"]*"' | sed 's/"version":"//;s/"//')"102    plat_block="$(printf '%s' "$meta" | grep -o "\"${platform}\":{[^}]*}" | head -1)"103    dl_url="$(printf '%s' "$plat_block" | grep -o '"url":"[^"]*"' | sed 's/"url":"//;s/"//')"104    cksum=""105  fi106107  [ -n "$dl_url" ] || return 1108  [ -n "$version" ] || version="latest"109110  say "downloading nina ${version} for ${platform}..."111  tmpfile="$(mktemp /tmp/nina.XXXXXX)"112  fetch_file "$dl_url" "$tmpfile" || { rm -f "$tmpfile"; return 1; }113114  if [ -n "$cksum" ] && have b3sum; then115    actual="$(b3sum --no-names "$tmpfile" 2>/dev/null | awk '{print $1}')"116    if [ "$actual" != "$cksum" ]; then117      say "checksum mismatch — aborting"118      rm -f "$tmpfile"119      return 1120    fi121    say "checksum verified"122  fi123124  chmod +x "$tmpfile"125  mkdir -p "$INSTALL_DIR"126  mv "$tmpfile" "${INSTALL_DIR}/nina"127  say "installed nina ${version} → ${INSTALL_DIR}/nina"128  return 0129}130131# ── NixOS: nix profile install (primary) ─────────────────────────────────────132133nixos_profile_install() {134  have nix || return 1135  say "installing nina to ~/.nix-profile/bin/nina via nix profile..."136  blank137  if nix profile install "${FLAKE_URL}#nina"; then138    return 0139  fi140  blank141  say "nix profile install failed. Common causes:"142  say "  flakes not enabled — add to /etc/nix/nix.conf or ~/.config/nix/nix.conf:"143  say "    experimental-features = nix-command flakes"144  say "  once nina is installed: nina setup enables this automatically"145  blank146  return 1147}148149# ── NixOS: nix-shell source build (fallback) ──────────────────────────────────150151nixos_shell_build() {152  have nix-shell || return 1153  say "opening nix-shell with zig_0_16 and building nina from source..."154  blank155  srcdir="$(fetch_source)"156  nix-shell -p zig_0_16 \157    --run "cd '${srcdir}' && zig build -Doptimize=ReleaseSafe" \158    || die "build failed — see output above"159  mkdir -p "$INSTALL_DIR"160  cp "${srcdir}/zig-out/bin/nina" "${INSTALL_DIR}/nina"161  chmod +x "${INSTALL_DIR}/nina"162  blank163  say "installed from source → ${INSTALL_DIR}/nina"164  say "note: this binary was built outside of nix — adopt ~/nina.nix into your"165  say "configuration.nix to make it nix-managed."166}167168# ── generate ~/nina.nix ───────────────────────────────────────────────────────169170generate_nixos_module() {171  # Write ~/nina.nix without invoking shell substitution on the nix code inside.172  cat > "$HOME/nina.nix" <<'NIXEOF'173# ~/nina.nix — generated by the nina installer174#175# Declares nina as a NixOS system package.176#177# Add to /etc/nixos/configuration.nix:178#   imports = [ /home/YOUR_USER/nina.nix ];179# Then: sudo nixos-rebuild switch180#181# To update:  nina update  (or re-run the installer)182# To remove:  nina goodbye  (or delete this file, then nixos-rebuild switch)183#184# For flake-based configurations, add nina as a flake input instead:185#   inputs.nina.url = "https://kepr.uk/nina/archive/HEAD.tar.gz";186#   # in your nixosModules list: nina.nixosModules.default187#   # then set: programs.nina.enable = true;188{ ... }:189{190  imports = [191    "${builtins.fetchTarball "https://kepr.uk/nina/archive/HEAD.tar.gz"}/nixos-module.nix"192  ];193  programs.nina.enable = true;194}195NIXEOF196  blank197  say "generated ~/nina.nix for system-wide installation:"198  say "  1. add to /etc/nixos/configuration.nix:"199  say "       imports = [ /home/$(id -un)/nina.nix ];"200  say "  2. sudo nixos-rebuild switch"201}202203# ── Zig version check ─────────────────────────────────────────────────────────204205check_zig() {206  if ! have zig; then207    blank208    say "Zig not found. Building nina requires Zig 0.${MIN_ZIG_MINOR}+."209    blank210    say "  Download: https://ziglang.org/download/"211    if is_nixos; then212      say "  NixOS:    nix-shell -p zig_0_16 --run 'sh install.sh'"213    fi214    blank215    die "Zig 0.${MIN_ZIG_MINOR}+ required"216  fi217218  zig_ver="$(zig version 2>/dev/null)"219  major="$(printf '%s' "$zig_ver" | cut -d. -f1)"220  minor="$(printf '%s' "$zig_ver" | cut -d. -f2)"221  ok=0222  [ "$major" -ge 1 ] 2>/dev/null && ok=1223  [ "$major" -eq 0 ] 2>/dev/null && [ "$minor" -ge "$MIN_ZIG_MINOR" ] 2>/dev/null && ok=1224225  if [ "$ok" -ne 1 ]; then226    blank227    say "found zig ${zig_ver}, but nina requires Zig 0.${MIN_ZIG_MINOR}+."228    if is_nixos; then229      say "  run: nix-shell -p zig_0_16 --run 'sh install.sh'"230    else231      say "  update: https://ziglang.org/download/"232    fi233    blank234    die "Zig 0.${MIN_ZIG_MINOR}+ required; found ${zig_ver}"235  fi236237  say "zig ${zig_ver} found"238}239240# ── generic source build (non-NixOS) ─────────────────────────────────────────241242build_from_source() {243  say "building from source..."244  check_zig245246  if [ -f "build.zig" ] && [ -f "src/main.zig" ]; then247    srcdir="."248  else249    srcdir="$(fetch_source)"250  fi251252  (cd "$srcdir" && zig build -Doptimize=ReleaseSafe) \253    || die "build failed — see output above"254255  mkdir -p "$INSTALL_DIR"256  cp "${srcdir}/zig-out/bin/nina" "${INSTALL_DIR}/nina"257  chmod +x "${INSTALL_DIR}/nina"258  say "installed from source → ${INSTALL_DIR}/nina"259}260261# ── PATH reminder ─────────────────────────────────────────────────────────────262263check_path() {264  case ":${PATH}:" in265    *":${INSTALL_DIR}:"*) ;;266    *)267      blank268      say "${INSTALL_DIR} is not in your PATH. Add it:"269      say "  export PATH=\"\${PATH}:${INSTALL_DIR}\""270      say "  echo 'export PATH=\"\${PATH}:${INSTALL_DIR}\"' >> ~/.bashrc"271      if is_nixos; then272        say "  NixOS: add to environment.sessionVariables in configuration.nix"273      fi274      ;;275  esac276}277278# ── closing message ──────────────────────────────────────────────────────────279280farewell() {281  printf '\n'282  printf '  thank you!! running nina update occasionally will keep me in tip top shape.\n'283  printf '  get started by running nina hello\n'284  printf '\n'285  printf '  if nina turns out not to be for you, nina goodbye will remove her cleanly.\n'286  printf '\n'287}288289# ── main ──────────────────────────────────────────────────────────────────────290291banner292293if is_nixos; then294  say "NixOS detected"295  if nixos_profile_install; then296    generate_nixos_module297    farewell298    exit 0299  fi300  nixos_shell_build301  generate_nixos_module302  check_path303  farewell304  exit 0305fi306307# macOS / generic Linux: download binary, fall back to source build308if install_binary; then309  check_path310  farewell311  exit 0312fi313314say "no pre-built binary for ${platform} — falling back to source build"315build_from_source316check_path317farewell