#!/bin/bash # Portr Installation Script # Usage: curl -sSL https://raw.githubusercontent.com/amalshaji/portr/main/install.sh | sh set -e # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' NC='\033[0m' # No Color # GitHub repository information REPO_OWNER="amalshaji" REPO_NAME="portr" GITHUB_REPO="https://github.com/${REPO_OWNER}/${REPO_NAME}" GITHUB_API="https://api.github.com/repos/${REPO_OWNER}/${REPO_NAME}" # Installation directory INSTALL_DIR="$HOME/.portr/bin" BINARY_NAME="portr" # Function to print colored output print_info() { printf "${GREEN}[INFO]${NC} %s\n" "$1" } print_warning() { printf "${YELLOW}[WARNING]${NC} %s\n" "$1" } print_error() { printf "${RED}[ERROR]${NC} %s\n" "$1" } # Function to detect OS detect_os() { case "$(uname -s)" in Linux*) echo "Linux";; Darwin*) echo "Darwin";; CYGWIN*|MINGW*|MSYS*) echo "Windows";; *) print_error "Unsupported operating system: $(uname -s)" exit 1 ;; esac } # Function to detect architecture detect_arch() { case "$(uname -m)" in x86_64|amd64) echo "x86_64";; arm64|aarch64) echo "arm64";; *) print_error "Unsupported architecture: $(uname -m)" exit 1 ;; esac } # Function to get the latest release version get_latest_version() { if command -v curl >/dev/null 2>&1; then curl -s "${GITHUB_API}/releases/latest" | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/' elif command -v wget >/dev/null 2>&1; then wget -qO- "${GITHUB_API}/releases/latest" | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/' else print_error "Neither curl nor wget is available. Please install one of them." exit 1 fi } # Function to download file download_file() { local url="$1" local output="$2" if command -v curl >/dev/null 2>&1; then curl -sSL "$url" -o "$output" elif command -v wget >/dev/null 2>&1; then wget -q "$url" -O "$output" else print_error "Neither curl nor wget is available. Please install one of them." exit 1 fi } # Function to verify checksum (optional) verify_checksum() { local file="$1" local expected_checksum="$2" if command -v sha256sum >/dev/null 2>&1; then local actual_checksum=$(sha256sum "$file" | cut -d' ' -f1) elif command -v shasum >/dev/null 2>&1; then local actual_checksum=$(shasum -a 256 "$file" | cut -d' ' -f1) else print_warning "No checksum utility found. Skipping verification." return 0 fi if [ "$actual_checksum" = "$expected_checksum" ]; then print_info "Checksum verification passed" return 0 else print_error "Checksum verification failed" return 1 fi } # Function to detect shell profile file detect_shell_profile() { local shell_name=$(basename "$SHELL") case "$shell_name" in bash) if [[ -f "$HOME/.bashrc" ]]; then echo "$HOME/.bashrc" elif [[ -f "$HOME/.bash_profile" ]]; then echo "$HOME/.bash_profile" else echo "$HOME/.profile" fi ;; zsh) echo "$HOME/.zshrc" ;; fish) echo "$HOME/.config/fish/config.fish" ;; *) echo "$HOME/.profile" ;; esac } # Function to add directory to PATH add_to_path() { local profile_file=$(detect_shell_profile) local path_line="export PATH=\"$INSTALL_DIR:\$PATH\"" if [[ "$profile_file" == "fish" ]]; then # Fish shell uses fish_add_path if command -v fish >/dev/null 2>&1; then # Check if path is already added to fish if fish -c "contains $INSTALL_DIR \$fish_user_paths" 2>/dev/null; then print_info "$INSTALL_DIR already in fish PATH configuration" return 0 fi fish -c "fish_add_path $INSTALL_DIR" 2>/dev/null || { print_warning "Failed to add to fish PATH automatically" print_info "Add manually: fish_add_path $INSTALL_DIR" return 1 } print_info "Added $INSTALL_DIR to fish PATH" else print_warning "Fish shell not found in PATH" return 1 fi else # Check if the exact PATH export line already exists in profile if grep -Fq "$path_line" "$profile_file" 2>/dev/null; then print_info "$INSTALL_DIR already in PATH configuration" return 0 fi # Also check for any existing PATH modification with this directory if grep -q "PATH.*$INSTALL_DIR" "$profile_file" 2>/dev/null; then print_info "$INSTALL_DIR already referenced in PATH configuration" return 0 fi echo "" >> "$profile_file" echo "# Added by Portr installer" >> "$profile_file" echo "$path_line" >> "$profile_file" print_info "Added $INSTALL_DIR to PATH in $profile_file" fi return 0 } # Main installation function install_portr() { print_info "Starting Portr installation..." # Detect system information OS=$(detect_os) ARCH=$(detect_arch) print_info "Detected OS: $OS, Architecture: $ARCH" # Get latest version print_info "Fetching latest release information..." VERSION=$(get_latest_version) if [ -z "$VERSION" ]; then print_error "Failed to get latest version information" exit 1 fi print_info "Latest version: $VERSION" # Construct download URL based on goreleaser template # Template: portr_{{.Version}}_{{title .Os}}_{{if eq .Arch "amd64"}}x86_64{{else}}{{.Arch}}{{end}}.zip ARCHIVE_NAME="${BINARY_NAME}_${VERSION}_${OS}_${ARCH}.zip" DOWNLOAD_URL="${GITHUB_REPO}/releases/download/${VERSION}/${ARCHIVE_NAME}" print_info "Download URL: $DOWNLOAD_URL" # Create temporary directory TEMP_DIR=$(mktemp -d) trap "rm -rf $TEMP_DIR" EXIT # Download the archive print_info "Downloading $ARCHIVE_NAME..." ARCHIVE_PATH="$TEMP_DIR/$ARCHIVE_NAME" if ! download_file "$DOWNLOAD_URL" "$ARCHIVE_PATH"; then print_error "Failed to download $ARCHIVE_NAME" exit 1 fi # Extract the archive print_info "Extracting archive..." if command -v unzip >/dev/null 2>&1; then unzip -q "$ARCHIVE_PATH" -d "$TEMP_DIR" else print_error "unzip command not found. Please install unzip." exit 1 fi # Find the binary BINARY_PATH="$TEMP_DIR/$BINARY_NAME" if [ "$OS" = "Windows" ]; then BINARY_PATH="$TEMP_DIR/${BINARY_NAME}.exe" fi if [ ! -f "$BINARY_PATH" ]; then print_error "Binary not found in archive" exit 1 fi # Create installation directory print_info "Creating installation directory: $INSTALL_DIR" mkdir -p "$INSTALL_DIR" # Install the binary print_info "Installing binary to $INSTALL_DIR" cp "$BINARY_PATH" "$INSTALL_DIR/" chmod +x "$INSTALL_DIR/$BINARY_NAME" # Handle PATH configuration if [[ ":$PATH:" != *":$INSTALL_DIR:"* ]]; then echo # Check for environment variable to control behavior AUTO_ADD_PATH=${PORTR_AUTO_ADD_PATH:-"yes"} case "$AUTO_ADD_PATH" in "no"|"false"|"0") print_warning "$INSTALL_DIR is not in your PATH." print_info "Add manually: export PATH=\"$INSTALL_DIR:\$PATH\"" ;; *) # Default: automatically add to PATH if add_to_path; then print_info "PATH updated! Restart your terminal or run: source $(detect_shell_profile)" else print_warning "Failed to add to PATH automatically" print_info "Add manually: export PATH=\"$INSTALL_DIR:\$PATH\"" fi ;; esac else print_info "$INSTALL_DIR is already in your PATH" fi echo print_info "Portr $VERSION installed successfully!" print_info "Please restart your terminal or run: source $(detect_shell_profile)" print_info "Then run 'portr --help' to get started" } # Check if running as root (not recommended) if [ "$EUID" -eq 0 ]; then print_warning "Running as root is not recommended. Consider running as a regular user." INSTALL_DIR="/usr/local/bin" fi # Run installation install_portr