#!/bin/bash # Script to install the Porter CLI on macOS, Linux, and WSL osname="" arch=$(arch) check_prereqs() { command -v curl >/dev/null 2>&1 || { echo "[ERROR] curl is required to install the Porter CLI." >&2; exit 1; } command -v unzip >/dev/null 2>&1 || { echo "[ERROR] unzip is required to install the Porter CLI." >&2; exit 1; } } download_file() { local filename="$1" local porter_version="$2" local output_file="$3" local download_url="https://install.porter.run/download/v${porter_version}/${filename}" local releases_url="https://releases.porter.run/releases/v${porter_version}/${filename}" local github_url="https://github.com/porter-dev/releases/releases/download/v${porter_version}/${filename}" echo "[INFO] Attempting to download ${filename}..." # The install server redirects to a presigned R2 URL, which is free egress. if curl -L --fail --connect-timeout 5 --max-time 60 "${download_url}" --output "${output_file}" 2>/dev/null; then echo "[INFO] Successfully downloaded" return 0 fi echo "[WARNING] presigned download failed, falling back to releases.porter.run..." if curl -L --fail --connect-timeout 5 --max-time 60 "${releases_url}" --output "${output_file}" 2>/dev/null; then echo "[INFO] Successfully downloaded" return 0 fi echo "[WARNING] release download failed, falling back to GitHub releases..." if curl -L --fail --connect-timeout 5 --max-time 60 --retry 2 --retry-connrefused --retry-delay 2 "${github_url}" --output "${output_file}"; then echo "[INFO] Successfully downloaded from GitHub" return 0 fi echo "[ERROR] Failed to download ${filename}" >&2 return 1 } download_and_install() { check_prereqs echo "[INFO] Since the Porter CLI gets installed in /usr/local/bin, you may be asked to input your password." echo "[INFO] Please make sure /usr/local/bin is included in your PATH." porter_version="0.69.8" if [[ -n "$PORTER_VERSION" ]]; then echo "[INFO] Using $PORTER_VERSION override instead of $porter_version" porter_version="$PORTER_VERSION" fi download_file "porter_${porter_version}_${osname}_${arch}" "${porter_version}" "porter-cli-executable" || exit 1 chmod +x ./porter-cli-executable sudo mv ./porter-cli-executable /usr/local/bin/porter command -v porter >/dev/null 2>&1 || { echo "[ERROR] There was an error installing the Porter CLI. Please try again." >&2; exit 1; } # only for legacy support (switchboard required) download_file "docker-credential-porter_${porter_version}_${osname}_${arch}" "${porter_version}" "docker-credential-porter" || exit 1 chmod +x ./docker-credential-porter sudo mv ./docker-credential-porter /usr/local/bin/docker-credential-porter echo "[INFO] Porter CLI installed. Run 'porter auth login' to get started." print_agent_notice exit } # curl | bash has no usable stdin, so this informs rather than prompts print_agent_notice() { cat <<'NOTICE' Using an AI coding agent (Claude Code, Codex, Cursor, ...)? Porter also ships an MCP server so your agent can deploy and manage apps for you: curl -fsSL https://agents.porter.run | sh Docs: https://docs.porter.run/mcp/overview NOTICE } if [[ "$arch" == "x86_64" ]]; then arch="amd64" elif [[ "$arch" == "aarch64" ]]; then arch="arm64" fi if [[ "$OSTYPE" == "linux-gnu"* ]]; then if uname -a | grep -q '^Linux.*Microsoft'; then echo "[WARNING] WSL support is experimental and may result in crashes." fi osname="linux" download_and_install elif [[ "$OSTYPE" == "darwin"* ]]; then osname="darwin" download_and_install fi echo "[ERROR] Unsupported operating system." exit 1