#!/bin/sh
# SecretServer.io CLI (ss) installer
#
#   curl -fsSL https://secretserver.io/install | sh
#
# Downloads the release archive for this OS/arch from GitHub over HTTPS,
# verifies its SHA-256 against the release's checksums file, and installs
# the `ss` binary. It never runs any other downloaded script, and it fails
# closed: no checksum, or a mismatch, means nothing is installed.
#
# Environment (all optional):
#   SS_VERSION       version to install, e.g. 1.0.0 (default: latest release)
#   SS_INSTALL_DIR   target directory (default: /usr/local/bin if writable,
#                    otherwise ~/.local/bin)
#   SS_RELEASES_URL  releases base URL, must be https://
#                    (default: https://github.com/afterdarksys/secretserver-cli/releases)
#
# Everything runs inside main() so a truncated download executes nothing.

set -eu

REPO="afterdarksys/secretserver-cli"
BINARY="ss"

say() { printf '%s\n' "$*"; }
die() { printf 'ss installer: error: %s\n' "$*" >&2; exit 1; }

# HTTPS only, including redirects; TLS 1.2+; fail on HTTP errors.
fetch() {
  curl --proto '=https' --proto-redir '=https' --tlsv1.2 -fsSL --retry 2 "$@"
}

sha256_of() {
  if command -v sha256sum >/dev/null 2>&1; then
    sha256sum "$1" | awk '{print $1}'
  elif command -v shasum >/dev/null 2>&1; then
    shasum -a 256 "$1" | awk '{print $1}'
  else
    die "need sha256sum or shasum to verify the download"
  fi
}

main() {
  command -v curl >/dev/null 2>&1 || die "curl is required"
  command -v tar >/dev/null 2>&1 || die "tar is required"

  base="${SS_RELEASES_URL:-https://github.com/$REPO/releases}"
  base="${base%/}"
  case "$base" in
    https://*) ;;
    *) die "SS_RELEASES_URL must start with https:// (got: $base)" ;;
  esac

  os="$(uname -s | tr '[:upper:]' '[:lower:]')"
  arch="$(uname -m)"
  case "$arch" in
    x86_64|amd64) arch="amd64" ;;
    aarch64|arm64) arch="arm64" ;;
    *) die "unsupported architecture: $arch" ;;
  esac
  case "$os" in
    linux|darwin) ;;
    *) die "unsupported OS: $os (Windows: download the .zip from $base)" ;;
  esac

  version="${SS_VERSION:-}"
  if [ -z "$version" ]; then
    say "Resolving latest release from $base ..."
    # /releases/latest redirects to /releases/tag/vX.Y.Z; read the final URL.
    final="$(fetch -o /dev/null -w '%{url_effective}' "$base/latest")" \
      || die "could not resolve the latest release at $base/latest (has a release been published?)"
    version="${final##*/}"
  fi
  version="${version#v}"
  # Only plain semver reaches a URL or a file name. The case rejects any
  # character outside the set (newlines included, which grep would split on).
  case "$version" in
    *[!0-9A-Za-z.-]*|'') die "could not determine a valid version (got: '$version'). See $base" ;;
  esac
  if ! printf '%s' "$version" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?$'; then
    die "could not determine a valid version (got: '$version'). See $base"
  fi

  archive="${BINARY}_${version}_${os}_${arch}.tar.gz"
  sums="${BINARY}_${version}_checksums.txt"
  url="$base/download/v$version"

  tmp="$(mktemp -d 2>/dev/null || mktemp -d -t ss-install)"
  trap 'rm -rf "$tmp"' EXIT
  trap 'exit 1' INT TERM HUP

  say "Downloading ss v$version ($os/$arch) ..."
  fetch -o "$tmp/$sums" "$url/$sums" || die "could not download $url/$sums"
  fetch -o "$tmp/$archive" "$url/$archive" || die "could not download $url/$archive"

  expected="$(awk -v f="$archive" '$2 == f || $2 == "*" f { print $1; exit }' "$tmp/$sums" | tr '[:upper:]' '[:lower:]')"
  printf '%s' "$expected" | grep -Eq '^[0-9a-f]{64}$' \
    || die "no SHA-256 for $archive in $sums; refusing to install"
  actual="$(sha256_of "$tmp/$archive")"
  [ "$actual" = "$expected" ] \
    || die "checksum mismatch for $archive (expected $expected, got $actual); refusing to install"
  say "Checksum verified (sha256 $actual)."

  mkdir "$tmp/x"
  tar -xzf "$tmp/$archive" -C "$tmp/x" "$BINARY" || die "archive does not contain $BINARY"
  if [ ! -f "$tmp/x/$BINARY" ] || [ -L "$tmp/x/$BINARY" ]; then
    die "archive entry $BINARY is not a regular file"
  fi

  dir="${SS_INSTALL_DIR:-}"
  if [ -z "$dir" ]; then
    if [ -d /usr/local/bin ] && [ -w /usr/local/bin ]; then
      dir="/usr/local/bin"
    else
      dir="$HOME/.local/bin"
      say "/usr/local/bin is not writable; installing to $dir instead."
      say "(For a system-wide install, re-run with sudo or move the binary afterwards.)"
    fi
  fi
  mkdir -p "$dir" || die "cannot create $dir"
  [ -w "$dir" ] || die "$dir is not writable; set SS_INSTALL_DIR or re-run with sudo"

  # Stage in a fresh unpredictable file beside the target (mktemp creates it
  # exclusively, so a pre-planted symlink is never followed), then rename so
  # an existing ss is never half-written.
  stage="$(mktemp "$dir/.$BINARY.XXXXXX")" || die "cannot create a temporary file in $dir"
  if ! cat "$tmp/x/$BINARY" > "$stage" || ! chmod 0755 "$stage" || ! mv -f "$stage" "$dir/$BINARY"; then
    rm -f "$stage"
    die "could not install to $dir/$BINARY"
  fi

  say ""
  say "ss v$version installed to $dir/$BINARY"
  case ":$PATH:" in
    *":$dir:"*) ;;
    *) say "Note: $dir is not on your PATH. Add it, e.g.: export PATH=\"$dir:\$PATH\"" ;;
  esac
  say ""
  say "Get started:"
  say "  ss login           # sign in with your organization's SSO"
  say "  ss secrets --help"
  say ""
  say "Docs: https://secretserver.io/docs/install"
}

main "$@"
