feat: persist transport flags via apm config#1308
Conversation
There was a problem hiding this comment.
Pull request overview
Adds support for persisting transport-selection preferences via apm config, so users no longer need to re-specify --ssh / --allow-protocol-fallback (or set env vars) on every apm install. This extends the existing config surface and wires the effective-value resolution into the install transport-selection path.
Changes:
- Introduces new persisted config keys:
allow-protocol-fallbackandssh, plus helpers that resolveenv > config > default. - Wires
apm installtransport selection to read the effective preference/fallback from config helpers when CLI flags are not provided. - Expands
apm configCLI support (valid keys, set/get/list output) and adds docs + unit tests for precedence and CLI behavior.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
src/apm_cli/config.py |
Adds stored getters/setters for transport flags and env/config resolution helpers used by install. |
src/apm_cli/commands/install.py |
Uses config helpers to resolve protocol preference and fallback behavior when CLI flags are absent. |
src/apm_cli/commands/config.py |
Registers new keys for apm config set/get and includes them in listing output and valid-keys messaging. |
tests/unit/test_config_command.py |
Adds unit tests for new config keys, CLI routing, and listing/valid-keys behavior. |
tests/unit/test_protocol_config_precedence.py |
Adds precedence-focused tests for CLI/env/config/default resolution and round-trip config persistence. |
docs/src/content/docs/reference/environment-variables.md |
Documents new transport env vars and how they relate to persisted apm config keys. |
docs/src/content/docs/reference/cli/config.md |
Documents new config keys, their precedence, and usage examples. |
Comments suppressed due to low confidence (3)
docs/src/content/docs/reference/environment-variables.md:41
- Documentation mismatch: this table says APM_GIT_PROTOCOL only accepts 'ssh' and 'https', but the implementation also accepts 'http' (and ProtocolPreference.from_str maps 'http' to HTTPS). Either document 'http' as accepted (and clarify how it's treated), or reject it in get_apm_protocol_pref() for consistency.
| Variable | Purpose | Default | Notes |
|---|---|---|---|
| `APM_GIT_PROTOCOL` | Preferred clone protocol for shorthand (`owner/repo`) dependencies. Accepted values: `ssh`, `https`. | unset | Equivalent to `--ssh` / `--https` flag. Resolution: CLI flag → env var → `ssh` key in `~/.apm/config.json` → git `insteadOf` rules → HTTPS. |
| `APM_ALLOW_PROTOCOL_FALLBACK` | Set to `1` (or `true`/`yes`/`on`) to enable the legacy cross-protocol fallback chain. When enabled, a failed clone is retried with the opposite protocol. | unset | Equivalent to `--allow-protocol-fallback`. Resolution: CLI flag → env var → `allow-protocol-fallback` key in `~/.apm/config.json` → `false`. |
tests/unit/test_protocol_config_precedence.py:185
- Non-ASCII Unicode right-arrow characters (U+2192) are used in this comment ('... -> fallthrough ...'), violating the printable-ASCII-only rule for source files. Replace with ASCII '->'.
def test_unrecognised_env_val_falls_through_to_config(self):
"""An unrecognised APM_GIT_PROTOCOL value falls through to config."""
import apm_cli.config as cfg_module
with (
patch.object(cfg_module, "get_ssh", return_value=True),
patch.dict(os.environ, {"APM_GIT_PROTOCOL": "git"}),
):
# 'git' is not in (ssh, https, http) → fallthrough to config (ssh=True)
assert cfg_module.get_apm_protocol_pref() == "ssh"
docs/src/content/docs/reference/cli/config.md:73
- This section includes non-ASCII punctuation: an em dash (U+2014) in the list item and a Unicode ellipsis (U+2026) in 'apm config set ...'. Replace with ASCII equivalents (e.g., '-' and '...') to comply with the printable-ASCII-only docs rule.
`allow-protocol-fallback` and `ssh` follow the layered transport precedence:
1. CLI flag (`--allow-protocol-fallback`, `--ssh`) — highest priority
2. Environment variable (`APM_ALLOW_PROTOCOL_FALLBACK=1`, `APM_GIT_PROTOCOL=ssh`)
3. Value in `~/.apm/config.json` (`apm config set …`)
4. Built-in default (`false` / no preference)
|
. Note 🔒 Integrity filter blocked 2 itemsThe following items were blocked because they don't meet the GitHub integrity level.
To allow these resources, lower tools:
github:
min-integrity: approved # merged | approved | unapproved | none
|
APM Review Panel:
|
| Persona | B | R | N | Takeaway |
|---|---|---|---|---|
| Python Architect | 0 | 1 | 2 | Clean, well-scoped feature; env-var constant duplication is the only compounding architectural debt. |
| CLI Logging Expert | 0 | 1 | 3 | New keys integrate cleanly; apm config get shows stored, not effective, value -- debugging friction for the target user segment. |
| DevX UX Expert | 0 | 3 | 2 | Solves a real pain point; key name ssh is ambiguous pre-merge decision; unconditional default-false display adds noise. |
| Supply Chain Security Expert | 0 | 2 | 1 | No blocking supply-chain issues; env-var constant duplication risks silent name-drift; http alias undocumented. |
| OSS Growth Hacker | 0 | 1 | 2 | Genuine friction-killer for SSH-only and corporate users; missing CHANGELOG entry is the highest-leverage gap before ship. |
| Auth Expert | 0 | 2 | 1 | Config layer correctly wired in install.py; ghost call-sites in github_downloader.py and validation.py bypass it entirely. |
| Doc Writer | 0 | 1 | 4 | Docs accurate and well-structured; APM_GIT_PROTOCOL Notes column omits http as accepted alias. |
| Test Coverage Expert | 0 | 2 | 2 | Install pipeline config-layer wiring has no integration test; apm config unset for new keys silently exits 1. |
B = blocking-severity findings, R = recommended, N = nits.
Counts are signal strength, not gates. The maintainer ships.
Top 5 follow-ups
-
[Auth Expert] Ghost call-sites in
github_downloader.pyandvalidation.pybypass the new config layer and silently revert to env-only transport semantics. -- Users who setssh=truein config will get inconsistent transport behavior depending on which internal code path is taken; this is the most user-visible correctness gap in the PR and the only one that could cause silent mis-behavior post-merge. -
[Test Coverage Expert]
apm config unset allow-protocol-fallbackandapm config unset sshsilently exit 1 -- the unset command handler has no branch for these keys and no backend implementation. -- Shipping a config key that cannot be unset without hand-editing JSON breaks a standard CLI contract and will generate support noise. -
[Test Coverage Expert] No integration-tier test validates the full config-to-install wiring -- the user promise "
apm config set ssh truecauses subsequent installs to use SSH" is unguarded at theintegration-with-fixturestier. -- Unit mocks validate the helper in isolation but cannot catch the ghost call-site regression or any future refactor that breaks the wiring. -
[OSS Growth Hacker] No CHANGELOG entry exists for this feature under [Unreleased]. -- The prior SSH/transport feature (Transport Selection v1: honor user-specified transport, no silent downgrade #778) set the changelog precedent that feeds release narrative and the FAQ; missing this entry means the feature ships silently and the existing narrative for Transport Selection v1: honor user-specified transport, no silent downgrade #778 feels incomplete.
-
[DevX UX Expert] Config key
sshis ambiguous -- preferprefer-sshto match npm conventions and signal protocol preference rather than capability toggle. -- Renaming after users have setsshin dotfiles is a breaking change; a deliberate decision now costs only a search-replace, deferring forecloses the option.
Architecture
classDiagram
direction LR
class config {
<<Module>>
+get_allow_protocol_fallback() bool
+set_allow_protocol_fallback(enabled) None
+get_ssh() bool
+set_ssh(enabled) None
+get_apm_allow_protocol_fallback() bool
+get_apm_protocol_pref() str|None
+get_config() dict
+update_config(patch) None
}
note for config "Resolution helpers follow env > config > default"
class transport_selection {
<<Module>>
+ENV_PROTOCOL: str
+ENV_ALLOW_FALLBACK: str
+protocol_pref_from_env() ProtocolPreference
+is_fallback_allowed() bool
}
note for transport_selection "Declares same env-var names as config.py"
class ProtocolPreference {
<<Enum>>
NONE
SSH
HTTPS
+from_str(value) ProtocolPreference
}
class install_cmd {
<<Module>>
+install(...)
}
class config_cmd {
<<Module>>
+VALID_KEYS: dict
+cmd_config_set(...)
+cmd_config_get(...)
}
install_cmd ..> config : "get_apm_protocol_pref()\nget_apm_allow_protocol_fallback()"
install_cmd ..> transport_selection : "ProtocolPreference"
transport_selection *-- ProtocolPreference : defines
config_cmd ..> config : "set_allow_protocol_fallback()\nset_ssh()"
class config:::touched
class install_cmd:::touched
class config_cmd:::touched
classDef touched fill:#fff3b0,stroke:#d47600
flowchart TD
A([apm install --ssh / --allow-protocol-fallback / neither]) --> B{CLI flag set?}
B -- yes --> C[ProtocolPreference.SSH / .HTTPS CLI flag wins]
B -- no --> D[config.get_apm_protocol_pref]
D --> E{APM_GIT_PROTOCOL env set?}
E -- ssh/https/http --> F[return env value]
E -- not set --> G{config.get_ssh from config.json}
G -- true --> H[return ssh]
G -- false --> I[return None - ProtocolPreference.NONE]
F --> J[ProtocolPreference.from_str]
H --> J
I --> J
C --> K{allow_protocol_fallback CLI flag set?}
J --> K
K -- no --> L[config.get_apm_allow_protocol_fallback]
L --> M{APM_ALLOW_PROTOCOL_FALLBACK env set?}
M -- 1/true/yes/on --> N[allow_protocol_fallback = True]
M -- not set --> O{config allow_protocol_fallback key}
O -- true --> N
O -- false --> P[allow_protocol_fallback = False]
K -- yes --> N
N --> Q[TransportSelector.select]
P --> Q
sequenceDiagram
actor User
participant CLI as apm install
participant Config as config.py
participant FS as ~/.apm/config.json
participant TS as TransportSelector
User->>CLI: apm config set ssh true
CLI->>Config: set_ssh(True)
Config->>FS: write {"ssh": true}
User->>CLI: apm install pkg
CLI->>Config: get_apm_protocol_pref()
Config->>FS: read config
FS-->>Config: {"ssh": true}
Config-->>CLI: "ssh"
CLI->>TS: ProtocolPreference.from_str("ssh") - SSH
CLI->>Config: get_apm_allow_protocol_fallback()
Config-->>CLI: False
CLI->>TS: select(pref=SSH, fallback=False)
TS-->>CLI: git clone via SSH
Recommendation
Address the two functional gaps -- ghost call-sites in github_downloader.py / validation.py and the broken apm config unset handler -- before this merges. Both are straightforward fixes: wire the config helpers into GithubDownloader's default-arg resolution path (mirroring the install.py change), add unset_ssh / unset_allow_protocol_fallback functions to config.py, and add the corresponding unset branches to the config command handler. Add a CHANGELOG entry under [Unreleased] in the same pass. Once those three items land, the maintainer should make a deliberate call on the ssh vs prefer-ssh naming question before merge -- that is the one decision that cannot be cleanly deferred. The remaining panel findings (integration test, effective-value display in apm config get, http alias doc, constant extraction, output noise) are solid post-merge follow-up material.
Full per-persona findings
Python Architect
-
[recommended] Env-var name constants are duplicated across two modules -- extract to a shared leaf at
src/apm_cli/config.py:9
APM_ALLOW_PROTOCOL_FALLBACKandAPM_GIT_PROTOCOLare declared asENV_ALLOW_FALLBACK/ENV_PROTOCOLintransport_selection.pyAND as_ENV_ALLOW_PROTOCOL_FALLBACK/_ENV_GIT_PROTOCOLinconfig.py. If either set is renamed, the two modules silently read different environment variables with no compiler or test catching the divergence. Fix: a zero-dependencysrc/apm_cli/deps/_env_keys.py(no imports) lets both modules import safely without a circular-import cycle.
Suggested: Createsrc/apm_cli/deps/_env_keys.pywithENV_PROTOCOL = "APM_GIT_PROTOCOL"andENV_ALLOW_FALLBACK = "APM_ALLOW_PROTOCOL_FALLBACK", then import from it in both modules. -
[nit]
get_apm_protocol_pref()can leak the raw string"http"-- normalize to"https"atsrc/apm_cli/config.py:220
Docstring promises"ssh","https", orNone.APM_GIT_PROTOCOL=httpreturns raw"http".ProtocolPreference.from_strabsorbs it correctly today, but any future caller pattern-matching on the return value will silently mis-handle it.
Suggested:return "https" if env_val == "http" else env_val -
[nit] Two unjustified function-body lazy imports in
install.py:1307
config.pyis a leaf module with no circular-import risk. The underscore-alias inline import pattern signals "circular import workaround" -- using it without a cycle inverts the convention's meaning for future readers.
CLI Logging Expert
-
[recommended]
apm config get/ show surfaces stored value, not effective value atsrc/apm_cli/commands/config.py
get_allow_protocol_fallback()/get_ssh()are called for display. If a user hasAPM_ALLOW_PROTOCOL_FALLBACK=1in their shell butfalseinconfig.json,apm config get allow-protocol-fallbackprintsFalsewhileapm installhonoursTrue. Call the env-resolving wrappers (get_apm_allow_protocol_fallback/get_apm_protocol_pref) for display, with source annotation when the env var wins. -
[nit] Fallback-path labels use raw kebab-case key, inconsistent with surrounding labels
Surrounding fallback lines use Title Case (APM CLI Version,Temp Directory). New lines emitallow-protocol-fallback:andssh:as raw keys. -
[nit] Both new keys shown unconditionally at default
False, adding noise to every user's output
Follow thetemp-dirpattern: only show when the value is non-default.apm config get <key>can still show the explicit value on direct request. -
[nit] Python
str(bool)rendersTrue/False; convention expectstrue/false
Config values are documented as lowercase booleans in the docs table. Usestr(value).lower()at output sites.
DevX UX Expert
-
[recommended] Config key
sshis ambiguous -- preferprefer-sshatsrc/apm_cli/commands/config.py
The bare keysshreads as a feature toggle. npm uses theprefer-*pattern for protocol preference keys (prefer-offline,prefer-dedupe).prefer-sshmaps immediately onto the mental model. Renaming after users have setsshin dotfiles is a breaking change.
Suggested: Rename config key toprefer-ssh(internal:prefer_ssh). Keep--sshas the CLI flag unchanged. -
[recommended]
apm configalways prints both keys atFalse, adding noise to every user's output
Violates "quiet on the happy path" -- noise for the 95% of users who have never changed these settings. Follow thetemp-dirpattern. -
[recommended] SSH auth error path doesn't hint at
apm config set ssh trueatsrc/apm_cli/commands/install.py
User who encountersPermission denied (publickey)or HTTPS clone failure has no way to discover the config key without reading docs. npm prints actionable hints at the point of failure.
Suggested: Add a transport-error hint in the clone failure branch pointing atapm config set ssh true. -
[nit] Docs example resets preference with
set ssh falseinstead ofunsetatdocs/src/content/docs/reference/cli/config.md
apm config unset sshis idiomatic;set falseleaves an explicitfalseinconfig.jsonwhileunsetremoves the entry entirely. -
[nit]
allow-protocol-fallbackcalled "legacy" in PR body but docs don't signal deprecation
If genuinely legacy, add a deprecation note so users know to prefer the explicit--sshpath going forward.
Supply Chain Security Expert
-
[recommended] Env-var constant duplication risks silent name-drift at
src/apm_cli/config.py
_ENV_ALLOW_PROTOCOL_FALLBACKand_ENV_GIT_PROTOCOLare copy-pasted strings inconfig.py(lines 11-12) whiletransport_selection.pydeclaresENV_ALLOW_FALLBACKandENV_PROTOCOLwith the same string values. No compile-time or test-time assertion ensures they stay in sync.
Suggested: Add a standalone test assertingconfig._ENV_ALLOW_PROTOCOL_FALLBACK == transport_selection.ENV_ALLOW_FALLBACK, or refactor into a zero-dependency_env_keys.pymodule. -
[recommended]
get_apm_protocol_pref()accepts and returns"http"but docstring omits it atsrc/apm_cli/config.py:220
ProtocolPreference.from_strmaps"http"to HTTPS correctly today, but any future call-site using the raw string would open an unauthenticated HTTP clone path. Normalize"http"to"https"before returning, or drop it from the accepted set. -
[nit] Raw config value for
allow_protocol_fallbackreturned without bool-coercion atsrc/apm_cli/config.py:202
A manually editedconfig.jsonwith string"yes"or integer1activates the flag without going through the env-var truthy-set check. Wrap inbool()or validate onset_allow_protocol_fallback.
OSS Growth Hacker
-
[recommended] Missing CHANGELOG entry in [Unreleased] at
CHANGELOG.md
The prior SSH/transport feature (Transport Selection v1: honor user-specified transport, no silent downgrade #778) got a detailed changelog line. Suggested: "apm config set ssh trueandapm config set allow-protocol-fallback truepersist transport preferences to~/.apm/config.json; SSH-only and corporate users no longer need to pass--sshon everyapm installinvocation. Closes Support persisting CLI flags viaapm config(e.g.allow-protocol-fallback,ssh) #1243. (feat: persist transport flags via apm config #1308)" -
[nit] No first-run hook -- SSH-only users won't find this without reading docs
Consider a one-sentence callout on theapm installreference page pointing atapm config set ssh true. -
[nit] Transport section placement on env-vars page buries the lead for new users
Moving "Transport and protocol" above "Registry (MCP and proxy)" keeps it closer to the auth/credential content where frustrated SSH users are already looking.
Auth Expert
-
[recommended] Ghost call-sites bypass config layer:
github_downloader.pyandvalidation.pystill callprotocol_pref_from_env()/is_fallback_allowed()directly atsrc/apm_cli/deps/github_downloader.py
GithubDownloader.__init__falls back toprotocol_pref_from_env()andis_fallback_allowed()when the caller passesprotocol_pref=None/allow_fallback=None. These functions read only the env var -- they never consult~/.apm/config.json. Any caller that constructsGithubDownloaderwithout explicit values silently reverts to env-only semantics.validation.py:317has the same gap foris_fallback_allowed(). -
[recommended]
get_apm_protocol_pref()silently accepts"http"but docstring promises only"ssh"/"https"atsrc/apm_cli/config.py:220
APM_GIT_PROTOCOL=httpreturns raw"http". Ininstall.pythis is immediately fed toProtocolPreference.from_str("http")which maps it toProtocolPreference.HTTPS-- no credentials travel over cleartext HTTP through this path. But if any future code uses the raw return value without passing it throughfrom_str(), it could reach the_HTTPtransport attempt. Reject"http"with a warning, or explicitly document and test the alias. -
[nit] Late
importinside function body for config helpers ininstall.py:1307is unnecessary
config.pyis already imported at the module level ininstall.py; no circular-import risk justifies the inline pattern.
Doc Writer
-
[recommended] APM_GIT_PROTOCOL Notes column omits
"http"as accepted value atdocs/src/content/docs/reference/environment-variables.md
The code accepts"http"andProtocolPreference.from_strmaps it to HTTPS. Change "Accepted values:ssh,https." to "Accepted values:ssh,https,http(alias forhttps)." -
[nit]
apm config unsetsection doesn't explicitly mention new keys are reset viaset false
A reader who triesapm config unset sshwill get an error with no explanation. Add: "This includesallow-protocol-fallbackandssh-- useapm config set allow-protocol-fallback falseorapm config set ssh falseto restore defaults." -
[nit]
allow-protocol-fallbackexample missing reset comment
Thesshexample includes# Reset to default: apm config set ssh falsebut theallow-protocol-fallbackexample does not. Add for consistency. -
[nit] Table ordering -- transport booleans appear between path-type keys
No change required for this PR. If the table grows, consider grouping by type. -
[nit] Transport section placement in env-vars page is reasonable
No change required. Placement after Authentication and before Registry is logical.
Test Coverage Expert
-
[recommended]
apm config unset allow-protocol-fallback/apm config unset sshsilently fails with exit 1
Theunsetcommand handler has explicit branches fortemp-dirandcopilot-cowork-skills-dir.allow-protocol-fallbackandsshhave no branch, soapm config unset allow-protocol-fallbackfalls through tologger.error("Unknown configuration key")andsys.exit(1). Nounset_allow_protocol_fallbackorunset_sshfunctions exist inconfig.py(grep confirmed). No test exercisesrunner.invoke(config, ["unset", "allow-protocol-fallback"]).
Proof (missing at unit):tests/unit/test_config_command.py::TestConfigUnset::test_unset_allow_protocol_fallback_exits_0-- proves: Runningapm config unset allow-protocol-fallbacksucceeds with exit 0, not silently fails with exit 1 [devx]
Suggested: Addunset_allow_protocol_fallbackandunset_sshtoconfig.py, wire them in theunsethandler, add tests. -
[recommended] Install pipeline config-layer wiring has no integration test
The diff ininstall.pyreplacesprotocol_pref_from_env()/is_fallback_allowed()with config helpers. All tests are unit-level withpatch.objectmocks.tests/integration/test_transport_selection_integration.pyexists but grep forconfig/set_ssh/get_apm_protocol_prefreturned 0 hits.
Proof (missing at integration-with-fixtures):tests/integration/test_transport_selection_integration.py::test_config_ssh_true_routes_install_to_ssh_transport-- proves:apm config set ssh truecausesapm installto attempt SSH URLs -- the full config-to-install wiring is live, not just the helper in isolation [portability-by-manifest, devx]
Suggested: Add fixture-based integration test: set_ssh(True) against isolated config file, invoke install command, assert attempted clone URL starts withgit@or `(redacted) -
[nit]
APM_GIT_PROTOCOL=httppath is untested
config.py:220accepts"http". Grep of test files forAPM_GIT_PROTOCOL.*httpreturned 0 hits.
Proof (missing at unit):tests/unit/test_protocol_config_precedence.py::TestProtocolPrefPrecedence::test_env_var_http_returns_http_string -
[nit] Precedence tests simulate CLI flag with Python variable, not real CLI invocation
Tests liketest_cli_flag_true_wins_over_everythingsetcli_flag = Trueand computecli_flag or get_apm_allow_protocol_fallback(). Correctly tests the OR expression ininstall.pybut does not verify Click wires the--allow-protocol-fallbackoption to the parameter.
Proof (passed at unit):tests/unit/test_protocol_config_precedence.py::TestAllowProtocolFallbackPrecedence::test_cli_flag_true_wins_over_everything-- proves: OR expression short-circuits correctly when CLI flag is True [devx]
This panel is advisory. It does not block merge. Re-apply the
panel-review label after addressing feedback to re-run.
Note
🔒 Integrity filter blocked 2 items
The following items were blocked because they don't meet the GitHub integrity level.
- feat: persist transport flags via apm config #1308
pull_request_read: has lower integrity than agent requires. The agent cannot read data with integrity below "approved". - #1308
pull_request_read: has lower integrity than agent requires. The agent cannot read data with integrity below "approved".
To allow these resources, lower min-integrity in your GitHub frontmatter:
tools:
github:
min-integrity: approved # merged | approved | unapproved | noneGenerated by PR Review Panel for issue #1308 · ● 4.5M · ◷
|
The copilot's panel-review flagged that the config key name |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 10 out of 10 changed files in this pull request and generated 10 comments.
Comments suppressed due to low confidence (2)
tests/unit/test_protocol_config_precedence.py:194
- This comment contains a non-ASCII Unicode arrow. Repo encoding guidance requires printable ASCII in source files; replace with ASCII (e.g., '->').
patch.dict(os.environ, {"APM_GIT_PROTOCOL": "git"}),
):
# 'git' is not in (ssh, https, http) → fallthrough to config (ssh=True)
assert cfg_module.get_apm_protocol_pref() == "ssh"
docs/src/content/docs/reference/cli/config.md:73
- This section introduces non-ASCII characters (an em dash and a Unicode ellipsis in
apm config set ...). Repo encoding guidance requires printable ASCII in docs; replace with ASCII equivalents (e.g., '-' and '...').
`allow-protocol-fallback` and `ssh` follow the layered transport precedence:
1. CLI flag (`--allow-protocol-fallback`, `--ssh`) — highest priority
2. Environment variable (`APM_ALLOW_PROTOCOL_FALLBACK=1`, `APM_GIT_PROTOCOL=ssh`)
3. Value in `~/.apm/config.json` (`apm config set …`)
4. Built-in default (`false` / no preference)
| @@ -4,6 +4,13 @@ | |||
| import os | |||
| from typing import Optional # noqa: F401 | |||
| # ``APM_ALLOW_PROTOCOL_FALLBACK=1`` env var (the same escape-hatch | ||
| # the clone path honors) restores the legacy permissive chain. |
| # ``apm config set ssh true`` is honoured even when the downloader | ||
| # is constructed without explicit args (e.g. in validation.py). | ||
| from ..config import get_apm_protocol_pref as _get_pref | ||
| from .transport_selection import ProtocolPreference |
| ### Added | ||
|
|
||
| - `apm config set ssh true` / `apm config set allow-protocol-fallback true` persist transport preferences to `~/.apm/config.json` so SSH-only and corporate GHES users no longer need to re-pass `--ssh` / `--allow-protocol-fallback` (or export env vars in shell profiles) on every `apm install`. Resolution order: CLI flag > `APM_GIT_PROTOCOL` / `APM_ALLOW_PROTOCOL_FALLBACK` env var > `apm config` > built-in default. `apm config unset ssh` and `apm config unset allow-protocol-fallback` remove the persisted value. (#1243) | ||
|
|
| def _make_mock_protocol_preference(): | ||
| """Return a fresh ProtocolPreference-like enum from the real module.""" | ||
| from apm_cli.deps.transport_selection import ProtocolPreference | ||
|
|
||
| return ProtocolPreference | ||
|
|
||
|
|
| Resolution order: | ||
| 1. ``APM_GIT_PROTOCOL`` environment variable | ||
| (``"ssh"``, ``"https"``, or ``"http"`` — ``"http"`` is treated | ||
| as an alias for ``"https"`` by the transport selector) |
| Controls how APM clones packages from Git hosts. These settings can also be persisted via [`apm config set`](./cli/config/) to avoid repeating flags or environment-variable exports. | ||
|
|
||
| | Variable | Purpose | Default | Notes | | ||
| |---|---|---|---| | ||
| | `APM_GIT_PROTOCOL` | Preferred clone protocol for shorthand (`owner/repo`) dependencies. Accepted values: `ssh`, `https`. | unset | Equivalent to `--ssh` / `--https` flag. Resolution: CLI flag → env var → `ssh` key in `~/.apm/config.json` → git `insteadOf` rules → HTTPS. | | ||
| | `APM_ALLOW_PROTOCOL_FALLBACK` | Set to `1` (or `true`/`yes`/`on`) to enable the legacy cross-protocol fallback chain. When enabled, a failed clone is retried with the opposite protocol. | unset | Equivalent to `--allow-protocol-fallback`. Resolution: CLI flag → env var → `allow-protocol-fallback` key in `~/.apm/config.json` → `false`. | |
| | `temp-dir` | path | system temp | Directory used for clone and download operations. Useful when the OS temp directory is locked down (for example, corporate Windows endpoints rejecting `%TEMP%` with `[WinError 5]`). | | ||
| | `allow-protocol-fallback` | boolean | `false` | Enable the legacy cross-protocol fallback chain. When true, APM retries a failed clone with the opposite protocol (SSH→HTTPS or HTTPS→SSH). Equivalent to `--allow-protocol-fallback` or `APM_ALLOW_PROTOCOL_FALLBACK=1`. | | ||
| | `ssh` | boolean | `false` | Prefer SSH transport for shorthand (`owner/repo`) dependencies. Equivalent to `--ssh` or `APM_GIT_PROTOCOL=ssh`. | |
| ): | ||
| os.environ.pop("APM_GIT_PROTOCOL", None) | ||
| # CLI flag path: use_ssh=True → ProtocolPreference.SSH without calling helper | ||
| # Just ensure the helper returns None here (not ssh) | ||
| assert cfg_module.get_apm_protocol_pref() is None |
| def _valid_config_keys() -> str: | ||
| """Return valid config keys for messages.""" | ||
| from ..core.experimental import is_enabled | ||
|
|
||
| keys = ["auto-integrate", "temp-dir"] | ||
| keys = ["auto-integrate", "temp-dir", "allow-protocol-fallback", "ssh"] | ||
| if is_enabled("copilot_cowork"): | ||
| keys.append("copilot-cowork-skills-dir") | ||
| return ", ".join(keys) |
danielmeppiel
left a comment
There was a problem hiding this comment.
Please rename key to prefer-ssh - thank you!
I have made the changes @danielmeppiel but the ruff checks are failing due to changes unrelated to this PR.
|
APM Review Panel:
|
| Persona | B | R | N | Takeaway |
|---|---|---|---|---|
| Python Architect | 0 | 2 | 2 | Solid env>config>default layering; stale display-name entry, duplicated env-var strings, and repeated unset boilerplate are the three concrete clean-ups worth making before the module grows further. |
| CLI Logging Expert | 0 | 3 | 2 | Two recommended fixes (always-on bool display adds noise; Python-capitalised True/False breaks CLI convention) plus one stale dead-code entry to remove; fallback-path label inconsistency is a nit. |
| DevX UX Expert | 1 | 3 | 1 | One correctness bug in the display-name map, one doc contradiction on unset, one stale env-var resolution string, and a protocol asymmetry gap that will confuse HTTPS-preferring users. |
| Supply Chain Security Expert | 0 | 3 | 1 | Defaults are secure (False); three recommended hardening gaps: config dir world-readable, stale FALLBACK_HINT, and silent CI persistence risk. One dead-code nit. No blocking issues. |
| OSS Growth Hacker | 0 | 2 | 2 | Solid friction-reducer for SSH/enterprise users; authentication.md still shows the old env-var escape hatch without mentioning the new persistent config alternative -- a quiet conversion miss. |
| Auth Expert | 0 | 1 | 1 | No auth bypasses or security regressions; config-aware transport resolution is consistent with AuthResolver initialization and env-var precedence is preserved. |
| Doc Writer | 2 | 1 | 1 | One blocking correctness bug (wrong config key name in env-var doc), one internal contradiction in config.md's unset description vs examples, one CHANGELOG structure issue, and a nit. |
| Test Coverage Expert | 0 | 2 | 1 | Precedence-chain unit coverage is solid; the persisted-config branch of the install-pipeline integration test is missing at the floor tier. |
B = blocking-severity findings, R = recommended, N = nits.
Counts are signal strength, not gates. The maintainer ships.
Top 5 follow-ups
- [Doc Writer] (blocking-severity) Fix environment-variables.md: change 'ssh key' to 'prefer-ssh key' in APM_GIT_PROTOCOL resolution chain -- Users who hand-edit config.json following the current docs will silently set an unrecognised key and see no effect. Direct user-harm correctness bug; should be fixed before merge.
- [Doc Writer] (blocking-severity) Fix config.md line 48: unset description must include prefer-ssh and allow-protocol-fallback as valid targets -- The current text contradicts the code examples two pages later. A user reading the description will believe unset does not work for boolean keys and reach for the wrong workaround.
- [Supply Chain Security Expert] Set mode=0o700 in os.makedirs and chmod 0o600 on config.json after initial creation -- One-liner fix; world-readable config on a multi-user host leaks allow_protocol_fallback state. Low effort, real hardening.
- [Test Coverage Expert] Add integration-with-fixtures tests for (1) config-file path of allow-protocol-fallback through _validate_package_exists and (2) constructor config-aware SSH default -- Both evidence blocks returned 'missing' on an install-pipeline surface. Unit mocks cover the logic but not the wiring; a future refactor could silently break either path.
- [CLI Logging Expert] Suppress False rows in 'config show' and render booleans as lowercase true/false -- Always-visible False rows add noise for users who never configured transport. Python-capitalised True/False breaks CLI convention. Both are low-effort polish fixes that reinforce the pragmatic-as-npm positioning.
Architecture
classDiagram
direction TB
class config_module {
<<Module>>
+get_apm_allow_protocol_fallback() bool
+get_apm_protocol_pref() str|None
+get_allow_protocol_fallback() bool
+set_allow_protocol_fallback(bool)
+unset_allow_protocol_fallback()
+get_prefer_ssh() bool
+set_prefer_ssh(bool)
+unset_prefer_ssh()
-_ENV_ALLOW_PROTOCOL_FALLBACK str
-_ENV_GIT_PROTOCOL str
}
note for config_module "env > config > default resolver\nenv-var strings duplicated from transport_selection"
class transport_selection_module {
<<Module>>
+is_fallback_allowed(cli_flag, env) bool
+protocol_pref_from_env(env) ProtocolPreference
+ENV_ALLOW_FALLBACK str
+ENV_PROTOCOL str
}
note for transport_selection_module "ENV_ALLOW_FALLBACK / ENV_PROTOCOL\nduplicate config_module private constants"
class TransportSelector {
<<PureDecisionEngine>>
+select(host, pref, allow_fallback) TransportPlan
}
class GitHubPackageDownloader {
<<Facade>>
-_protocol_pref ProtocolPreference
-_allow_fallback bool
-_transport_selector TransportSelector
+__init__(auth_resolver, transport_selector, protocol_pref, allow_fallback)
}
note for GitHubPackageDownloader "Deferred imports of get_apm_protocol_pref\nand get_apm_allow_protocol_fallback in __init__\nwork around circular import"
class commands_config_module {
<<CLIGroup>>
+set(key, value)
+get(key)
+unset(key)
-_CONFIG_KEY_DISPLAY_NAMES dict
}
note for commands_config_module "_CONFIG_KEY_DISPLAY_NAMES has stale\n'ssh':'ssh' entry; missing 'prefer_ssh':'prefer-ssh'"
class config_module:::touched
class commands_config_module:::touched
class GitHubPackageDownloader:::touched
class transport_selection_module:::touched
classDef touched fill:#fff3b0,stroke:#d47600
config_module ..> transport_selection_module : duplicates env-var strings
GitHubPackageDownloader *-- TransportSelector : owns
GitHubPackageDownloader ..> config_module : deferred import in __init__
commands_config_module ..> config_module : lazy imports via _get_config_setters/getters
flowchart TD
A(["CLI: apm install / apm config set prefer-ssh true"]) --> B
subgraph commands_config ["commands/config.py"]
B["unset / set / get subcommand"] --> C["_get_config_setters() / _get_config_getters()\n[lazy import from config.py]"]
end
subgraph config_py ["config.py"]
C --> D["set_prefer_ssh(bool) / set_allow_protocol_fallback(bool)\nupdate_config({key: val})\n[FS] writes ~/.apm/config.json"]
E["get_apm_protocol_pref()"] --> F{"APM_GIT_PROTOCOL env set?"}
F -- yes --> G["return env value\n(ssh/https/http)"]
F -- no --> H{"get_prefer_ssh() == True?"}
H -- yes --> I["return 'ssh'"]
H -- no --> J["return None"]
K["get_apm_allow_protocol_fallback()"] --> L{"_parse_allow_protocol_fallback_env\n(APM_ALLOW_PROTOCOL_FALLBACK) is not None?"}
L -- yes --> M["return parsed bool"]
L -- no --> N["get_allow_protocol_fallback()\n[FS] reads ~/.apm/config.json via get_config()"]
end
subgraph downloader ["deps/github_downloader.py"]
O["GitHubPackageDownloader.__init__\nprotocol_pref=None, allow_fallback=None"] --> P["[deferred import] get_apm_protocol_pref()"]
O --> Q["[deferred import] get_apm_allow_protocol_fallback()"]
P --> E
Q --> K
P --> R["ProtocolPreference.from_str(pref_str)"]
Q --> S["self._allow_fallback = bool"]
end
subgraph validation ["install/validation.py"]
T["_validate_package_exists()"] --> U["get_apm_allow_protocol_fallback()"]
U --> K
end
subgraph install_cmd ["commands/install.py"]
V["_build_downloader() / install flow"] --> W["get_apm_protocol_pref()\nget_apm_allow_protocol_fallback()"]
W --> E
W --> K
end
Recommendation
Ship after the two doc correctness bugs are resolved in this PR (wrong key name in environment-variables.md; unset description contradiction in config.md) and the config directory permissions one-liner is added. Everything else -- display-name dead-code cleanup, bool rendering, FALLBACK_HINT update, CI warning, integration test gaps -- should be filed as follow-up issues and linked from the PR description. The feature logic is sound, the security defaults are correct, and the user payoff is real. Do not let the accumulation of nits delay a change that directly unblocks SSH-first enterprise teams.
Full per-persona findings
Python Architect
-
[recommended]
_ENV_ALLOW_PROTOCOL_FALLBACK/_ENV_GIT_PROTOCOLare declared twice -- once inconfig.pyand once intransport_selection.pyatsrc/apm_cli/config.py:11
The comment says 'to avoid a circular import' but duplicating the string literals is a latent drift risk: if either side renames the constant the other side silently diverges. Canonical fix: extract both constants to a thin third module (e.g.apm_cli/deps/transport_constants.py) with no imports from either consumer.
Suggested: Createsrc/apm_cli/deps/transport_constants.pycontaining the two constants; import from there in bothconfig.pyandtransport_selection.py. -
[recommended] Four
unset_*functions inconfig.pyrepeat the same invalidate->load->del->write->invalidate pattern verbatim atsrc/apm_cli/config.py:136
DRY threshold exceeded (4 call sites). A_unset_config_key(key: str)private helper eliminates all repetition and makes future bug fixes single-site.
Suggested: Adddef _unset_config_key(key: str) -> None:and delegate from all fourunset_*functions. -
[nit]
_CONFIG_KEY_DISPLAY_NAMESstill contains"ssh": "ssh"-- leftover from before theprefer_sshrename atsrc/apm_cli/commands/config.py:24
Dead entry, correct key should be"prefer_ssh": "prefer-ssh".
Suggested: Replace"ssh": "ssh"with"prefer_ssh": "prefer-ssh". -
[nit] In-constructor deferred imports in
GitHubPackageDownloader.__init__signal an unresolved layering tension atsrc/apm_cli/deps/github_downloader.py:188
Workaround for circular import; resolves cleanly once constants are extracted to a shared module.
CLI Logging Expert
-
[recommended] Stale
"ssh": "ssh"entry in_CONFIG_KEY_DISPLAY_NAMESshould be removed atsrc/apm_cli/commands/config.py:24
Maps to nothing used elsewhere and will silently diverge if the dict is ever iterated for validation or documentation generation.
Suggested: Delete"ssh": "ssh". Add"prefer_ssh": "prefer-ssh"if the dict is used for normalisation. -
[recommended]
allow-protocol-fallbackandprefer-sshalways appear inconfig show, even when at their defaults atsrc/apm_cli/commands/config.py:137
temp-diris guarded byif _temp_dir_val:. The new bools unconditionally emit rows showingFalsewith no signal that these are defaults, not explicit choices. Violates signal-to-noise rule.
Suggested: Mirror thetemp-dirpattern: only emit rows when value deviates from the default (True). -
[recommended] Booleans render as Python-capitalised
True/Falseinstead of lowercasetrue/falseatsrc/apm_cli/commands/config.py:137
CLI conventions (POSIX, YAML, JSON, every other config tool) use lowercase. Creates friction for users comparing output to documented values.
Suggested: Usestr(val).lower()or a_fmt_boolhelper throughout. -
[nit] Fallback
click.echopath uses kebab-case key names for new entries but a human-readable label fortemp-diratsrc/apm_cli/commands/config.py:181
Minor inconsistency in the fallback output path.
Suggested: Use human-readable labels:' Allow Protocol Fallback: ...'and' Prefer SSH Transport: ...'. -
[nit]
apm config getprintsFalsefor unset booleans butNot set (using system default)for unsettemp-diratsrc/apm_cli/commands/config.py:315
A user debugging the precedence chain cannot tell from this output which layer resolved the value.
DevX UX Expert
-
[blocking]
_CONFIG_KEY_DISPLAY_NAMESstill maps"ssh": "ssh"instead of"prefer_ssh": "prefer-ssh"atsrc/apm_cli/commands/config.py:24
Internal config key was renamed but display-name table was not updated. Any code path that looks up the display name via the internal key will silently miss the entry, leavingprefer-sshunlabelled or invisible in rendered output.
Suggested: Change"ssh": "ssh"to"prefer_ssh": "prefer-ssh". -
[recommended] Docs say
unsetonly works for path keys; code supports it for both new boolean keys too atdocs/src/content/docs/reference/cli/config.md:48
config.md line 48 contradicts examples at lines 96-97 showingapm config unset prefer-ssh. Cannot both be correct.
Suggested: Update the### apm config unset KEYdescription to includeallow-protocol-fallbackandprefer-sshas valid targets. -
[recommended] Stale
sshkey reference in environment-variables.md resolution chain atdocs/src/content/docs/reference/environment-variables.md:40
Line 40 describesAPM_GIT_PROTOCOLresolution usingsshkey in config.json, but the actual key isprefer_ssh. Users hand-editing config.json will write the wrong key and get silent no-op behaviour.
Suggested: Replace 'sshkey in~/.apm/config.json' with 'prefer_sshkey' or reference the CLI name. -
[recommended]
prefer-sshboolean has noprefer-httpscounterpart; users who want to force HTTPS have no config escape hatch atdocs/src/content/docs/reference/cli/config.md:57
APM_GIT_PROTOCOLaccepts bothsshandhttps, but config only controls SSH preference. No way to persist 'always use HTTPS' in config.
Suggested: Add a note: 'To force HTTPS in all contexts, useAPM_GIT_PROTOCOL=https; there is currently no config-file equivalent for pinning HTTPS.' -
[nit]
config getoutput prints Python-casedFalse/Trueinstead of lowercasefalse/trueatsrc/apm_cli/commands/config.py:181
Suggested: Usestr(val).lower()or explicit"true"/"false"strings.
Supply Chain Security Expert
-
[recommended]
~/.apm/directory created without explicit mode restriction atsrc/apm_cli/config.py:23
ensure_config_exists()callsos.makedirs(CONFIG_DIR)with nomodeargument. On a multi-user system the directory inherits the process umask (commonly 755), making config.json world-readable.allow_protocol_fallback: trueis a security-relevant state that other local users could observe or tamper with.
Suggested: Useos.makedirs(CONFIG_DIR, mode=0o700, exist_ok=True)andos.chmod(CONFIG_FILE, 0o600)after initial creation. -
[recommended]
FALLBACK_HINTintransport_selection.pyis now incomplete after this PR atsrc/apm_cli/deps/transport_selection.py:31
The hint reads 'pass --allow-protocol-fallback or set APM_ALLOW_PROTOCOL_FALLBACK=1'. After this PR a third source exists:apm config set allow-protocol-fallback true. Users will be confused about which mechanism to use.
Suggested: UpdateFALLBACK_HINTto include the config path. Add a note that the setting persists across sessions. -
[recommended] No CI/shared-home warning when persisting
allow_protocol_fallback: trueatsrc/apm_cli/commands/config.py:254
A user who runsapm config set allow-protocol-fallback truein a CI environment where$HOMEis shared silently enables fallback for all subsequent installs. No warning at write time.
Suggested: Emitlogger.warning(...)when settingallow-protocol-fallbackto True, noting that the setting persists and advising use of the env var in CI. -
[nit]
_CONFIG_KEY_DISPLAY_NAMEScontains stale"ssh": "ssh"and is never read -- dead code atsrc/apm_cli/commands/config.py:24
Either delete the dict or correct the entry to"prefer_ssh": "prefer-ssh"and wire it up.
OSS Growth Hacker
-
[recommended] authentication.md still teaches
export APM_GIT_PROTOCOL=httpswithout a forward-pointer toapm config set prefer-ssh falseatdocs/src/content/docs/getting-started/authentication.md
Line 449 is the exact moment an SSH-blocked user is looking for the escape hatch. Showing only the env-var form leaves them one shell-profile edit away from forgetting about APM config -- a high-intent conversion miss.
Suggested: After theexport APM_GIT_PROTOCOL=httpsblock, add: 'To persist this preference across sessions:apm config set prefer-ssh false'. -
[recommended] CHANGELOG entry buries the user-facing hook inside a resolution-order explanation at
CHANGELOG.md
The lead sentence front-loads the mechanism rather than the payoff. 'SSH users: you never have to type--sshagain' is what gets copy-pasted into a Slack or tweet.
Suggested: Rewrite the opening to lead with payoff: 'SSH-first teams can now runapm config set prefer-ssh trueonce and drop--sshfrom every subsequentapm install.' -
[nit]
apm config unset prefer-sshworks but docs say only path keys are unsettable -- contradictory atdocs/src/content/docs/reference/cli/config.md:48 -
[nit] No
apm initor first-install touchpoint guides SSH-first users to discoverprefer-sshatdocs/src/content/docs/getting-started/installation.md
Suggested: Add a post-install callout: 'SSH users: runapm config set prefer-ssh trueonce to avoid passing--sshon every install.'
Auth Expert
-
[recommended] Constructor docstring still says 'reads APM_GIT_PROTOCOL env' -- config layer is now consulted but not mentioned at
src/apm_cli/deps/github_downloader.py:173
Lines 173-176 describe theprotocol_pref=Nonefallback as env-only. The implementation now resolves env -> config -> default. A caller reading only the docstring will not know that a persisted config value can override their expectations.
Suggested: Update the docstring to mention~/.apm/config.jsonas a resolution source. -
[nit]
allow_fallback_envvariable name in validation.py is now misleading atsrc/apm_cli/install/validation.py:317
Reads from config (env > config > False) but_envsuffix implies env-only. Will confuse future readers tracing why fallback is enabled despite no env var being set.
Suggested: Rename toallow_fallbackorallow_fallback_cfg.
Doc Writer
-
[blocking] environment-variables.md names the config key as
ssh, notprefer-sshatdocs/src/content/docs/reference/environment-variables.md:40
Line 40 reads '->sshkey in~/.apm/config.json'. The actual CLI key isprefer-ssh. A user who runsapm config set ssh truewill set an unrecognised key and get silent no-op behaviour. config.md correctly usesprefer-sshthroughout; env-vars.md contradicts it.
Suggested: Change '->sshkey in~/.apm/config.json' to '->prefer-sshkey in~/.apm/config.json'. -
[blocking]
apm config unsetdescription contradicts the examples forprefer-sshatdocs/src/content/docs/reference/cli/config.md:48
Line 48 states 'Onlytemp-dirandcopilot-cowork-skills-dirare unsettable; boolean keys are reset byset-ing them to their default.' Lines 96-97 showapm config unset prefer-sshas valid. These cannot both be correct.
Suggested: Update line 48: 'Boolean keys (prefer-ssh,allow-protocol-fallback) can be removed withunset; the built-in default (false) then applies.temp-dirandcopilot-cowork-skills-dirare also unsettable.' -
[recommended] CHANGELOG [Unreleased] contains two separate
### Addedsections atCHANGELOG.md:21
Keep a Changelog specifies one subsection per type per release. Having two### Addedheadings will confuse changelog parsers and readers.
Suggested: Merge the second### Addedblock's bullet points into the first, then delete the duplicate heading. -
[nit] env-vars.md transport section intro has explanatory padding that could be tightened at
docs/src/content/docs/reference/environment-variables.md:36
Suggested: 'These settings can also be persisted viaapm config set.'
Test Coverage Expert
-
[recommended] No integration test exercises the config-file (not env-var) path of
get_apm_allow_protocol_fallbackthrough_validate_package_existsattests/unit/install/test_validation_strict_transport.py
The existing integration test covers the env-var branch (APM_ALLOW_PROTOCOL_FALLBACK=1). The persisted-config branch -- where env var is absent and config file hasallow_protocol_fallback: true-- is only covered by unit-level mocks. Install-pipeline surface floor tier isintegration-with-fixtures.
Proof (missing):tests/unit/install/test_validation_strict_transport.py::test_config_persisted_allow_fallback_chains_both_schemes-- proves: When user has runapm config set allow-protocol-fallback trueand no env var is set,_validate_package_existsstill probes both transports [secure-by-default] -
[recommended] Downloader constructor's config-aware default for
protocol_prefhas no integration test attests/unit/deps/test_github_downloader_validation.py
The new branch where the constructor is instantiated without explicitprotocol_prefand config file saysprefer_ssh: truehas no integration-with-fixtures test. Grepped all oftests/forget_apm_protocol_prefandget_apm_allow_protocol_fallback: only hits in the two new unit files.
Proof (missing):tests/unit/deps/test_github_downloader_validation.py::test_constructor_reads_ssh_preference_from_config-- proves: Whenget_prefer_sshreturns True and no explicit protocol_pref arg is given, downloader selects SSH [devx] -
[nit]
test_protocol_config_precedence.pysimulates CLI-flag path inline rather than calling real install dispatch attests/unit/test_protocol_config_precedence.py
Future refactors of install.py could silently break the wiring. Not blocking -- install invocation is covered elsewhere and the config-module unit tests are well-targeted.
This panel is advisory. It does not block merge. Re-apply the
panel-review label after addressing feedback to re-run.
Note
🔒 Integrity filter blocked 2 items
The following items were blocked because they don't meet the GitHub integrity level.
- feat: persist transport flags via apm config #1308
pull_request_read: has lower integrity than agent requires. The agent cannot read data with integrity below "approved". - #1308
pull_request_read: has lower integrity than agent requires. The agent cannot read data with integrity below "approved".
To allow these resources, lower min-integrity in your GitHub frontmatter:
tools:
github:
min-integrity: approved # merged | approved | unapproved | noneGenerated by PR Review Panel for issue #1308 · ● 3.9M · ◷
Add allow-protocol-fallback and ssh as user-settable apm config keys so users can persist their preferred transport mode without re-typing CLI flags or modifying shell profiles on every session.
Precedence chain (highest to lowest):
CLI flag > APM_ALLOW_PROTOCOL_FALLBACK/APM_GIT_PROTOCOL env var
> apm config > built-in default
Changes:
- config.py: add get/set_allow_protocol_fallback(), get/set_ssh(), get_apm_allow_protocol_fallback() and get_apm_protocol_pref() resolution helpers that encode the full env > config > default chain
- commands/install.py: wire config into the transport-selection block;
- commands/config.py: register both keys in setters/getters/valid-keys, add them to all listing paths (rich table, plain-text, get all-keys)
- docs/reference/cli/config.md: new rows in keys table, resolution-order
section for transport keys, usage examples
- docs/reference/environment-variables.md: new Transport and protocol section documenting APM_GIT_PROTOCOL and APM_ALLOW_PROTOCOL_FALLBACK
- tests: 74 new test cases across test_config_command.py and new test_protocol_config_precedence.py (149 pass, 0 fail)
Closes microsoft#1243
- Wire get_apm_allow_protocol_fallback / get_apm_protocol_pref into GitHubPackageDownloader defaults and validation.py so apm config keys are honoured on all code paths, not just install.py - Add unset_allow_protocol_fallback() and unset_ssh() to config.py; register both in the unset command handler - Add CHANGELOG entry under [Unreleased]
- Fix stale _CONFIG_KEY_DISPLAY_NAMES entry (ssh -> prefer_ssh) - Lowercase bool output in apm config get (True/False -> true/false) - Suppress false-default transport rows in apm config / apm config get - Success message: "set to true/false" instead of "enabled/disabled" - FALLBACK_HINT: mention apm config set as persistent alternative config.py - Add _unset_config_key(key) private helper; delegate all four unset_* functions through it (DRY, Python Architect recommended) commands/config.py - Emit logger.warning() when allow-protocol-fallback is persisted in a CI environment ($CI set) advising APM_ALLOW_PROTOCOL_FALLBACK=1 for invocation-scoped use instead (Supply Chain Security recommended) install/validation.py - Rename allow_fallback_env -> allow_fallback; _env suffix was misleading after the value started resolving from config too (Auth Expert nit) deps/github_downloader.py - Update __init__ docstring for protocol_pref and allow_fallback to accurately describe env > config > default resolution instead of implying env-only lookups (Auth Expert recommended) CHANGELOG.md - Merge second ### Added block bullets into first; restore ### Fixed section; one subsection per type per release per Keep a Changelog (Doc Writer recommended) docs/getting-started/authentication.md - Add forward-pointer to "apm config set prefer-ssh false" after the APM_GIT_PROTOCOL=https escape-hatch block; highest-intent conversion point for the new config surface (OSS Growth Hacker recommended)
|
I've addressed all the blockers and nearly all the recommendations/nitpicks in the latest commits. |

Description
Users who authenticate to GitHub via SSH keys (no PAT configured) today
must pass
--sshor--allow-protocol-fallbackon everyapm installinvocation, or export environment variables in their shell profile which is a
cross-platform friction point (especially on Windows PowerShell / cmd).
This PR adds
allow-protocol-fallbackandsshas first-classapm configkeys, so users can persist their transport preference onceand forget about it:
Changes:
section for transport keys, usage examples
Closes #1243.
Type of change
Testing