#!/usr/bin/env bash source ./tests/download_dependencies.sh export AWS_ARCHIVE_NAME="awscliv2.zip" DOWNLOAD_ONLY_FOLDER="" INSTALL_ONLY_FOLDER="" show_help() { echo "Usage: $0 [--download-only ] [--install-only ]" echo " --download-only Download dependency artifacts into " echo " --install-only Install dependency artifacts from " } parse_args() { while [ "$#" -gt 0 ]; do case "$1" in -h|--help) show_help exit 0 ;; --download-only) if [ -z "$2" ]; then echo "error: --download-only requires a folder argument" >&2 return 1 fi DOWNLOAD_ONLY_FOLDER="$2" shift 2 ;; --install-only) if [ -z "$2" ]; then echo "error: --install-only requires a folder argument" >&2 return 1 fi INSTALL_ONLY_FOLDER="$2" shift 2 ;; *) echo "error: unknown argument '$1'" >&2 show_help >&2 return 1 ;; esac done if [ -n "$DOWNLOAD_ONLY_FOLDER" ] && [ -n "$INSTALL_ONLY_FOLDER" ]; then echo "error: --download-only and --install-only cannot be used together" >&2 return 1 fi return 0 } pre_install_report() { local os arch cwd detected_pkg_manager xcode_clt cmd os=$(uname -s) arch=$(uname -m) cwd=$(pwd) detected_pkg_manager="$(detect_linux_package_manager)" echo "=== Pre-install report ===" printf "OS: %s\n" "$os" printf "Architecture: %s\n" "$arch" printf "Working directory: %s\n" "$cwd" printf "Current user: %s (uid=%s)\n" "$(id -un)" "$(id -u)" printf "Root user: %s\n" "$(is_root && echo yes || echo no)" printf "CI: %s\n" "${CI:-false}" printf "PATH: %s\n" "$PATH" printf "Home: %s\n" "$HOME" printf "Download-only folder: %s\n" "${DOWNLOAD_ONLY_FOLDER:-unset}" printf "Install-only folder: %s\n" "${INSTALL_ONLY_FOLDER:-unset}" if [ "$os" = "Linux" ]; then printf "Detected Linux package manager: %s\n" "$detected_pkg_manager" elif [ "$os" = "Darwin" ]; then if command -v xcode-select >/dev/null 2>&1 && xcode-select -p >/dev/null 2>&1; then xcode_clt=$(xcode-select -p) printf "Xcode CLT: %s\n" "$xcode_clt" else printf "Xcode CLT: not installed\n" fi fi for cmd in bash brew go aws bats git make curl wget unzip jq python3 xmllint xmlstarlet s3cmd uuidgen xxd; do if command -v "$cmd" >/dev/null 2>&1; then printf "%-10s %s\n" "$cmd:" "$(command -v "$cmd")" else printf "%-10s %s\n" "$cmd:" "missing" fi done if command -v bash >/dev/null 2>&1; then printf "Bash version: %s\n" "$BASH_VERSION" fi if command -v go >/dev/null 2>&1; then printf "Go version: %s\n" "$(go version)" fi if command -v aws >/dev/null 2>&1; then printf "AWS CLI version: %s\n" "$(aws --version 2>&1)" fi if command -v bats >/dev/null 2>&1; then printf "BATS version: %s\n" "$(bats --version 2>&1)" fi echo "==========================" return 0 } check_for_xcode() { local xcode_tools if xcode-select -p >/dev/null 2>&1; then xcode_tools=$(xcode-select -p) printf "Xcode Command Line Tools already installed: %s\n" "$xcode_tools" return 0 fi echo "Xcode Command Line Tools are required." if [ "${CI:-false}" = "true" ]; then echo "Cannot use interactive xcode-select --install in CI." >&2 return 1 fi echo "Opening installer..." if xcode-select --install; then echo "Installer opened. Re-run this script after installation completes." else echo "Failed to open installer, or installer is already active." >&2 fi return 1 } install_managed_libraries() { if [ "$#" -ne 1 ]; then echo "install_managed_libraries requires OS type" >&2 return 1 fi local os="$1" case "$os" in Darwin) if ! install_mac_libraries; then echo "error installing mac libraries" >&2 return 1 fi ;; Linux) if ! install_linux_libraries; then echo "error installing linux libraries" >&2 return 1 fi ;; *) echo "unrecognized os: $os" >&2 return 1 ;; esac return 0 } install_linux_libraries() { local package_manager package_manager="$(detect_linux_package_manager)" case "$package_manager" in apt) if ! install_apt_libraries; then echo "error installing apt libraries" >&2 return 1 fi ;; dnf) if ! install_dnf_libraries; then echo "error installing dnf libraries" >&2 return 1 fi ;; yum) if ! install_yum_libraries; then echo "error installing yum libraries" >&2 return 1 fi ;; apk) if ! install_apk_libraries; then echo "error installing apk libraries" >&2 return 1 fi ;; *) echo "unrecognized package manager: '$package_manager'" >&2 return 1 ;; esac return 0 } install_mac_libraries() { if ! check_for_xcode; then return 1 fi if ! command -v brew >/dev/null 2>&1; then echo "Homebrew is required to install macOS packages." >&2 echo "Install it from https://brew.sh/, add to PATH as instructed, and rerun this script." >&2 return 1 fi local packages=( wget s3cmd libxml2 xmlstarlet jq go bats-core ) if command -v aws >/dev/null 2>&1; then printf "AWS CLI already installed: %s\n" "$(aws --version 2>&1)" else packages+=(awscli) fi local package for package in "${packages[@]}"; do if brew list --formula "$package" >/dev/null 2>&1; then echo "ok: $package already installed" continue fi echo "Installing $package..." if ! brew install "$package"; then echo "error: failed to install $package" >&2 return 1 fi done if ! upgrade_bash_on_mac; then echo "error upgrading bash" >&2 return 1 fi return 0 } is_root() { [ "$(id -u)" -eq 0 ] } run_as_root() { if is_root; then "$@" elif command -v sudo >/dev/null 2>&1; then sudo "$@" else echo "error: root privileges required and sudo not found" >&2 return 1 fi # just returns specific error code from command run as root } detect_linux_package_manager() { if command -v apt-get >/dev/null 2>&1; then echo "apt" elif command -v dnf >/dev/null 2>&1; then echo "dnf" elif command -v yum >/dev/null 2>&1; then echo "yum" elif command -v apk >/dev/null 2>&1; then echo "apk" else echo "unknown" fi return 0 } run_apt_update_and_install() { if [ $# -le 0 ]; then echo "run_apt_update_and_install requires libraries" >&2 return 1 fi local libraries=("$@") run_as_root env DEBIAN_FRONTEND=noninteractive apt-get update || return 1 run_as_root env DEBIAN_FRONTEND=noninteractive TZ=Etc/UTC apt-get install -y "${libraries[@]}" || return 1 } install_apt_libraries() { local libraries=(git make \ wget \ curl \ unzip \ tzdata \ s3cmd \ jq \ bc \ libxml2-utils \ xmlstarlet \ python3-pip \ python3-venv \ xxd \ uuid-runtime \ ca-certificates \ build-essential \ libc6-dev \ bats) if command -v go >/dev/null 2>&1; then printf "Go already installed: %s\n" "$(go version)" else libraries+=(golang-go) fi if [ -z "$DOWNLOAD_ONLY_FOLDER" ]; then if [ -z "$INSTALL_ONLY_FOLDER" ]; then run_apt_update_and_install "${libraries[@]}" || return 1 else local install_result=0 run_as_root env DEBIAN_FRONTEND=noninteractive apt install -y "$(realpath -m "$INSTALL_ONLY_FOLDER")"/apt/*.deb || install_result=$? # if install from packages fails, fall back if [ "$install_result" -ne 0 ]; then run_apt_update_and_install "${libraries[@]}" || return 1 fi fi else if ! download_apt_packages "$DOWNLOAD_ONLY_FOLDER" "${libraries[@]}"; then echo "error downloading apt packages" >&2 return 1 fi fi return 0 } install_dnf_libraries() { local libraries=(git \ make \ wget \ curl \ unzip \ tzdata \ s3cmd \ jq \ bc \ libxml2 \ xmlstarlet \ python3-pip \ python3-virtualenv \ vim-common \ util-linux \ ca-certificates \ bats) if command -v go >/dev/null 2>&1; then printf "Go already installed: %s\n" "$(go version)" else libraries+=(golang) fi run_as_root dnf install -y "${libraries[@]}" } install_yum_libraries() { local libraries=(git \ make \ wget \ curl \ unzip \ tzdata \ s3cmd \ jq \ bc \ libxml2 \ xmlstarlet \ python3-pip \ python3-virtualenv \ vim-common \ util-linux \ ca-certificates \ bats) if command -v go >/dev/null 2>&1; then printf "Go already installed: %s\n" "$(go version)" else libraries+=(golang) fi run_as_root yum install -y "${libraries[@]}" } install_apk_libraries() { local libraries=(git \ make \ wget \ curl \ unzip \ tzdata \ s3cmd \ jq \ bc \ libxml2-utils \ xmlstarlet \ py3-pip \ python3 \ py3-virtualenv \ xxd \ util-linux \ ca-certificates \ bats) if command -v go >/dev/null 2>&1; then printf "Go already installed: %s\n" "$(go version)" else libraries+=(go) fi run_as_root apk add --no-cache "${libraries[@]}" } check_required_commands() { local commands=( aws go git make wget curl unzip s3cmd jq bc xmllint xmlstarlet python3 uuidgen xxd ) #if [ "${INSTALL_MC:-false}" = "true" ]; then # commands+=(mc) #fi local cmd for cmd in "${commands[@]}"; do if ! command -v "$cmd" >/dev/null 2>&1; then echo "error: required command not found: $cmd" >&2 return 1 fi done return 0 } upgrade_bash_on_mac() { local bash_major brew_bash bash_path bash_major="${BASH_VERSINFO[0]:-0}" if [ "$bash_major" -ge 4 ]; then printf "Bash already modern enough: %s\n" "$BASH_VERSION" return 0 fi if ! command -v brew >/dev/null 2>&1; then echo "error: Homebrew is required to upgrade Bash on macOS" >&2 return 1 fi echo "Upgrading bash with Homebrew..." if ! brew install bash; then echo "error: failed to install bash with Homebrew" >&2 return 1 fi if brew --prefix >/dev/null 2>&1; then brew_bash="$(brew --prefix)/bin/bash" if [ -x "$brew_bash" ]; then bash_path="$brew_bash" fi fi if [ -z "$bash_path" ] && command -v bash >/dev/null 2>&1; then bash_path="$(command -v bash)" fi if [ -n "$bash_path" ] && [ -x "$bash_path" ]; then printf "Installed newer bash: %s\n" "$bash_path" printf "Use this shell explicitly if needed: %s\n" "$bash_path" return 0 fi echo "error: bash install completed but upgraded bash path could not be found" >&2 return 1 } install_aws_cli() { if [ "$#" -ne 1 ]; then echo "install_aws requires archive location" >&2 return 1 fi local archive_location="$1" local tmp_dir install_dir bin_dir tmp_dir=$(mktemp -d) install_dir="/usr/local/aws-cli" bin_dir="/usr/local/bin" if ! unzip -q "$archive_location" -d "$tmp_dir"; then echo "error: failed to unzip AWS CLI archive" >&2 rm -rf "$tmp_dir" return 1 fi if ! run_as_root "$tmp_dir/aws/install" -i "$install_dir" -b "$bin_dir" --update; then echo "error: failed to install AWS CLI" >&2 rm -rf "$tmp_dir" return 1 fi rm -rf "$tmp_dir" return 0 } download_and_install_aws_cli() { local response package_location if command -v aws >/dev/null 2>&1; then printf "AWS CLI already installed: %s\n" "$(aws --version 2>&1)" return 0 fi if [ -z "$INSTALL_ONLY_FOLDER" ]; then if ! response=$(download_aws "$DOWNLOAD_ONLY_FOLDER" 2>&1); then echo "error downloading aws package: $response" >&2 return 1 fi package_location="$response" else package_location="$INSTALL_ONLY_FOLDER/aws/$AWS_ARCHIVE_NAME" fi printf '%s\n' "aws package location: $package_location" if [ -z "$DOWNLOAD_ONLY_FOLDER" ]; then if ! install_aws_cli "$package_location"; then echo "error installing aws cli" >&2 return 1 fi fi return 0 } download_and_install_mc() { if [ $# -ne 1 ]; then echo "download_and_install_mc requires os" >&2 return 1 fi local os="$1" local response package_location if command -v mc >/dev/null 2>&1; then printf "MinIO mc already installed: %s\n" "$(mc --version 2>&1 | head -n 1)" return 0 fi if [ -z "$INSTALL_ONLY_FOLDER" ]; then if ! response=$(download_mc "$os" "$DOWNLOAD_ONLY_FOLDER" 2>&1); then echo "error downloading mc package: $response" >&2 return 1 fi package_location="$response" else package_location="$INSTALL_ONLY_FOLDER/mc/mc" fi printf 'mc package location: %s\n' "$package_location" if [ -z "$DOWNLOAD_ONLY_FOLDER" ]; then if ! install_mc "$package_location"; then echo "error installing mc" >&2 return 1 fi fi return 0 } install_mc() { if [ "$#" -ne 1 ]; then echo "install_aws requires mc install script location" >&2 return 1 fi local mc_location="$1" local target_path="/usr/local/bin/mc" response if ! run_as_root install "$mc_location" "$target_path"; then echo "error: failed to install mc to $target_path" >&2 rm -rf "$mc_location" return 1 fi rm -rf "$mc_location" if ! command -v mc >/dev/null 2>&1; then echo "error: mc still not found after installation" >&2 return 1 fi if ! response=$(mc --version 2>&1 | head -n 1); then echo "error: failed to run installed mc" >&2 return 1 fi printf "Installed MinIO mc: %s\n" "$response" return 0 } download_and_install_bats_helpers() { local download_folder if [ -z "$INSTALL_ONLY_FOLDER" ]; then if [ -n "$DOWNLOAD_ONLY_FOLDER" ]; then download_folder="$DOWNLOAD_ONLY_FOLDER/bats-helpers" else download_folder="$(dirname "${BASH_SOURCE[0]}")" fi if ! download_bats_helpers "$download_folder"; then echo "error downloading bats helpers" >&2 return 1 fi fi if [ -z "$DOWNLOAD_ONLY_FOLDER" ]; then destination_folder="$(dirname "${BASH_SOURCE[0]}")" if [ -d "${destination_folder}/bats-support" ]; then echo "bats-support already installed" else if ! mv "$INSTALL_ONLY_FOLDER/bats-helpers/bats-support" "${destination_folder}/bats-support"; then echo "error installing bats-support" return 1 fi fi if [ -d "${destination_folder}/bats-assert" ]; then echo "bats-assert already installed" else if ! mv "$INSTALL_ONLY_FOLDER/bats-helpers/bats-assert" "${destination_folder}/bats-assert"; then echo "error installing bats-assert" return 1 fi fi fi return 0 } os=$(uname -s) if ! parse_args "$@"; then exit 1 fi if [ -z "$DOWNLOAD_ONLY_FOLDER" ]; then pre_install_report fi if ! install_managed_libraries "$os"; then echo "error installing package-managed libraries" >&2 exit 1 fi if [ -z "$DOWNLOAD_ONLY_FOLDER" ]; then if [ -n "$INSTALL_ONLY_FOLDER" ]; then if ! response=$(GOPROXY=off go mod download 2>&1); then printf "error installing go dependencies: %s\n" "$response" exit 1 fi else if ! response=$(go mod download 2>&1); then printf "error installing go dependencies: %s\n" "$response" exit 1 fi fi fi if [ "$os" == "Linux" ] && ! download_and_install_aws_cli; then exit 1 fi #if ! download_and_install_mc "$os"; then # exit 1 #fi if ! download_and_install_bats_helpers; then exit 1 fi if [ -z "$DOWNLOAD_ONLY_FOLDER" ]; then if ! check_required_commands; then exit 1 fi fi echo "Install complete" exit 0