#!/bin/sh # Reviewed development-channel Linux/macOS installer (#12446/#12097). # Linux downloads are selected only from a signed exact-target index. The # fixed macOS package is authenticated by its pinned Developer ID Installer # identity plus Apple's notarization ticket. Registration handles are # non-secret routing metadata and are never transformed into an enrollment # credential by this script. set -eu BASE_URL=${ARIVARAN_DISTRIBUTION_BASE_URL:-https://get.arivaran.ai} CHANNEL=${ARIVARAN_DISTRIBUTION_CHANNEL:-development} RELEASE_BASE="$BASE_URL/channel/$CHANNEL/latest" RELEASE_KEY_SHA256=f50ead58c94c6bf7de0f4f44d3806f1b0690d2c2a22774af1b2b0bd4147eb312 TARGET=${1:-aagit} if [ "$#" -gt 0 ]; then shift; fi case "$TARGET" in *[!a-z0-9_]*|'') echo "ERROR: target must contain only lowercase letters, digits, and underscore" >&2 exit 2 ;; esac case "$(uname -s)" in Linux) PLATFORM=linux case "$(uname -m)" in x86_64|amd64) ARCH=x86_64 ;; aarch64|arm64) ARCH=aarch64 ;; *) echo "ERROR: unsupported Linux architecture: $(uname -m)" >&2; exit 3 ;; esac if command -v dpkg >/dev/null 2>&1; then EXT=.deb command -v apt-get >/dev/null 2>&1 || { echo "ERROR: Debian package resolution requires apt-get" >&2 exit 3 } elif command -v rpm >/dev/null 2>&1; then EXT=.rpm command -v dnf >/dev/null 2>&1 || { echo "ERROR: RPM package resolution requires dnf" >&2 exit 3 } else echo "ERROR: this Linux host has neither dpkg nor rpm" >&2 exit 3 fi HASH_TOOL=sha256sum ;; Darwin) PLATFORM=macos case "$(uname -m)" in arm64|aarch64) ARCH=arm64 ;; x86_64|amd64) ARCH=x86_64 ;; *) echo "ERROR: unsupported macOS architecture: $(uname -m)" >&2; exit 3 ;; esac MACOS_MAJOR=$(sw_vers -productVersion | awk -F. '{print $1}') case "$MACOS_MAJOR" in *[!0-9]*|'') echo "ERROR: cannot determine the macOS version" >&2; exit 3 ;; esac if [ "$MACOS_MAJOR" -lt 13 ]; then echo "ERROR: macOS 13 or newer is required; this host is unsupported" >&2 exit 3 fi EXT=.pkg HASH_TOOL=shasum ;; *) echo "ERROR: this entrypoint supports Linux and macOS; use install.ps1 on Windows." >&2 exit 3 ;; esac if [ "$PLATFORM" = macos ] && { [ "$TARGET" != aagit ] || [ "$ARCH" != arm64 ]; }; then echo "ERROR: target '$TARGET' is unavailable for macos/$ARCH ($EXT)" >&2 exit 5 fi for tool in curl awk grep "$HASH_TOOL"; do command -v "$tool" >/dev/null 2>&1 || { echo "ERROR: required tool is missing: $tool" >&2 exit 3 } done if [ "$PLATFORM" = linux ]; then command -v openssl >/dev/null 2>&1 || { echo "ERROR: required tool is missing: openssl" >&2 exit 3 } else for tool in installer pkgutil spctl; do command -v "$tool" >/dev/null 2>&1 || { echo "ERROR: required macOS tool is missing: $tool" >&2 exit 3 } done fi if [ "$(id -u)" -ne 0 ]; then command -v sudo >/dev/null 2>&1 || { echo "ERROR: administrator installation requires sudo" >&2 exit 3 } fi sha256_file() { if [ "$HASH_TOOL" = sha256sum ]; then sha256sum "$1" | awk '{print $1}' else shasum -a 256 "$1" | awk '{print $1}' fi } TMP=$(mktemp -d "${TMPDIR:-/tmp}/arivaran-install.XXXXXX") trap 'rm -rf "$TMP"' EXIT HUP INT TERM fetch() { curl --fail --silent --show-error --location --proto '=https' --tlsv1.2 \ "$RELEASE_BASE/$1" --output "$TMP/$1" } if [ "$PLATFORM" = linux ]; then fetch release-ed25519.pub.pem ACTUAL_KEY_SHA256=$(sha256_file "$TMP/release-ed25519.pub.pem") if [ "$ACTUAL_KEY_SHA256" != "$RELEASE_KEY_SHA256" ]; then echo "ERROR: release verification key digest mismatch" >&2 exit 4 fi fetch release-index.tsv fetch release-index.tsv.sig openssl pkeyutl -verify -pubin -rawin \ -inkey "$TMP/release-ed25519.pub.pem" \ -in "$TMP/release-index.tsv" -sigfile "$TMP/release-index.tsv.sig" \ >/dev/null ROW=$(awk -F '\t' -v target="$TARGET" -v arch="$ARCH" -v ext="$EXT" ' NR > 1 && $1 == target && $3 == "linux" && $4 == arch && index($5, ext) == length($5)-length(ext)+1 { print $2 "\t" $5 "\t" $6 "\t" $7 count++ } END { if (count != 1) exit 2 } ' "$TMP/release-index.tsv") || { echo "ERROR: target '$TARGET' is unavailable for linux/$ARCH ($EXT)" >&2 exit 5 } INSTALL_NAME=$(printf '%s\n' "$ROW" | awk -F '\t' '{print $1}') ARTIFACT=$(printf '%s\n' "$ROW" | awk -F '\t' '{print $2}') EXPECTED_SHA256=$(printf '%s\n' "$ROW" | awk -F '\t' '{print $3}') SIGNATURE=$(printf '%s\n' "$ROW" | awk -F '\t' '{print $4}') fetch "$ARTIFACT" fetch "$SIGNATURE" ACTUAL_SHA256=$(sha256_file "$TMP/$ARTIFACT") if [ "$ACTUAL_SHA256" != "$EXPECTED_SHA256" ]; then echo "ERROR: package checksum mismatch" >&2 exit 4 fi openssl pkeyutl -verify -pubin -rawin \ -inkey "$TMP/release-ed25519.pub.pem" \ -in "$TMP/$ARTIFACT" -sigfile "$TMP/$SIGNATURE" >/dev/null else INSTALL_NAME=aagit ARTIFACT=arivaran-aagit.pkg fetch "$ARTIFACT" ACTUAL_SHA256=$(sha256_file "$TMP/$ARTIFACT") fi as_root() { if [ "$(id -u)" -eq 0 ]; then "$@"; else sudo "$@"; fi } if [ "$EXT" = .deb ]; then PACKAGE_NAME=$(dpkg-deb -f "$TMP/$ARTIFACT" Package) case "$PACKAGE_NAME" in *[!a-z0-9+.-]*|'') echo "ERROR: invalid DEB package identity" >&2; exit 6;; esac APT_REINSTALL= if dpkg-query -W -f='${Status}' "$PACKAGE_NAME" 2>/dev/null | grep -q '^install ok installed$'; then APT_REINSTALL=--reinstall fi if [ "$(id -u)" -eq 0 ]; then ARIVARAN_DEFER_ACTIVATION=1 DEBIAN_FRONTEND=noninteractive \ apt-get install $APT_REINSTALL -y "$TMP/$ARTIFACT" else sudo env ARIVARAN_DEFER_ACTIVATION=1 DEBIAN_FRONTEND=noninteractive \ apt-get install $APT_REINSTALL -y "$TMP/$ARTIFACT" fi elif [ "$EXT" = .rpm ]; then PACKAGE_NAME=$(rpm -qp --qf '%{NAME}' "$TMP/$ARTIFACT") case "$PACKAGE_NAME" in *[!A-Za-z0-9+._-]*|'') echo "ERROR: invalid RPM package identity" >&2; exit 6;; esac DNF_ACTION=install CANDIDATE_NEVRA=$(rpm -qp --qf '%{VERSION}-%{RELEASE}.%{ARCH}' "$TMP/$ARTIFACT") if rpm -q "$PACKAGE_NAME" >/dev/null 2>&1; then INSTALLED_NEVRA=$(rpm -q --qf '%{VERSION}-%{RELEASE}.%{ARCH}' "$PACKAGE_NAME") if [ "$INSTALLED_NEVRA" = "$CANDIDATE_NEVRA" ]; then DNF_ACTION=reinstall; fi fi if [ "$(id -u)" -eq 0 ]; then ARIVARAN_DEFER_ACTIVATION=1 dnf "$DNF_ACTION" -y "$TMP/$ARTIFACT" else sudo env ARIVARAN_DEFER_ACTIVATION=1 dnf "$DNF_ACTION" -y "$TMP/$ARTIFACT" fi else SIGNATURE_INFO=$(LC_ALL=C pkgutil --check-signature "$TMP/$ARTIFACT" 2>&1) || { printf '%s\n' "$SIGNATURE_INFO" >&2 echo "ERROR: macOS package signature validation failed" >&2 exit 4 } printf '%s\n' "$SIGNATURE_INFO" | grep -F "Developer ID Installer: Arivaran.ai Inc. (Y8B4DV39QC)" >/dev/null || { echo "ERROR: macOS package signer identity mismatch" >&2 exit 4 } spctl --assess --type install "$TMP/$ARTIFACT" echo "Verified package SHA-256: $ACTUAL_SHA256" if [ "$(id -u)" -eq 0 ]; then ARIVARAN_DEFER_ACTIVATION=1 installer -pkg "$TMP/$ARTIFACT" -target / else sudo env ARIVARAN_DEFER_ACTIVATION=1 \ installer -pkg "$TMP/$ARTIFACT" -target / fi fi WRAPPER="/usr/local/bin/$TARGET" if [ "$PLATFORM" = macos ]; then AGENT="/Library/Application Support/Arivaran/installer/$INSTALL_NAME/aaagent" else AGENT="/opt/arivaran/installer/$INSTALL_NAME/aaagent" fi as_root ln -sfn "$AGENT" /usr/local/bin/aasetup if [ "$PLATFORM" = macos ]; then # Installer.app/package scripts run as root and do not reliably inherit the # caller's ARIVARAN_DEFER_ACTIVATION value. The PKG postinstall may therefore # have already replaced the packaged wrapper with the installed aaGit # runtime. Dispatch through the immutable package-owned agent path so # bootstrap flags are never forwarded to the Git-compatible runtime. if [ ! -x "$AGENT" ]; then echo "ERROR: installed package did not provide $AGENT" >&2 exit 6 fi as_root "$AGENT" --installtime --install-name "$INSTALL_NAME" "$@" else if [ ! -x "$WRAPPER" ]; then echo "ERROR: installed package did not provide $WRAPPER" >&2 exit 6 fi "$WRAPPER" "$@" fi echo "Installed Arivaran target '$INSTALL_NAME'."