#!/bin/sh set -eu APP_NAME="evade" BASE_URL="${EVADE_INSTALL_BASE_URL:-https://evade.fail/cdn}" VERSION="${EVADE_VERSION:-latest}" INSTALL_DIR="${EVADE_INSTALL_DIR:-}" AUTO_DEPS="${EVADE_AUTO_DEPS:-0}" trim_trailing_slash() { value="$1" case "$value" in */) printf '%s' "${value%/}" ;; *) printf '%s' "$value" ;; esac } log() { printf '%s\n' "evade-install: $*" } warn() { printf '%s\n' "evade-install: warning: $*" >&2 } die() { printf '%s\n' "evade-install: error: $*" >&2 exit 1 } has_cmd() { command -v "$1" >/dev/null 2>&1 } is_truthy() { case "${1:-}" in 1|true|TRUE|yes|YES|on|ON) return 0 ;; *) return 1 ;; esac } detect_package_manager() { for pm in apt-get dnf yum pacman zypper apk xbps-install brew; do if has_cmd "$pm"; then printf '%s' "$pm" return 0 fi done return 1 } dependency_hint() { pm="$1" case "$pm" in apt-get) printf '%s\n' "Try: sudo apt-get update && sudo apt-get install -y curl tar ca-certificates coreutils" ;; dnf) printf '%s\n' "Try: sudo dnf install -y curl tar gzip ca-certificates coreutils" ;; yum) printf '%s\n' "Try: sudo yum install -y curl tar gzip ca-certificates coreutils" ;; pacman) printf '%s\n' "Try: sudo pacman -Sy --noconfirm curl tar gzip ca-certificates coreutils" ;; zypper) printf '%s\n' "Try: sudo zypper install -y curl tar gzip ca-certificates coreutils" ;; apk) printf '%s\n' "Try: sudo apk add --no-cache curl tar gzip ca-certificates coreutils openssl" ;; xbps-install) printf '%s\n' "Try: sudo xbps-install -Sy curl tar gzip ca-certificates coreutils" ;; brew) printf '%s\n' "Try: brew install curl gnu-tar coreutils openssl" ;; *) printf '%s\n' "Install curl, tar, and a SHA256 tool (sha256sum, shasum, or openssl), then rerun." ;; esac } run_with_optional_sudo() { if [ "$(id -u)" -eq 0 ]; then "$@" elif has_cmd sudo; then sudo "$@" else die "Need root privileges for dependency installation but sudo is unavailable." fi } install_dependencies() { pm="$1" case "$pm" in apt-get) run_with_optional_sudo apt-get update run_with_optional_sudo apt-get install -y curl tar ca-certificates coreutils ;; dnf) run_with_optional_sudo dnf install -y curl tar gzip ca-certificates coreutils ;; yum) run_with_optional_sudo yum install -y curl tar gzip ca-certificates coreutils ;; pacman) run_with_optional_sudo pacman -Sy --noconfirm curl tar gzip ca-certificates coreutils ;; zypper) run_with_optional_sudo zypper install -y curl tar gzip ca-certificates coreutils ;; apk) run_with_optional_sudo apk add --no-cache curl tar gzip ca-certificates coreutils openssl ;; xbps-install) run_with_optional_sudo xbps-install -Sy curl tar gzip ca-certificates coreutils ;; brew) brew install curl gnu-tar coreutils openssl ;; *) return 1 ;; esac return 0 } linux_distro_id() { if [ ! -r /etc/os-release ]; then return 1 fi while IFS='=' read -r key value; do if [ "$key" = "ID" ]; then value="${value#\"}" value="${value%\"}" printf '%s' "$value" return 0 fi done < /etc/os-release return 1 } required_tools_missing() { missing="" for cmd in curl tar mktemp; do if ! has_cmd "$cmd"; then missing="$missing $cmd" fi done if ! has_cmd sha256sum && ! has_cmd shasum && ! has_cmd openssl; then missing="$missing sha256" fi printf '%s' "$missing" } detect_os() { os_name="$(uname -s 2>/dev/null || true)" case "$os_name" in Linux) printf '%s' "linux" ;; Darwin) printf '%s' "darwin" ;; *) die "Unsupported operating system: $os_name" ;; esac } detect_arch() { arch_name="$(uname -m 2>/dev/null || true)" case "$arch_name" in x86_64|amd64) printf '%s' "x86_64" ;; arm64|aarch64) printf '%s' "aarch64" ;; *) die "Unsupported architecture: $arch_name" ;; esac } detect_libc() { if [ -r /etc/alpine-release ]; then printf '%s' "musl" return fi if has_cmd getconf && getconf GNU_LIBC_VERSION >/dev/null 2>&1; then printf '%s' "gnu" return fi if has_cmd ldd; then ldd_out="$(ldd --version 2>&1 || true)" case "$ldd_out" in *musl*) printf '%s' "musl" return ;; *glibc*|*GNU*) printf '%s' "gnu" return ;; esac fi printf '%s' "gnu" } sha256_of_file() { file_path="$1" if has_cmd sha256sum; then set -- $(sha256sum "$file_path") printf '%s' "$1" return fi if has_cmd shasum; then set -- $(shasum -a 256 "$file_path") printf '%s' "$1" return fi if has_cmd openssl; then set -- $(openssl dgst -sha256 "$file_path") printf '%s' "$2" return fi die "No SHA256 tool found." } expected_checksum_from_file() { asset_name="$1" checksums_file="$2" while IFS= read -r line; do case "$line" in ""|\#*) continue ;; esac set -- $line checksum="${1:-}" filename="${2:-}" case "$filename" in "$asset_name"|\*"$asset_name") printf '%s' "$checksum" return 0 ;; esac done < "$checksums_file" return 1 } choose_install_dir() { if [ -n "$INSTALL_DIR" ]; then printf '%s' "$INSTALL_DIR" return fi if [ -w /usr/local/bin ]; then printf '%s' "/usr/local/bin" return fi printf '%s' "$HOME/.local/bin" } ensure_dir_exists() { dir_path="$1" if [ -d "$dir_path" ]; then return fi if mkdir -p "$dir_path" 2>/dev/null; then return fi if has_cmd sudo; then sudo mkdir -p "$dir_path" return fi die "Cannot create install directory: $dir_path" } copy_binary() { src="$1" dst="$2" if cp "$src" "$dst" 2>/dev/null; then chmod 755 "$dst" return fi if has_cmd sudo; then sudo cp "$src" "$dst" sudo chmod 755 "$dst" return fi die "Cannot write $dst. Set EVADE_INSTALL_DIR to a writable path." } BASE_URL="$(trim_trailing_slash "$BASE_URL")" OS="$(detect_os)" ARCH="$(detect_arch)" if [ "$OS" = "linux" ]; then DISTRO="unknown" if distro_candidate="$(linux_distro_id 2>/dev/null)"; then DISTRO="$distro_candidate" fi PM="none" if pm_candidate="$(detect_package_manager 2>/dev/null)"; then PM="$pm_candidate" fi log "Detected linux distro=$DISTRO package-manager=$PM" fi missing_tools="$(required_tools_missing)" if [ -n "$missing_tools" ]; then log "Missing required tools:$missing_tools" pm_for_deps="" if pm_candidate="$(detect_package_manager 2>/dev/null)"; then pm_for_deps="$pm_candidate" fi if is_truthy "$AUTO_DEPS" && [ -n "$pm_for_deps" ]; then log "Attempting dependency installation via $pm_for_deps" install_dependencies "$pm_for_deps" || die "Dependency installation failed" missing_tools="$(required_tools_missing)" [ -z "$missing_tools" ] || die "Dependencies still missing:$missing_tools" else dependency_hint "$pm_for_deps" die "Install prerequisites or set EVADE_AUTO_DEPS=1" fi fi if [ "$OS" = "linux" ]; then LIBC="$(detect_libc)" TARGET="${ARCH}-unknown-linux-${LIBC}" else TARGET="${ARCH}-apple-darwin" fi ASSET_ROOT="${BASE_URL}/${VERSION}" CHECKSUMS_URL="${ASSET_ROOT}/checksums.txt" TMP_DIR="$(mktemp -d 2>/dev/null || mktemp -d -t evade-install)" cleanup() { rm -rf "$TMP_DIR" } trap cleanup 0 INT TERM HUP ARCHIVE_PATH="${TMP_DIR}/${APP_NAME}.tar.gz" CHECKSUMS_PATH="${TMP_DIR}/checksums.txt" ASSET_NAME="" PRIMARY_ASSET="${APP_NAME}_${TARGET}.tar.gz" FALLBACK_ASSET="" if [ "$VERSION" != "latest" ]; then FALLBACK_ASSET="${APP_NAME}_${VERSION}_${TARGET}.tar.gz" fi for candidate in "$PRIMARY_ASSET" "$FALLBACK_ASSET"; do [ -n "$candidate" ] || continue ARCHIVE_URL="${ASSET_ROOT}/${candidate}" log "Downloading ${ARCHIVE_URL}" if curl -fsSL "$ARCHIVE_URL" -o "$ARCHIVE_PATH"; then ASSET_NAME="$candidate" break fi done [ -n "$ASSET_NAME" ] || die "Failed to download installer asset for target ${TARGET}" curl -fsSL "$CHECKSUMS_URL" -o "$CHECKSUMS_PATH" || die "Failed to download checksums.txt" EXPECTED_CHECKSUM="$(expected_checksum_from_file "$ASSET_NAME" "$CHECKSUMS_PATH" || true)" [ -n "$EXPECTED_CHECKSUM" ] || die "Could not find checksum entry for ${ASSET_NAME}" ACTUAL_CHECKSUM="$(sha256_of_file "$ARCHIVE_PATH")" [ "$EXPECTED_CHECKSUM" = "$ACTUAL_CHECKSUM" ] || die "Checksum mismatch for ${ASSET_NAME}" tar -xzf "$ARCHIVE_PATH" -C "$TMP_DIR" || die "Failed to extract archive" BINARY_PATH="" for candidate in "$TMP_DIR/$APP_NAME" "$TMP_DIR/bin/$APP_NAME" "$TMP_DIR"/*/"$APP_NAME"; do if [ -f "$candidate" ]; then BINARY_PATH="$candidate" break fi done [ -n "$BINARY_PATH" ] || die "Archive did not contain ${APP_NAME} binary" TARGET_DIR="$(choose_install_dir)" TARGET_PATH="${TARGET_DIR}/${APP_NAME}" ensure_dir_exists "$TARGET_DIR" copy_binary "$BINARY_PATH" "$TARGET_PATH" log "Installed to ${TARGET_PATH}" case ":$PATH:" in *":$TARGET_DIR:"*) ;; *) warn "$TARGET_DIR is not on PATH. Add: export PATH=\"$TARGET_DIR:\$PATH\"" ;; esac log "Run '${APP_NAME} --version' to verify"