#!/bin/sh # Installs Research Vault for Mac from eggnomic.com: # # curl -fsSL https://eggnomic.com/research-vault/install.sh | sh # # It reads the selected release from release.json, downloads that DMG, checks its size and SHA-256, # copies Research Vault into /Applications (or ~/Applications if /Applications is not writable) and # opens it. Nothing else on your Mac is changed. To remove it, drag Research Vault to the Trash. # # A download made with curl is not marked as coming from the internet, so macOS does not show its # first-launch warning for this alpha build. Read this file before running it if you want to: # curl -fsSL https://eggnomic.com/research-vault/install.sh | less # # Everything runs inside main, so a partly downloaded script never runs. set -eu main() { base="${RV_BASE:-https://eggnomic.com/research-vault}" apps="${RV_APPS_DIR:-/Applications}" app_name="Research Vault.app" # Colour and motion only when someone is watching a terminal; plain lines in logs and CI. fancy= if [ -t 1 ] && [ "${TERM:-dumb}" != dumb ] && [ -z "${NO_COLOR:-}" ]; then fancy=1; fi if [ -n "$fancy" ]; then Y=$(printf '\033[38;5;221m') G=$(printf '\033[38;5;149m'); D=$(printf '\033[38;5;239m'); R=$(printf '\033[0m') else Y= G= D= R= fi say() { printf '%s\n' "$*"; } ok() { printf '%s✓%s %s\n' "$G" "$R" "$*"; } fail() { printf 'Research Vault: %s\n' "$*" >&2; exit 1; } dl= [ "$(uname -s)" = Darwin ] || fail "this installer is for macOS." major=$(sw_vers -productVersion | cut -d. -f1) [ "$major" -ge 14 ] || fail "macOS 14 or later is required (this Mac has $(sw_vers -productVersion))." work=$(mktemp -d "${TMPDIR:-/tmp}/research-vault.XXXXXX") mount="$work/mount" cleanup() { status=$? [ -n "$dl" ] && kill "$dl" 2>/dev/null [ -n "$fancy" ] && printf '\033[?25h' [ -d "$mount" ] && hdiutil detach -quiet "$mount" >/dev/null 2>&1 || true rm -rf "$work" exit "$status" } trap cleanup EXIT trap 'exit 130' INT TERM say "Checking the latest Research Vault…" curl -fsSL "$base/release.json" -o "$work/release.json" || fail "couldn't reach $base/release.json." field() { plutil -extract "$1" raw -o - "$work/release.json" 2>/dev/null || fail "release.json is missing $1."; } version=$(field version) file=$(field artifacts.dmg.file) sha=$(field artifacts.dmg.sha256) bytes=$(field artifacts.dmg.bytes) case "$file" in */* | .* | "" ) fail "unexpected file name in release.json: $file" ;; *.dmg) ;; *) fail "unexpected file name in release.json: $file" ;; esac label="Downloading Research Vault ${version}…" if [ -n "$fancy" ]; then # A yellow bar, drawn from the bytes on disk against the size in release.json. bar() { n=$(( $1 * 24 / bytes )); [ "$n" -gt 24 ] && n=24 printf '\r%s %s%s%s%s%s' "$label" "$Y" "$(printf '%*s' "$n" '' | sed 's/ /━/g')" \ "$D" "$(printf '%*s' $((24 - n)) '' | sed 's/ /━/g')" "$R" } printf '\033[?25l' curl -fsSL "$base/releases/$file" -o "$work/$file" & dl=$! while kill -0 "$dl" 2>/dev/null; do bar "$(stat -f %z "$work/$file" 2>/dev/null || echo 0)" sleep 0.08 done wait "$dl" || { dl=; printf '\n'; fail "download failed."; } dl= bar "$bytes" printf '\033[?25h\n' else say "$label" curl -fsSL "$base/releases/$file" -o "$work/$file" || fail "download failed." fi [ "$(stat -f %z "$work/$file")" = "$bytes" ] || fail "the download is the wrong size; try again." [ "$(shasum -a 256 "$work/$file" | cut -d' ' -f1)" = "$sha" ] || fail "the download's checksum doesn't match; nothing was installed." ok "Verified the download" mkdir -p "$mount" hdiutil attach -quiet -nobrowse -readonly -mountpoint "$mount" "$work/$file" || fail "couldn't open the disk image." [ -d "$mount/$app_name" ] || fail "the disk image doesn't contain $app_name." if [ ! -w "$apps" ]; then apps="$HOME/Applications" mkdir -p "$apps" fi if pgrep -xq "Research Vault"; then say "Quitting the running Research Vault…" osascript -e 'quit app "Research Vault"' >/dev/null 2>&1 || true sleep 2 fi rm -rf "$apps/$app_name.new" ditto "$mount/$app_name" "$apps/$app_name.new" rm -rf "$apps/$app_name" mv "$apps/$app_name.new" "$apps/$app_name" ok "Installed in $apps" say "Opening Research Vault. It will walk you through setup." [ -n "${RV_NO_OPEN:-}" ] || open "$apps/$app_name" } main "$@"