Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 25 additions & 4 deletions docs/src/content/docs/getting-started/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,28 @@ irm https://aka.ms/apm-windows | iex

The installer automatically detects your platform (macOS/Linux/Windows, Intel/ARM), downloads the latest binary, and adds `apm` to your `PATH`.

### Installer options

The Unix installer supports environment variables for custom environments:

```bash
# Install a specific version
curl -sSL https://aka.ms/apm-unix | sh -s -- @v1.2.3

# Custom install directory
curl -sSL https://aka.ms/apm-unix | APM_INSTALL_DIR=$HOME/.local/bin sh

# Air-gapped / GitHub Enterprise mirror
GITHUB_URL=https://github.corp.com VERSION=v1.2.3 sh install.sh
```

| Variable | Default | Description |
|----------|---------|-------------|
| `APM_INSTALL_DIR` | `/usr/local/bin` | Directory for the `apm` symlink |
| `GITHUB_URL` | `https://github.com` | Base URL for downloads (mirrors, GHE) |
| `REPO` | `microsoft/apm` | GitHub repository |
| `VERSION` | *(latest)* | Pin a specific release (skips GitHub API) |

## Package managers

**Homebrew (macOS/Linux):**
Expand Down Expand Up @@ -124,7 +146,7 @@ apm --version

### `apm: command not found` (macOS / Linux)

Ensure `/usr/local/bin` is in your `PATH`:
Ensure your install directory is in your `PATH`. The default is `/usr/local/bin`:

