#!/usr/bin/env bash set -euo pipefail platform=$(uname -ms) # If running on Windows (e.g. Git Bash), redirect to PowerShell installer if [[ ${OS:-} = Windows_NT ]]; then powershell -c "irm clams.tech/install.ps1|iex" exit $? fi # Reset Color_Off='' # Regular Colors Red='' Green='' Dim='' Yellow='' # Bold Bold_White='' Bold_Green='' if [[ -t 1 ]]; then Color_Off='\033[0m' Red='\033[0;31m' Green='\033[0;32m' Dim='\033[0;2m' Yellow='\033[0;33m' Bold_Green='\033[1;32m' Bold_White='\033[1m' fi error() { printf "${Red}error${Color_Off}: %s\n" "$*" >&2 exit 1 } warn() { printf "${Yellow}warn${Color_Off}: %s\n" "$*" >&2 } info() { printf "${Dim}%s${Color_Off}\n" "$*" } info_bold() { printf "${Bold_White}%s${Color_Off}\n" "$*" } success() { printf "${Green}%s${Color_Off}\n" "$*" } # --- Dependency checks --- command -v curl >/dev/null || error 'curl is required to install clams. Install it with your package manager (e.g. apt install curl)' command -v tar >/dev/null || error 'tar is required to install clams. Install it with your package manager (e.g. apt install tar)' if ! command -v xz >/dev/null && ! command -v xzcat >/dev/null && ! command -v unxz >/dev/null; then error 'xz is required to install clams. Install it with your package manager (e.g. apt install xz-utils, brew install xz)' fi # --- Argument handling --- if [[ $# -gt 1 ]]; then error 'Too many arguments. Usage: install.sh [version] (e.g. "install.sh 1.0.0-beta.1")' fi # --- Platform detection --- case $platform in 'Darwin x86_64') target=x86_64-apple-darwin ;; 'Darwin arm64') target=aarch64-apple-darwin ;; 'Linux aarch64' | 'Linux arm64') target=aarch64-unknown-linux-gnu ;; 'Linux x86_64') target=x86_64-unknown-linux-gnu ;; *) error "Unsupported platform: ${platform}. Clams supports macOS (x86_64, arm64) and Linux (x86_64, aarch64)." ;; esac # Detect Rosetta 2 on macOS — prefer native ARM64 build if [[ $target = x86_64-apple-darwin ]]; then if [[ $(sysctl -n sysctl.proc_translated 2>/dev/null) = 1 ]]; then target=aarch64-apple-darwin info "Your shell is running in Rosetta 2. Downloading clams for aarch64-apple-darwin instead." fi fi # --- Version resolution --- GITHUB=${GITHUB-"https://github.com"} GITHUB_API=${GITHUB_API-"https://api.github.com"} github_repo="$GITHUB/clams-tech/releases" api_repo="$GITHUB_API/repos/clams-tech/releases" if [[ $# = 0 ]]; then # Resolve latest version from GitHub API info "Fetching latest release..." release_info=$(curl --fail --silent "$api_repo/releases/latest") || error "Failed to fetch latest release from GitHub API. Check your internet connection." tag=$(echo "$release_info" | grep -o '"tag_name": *"[^"]*"' | head -1 | cut -d'"' -f4) if [[ -z $tag ]]; then error "Failed to parse latest release tag from GitHub API response." fi version=${tag#clams-v} info "Latest version: ${version}" else version=$1 # Strip leading 'v' if provided version=${version#v} tag="clams-v${version}" fi # --- Download --- archive_name="clams-${target}.tar.xz" checksum_name="${archive_name}.sha256" download_url="$github_repo/releases/download/${tag}/${archive_name}" checksum_url="$github_repo/releases/download/${tag}/${checksum_name}" install_env=CLAMS_INSTALL install_dir=${!install_env:-$HOME/.clams} bin_dir=$install_dir/bin exe=$bin_dir/clams if [[ ! -d $bin_dir ]]; then mkdir -p "$bin_dir" || error "Failed to create install directory \"$bin_dir\"" fi # Download archive info "Downloading clams v${version} for ${target}..." curl --fail --location --progress-bar --output "$exe.tar.xz" "$download_url" || error "Failed to download clams from \"$download_url\"\n\nIs clams-v${version} a valid release? Check: ${github_repo}/releases" # Download checksum curl --fail --silent --location --output "$exe.tar.xz.sha256" "$checksum_url" || error "Failed to download checksum from \"$checksum_url\"" # --- Checksum verification --- info "Verifying checksum..." expected_checksum=$(cut -d' ' -f1 < "$exe.tar.xz.sha256") if command -v sha256sum >/dev/null; then actual_checksum=$(sha256sum "$exe.tar.xz" | cut -d' ' -f1) elif command -v shasum >/dev/null; then actual_checksum=$(shasum -a 256 "$exe.tar.xz" | cut -d' ' -f1) else warn "Neither sha256sum nor shasum found. Skipping checksum verification." actual_checksum=$expected_checksum fi if [[ $actual_checksum != "$expected_checksum" ]]; then rm -f "$exe.tar.xz" "$exe.tar.xz.sha256" error "Checksum verification failed!\n Expected: ${expected_checksum}\n Actual: ${actual_checksum}\n\nThe download may be corrupted. Please try again." fi # --- Extract --- tar xf "$exe.tar.xz" -C "$bin_dir" || error "Failed to extract clams archive" # The archive contains a nested directory (e.g. clams-aarch64-apple-darwin/clams) mv "$bin_dir/clams-${target}/clams" "$exe" || error "Failed to move clams binary from extracted directory" rm -rf "${bin_dir:?}/clams-${target}" chmod +x "$exe" || error 'Failed to set permissions on clams executable' rm -f "$exe.tar.xz" "$exe.tar.xz.sha256" # --- Verify binary --- tildify() { if [[ $1 = $HOME/* ]]; then local replacement=\~/ echo "${1/$HOME\//$replacement}" else echo "$1" fi } installed_version=$("$exe" --version 2>&1) || error "clams was installed but failed to run. The command 'clams --version' exited with code $?." success "clams ${installed_version} was installed successfully to ${Bold_Green}$(tildify "$exe")" if command -v clams >/dev/null; then echo "Run 'clams --help' to get started" exit fi # --- PATH setup --- refresh_command='' tilde_bin_dir=$(tildify "$bin_dir") quoted_install_dir=\"${install_dir//\"/\\\"}\" if [[ $quoted_install_dir = \"$HOME/* ]]; then quoted_install_dir=${quoted_install_dir/$HOME\//\$HOME/} fi bin_env=\$$install_env/bin echo case $(basename "$SHELL") in fish) commands=( "set --export $install_env $quoted_install_dir" "set --export PATH $bin_env \$PATH" ) fish_config=$HOME/.config/fish/config.fish tilde_fish_config=$(tildify "$fish_config") if [[ -w $fish_config ]]; then # Check if already added (idempotent) if ! grep -q "# clams" "$fish_config" 2>/dev/null; then { printf '\n# clams\n' for command in "${commands[@]}"; do echo "$command" done } >>"$fish_config" info "Added \"$tilde_bin_dir\" to \$PATH in \"$tilde_fish_config\"" else info "PATH already configured in \"$tilde_fish_config\"" fi refresh_command="source $tilde_fish_config" else echo "Manually add the directory to $tilde_fish_config (or similar):" for command in "${commands[@]}"; do info_bold " $command" done fi ;; zsh) commands=( "export $install_env=$quoted_install_dir" "export PATH=\"$bin_env:\$PATH\"" ) zsh_config=$HOME/.zshrc tilde_zsh_config=$(tildify "$zsh_config") if [[ -w $zsh_config ]]; then if ! grep -q "# clams" "$zsh_config" 2>/dev/null; then { printf '\n# clams\n' for command in "${commands[@]}"; do echo "$command" done } >>"$zsh_config" info "Added \"$tilde_bin_dir\" to \$PATH in \"$tilde_zsh_config\"" else info "PATH already configured in \"$tilde_zsh_config\"" fi refresh_command="exec $SHELL" else echo "Manually add the directory to $tilde_zsh_config (or similar):" for command in "${commands[@]}"; do info_bold " $command" done fi ;; bash) commands=( "export $install_env=$quoted_install_dir" "export PATH=\"$bin_env:\$PATH\"" ) bash_configs=( "$HOME/.bashrc" "$HOME/.bash_profile" ) if [[ ${XDG_CONFIG_HOME:-} ]]; then bash_configs+=( "$XDG_CONFIG_HOME/.bashrc" "$XDG_CONFIG_HOME/.bash_profile" "$XDG_CONFIG_HOME/bashrc" "$XDG_CONFIG_HOME/bash_profile" ) fi set_manually=true for bash_config in "${bash_configs[@]}"; do tilde_bash_config=$(tildify "$bash_config") if [[ -w $bash_config ]]; then if ! grep -q "# clams" "$bash_config" 2>/dev/null; then { printf '\n# clams\n' for command in "${commands[@]}"; do echo "$command" done } >>"$bash_config" info "Added \"$tilde_bin_dir\" to \$PATH in \"$tilde_bash_config\"" else info "PATH already configured in \"$tilde_bash_config\"" fi refresh_command="source $bash_config" set_manually=false break fi done if [[ $set_manually = true ]]; then echo "Manually add the directory to ~/.bashrc (or similar):" for command in "${commands[@]}"; do info_bold " $command" done fi ;; *) echo 'Manually add the directory to ~/.bashrc (or similar):' info_bold " export $install_env=$quoted_install_dir" info_bold " export PATH=\"$bin_env:\$PATH\"" ;; esac echo info "To get started, run:" echo if [[ $refresh_command ]]; then info_bold " $refresh_command" fi info_bold " clams --help"