#!/bin/sh # nudge installer: curl -fsSL https://nudge.reintelligence.live/install.sh | sh # # Installs the latest nudge into ~/.local/bin (no sudo needed). Run it again to upgrade. # NUDGE_VERSION=0.1.0 install that version instead of the latest # NUDGE_INSTALL_DIR= install into another folder # NUDGE_NO_MODIFY_PATH=1 don't add the folder to your shell profile # NUDGE_DOWNLOAD_URL= download from a mirror instead of nudge.reintelligence.live # # Everything runs from main() at the very end, so a partly downloaded script does nothing. set -eu say() { printf '%s\n' "$*"; } fail() { printf 'nudge install: %s\n' "$*" >&2; exit 1; } fetch() { curl -fsSL --retry 2 "$1" || fail "download failed: $1"; } tilde() { case $1 in "$HOME"/*) printf '~/%s' "${1#"$HOME"/}" ;; *) printf '%s' "$1" ;; esac; } # Runs on every exit. bash 3.2 (macOS's /bin/sh) exits 0 after a shell error such as an unbound # variable once a trap has run, and $? doesn't show it, so success is marked explicitly instead. cleanup() { if [ -n "${tmp:-}" ]; then rm -rf "$tmp"; fi if [ -z "${installed:-}" ]; then exit 1; fi } check_mac() { [ "$(uname -s)" = Darwin ] || fail "nudge runs on macOS only." # hw.optional.arm64 is 1 on Apple Silicon even when this shell runs under Rosetta. Full paths: # agents often run with a minimal PATH that leaves out /usr/sbin. [ "$(/usr/sbin/sysctl -n hw.optional.arm64 2>/dev/null || echo 0)" = 1 ] || fail "nudge needs a Mac with Apple Silicon (M1 or later)." macos=$(/usr/bin/sw_vers -productVersion) [ "${macos%%.*}" -ge 13 ] || fail "nudge needs macOS 13 or later; this Mac runs macOS $macos." } # Adds the install folder to PATH in the shell's profile, once. Prints what it did. add_to_path() { dir=$1 case ":$PATH:" in *":$dir:"*) return ;; esac [ -z "${NUDGE_NO_MODIFY_PATH:-}" ] || return 0 shown=$dir [ "$dir" = "$HOME/.local/bin" ] && shown='$HOME/.local/bin' case $(basename "${SHELL:-zsh}") in zsh) profile=${ZDOTDIR:-$HOME}/.zshrc; line="export PATH=\"$shown:\$PATH\"" ;; bash) profile=$HOME/.bash_profile; line="export PATH=\"$shown:\$PATH\"" ;; fish) profile=$HOME/.config/fish/conf.d/nudge.fish; line="fish_add_path \"$dir\"" ;; *) say "Add $(tilde "$dir") to your PATH to run nudge by name."; return ;; esac if grep -qsF "$line" "$profile"; then say "$(tilde "$profile") already adds $(tilde "$dir") to your PATH. Open a new terminal if nudge isn't found." return fi mkdir -p "$(dirname "$profile")" printf '\n# added by the nudge installer\n%s\n' "$line" >>"$profile" say "Added $(tilde "$dir") to your PATH in $(tilde "$profile"). Open a new terminal to use it." } main() { trap cleanup EXIT base=${NUDGE_DOWNLOAD_URL:-https://nudge.reintelligence.live} dir=${NUDGE_INSTALL_DIR:-$HOME/.local/bin} check_mac version=${NUDGE_VERSION:-$(fetch "$base/latest" | tr -d '[:space:]')} case $version in ''|*[!0-9.]*) fail "couldn't read the latest version from $base/latest" ;; esac name=nudge-$version-macos-arm64.tar.gz tmp=$(mktemp -d) # Brace variables next to non-ASCII text: macOS's /bin/sh (bash 3.2) in a UTF-8 locale reads # the first byte of "…" as part of the name, and set -u then stops with "unbound variable". say "Downloading nudge ${version}…" fetch "$base/$version/$name" >"$tmp/$name" fetch "$base/$version/$name.sha256" >"$tmp/$name.sha256" (cd "$tmp" && shasum -a 256 -c "$name.sha256" >/dev/null 2>&1) || fail "the download doesn't match its checksum; nothing was installed." tar -xzf "$tmp/$name" -C "$tmp" [ -f "$tmp/nudge" ] || fail "$name doesn't contain nudge; nothing was installed." previous= [ -x "$dir/nudge" ] && previous=$("$dir/nudge" --version 2>/dev/null | awk '{print $2}') || true # Builds can change without a new version number, so compare the binaries themselves. if [ -n "$previous" ] && cmp -s "$tmp/nudge" "$dir/nudge"; then installed=1 say "nudge ${version} is already up to date." add_to_path "$dir" return fi mkdir -p "$dir" 2>/dev/null && [ -w "$dir" ] || fail "can't write to $dir. Set NUDGE_INSTALL_DIR to a folder you own." # Move a fresh file into place instead of copying over the old one: macOS kills a signed # binary that was overwritten in place. cp "$tmp/nudge" "$dir/.nudge.new" chmod 755 "$dir/.nudge.new" xattr -d com.apple.quarantine "$dir/.nudge.new" 2>/dev/null || true mv -f "$dir/.nudge.new" "$dir/nudge" "$dir/nudge" --version >/dev/null 2>&1 || fail "installed $dir/nudge, but it doesn't run." installed=1 if [ -z "$previous" ]; then say "Installed nudge $version to $(tilde "$dir/nudge")." elif [ "$previous" = "$version" ]; then say "Updated nudge ${version} to the latest build in $(tilde "$dir/nudge")." else say "Upgraded nudge ${previous} → ${version} in $(tilde "$dir/nudge")." fi add_to_path "$dir" [ -n "$previous" ] || say "Try it: nudge -m \"Hello from nudge\"" } main "$@"