feat: make install.sh configurable for air-gapped environments#660
feat: make install.sh configurable for air-gapped environments#660danielmeppiel merged 16 commits intomicrosoft:mainfrom
Conversation
Defaults to /usr/local/bin when not set. Usage: curl -sSL https://aka.ms/apm-unix | INSTALL_DIR=~/.local/bin sh Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ents - APM_INSTALL_DIR: override install path (default /usr/local/bin) - GITHUB_URL: override GitHub base URL for mirrors/GHE - REPO: override repository (default microsoft/apm) - VERSION: pin a specific version (@v1.2.3 arg or env var), skips GitHub API entirely when set Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR updates the install.sh bootstrap installer to support air-gapped and enterprise environments by allowing callers to override the GitHub host/repo, install location, and (optionally) pin a version to avoid the GitHub API.
Changes:
- Added configurable environment variables:
APM_INSTALL_DIR,GITHUB_URL,REPO, andVERSION(plus@vX.Y.Zpositional argument support). - When
VERSIONis provided, the installer skips the GitHub API and constructs the release download URL directly. - Updated user-facing URLs/messages to use the configurable GitHub base URL.
Comments suppressed due to low confidence (3)
install.sh:224
GITHUB_URLis configurable, but the script still hardcodeshttps://api.github.com/...for the latest release lookup. This will break GitHub Enterprise / mirrored setups whenVERSIONis not provided, and it also conflicts with the PR description mentioningGITHUB_API_URL. Consider adding aGITHUB_API_URL(defaulting tohttps://api.github.com, or derived fromGITHUB_URL) and using it for both unauthenticated and authenticated API calls.
if [ -z "$TAG_NAME" ]; then
# Get latest release info
echo -e "${YELLOW}Fetching latest release information...${NC}"
# Try to fetch release info without authentication first (for public repos)
LATEST_RELEASE=$(curl -s "https://api.github.com/repos/$REPO/releases/latest")
CURL_EXIT_CODE=$?
install.sh:518
- Output strings in this modified block include non-ASCII characters (e.g.,
✓,⚠,🎉). The repo encoding guideline requires printable ASCII only for source/CLI output; please replace these with the project's ASCII status symbols (e.g.[+],[!]) to avoid Windows cp1252UnicodeEncodeErrorissues.
echo -e "${GREEN}✓ APM installed successfully!${NC}"
echo -e "${BLUE}Version: $INSTALLED_VERSION${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 $APM_INSTALL_DIR to your PATH environment variable."
echo "Add this line to your shell profile (.bashrc, .zshrc, etc.):"
echo " export PATH=\"$APM_INSTALL_DIR:\$PATH\""
fi
echo ""
echo -e "${GREEN}🎉 Installation complete!${NC}"
install.sh:12
- The private-repo usage example still hardcodes
https://raw.githubusercontent.com/microsoft/apm/...even thoughREPO/GITHUB_URLare now configurable for GHE/mirrors. Consider either updating these instructions to match the new configuration knobs (or explicitly documenting that these curl examples apply only to github.com).
# For private repositories, use with authentication:
# curl -sSL -H "Authorization: token $GITHUB_APM_PAT" \
# https://raw.githubusercontent.com/microsoft/apm/main/install.sh | \
# GITHUB_APM_PAT=$GITHUB_APM_PAT sh
- Use $HOME instead of ~ in usage example (dash/sh compatibility) - Set AUTH_HEADER_VALUE before download section so private repo auth works even when VERSION skips the API path - mkdir -p APM_INSTALL_DIR before symlink to avoid unnecessary sudo - Update stale comment referencing /usr/local/bin Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Update installation docs and apm-guide skill to cover APM_INSTALL_DIR, GITHUB_URL, REPO, VERSION, and @Version syntax. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
sergio-sisternes-epam
left a comment
There was a problem hiding this comment.
Great contribution, @chkp-roniz! Air-gapped and GHE support for the installer is a real need. A few things to address before we can merge:
Must-fix
-
GitHub API URL is hardcoded —
https://api.github.com/repos/$REPO/releases/latestdoesn't change whenGITHUB_URLpoints to GHE (API lives at$GITHUB_URL/api/v3/...). Either derive aGITHUB_API_URLor document thatVERSIONis mandatory for GHE setups. -
REPOenv var is too generic — Rename toAPM_REPOfor consistency withAPM_INSTALL_DIRand to avoid collisions with the user's environment. -
Missing CHANGELOG entry — Please add a line under
## [Unreleased] / ### Addedper our contributing guide.
Should-fix
-
Document
APM_LIB_DIRderivation — The bundle goes to$(dirname "$APM_INSTALL_DIR")/lib/apm, which is reasonable but undiscoverable. Add it to the docs table so users know where the full binary directory lands. -
Non-ASCII characters (
✓,⚠,🎉) — These pre-date your PR, but since you're touching those lines anyway, would you mind swapping them for ASCII equivalents ([+],[!], etc.)? We keep output within printable ASCII for Windows cp1252 compatibility.
Follow-ups (we'll open issues for these)
-
apm configintegration —install.shis the bootstrap, so env vars are the right call here. But once installed, users will expectapm updateto remember their custom install dir and mirror URL without re-exporting env vars. We should persist these settings viaapm config(e.g.,install-dir,mirror-url) and have the self-update path read them. Env vars would still override for CI/unattended scenarios. -
Windows parity —
install.ps1should support the same configurability (APM_INSTALL_DIR,GITHUB_URL,APM_REPO,VERSION) so Windows users in air-gapped/GHE environments get the same experience.
sergio-sisternes-epam
left a comment
There was a problem hiding this comment.
Apologies, my previous feedback was not fixed yet. Please review my it.
- Rename REPO -> APM_REPO for consistency with APM_INSTALL_DIR - Replace non-ASCII chars (✓ ⚠ 🎉) with ASCII equivalents ([+] [!]) - Add APM_LIB_DIR to docs table and GHE note (VERSION required for air-gapped) - Add CHANGELOG entry under [Unreleased] / Added Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
danielmeppiel
left a comment
There was a problem hiding this comment.
Thanks @chkp-roniz — this is a real user need and the diff is in good shape overall. CI is green and most of @sergio-sisternes-epam's earlier feedback is addressed (APM_REPO rename, CHANGELOG entry, docs table). Three small items to fix before merge:
Must-fix
1. APM_LIB_DIR is documented as overridable but the script ignores user-supplied values.
The docs table in installation.md lists APM_LIB_DIR with a default of $(dirname APM_INSTALL_DIR)/lib/apm, implying users can override it. But in install.sh:
APM_LIB_DIR="$(dirname "$APM_INSTALL_DIR")/lib/apm"This unconditionally overwrites any value the user sets. Either:
- Honour the env var:
APM_LIB_DIR="${APM_LIB_DIR:-$(dirname "$APM_INSTALL_DIR")/lib/apm}", or - Change the doc row to mark it derived/read-only.
The first option is more useful — a user who sets APM_INSTALL_DIR=$HOME/bin today gets the bundle at $HOME/lib/apm, which is surprising.
2. Status symbols are missing the trailing space.
Per the codebase convention (STATUS_SYMBOLS in src/apm_cli/utils/console.py), bracket symbols are always followed by a single space. Current output reads:
[+]APM installed successfully!
[!]APM installed but not found in PATH
[!]Compatibility Issue Detected
[!]glibc version incompatibility detected
[!]Container/Dev Container environment detected
[+]Download successful
[+]Extraction successful
[+]Binary test successful
Should be [+] APM installed successfully!, etc. ~10 sites in install.sh.
3. Spurious "will need sudo" notice for non-existent custom dirs.
The writability check runs before mkdir -p:
if [ ! -w "$APM_INSTALL_DIR" ]; then
echo -e "${YELLOW}Note: Will need sudo permissions to install to $APM_INSTALL_DIR${NC}"
fiIf a user sets APM_INSTALL_DIR=$HOME/.local/bin and that dir doesn't exist yet, [ ! -w ... ] is true and they see a misleading sudo warning even though mkdir -p later succeeds without it. Suggest:
if [ -e "$APM_INSTALL_DIR" ] && [ ! -w "$APM_INSTALL_DIR" ]; then
...
fiAcceptable as-is
- Hardcoded
api.github.comfor latest-release lookup. The doc note explicitly tells GHE/air-gapped users to setVERSION=, which is a reasonable workaround for a bootstrap installer. A properGITHUB_API_URL(or derivation fromGITHUB_URL) would be cleaner but is fine as a follow-up.
Nits (optional)
- The header comment block (lines 4–6) documents
APM_INSTALL_DIR,GITHUB_URL, and@v1.2.3but omitsAPM_REPOandVERSIONenv var. Quick consistency tidy.
Follow-ups (not for this PR)
Sergio's earlier suggestions still stand for separate issues:
apm configintegration soapm updateremembers custom install dir / mirror without re-exporting env vars.install.ps1parity for Windows users in air-gapped/GHE environments.
Once items 1–3 land, ping a maintainer to dismiss the stale CHANGES_REQUESTED and approve.
…mbol spacing, writability guard - Honour user-supplied APM_LIB_DIR (was unconditionally overwritten, contradicting the docs table that lists it as overridable). - Restore the trailing space after every [+] / [!] bracket symbol to match the STATUS_SYMBOLS convention in src/apm_cli/utils/console.py (13 sites). - Skip the "will need sudo" notice when APM_INSTALL_DIR doesn't exist yet — mkdir -p handles that case without elevation, so the warning was misleading for the common $HOME/.local/bin override. - Header comment block now mentions VERSION and APM_REPO env vars to match the docs table. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
# Conflicts: # CHANGELOG.md
APM Review Panel VerdictDisposition: APPROVE (two minor optional follow-ups noted below; no required pre-merge actions) Per-persona findingsPython Architect: This PR is purely procedural bash ( OO / class diagram (module boundaries): classDiagram
direction LR
class install_sh {
<<IOBoundary>>
+APM_REPO string
+APM_INSTALL_DIR string
+GITHUB_URL string
+VERSION string
+APM_LIB_DIR string
+check_python_requirements()
+try_pip_installation()
}
class GitHubReleasesAPI {
<<ExternalService>>
+api.github.com repos releases latest
}
class GitHubDownloadHost {
<<ExternalService>>
+GITHUB_URL/APM_REPO/releases/download/TAG/BINARY
}
class LocalFilesystem {
<<IOBoundary>>
+APM_LIB_DIR binary bundle dir
+APM_INSTALL_DIR symlink dir
}
class install_sh:::touched
classDef touched fill:#fff3b0,stroke:#d47600
install_sh ..> GitHubReleasesAPI : curl (skipped when VERSION set)
install_sh ..> GitHubDownloadHost : curl -L download
install_sh ..> LocalFilesystem : cp + ln -sf
note for install_sh "Pure procedural bash. No Python classes in scope."
Execution-flow diagram: flowchart TD
A([curl pipe sh install.sh]) --> B{VERSION env set\nor `@v` arg present?}
B -- yes --> C[VERSION=arg strip @\nTAG_NAME=VERSION\nDOWNLOAD_URL=GITHUB_URL/APM_REPO/releases/download/TAG/BINARY]
B -- no --> D["[NET] curl api.github.com/repos/APM_REPO/releases/latest"]
C --> E["[NET] curl -L DOWNLOAD_URL"]
D --> F{API response valid?}
F -- Not Found or empty --> G["[NET] curl with Authorization: token AUTH_HEADER_VALUE"]
F -- yes --> H[grep TAG_NAME\nDOWNLOAD_URL=GITHUB_URL/APM_REPO/releases/download/TAG/BINARY]
G --> H
H --> I["[+] Latest version TAG_NAME\nDownload URL DOWNLOAD_URL"]
I --> E
E --> J{curl exit 0?}
J -- no + AUTH_HEADER_VALUE --> K["[NET] curl ASSET_URL with auth or DOWNLOAD_URL with auth"]
J -- yes --> L["[FS] tar -xzf TMP_DIR/BINARY"]
K --> L
L --> M["[EXEC] EXTRACTED_DIR/apm --version"]
M --> N{Binary test exit 0?}
N -- no + glibc error --> O["[EXEC] try_pip_installation()"]
N -- yes --> P["[FS] rm -rf APM_LIB_DIR\nmkdir -p APM_LIB_DIR\ncp -r EXTRACTED/* APM_LIB_DIR/"]
P --> Q["[FS] mkdir -p APM_INSTALL_DIR\nln -sf APM_LIB_DIR/apm APM_INSTALL_DIR/apm"]
Q --> R["[+] APM installed successfully!"]
O --> Z([Done])
R --> Z
Design patterns
Structural notes: (1) The name collision between the old CLI Logging Expert: All emoji-to-ASCII symbol replacements are correct and complete: DevX UX Expert: Strong UX improvement for enterprise and CI users. Supply Chain Security Expert: No new critical attack surfaces opened. Detailed scan:
Binary SHA256 checksum verification remains the strongest available improvement to installer integrity posture -- recommend as a follow-up issue independent of this PR. Auth Expert: Not activated -- files touched are OSS Growth Hacker: High-value growth unlock. Air-gapped and GHE mirror support directly removes the top enterprise blocker: "we cannot use it because our network cannot reach github.com at install time." Regulated industries (finance, healthcare, defense) are high-value early adopters with strong community influence. External contributor VERSION pinning makes Story angle: "Install APM anywhere -- including behind your firewall. Side-channel to CEO: recommend a release note and short social post leading with the air-gapped one-liner. Credit the external contributor by handle. This is also an opportunity to add an "enterprise install" section to the docs site that aggregates CEO arbitrationAll five mandatory specialists converge on APPROVE with no disagreements to arbitrate. The change is additive, non-breaking, and correctly documented. The external contributor ( Required actions before mergeNone. Optional follow-ups
|
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* chore(release): cut 0.9.4 CHANGELOG entry for 0.9.4 covers all 7 PRs merged since v0.9.3: - #974 SKILL_BUNDLE day-0 install parity (Added) - #954 automate apm-triage-panel workflow (Added) - #970 python-architect mermaid classDiagram trap (Changed) - #911 REQUESTS_CA_BUNDLE TLS validation (Fixed) - #971 triage-panel project-sync dispatch (Fixed) - #910 CLI consistency cleanup (Fixed) - #958 issue templates label taxonomy (Fixed) - #953 docs auto-deploy after bot-cut releases (Fixed) Open milestone 0.9.4 issues (41) reassigned to 0.9.5. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * chore(changelog): tighten 0.9.4 entries (so-what for developers) Refactor per Keep-a-Changelog spirit: lead with developer impact, trim agent-internals prose, group maintainer-only changes. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * chore(changelog): add #660 install.sh air-gapped entry to 0.9.4 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Summary
APM_INSTALL_DIRenv var to override install path (default/usr/local/bin)GITHUB_URLenv var to override GitHub base URL for mirrors/GHEREPOenv var to override repository (defaultmicrosoft/apm)VERSIONenv var or@v1.2.3arg to pin a specific version — skips GitHub API entirely when setUsage
Test plan
GITHUB_URL+VERSION=v0.8.10— downloads from mirror, skips APIAPM_INSTALL_DIR=$HOME/.local/bin— installs to custom dir without sudobash -n install.shsyntax check passeshttps://github.bokerqi.topoutside Configuration defaults🤖 Generated with Claude Code