123456set -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=161213141516if [ -t 1 ] && [ -z "${NO_COLOR:-}" ]; then17 PINK="$(printf '\033[38;2;255;183;197m')"18 CYAN="$(printf '\033[36m')"19 RED="$(printf '\033[31m')"20 DIM="$(printf '\033[2m')"21 RESET="$(printf '\033[0m')"22else23 PINK=""; CYAN=""; RED=""; DIM=""; RESET=""24fi2526os="$(uname -s)"27arch="$(uname -m)"28case "${os}-${arch}" in29 Linux-x86_64) platform="linux-x86_64" ;;30 Linux-aarch64|Linux-arm64) platform="linux-aarch64" ;;31 Darwin-arm64) platform="macos-aarch64" ;;32 Darwin-x86_64) platform="macos-x86_64" ;;33 *)34 printf 'nina: unsupported platform %s-%s\n' "$os" "$arch" >&235 exit 1 ;;36esac3738have() { command -v "$1" >/dev/null 2>&1; }39say() { printf '%snina:%s %s\n' "$PINK" "$RESET" "$1"; }40die() { printf '%snina:%s %serror:%s %s\n' "$PINK" "$RESET" "$RED" "$RESET" "$1" >&2; exit 1; }41blank() { printf '\n'; }42is_nixos() { [ -e /etc/NIXOS ]; }4344fetch_text() {45 if have curl; then curl -fsSL "$1"46 elif have wget; then wget -qO- "$1"47 else die "curl or wget required"; fi48}4950fetch_file() {51 if have curl; then curl -fsSL "$1" -o "$2"52 elif have wget; then wget -qO "$2" "$1"53 else die "curl or wget required"; fi54}55565758banner() {59 cat <<'BANNER'6061 _ __ _ _ __ __ _62 | '_ \ (_)| '_ \ / _` |63 | | | || || | | || (_| |64 |_| |_||_||_| |_| \__,_|6566 NixOS Intuitive Navigation Assistant · kepr.uk/nina6768BANNER69}70717273fetch_source() {74 75 76 77 srcdir="$(mktemp -d /tmp/nina-src.XXXXXX)"78 say "fetching source from Kepr..." >&279 fetch_file "$FLAKE_URL" "${srcdir}/nina.tar.gz" \80 || die "could not download source archive — check your connection"81 tar -xzf "${srcdir}/nina.tar.gz" -C "$srcdir" --strip-components=1 \82 || die "could not extract source archive"83 printf '%s' "$srcdir"84}85868788install_binary() {89 say "fetching release metadata..."90 meta="$(fetch_text "$RELEASES_URL" 2>/dev/null)" || return 191 [ -n "$meta" ] || return 19293 if have python3; then94 version="$(printf '%s' "$meta" | python3 -c "95import sys, json96d = json.load(sys.stdin)97print(d.get('version') or '')98" 2>/dev/null)"99 dl_url="$(printf '%s' "$meta" | python3 -c "100import sys, json101d = json.load(sys.stdin)102print((d.get('platforms') or {}).get('${platform}', {}).get('url') or '')103" 2>/dev/null)"104 cksum="$(printf '%s' "$meta" | python3 -c "105import sys, json106d = json.load(sys.stdin)107p = (d.get('platforms') or {}).get('${platform}', {})108raw = p.get('blake3') or p.get('checksum') or ''109print(raw[len('blake3:'):] if raw.startswith('blake3:') else raw)110" 2>/dev/null)"111 elif have jq; then112 version="$(printf '%s' "$meta" | jq -r '.version // empty')"113 dl_url="$(printf '%s' "$meta" | jq -r ".platforms.\"${platform}\".url // empty")"114 cksum_raw="$(printf '%s' "$meta" | jq -r ".platforms.\"${platform}\".blake3 // .platforms.\"${platform}\".checksum // empty")"115 cksum="${cksum_raw#blake3:}"116 else117 version="$(printf '%s' "$meta" | grep -o '"version":"[^"]*"' | sed 's/"version":"//;s/"//')"118 plat_block="$(printf '%s' "$meta" | grep -o "\"${platform}\":{[^}]*}" | head -1)"119 dl_url="$(printf '%s' "$plat_block" | grep -o '"url":"[^"]*"' | sed 's/"url":"//;s/"//')"120 cksum=""121 fi122123 [ -n "$dl_url" ] || return 1124 [ -n "$version" ] || version="latest"125126 say "downloading nina ${version} for ${platform}..."127 tmpfile="$(mktemp /tmp/nina.XXXXXX)"128 fetch_file "$dl_url" "$tmpfile" || { rm -f "$tmpfile"; return 1; }129130 if [ -n "$cksum" ] && have b3sum; then131 actual="$(b3sum --no-names "$tmpfile" 2>/dev/null | awk '{print $1}')"132 if [ "$actual" != "$cksum" ]; then133 say "checksum mismatch — aborting"134 rm -f "$tmpfile"135 return 1136 fi137 say "checksum verified"138 fi139140 chmod +x "$tmpfile"141 mkdir -p "$INSTALL_DIR"142 mv "$tmpfile" "${INSTALL_DIR}/nina"143 say "installed nina ${version} → ${INSTALL_DIR}/nina"144 return 0145}146147148149nixos_profile_install() {150 have nix || return 1151 say "installing nina to ~/.nix-profile/bin/nina via nix profile..."152 blank153 154 155 156 157 158 if nix profile add --no-write-lock-file "${FLAKE_URL}#nina"; then159 return 0160 fi161 blank162 say "nix profile add failed. Common causes:"163 say " flakes not enabled — add to /etc/nix/nix.conf or ~/.config/nix/nix.conf:"164 say " experimental-features = nix-command flakes"165 say " once nina is installed: nina setup enables this automatically"166 blank167 return 1168}169170171172nixos_shell_build() {173 have nix-shell || return 1174 say "fetching source and building nina using nix-shell..."175 blank176 srcdir="$(fetch_source)"177178 179 180 build_cmd="cd '${srcdir}' && zig build -Doptimize=ReleaseSafe"181 if nix-shell -p zig_0_16 --run "$build_cmd" 2>/dev/null; then182 :183 else184 say "zig_0_16 not in channel — fetching from nixpkgs-unstable..."185 186 187 nix-shell \188 -I nixpkgs=https://channels.nixos.org/nixos-unstable/nixexprs.tar.xz \189 -p zig_0_16 \190 --run "$build_cmd" \191 || die "build failed — see output above"192 fi193194 mkdir -p "$INSTALL_DIR"195 cp "${srcdir}/zig-out/bin/nina" "${INSTALL_DIR}/nina"196 chmod +x "${INSTALL_DIR}/nina"197 blank198 say "installed from source → ${INSTALL_DIR}/nina"199 say "note: per-user binary, not nix-managed (nina goodbye removes it cleanly)"200}201202203204check_zig() {205 if ! have zig; then206 blank207 say "Zig not found. Building nina requires Zig 0.${MIN_ZIG_MINOR}+."208 blank209 say " Download: https://ziglang.org/download/"210 if is_nixos; then211 say " NixOS: nix-shell -p zig_0_16 --run 'sh install.sh'"212 fi213 blank214 die "Zig 0.${MIN_ZIG_MINOR}+ required"215 fi216217 zig_ver="$(zig version 2>/dev/null)"218 major="$(printf '%s' "$zig_ver" | cut -d. -f1)"219 minor="$(printf '%s' "$zig_ver" | cut -d. -f2)"220 ok=0221 [ "$major" -ge 1 ] 2>/dev/null && ok=1222 [ "$major" -eq 0 ] 2>/dev/null && [ "$minor" -ge "$MIN_ZIG_MINOR" ] 2>/dev/null && ok=1223224 if [ "$ok" -ne 1 ]; then225 blank226 say "found zig ${zig_ver}, but nina requires Zig 0.${MIN_ZIG_MINOR}+."227 if is_nixos; then228 say " run: nix-shell -p zig_0_16 --run 'sh install.sh'"229 else230 say " update: https://ziglang.org/download/"231 fi232 blank233 die "Zig 0.${MIN_ZIG_MINOR}+ required; found ${zig_ver}"234 fi235236 say "zig ${zig_ver} found"237}238239240241build_from_source() {242 say "building from source..."243 check_zig244245 if [ -f "build.zig" ] && [ -f "src/main.zig" ]; then246 srcdir="."247 else248 srcdir="$(fetch_source)"249 fi250251 (cd "$srcdir" && zig build -Doptimize=ReleaseSafe) \252 || die "build failed — see output above"253254 mkdir -p "$INSTALL_DIR"255 cp "${srcdir}/zig-out/bin/nina" "${INSTALL_DIR}/nina"256 chmod +x "${INSTALL_DIR}/nina"257 say "installed from source → ${INSTALL_DIR}/nina"258}259260261262check_path() {263 case ":${PATH}:" in264 *":${INSTALL_DIR}:"*) ;;265 *)266 blank267 say "${INSTALL_DIR} is not in your PATH. Add it:"268 say " export PATH=\"\${PATH}:${INSTALL_DIR}\""269 say " echo 'export PATH=\"\${PATH}:${INSTALL_DIR}\"' >> ~/.bashrc"270 if is_nixos; then271 say " NixOS: add to environment.sessionVariables in configuration.nix"272 fi273 ;;274 esac275}276277278279280281282maybe_setup_features() {283 have nix || return 0284 285 nix eval --expr '1' >/dev/null 2>&1 && return 0286287 NINA="${INSTALL_DIR}/nina"288 [ -x "$NINA" ] || NINA="nina"289290 blank291 say "nina needs nix's experimental features (nix-command and flakes) to"292 say "search and install packages."293294 if [ -r /dev/tty ]; then295 printf 'nina: enable them now? [Y/n] ' > /dev/tty296 IFS= read -r ans < /dev/tty 2>/dev/null || ans="y"297 case "$ans" in298 [Nn]*) say "skipped — enable later with: nina setup"; return 0 ;;299 esac300 "$NINA" setup || say "automatic setup failed — run: nina setup"301 else302 say "run nina setup to enable them"303 fi304}305306307308farewell() {309 blank310 printf ' %sthank you!!%s get started by running %snina hello%s\n' "$PINK" "$RESET" "$CYAN" "$RESET"311 printf ' run %snina update%s occasionally to keep me in tip-top shape\n' "$CYAN" "$RESET"312 blank313 printf ' %snot for you?%s %snina goodbye%s %sremoves her cleanly%s\n' "$DIM" "$RESET" "$CYAN" "$RESET" "$DIM" "$RESET"314 blank315}316317318319banner320321if is_nixos; then322 say "NixOS detected"323 if nixos_profile_install; then324 maybe_setup_features325 farewell326 exit 0327 fi328 329 330 say "trying prebuilt binary from Kepr..."331 if install_binary; then332 check_path333 maybe_setup_features334 farewell335 exit 0336 fi337 say "no prebuilt binary available — building from source"338 nixos_shell_build339 check_path340 maybe_setup_features341 farewell342 exit 0343fi344345346if install_binary; then347 check_path348 maybe_setup_features349 farewell350 exit 0351fi352353say "no pre-built binary for ${platform} — falling back to source build"354build_from_source355check_path356maybe_setup_features357farewell