#!/bin/sh
# Install or upgrade DevDash, the local service behind app.devdash.pro.
#
#   curl -fsSL https://devdash.pro/install.sh | sh
#
# What it does, in order:
#   1. checks this is a Mac (Apple silicon or Intel);
#   2. downloads version.json, the universal binary and its signature from
#      devdash.pro, checks the binary's sha256 against version.json, and its
#      minisign signature against the release key below (with `minisign`
#      installed; without it, says so and relies on the checksum);
#   3. installs it to ~/.devdash/bin/devdash (no sudo), and offers a PATH line;
#   4. allows the app's origin in the service's config;
#   5. installs the LaunchAgents so the service and its menu bar item start at login, and starts them;
#   6. waits for it to answer, then opens the app with a one-time pairing code.
#
# Running it again upgrades in place. Settings and data are kept.
#
# Overrides, for testing a local build of the sites:
#   DEVDASH_SITE       where install.sh, /downloads/ live (default https://devdash.pro)
#   DEVDASH_APP        the app (default https://app.devdash.pro)
#   DEVDASH_PORT       the service's port (default 9999)
#   DEVDASH_NO_OPEN=1  do not open the browser at the end
set -eu

# Replaced with the serving site's origin when the site is built. Left as is,
# the official site is used.
BUILT_SITE="__DEVDASH_SITE__"
case "$BUILT_SITE" in
  __*) BUILT_SITE="https://devdash.pro" ;;
esac
SITE="${DEVDASH_SITE:-$BUILT_SITE}"
SITE="${SITE%/}"
APP="${DEVDASH_APP:-https://app.devdash.pro}"
APP="${APP%/}"
PORT="${DEVDASH_PORT:-9999}"
# The release signing key (the base64 line of minisign's release.pub); empty
# until the first signed release. See v9/scripts/release.sh.
RELEASE_PUBLIC_KEY="RWQPqZuPqUBPRtAGQl05fvIeJkfHH9QY7KaGKdLyt6+6X1Cl+ucf+/uF"
INSTALL_DIR="$HOME/.devdash/bin"
BIN="$INSTALL_DIR/devdash"

say() { printf '%s\n' "$*"; }
fail() { printf 'devdash install: %s\n' "$*" >&2; exit 1; }

# 1. This Mac.
[ "$(uname -s)" = "Darwin" ] || fail "DevDash runs on macOS only."
case "$(uname -m)" in
  arm64|x86_64) ;;
  *) fail "Unsupported processor: $(uname -m). DevDash needs Apple silicon or Intel." ;;
esac
command -v curl >/dev/null 2>&1 || fail "curl is required."
command -v shasum >/dev/null 2>&1 || fail "shasum is required."

WORK="$(mktemp -d)"
trap 'rm -rf "$WORK"' EXIT INT TERM

# 2. Download and verify.
say "Downloading DevDash from $SITE ..."
curl -fsSL "$SITE/downloads/version.json" -o "$WORK/version.json" \
  || fail "Could not download $SITE/downloads/version.json."
# version.json is one flat object written by scripts/release.sh, so a sed is enough.
field() { sed -n "s/.*\"$1\"[[:space:]]*:[[:space:]]*\"\([^\"]*\)\".*/\1/p" "$WORK/version.json" | head -n 1; }
VERSION="$(field version)"
EXPECTED="$(field sha256)"
[ -n "$VERSION" ] && [ -n "$EXPECTED" ] || fail "version.json does not name a version and a sha256."

if [ -x "$BIN" ]; then
  CURRENT="$("$BIN" --version 2>/dev/null | awk '{print $2}')" || CURRENT=""
  if [ "$CURRENT" = "$VERSION" ]; then
    say "DevDash $VERSION is already installed; reinstalling the service settings."
  else
    say "Upgrading DevDash ${CURRENT:-unknown} to $VERSION."
  fi
fi