```bash
echo $PATH | tr ':' '\n' | grep /usr/local/bin
Expand All @@ -138,11 +160,10 @@ export PATH="/usr/local/bin:$PATH"

### Permission denied during install (macOS / Linux)

Use `sudo` for system-wide installation, or install to a user-writable directory instead:
Use `sudo` for system-wide installation, or install to a user-writable directory:

```bash
mkdir -p ~/bin
# then install the binary to ~/bin/apm and add ~/bin to PATH
curl -sSL https://aka.ms/apm-unix | APM_INSTALL_DIR=$HOME/.local/bin sh
```

### Authentication errors when installing packages
Expand Down
102 changes: 65 additions & 37 deletions install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ set -e

# APM CLI Installer Script
# Usage: curl -sSL https://aka.ms/apm-unix | sh
# Specific version: curl -sSL https://aka.ms/apm-unix | sh -s -- @v1.2.3
# Custom install dir: curl -sSL https://aka.ms/apm-unix | APM_INSTALL_DIR=$HOME/.local/bin sh
# GitHub Enterprise: GITHUB_URL=https://gh.corp.com sh install.sh
Comment on lines 4 to +8
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change introduces new supported installer inputs (APM_INSTALL_DIR, GITHUB_URL, REPO, VERSION / @vX.Y.Z). The repo docs currently describe a fixed /usr/local/bin install path and don't mention version pinning or air-gapped/GHE usage (e.g. docs/src/content/docs/getting-started/installation.md). Please update the relevant docs pages (and the apm-guide skill docs under packages/apm-guide/.apm/skills/apm-usage/) to reflect the new options.

Copilot uses AI. Check for mistakes.
# For private repositories, use with authentication:
# curl -sSL -H "Authorization: token $GITHUB_APM_PAT" \
# https://raw.githubusercontent.com/microsoft/apm/main/install.sh | \
Expand All @@ -15,10 +18,11 @@ BLUE='\033[0;34m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color

# Configuration
REPO="microsoft/apm"
INSTALL_DIR="/usr/local/bin"
# Configuration (all overridable via environment variables)
REPO="${REPO:-microsoft/apm}"
APM_INSTALL_DIR="${APM_INSTALL_DIR:-/usr/local/bin}"
BINARY_NAME="apm"
GITHUB_URL="${GITHUB_URL:-https://github.com}"

# Banner
echo -e "${BLUE}"
Expand Down Expand Up @@ -69,6 +73,12 @@ esac
echo -e "${BLUE}Detected platform: $PLATFORM-$ARCH${NC}"
echo -e "${BLUE}Target binary: $DOWNLOAD_BINARY${NC}"

# Parse version: @v1.2.3 as arg, or VERSION env var
# Usage: sh install.sh @v1.2.3 or VERSION=v1.2.3 sh install.sh
if [ -z "$VERSION" ] && [ -n "$1" ]; then
VERSION="${1#@}"
fi

# Function to check Python availability and version
check_python_requirements() {
# Check if Python is available
Expand Down Expand Up @@ -135,7 +145,7 @@ try_pip_installation() {
echo " cd my-app && apm install # Install dependencies"
echo " apm run # Run your first prompt"
echo ""
echo -e "${BLUE}Documentation:${NC} https://github.com/$REPO"
echo -e "${BLUE}Documentation:${NC} $GITHUB_URL/$REPO"
return 0
else
echo -e "${RED}Error: pip installation failed${NC}"
Expand Down Expand Up @@ -172,7 +182,7 @@ if [ "$PLATFORM" = "linux" ]; then
echo "To install APM, you need either:"
echo " 1. Python 3.9+ and pip: pip install --user apm-cli"
echo " 2. A system with glibc 2.35+ to use the prebuilt binary"
echo " 3. Build from source: git clone https://github.com/$REPO.git && cd apm && uv sync && uv run pip install -e ."
echo " 3. Build from source: git clone $GITHUB_URL/$REPO.git && cd apm && uv sync && uv run pip install -e ."
echo ""
echo "To install Python 3.9+:"
echo " Ubuntu/Debian: sudo apt-get update && sudo apt-get install python3 python3-pip"
Expand All @@ -192,11 +202,27 @@ if [ -f "/.dockerenv" ] || [ -f "/run/.containerenv" ] || grep -q "/docker/" /pr
echo ""
fi

# Check if we have permission to install to /usr/local/bin
if [ ! -w "$INSTALL_DIR" ]; then
echo -e "${YELLOW}Note: Will need sudo permissions to install to $INSTALL_DIR${NC}"
# Check if we have permission to install to the configured directory
if [ ! -w "$APM_INSTALL_DIR" ]; then
echo -e "${YELLOW}Note: Will need sudo permissions to install to $APM_INSTALL_DIR${NC}"
fi

# Resolve auth token (needed for both API and download paths)
if [ -n "$GITHUB_APM_PAT" ]; then
AUTH_HEADER_VALUE="$GITHUB_APM_PAT"
elif [ -n "$GITHUB_TOKEN" ]; then
AUTH_HEADER_VALUE="$GITHUB_TOKEN"
fi

# When VERSION is provided, skip GitHub API and compute download URL directly
if [ -n "$VERSION" ]; then
TAG_NAME="$VERSION"
DOWNLOAD_URL="$GITHUB_URL/$REPO/releases/download/$TAG_NAME/$DOWNLOAD_BINARY"
echo -e "${GREEN}Version: $TAG_NAME${NC}"
echo -e "${BLUE}Download URL: $DOWNLOAD_URL${NC}"
fi

if [ -z "$TAG_NAME" ]; then
# Get latest release info
echo -e "${YELLOW}Fetching latest release information...${NC}"

Expand All @@ -208,7 +234,7 @@ CURL_EXIT_CODE=$?
# Only try authentication if curl failed OR we got a "Not Found" message OR response is empty
if [ $CURL_EXIT_CODE -ne 0 ] || [ -z "$LATEST_RELEASE" ] || echo "$LATEST_RELEASE" | grep -q '"message".*"Not Found"'; then
echo -e "${BLUE}Repository appears to be private, trying with authentication...${NC}"

# Check if we have GitHub token for private repo access
AUTH_HEADER_VALUE=""
if [ -n "$GITHUB_APM_PAT" ]; then
Expand All @@ -226,7 +252,7 @@ if [ $CURL_EXIT_CODE -ne 0 ] || [ -z "$LATEST_RELEASE" ] || echo "$LATEST_RELEAS
echo " GITHUB_APM_PAT=\$GITHUB_APM_PAT sh"
exit 1
fi

# Retry with authentication
LATEST_RELEASE=$(curl -s -H "Authorization: token $AUTH_HEADER_VALUE" "https://api.github.com/repos/$REPO/releases/latest")
CURL_EXIT_CODE=$?
Expand All @@ -241,7 +267,7 @@ fi
# Check if we got a valid response (should contain tag_name)
if ! echo "$LATEST_RELEASE" | grep -q '"tag_name":'; then
echo -e "${RED}Error: Invalid API response received${NC}"

# Check if the response contains an error message
if echo "$LATEST_RELEASE" | grep -q '"message"'; then
echo -e "${RED}GitHub API Error:${NC}"
Expand All @@ -253,7 +279,7 @@ fi
# Extract tag name and download URLs
# Use grep -o to extract just the matching portion (handles single-line JSON)
TAG_NAME=$(echo "$LATEST_RELEASE" | grep -o '"tag_name": *"[^"]*"' | awk -F'"' '{print $4}')
DOWNLOAD_URL="https://github.com/$REPO/releases/download/$TAG_NAME/$DOWNLOAD_BINARY"
DOWNLOAD_URL="$GITHUB_URL/$REPO/releases/download/$TAG_NAME/$DOWNLOAD_BINARY"

# Extract API asset URL for private repository downloads
ASSET_URL=$(echo "$LATEST_RELEASE" | grep -B 3 "\"name\": \"$DOWNLOAD_BINARY\"" | grep -o '"url": *"[^"]*"' | awk -F'"' '{print $4}')
Expand All @@ -273,6 +299,7 @@ fi

echo -e "${GREEN}Latest version: $TAG_NAME${NC}"
echo -e "${BLUE}Download URL: $DOWNLOAD_URL${NC}"
fi

# Create temporary directory
TMP_DIR=$(mktemp -d)
Expand Down Expand Up @@ -313,7 +340,7 @@ else
echo ""
echo "For private repositories, ensure your token has the required permissions."
echo "You can try installing from source instead:"
echo " git clone https://github.com/$REPO.git"
echo " git clone $GITHUB_URL/$REPO.git"
echo " cd apm && uv sync && uv run pip install -e ."
exit 1
fi
Expand All @@ -333,7 +360,7 @@ else
echo ""
echo "For private repositories, ensure your token has the required permissions."
echo "You can try installing from source instead:"
echo " git clone https://github.com/$REPO.git"
echo " git clone $GITHUB_URL/$REPO.git"
echo " cd apm && uv sync && uv run pip install -e ."
exit 1
fi
Expand All @@ -354,7 +381,7 @@ else
echo " GITHUB_APM_PAT=\$GITHUB_APM_PAT sh"
echo ""
echo "You can also try installing from source:"
echo " git clone https://github.com/$REPO.git"
echo " git clone $GITHUB_URL/$REPO.git"
echo " cd apm && uv sync && uv run pip install -e ."
exit 1
fi
Expand Down Expand Up @@ -436,7 +463,7 @@ else
echo "2. Homebrew (macOS/Linux): brew install microsoft/apm/apm"
echo ""
echo "3. From source:"
echo " git clone https://github.com/$REPO.git"
echo " git clone $GITHUB_URL/$REPO.git"
echo " cd apm && uv sync && uv run pip install -e ."
echo ""

Expand All @@ -446,52 +473,53 @@ else
echo ""
fi

echo "Need help? Create an issue at: https://github.com/$REPO/issues"
echo "Need help? Create an issue at: $GITHUB_URL/$REPO/issues"
exit 1
fi

# Install binary directory structure
echo -e "${YELLOW}Installing APM CLI to $INSTALL_DIR...${NC}"
echo -e "${YELLOW}Installing APM CLI to $APM_INSTALL_DIR...${NC}"

# APM installation directory (for the complete bundle)
APM_INSTALL_DIR="/usr/local/lib/apm"
APM_LIB_DIR="$(dirname "$APM_INSTALL_DIR")/lib/apm"

# Remove any existing installation
if [ -d "$APM_INSTALL_DIR" ]; then
if [ -w "/usr/local/lib" ]; then
rm -rf "$APM_INSTALL_DIR"
if [ -d "$APM_LIB_DIR" ]; then
if [ -w "$(dirname "$APM_LIB_DIR")" ]; then
rm -rf "$APM_LIB_DIR"
else
sudo rm -rf "$APM_INSTALL_DIR"
sudo rm -rf "$APM_LIB_DIR"
fi
fi

# Create installation directory
if [ -w "/usr/local/lib" ]; then
mkdir -p "$APM_INSTALL_DIR"
cp -r "$TMP_DIR/$EXTRACTED_DIR"/* "$APM_INSTALL_DIR/"
if [ -w "$(dirname "$APM_LIB_DIR")" ]; then
mkdir -p "$APM_LIB_DIR"
cp -r "$TMP_DIR/$EXTRACTED_DIR"/* "$APM_LIB_DIR/"
else
sudo mkdir -p "$APM_INSTALL_DIR"
sudo cp -r "$TMP_DIR/$EXTRACTED_DIR"/* "$APM_INSTALL_DIR/"
sudo mkdir -p "$APM_LIB_DIR"
sudo cp -r "$TMP_DIR/$EXTRACTED_DIR"/* "$APM_LIB_DIR/"
fi

# Create symlink in /usr/local/bin pointing to the actual binary
if [ -w "$INSTALL_DIR" ]; then
ln -sf "$APM_INSTALL_DIR/$BINARY_NAME" "$INSTALL_DIR/$BINARY_NAME"
# Create symlink pointing to the actual binary
if mkdir -p "$APM_INSTALL_DIR" 2>/dev/null && [ -w "$APM_INSTALL_DIR" ]; then
ln -sf "$APM_LIB_DIR/$BINARY_NAME" "$APM_INSTALL_DIR/$BINARY_NAME"
else
sudo ln -sf "$APM_INSTALL_DIR/$BINARY_NAME" "$INSTALL_DIR/$BINARY_NAME"
sudo mkdir -p "$APM_INSTALL_DIR"
sudo ln -sf "$APM_LIB_DIR/$BINARY_NAME" "$APM_INSTALL_DIR/$BINARY_NAME"
fi

# Verify installation
if command -v apm >/dev/null 2>&1; then
INSTALLED_VERSION=$(apm --version 2>/dev/null || echo "unknown")
echo -e "${GREEN}✓ APM installed successfully!${NC}"
echo -e "${BLUE}Version: $INSTALLED_VERSION${NC}"
echo -e "${BLUE}Location: $INSTALL_DIR/$BINARY_NAME -> $APM_INSTALL_DIR/$BINARY_NAME${NC}"
echo -e "${BLUE}Location: $APM_INSTALL_DIR/$BINARY_NAME -> $APM_LIB_DIR/$BINARY_NAME${NC}"
else
echo -e "${YELLOW}⚠ APM installed but not found in PATH${NC}"
echo "You may need to add $INSTALL_DIR to your PATH environment variable."
echo "You may need to add $APM_INSTALL_DIR to your PATH environment variable."
echo "Add this line to your shell profile (.bashrc, .zshrc, etc.):"
echo " export PATH=\"$INSTALL_DIR:\$PATH\""
echo " export PATH=\"$APM_INSTALL_DIR:\$PATH\""
fi

echo ""
Expand All @@ -502,5 +530,5 @@ echo " apm init my-app # Create a new APM project"
echo " cd my-app && apm install # Install dependencies"
echo " apm run # Run your first prompt"
echo ""
echo -e "${BLUE}Documentation:${NC} https://github.com/$REPO"
echo -e "${BLUE}Need help?${NC} Create an issue at https://github.com/$REPO/issues"
echo -e "${BLUE}Documentation:${NC} $GITHUB_URL/$REPO"
echo -e "${BLUE}Need help?${NC} Create an issue at $GITHUB_URL/$REPO/issues"
16 changes: 15 additions & 1 deletion packages/apm-guide/.apm/skills/apm-usage/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,21 @@ apm update # update APM itself
apm update --check # check for updates without installing
```

## Installer options (macOS / Linux)

```bash
# Specific version
curl -sSL https://aka.ms/apm-unix | sh -s -- @v1.2.3

# Custom install dir
curl -sSL https://aka.ms/apm-unix | APM_INSTALL_DIR=$HOME/.local/bin sh

# Air-gapped / GHE mirror (skips GitHub API when VERSION is set)
GITHUB_URL=https://github.corp.com VERSION=v1.2.3 sh install.sh
```

## Troubleshooting

- **macOS/Linux "command not found":** ensure `/usr/local/bin` is in `$PATH`.
- **macOS/Linux "command not found":** ensure your install directory (default `/usr/local/bin`) is in `$PATH`.
- **Permission denied:** use `APM_INSTALL_DIR=$HOME/.local/bin` to install without sudo.
- **Windows antivirus locks:** set `$env:APM_DEBUG = "1"` and retry.