#!/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 "$@"