curl -fsSL "$SITE/downloads/devdash-darwin-universal" -o "$WORK/devdash" \
  || fail "Could not download the DevDash binary."
ACTUAL="$(shasum -a 256 "$WORK/devdash" | awk '{print $1}')"
[ "$ACTUAL" = "$EXPECTED" ] || fail "The download does not match its published checksum (expected $EXPECTED, got $ACTUAL). Nothing was installed."
# The checksum lives on the same site as the binary, so it only catches a broken download. The
# signature is what proves the release: checked with minisign when it's installed.
VERIFIED=""
if [ -n "$RELEASE_PUBLIC_KEY" ]; then
  curl -fsSL "$SITE/downloads/devdash-darwin-universal.minisig" -o "$WORK/devdash.minisig" \
    || fail "Could not download the release signature. Nothing was installed."
  if command -v minisign >/dev/null 2>&1; then
    minisign -V -q -P "$RELEASE_PUBLIC_KEY" -m "$WORK/devdash" -x "$WORK/devdash.minisig" \
      || fail "The download's signature does not match the DevDash release key. Nothing was installed."
    VERIFIED=1
  else
    say "Note: install minisign (brew install minisign) to check the release signature; relying on the checksum only."
  fi
fi

# 3. Install. A rename within one directory is atomic, so a running service
# keeps its old image until it restarts below.
mkdir -p "$INSTALL_DIR"
chmod 755 "$WORK/devdash"
# Downloaded files may carry a quarantine flag. It is lifted only for a binary whose release
# signature verified; otherwise macOS's own checks still apply.
if [ -n "$VERIFIED" ]; then
  xattr -d com.apple.quarantine "$WORK/devdash" 2>/dev/null || true
fi
mv "$WORK/devdash" "$INSTALL_DIR/.devdash.new"
mv "$INSTALL_DIR/.devdash.new" "$BIN"
say "Installed $BIN ($VERSION)."

case ":$PATH:" in
  *":$INSTALL_DIR:"*) ;;
  *)
    PROFILE="$HOME/.zprofile"
    LINE="export PATH=\"\$HOME/.devdash/bin:\$PATH\""
    if [ -f "$PROFILE" ] && grep -qF '.devdash/bin' "$PROFILE"; then
      :
    elif [ -t 0 ] || [ -r /dev/tty ]; then
      printf 'Add %s to your PATH in %s? [Y/n] ' "$INSTALL_DIR" "$PROFILE"
      read -r ANSWER </dev/tty || ANSWER="n"
      case "$ANSWER" in
        n|N|no|No) say "Skipped. To use the devdash command, add: $LINE" ;;
        *) printf '\n# DevDash\n%s\n' "$LINE" >>"$PROFILE"; say "Added to $PROFILE. Open a new terminal to use the devdash command." ;;
      esac
    else
      say "To use the devdash command, add this to your shell profile: $LINE"
    fi
    ;;
esac

# 4. Allow the app to reach the service.
DEVDASH_PORT="$PORT" "$BIN" configure --site "$APP" --downloads "$SITE/downloads" >/dev/null \
  || fail "Could not write the service configuration."

# 5. Start at login, and now. Reinstalling the agents restarts the service on
# the new binary.
DEVDASH_PORT="$PORT" "$BIN" install || fail "Could not install the LaunchAgents."

# 6. Wait until it answers, then pair the browser.
say "Waiting for the service to start ..."
TRIES=0
until curl -fsS "http://127.0.0.1:$PORT/health" >/dev/null 2>&1; do
  TRIES=$((TRIES + 1))
  [ "$TRIES" -lt 60 ] || fail "The service did not start. See ~/Library/Logs/DevDash/devdash.log."
  sleep 0.5
done
say "DevDash $VERSION is running on port $PORT."

if [ "${DEVDASH_NO_OPEN:-0}" != "1" ]; then
  DEVDASH_PORT="$PORT" "$BIN" open || say "Open $APP and pair from there."
fi
