diff --git a/CHANGELOG.md b/CHANGELOG.md index 4dc695ab4..0b612c26e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,59 +7,59 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.13.0] - 2026-05-11 + ### Breaking Changes - The CLI self-updater moved from `apm update` to `apm self-update`. Inside an `apm.yml` project the bare `apm update` verb now refreshes project dependencies (matching `npm update`, `uv lock --upgrade`, `cargo update`); outside a project it forwards to `apm self-update` with a deprecation banner for one release. CI scripts that called `apm update` to refresh the binary should migrate now: `sed -i 's/apm update/apm self-update/g' your_scripts`. (#1244) ### Added -- `apm update` now does what every npm/pip/cargo user expects -- resolve latest refs, show a structured plan, and ask before touching anything. Refreshes APM dependencies in the current project: resolves `apm.yml` against the latest refs, prints a structured plan (added/updated/removed/unchanged) with an inline legend, and prompts `[y/N]` before mutating anything (`--yes` skips the prompt; `--dry-run` previews). (#1244) -- `apm self-update`: updates the APM CLI binary itself (or shows distributor guidance when self-update is disabled at build time). Replaces the former `apm update` self-update path; supports `--check` to only check for a newer version. (#1244) -- `apm install --frozen` performs a CI-safe, read-only install that fails fast (exit 1) when `apm.lock.yaml` is missing or out of sync with `apm.yml`; mutually exclusive with `--update`. Structural presence check only; use `apm audit` for on-disk SHA integrity. (#1244) -- `apm install` now emits a one-line "Run 'apm update' to check for newer versions." hint on the no-op path when a lockfile is already present, pointing users at the verb that actually checks for newer refs. (#1244) +- `apm update` refreshes project dependencies the way npm/pip/cargo users expect: resolves `apm.yml` against latest refs, shows an added/updated/removed/unchanged plan, and prompts before mutating anything (`--yes` skips, `--dry-run` previews). (#1244) +- `apm self-update` updates the APM CLI binary itself (or shows distributor guidance when self-update is disabled at build time); `--check` only checks for a newer version. (#1244) +- `apm install --frozen` performs a CI-safe, read-only install that fails fast (exit 1) when `apm.lock.yaml` is missing or out of sync with `apm.yml`; mutually exclusive with `--update`. (#1244) +- Zero-config private-package auth on github.com, `*.ghe.com`, and GHES when the `gh` CLI is logged in: APM uses `gh auth token --hostname ` before falling back to `git credential fill`. (#630) +- GitLab marketplace and install support: `gitlab.com` and self-managed instances (via `GITLAB_HOST` / `APM_GITLAB_HOSTS`) use GitLab REST v4 for `marketplace.json` and raw file reads; nested group paths are disambiguated via object-form `git:` + `path:`. (#1149) - Virtual subdirectory and raw-file packages now resolve from self-hosted Git services (Gitea, Gogs) via raw URL with API v1/v3 fallback. (#587) -- `shared/apm.md` gh-aw shared workflow exposes a `target:` import input (default `all`) so consumer workflows can ship slim, single-harness bundles instead of always packing every layout. (#1184) -- **GitLab host support:** `gitlab.com` and self-managed instances (via `GITLAB_HOST` / `APM_GITLAB_HOSTS`) use GitLab REST **v4** for `marketplace.json` and install-time raw file reads; nested GitLab group paths are disambiguated in dependency references with object-form `git:` + `path:` where shorthand is ambiguous. GitHub, GHES, Azure DevOps, and registry-proxy behavior remain unchanged. (#1149) -- **`git: parent` monorepo transitive dependency inheritance:** packages in a git monorepo can reference sibling paths via `{ git: parent, path: ... }` without repeating the full `git:` URL; the lockfile stores expanded host, repository, subdirectory path, and resolved ref/commit like other virtual git dependencies (no `parent` sentinel as durable identity). (#1149) -- If you use the `gh` CLI, APM is now zero-config for private GitHub packages on github.com, `*.ghe.com`, and GHES: APM uses your active `gh auth login` token (`gh auth token --hostname `) before falling back to `git credential fill`. Silently skipped when `gh` is not installed or not logged in for the host. (#630) +- `git: parent` lets packages in a git monorepo reference sibling paths via `{ git: parent, path: ... }` without repeating the full `git:` URL; the lockfile stores expanded host, repo, and resolved ref like every other virtual git dependency. (#1149) +- `shared/apm.md` gh-aw workflow exposes a `target:` import input (default `all`) so consumer workflows can ship slim, single-harness bundles. (#1184) ### Changed -- `apm install --force` help text now states explicitly that the flag does NOT refresh refs; users who want newer commits should run `apm update`. (#1244) -- `apm update` now walks parent directories to find `apm.yml` (matching npm/cargo ergonomics), emits a one-time CI banner when invoked from a project root under `CI`/`GITHUB_ACTIONS` so pipelines see the dependency-refresh shift, and the no-op nudge copy now reads "Lockfile already satisfied -- run `apm update` to resolve latest refs." `apm install --frozen` adds a post-success "Run `apm audit` for on-disk content integrity." footer to set the right expectation, and FrozenInstallError now renders identically across `install` and `update`. (#1244) -- `apm marketplace browse/search/add/update` route through the registry proxy when `PROXY_REGISTRY_URL` is set; `PROXY_REGISTRY_ONLY=1` blocks direct GitHub and GitLab host API fallbacks. (#1149) -- Registry proxy now warns when `PROXY_REGISTRY_TOKEN` is set and `PROXY_REGISTRY_URL` uses `http://`, since the bearer token would be transmitted in plaintext; set `PROXY_REGISTRY_ALLOW_HTTP=1` to silence the warning for trusted internal proxies. (#1149) -- Integration tests now use marker-driven discovery: 21 `pytestmark = pytest.mark.skipif(...)` chains across `tests/integration/` are replaced with declarative `requires_*` markers, with precondition logic centralized in `tests/integration/conftest.py` and auto-skipping at collection time. PR1 of #1166. (#1167) -- Integration test apm-binary resolution now prefers the local build (`./dist/apm--/apm`) over a system-wide `apm` on `PATH`, so contributors validating the binary under test are not silently shadowed by a global install; the bearer-token marker (`requires_ado_bearer`) discards the captured JWT immediately and persists only the boolean outcome. (#1167) -- `scripts/test-integration.sh` is now a thin orchestrator: it builds/locates the apm binary, sets up runtimes and tokens, then invokes `pytest tests/integration/` exactly once. The 28 per-file pytest enumerations were removed; the marker registry handles per-test gating, and new test files dropped into `tests/integration/` are picked up automatically. PR2 of #1166. (#1247) -- Integration-test marker procedure codified as `.apm/instructions/tests.instructions.md` (wired into `test-coverage-expert` persona) and guarded by a regression-trap test that asserts `pyproject.toml`, `tests/integration/conftest.py::_MARKER_CHECKS`, the docs registry table, and the instructions rule stay in sync. (#1166) -- Tier-2 smoke job runs `tests/integration/test_core_smoke.py` against the built apm binary, exercising `init` / `install` / `compile` / `audit` / `policy status` to fail fast on the README's three promises; replaces `test_runtime_smoke.py` (kept in heavy integration). (#1251) +- `apm marketplace browse/search/add/update` route through the registry proxy when `PROXY_REGISTRY_URL` is set; `PROXY_REGISTRY_ONLY=1` blocks direct GitHub and GitLab host fallbacks; a plaintext-bearer warning fires on `http://` proxies unless `PROXY_REGISTRY_ALLOW_HTTP=1` is set. (#1149) +- `apm install --force` help text now states the flag does NOT refresh refs; the no-op nudge now reads "Lockfile already satisfied -- run `apm update` to resolve latest refs"; a one-time CI banner fires when `apm update` runs under `CI`/`GITHUB_ACTIONS` so pipelines see the dependency-refresh shift. (#1244) +- Tier-2 smoke job runs `tests/integration/test_core_smoke.py` against the built binary, exercising `init` / `install` / `compile` / `audit` / `policy status` to fail fast on the README promises; replaces `test_runtime_smoke.py`. (#1251) +- Integration tests now use marker-driven discovery: `requires_*` markers replace 21 `pytestmark = pytest.mark.skipif(...)` chains, `scripts/test-integration.sh` is a thin orchestrator, and new test files dropped into `tests/integration/` are picked up automatically. (#1167, #1247, #1249) +- Integration test apm-binary resolution prefers the local build (`./dist/apm--/apm`) over a system-wide `apm` on `PATH`, so contributors validating the binary under test are not silently shadowed by a global install. (#1167) +- Integration Tests merge-queue job is now sharded 4-way via pytest-split with `-n 2` xdist per shard, cutting wall-clock from ~30 min to ~6 min while keeping HOME-mutating tests race-safe. (#1263) ### Fixed -- `apm install` no longer silently overwrites pre-existing governance files; `check_collision()` now treats `managed_files=None` (first install, no lockfile yet) as an empty set so hand-rolled files in `.github/instructions/` and other governance directories are correctly detected and protected from silent overwrite. (#1256) -- `test_find_server_by_reference_uuid_not_found` no longer leaks a live HTTP call to `api.mcp.github.com` (fixing Windows CI failures from `socket.gaierror`); the `search_servers` fallback is now mocked. (#1264) -- ADO full HTTPS URLs with sub-path virtual packages (e.g. `https://dev.azure.com/org/proj/_git/repo/sub/path`) are now parsed correctly instead of being rejected. (#1254) -- Fixed `apm install` crash (exit code 128) when a mono-repo package depends on a sibling pinned to a non-HEAD commit; installs now resolve with a single in-place fetch, and multiple SHA-pinned references to the same repository share a single cached clone. (#1258) +- `apm install` no longer silently overwrites pre-existing governance files; `check_collision()` now treats `managed_files=None` (first install, no lockfile) as an empty set so hand-rolled files in `.github/instructions/` are detected and protected. (#1256) +- Policy inheritance now fails closed: child policies that omit `unmanaged_files` inherit the parent's action instead of silently defaulting to `ignore`. (#1253) - MCP server token injection now requires both an allowlisted server name and a verified HTTPS GitHub hostname, preventing PAT exfiltration via poisoned registry entries. (#1239) -- `apm marketplace add` accepts GitLab-class hosts (`gitlab.com` and self-managed instances configured via `GITLAB_HOST` / `APM_GITLAB_HOSTS`); unsupported generic hosts now show separate recovery hints for GHES (`GITHUB_HOST`) and self-managed GitLab instead of only `GITHUB_HOST`. (#1149) -- **GitLab monorepo marketplaces:** `apm install plugin@marketplace` now resolves plugins whose sources live in a subdirectory of the marketplace repository on GitLab-class hosts (`gitlab.com` and self-managed GitLab when classified as GitLab), matching explicit `git:` + `path:` semantics without requiring that hand-written object form. (#1149) -- `apm install` now rejects unsupported flat-format `dependencies` (e.g. `dependencies: [owner/repo]`) with a clear error and structured-format hint instead of silently ignoring them; the resolver also surfaces `ValueError` from malformed transitive manifests as warnings instead of swallowing them. (#1189) - `apm install --target cursor` now emits Cursor-native MCP schema (`type: stdio` / `type: http`) instead of Copilot-only fields that Cursor silently discards; `.cursor/mcp.json` is gitignored to prevent accidental token commits. (#1240) -- `shared/apm.md` no longer wraps the `target` input in a `|| 'all'` fallback. The defensive expression broke gh-aw's bare-expression substitution regex, causing consumer-supplied `target:` values to be silently dropped; the `import-schema` default already covers the omitted-input case. (#1185) -- `apm install --target all` no longer enumerates the experimental `copilot-cowork` target, which was crashing project-scope installs with a "requires --global" error and made `gh aw` workflows that pin `target: all` unusable. (#1191) -- Stabilized `test_install_over_defer_threshold_starts_live_once` on slow CI runners by joining the deferred-start timer thread instead of relying on a 100ms grace window. (#1191) -- `triage-panel` scheduled sweep now paginates the candidate query oldest-first via the GitHub MCP `list_issues` tool instead of a single 200-issue page, so daily runs actually drain the untriaged backlog rather than processing one issue per cron tick. (#1193) -- `triage-panel` scheduled sweep switches the candidate query from `list_issues`+prose-driven pagination to `search_issues` with `-label:status/triaged sort:created-asc`, so untriaged candidates are filtered server-side; the previous approach silently noop'd because the MCP gateway DIFC filter dropped non-collaborator issues mid-page and the agent inferred a false `hasNextPage:false`. -- `apm install` now accepts the YAML list form under `target:` (e.g. `target: [copilot, claude]`); previously crashed with a garbled `Unknown target` error. (#1197) -- `apm install --update` now falls back from a stale `ADO_APM_PAT` to an `az login` AAD bearer in the preflight auth probe, matching the behavior of `apm install` and every other ADO call site. Previously the preflight raised `AuthenticationError` on 401/403 even when `az login` would have succeeded. The bearer env also pops any pre-existing `GIT_TOKEN` so the JWT flows only via `GIT_CONFIG_VALUE_0`, and the per-host stale-PAT warning dedup is lock-guarded so parallel installs against the same ADO host emit one warning instead of one-per-thread. (#1212) -- `Unknown target` error suggestions no longer advertise the `agent-skills` meta-target, which `apm targets` intentionally omits from its table. The canonical set still accepts `agent-skills` via `--target` and `apm.yml`, but the recovery path printed on errors now matches what the discovery command actually lists. (#1215) -- `apm pack` no longer hardcodes `pack.target` into bundles; bundles are target-agnostic and `apm install ` resolves the consumer target from project context and wires bundle `.mcp.json` servers per target via `MCPIntegrator`. (#1217) -- Multi-account Git Credential Manager users: APM now selects the right GitHub account automatically per repository (no account-picker prompt) when `credential.useHttpPath = true` is set. Existing single-account setups are unaffected. (#1226) -- Policy inheritance now fails closed: child policies that omit `unmanaged_files` inherit the parent's action instead of silently defaulting to `ignore`. (#1253) -- Protocol-fallback port warnings are now deduplicated across parallel download workers via a threading lock, so each (host, repo, port) triple warns exactly once. (#1238) -- `apm install --global ` no longer rejects absolute local paths at user scope (regression introduced by #1149); restores the post-#937 contract that only relative local paths are ambiguous at user scope. (#1247) -- Realigned the failing integration suite with current product contracts: copilot-target detection requires `.github/copilot-instructions.md` (post-#1154), `apm marketplace build` is removed in favor of `apm pack`, ADO virtual collections use the SUBDIRECTORY layout (post-#1094), and the `repo:` apm.yml key is replaced by `git:`. (#1247) +- `apm install` accepts full ADO HTTPS URLs with sub-path virtual packages (e.g. `https://dev.azure.com/org/proj/_git/repo/sub/path`) instead of rejecting them. (#1254) +- `apm install` no longer crashes with exit 128 when a mono-repo package depends on a sibling pinned to a non-HEAD commit; multiple SHA-pinned refs to the same repository now share a single cached clone. (#1258, #1259) +- `apm install --update` falls back from a stale `ADO_APM_PAT` to an `az login` AAD bearer in the preflight auth probe, matching the behavior of `apm install`; per-host stale-PAT warnings are lock-guarded so parallel installs emit one warning instead of one-per-thread. (#1212, #1214) +- `apm install` rejects unsupported flat-format `dependencies` (e.g. `dependencies: [owner/repo]`) with a clear error and structured-format hint instead of silently ignoring them. (#1189) +- `apm install` accepts the YAML list form under `target:` (e.g. `target: [copilot, claude]`); previously crashed with a garbled `Unknown target` error. (#1197) +- `apm install --target all` no longer enumerates the experimental `copilot-cowork` target, which was crashing project-scope installs and making `gh aw` workflows that pin `target: all` unusable. (#1191) +- `apm install --global ` no longer rejects absolute local paths at user scope (regression from #1149); restores the post-#937 contract that only relative local paths are ambiguous at user scope. (#1247) +- `apm install` against a `--global` scope now writes MCP entries to the scope-resolved lockfile path instead of the project lockfile. (#1236) +- `apm install ` direct refs now apply the same manifest-driven credential validation as `apm.yml`-declared dependencies, closing a silent auth-fallback gap. (#1242) +- Dependency references no longer treat `:443`/`:80`/`:22` as distinct from default-scheme ports, so two manifest entries that differ only by an explicit default port resolve to the same package. (#1237) +- The experimental `cowork` config key is hidden from `apm config` output, and the verbose target-resolution log no longer leaks internal cowork plumbing. (#1241) +- Multi-account Git Credential Manager users: APM selects the right GitHub account automatically per repository (no account-picker prompt) when `credential.useHttpPath = true` is set. (#1226) +- `apm install` target-agnostic local bundles: `apm pack` no longer hardcodes `pack.target` into bundles; `apm install ` resolves the consumer target from project context and wires `.mcp.json` servers per target. (#1207, #1217) +- `Unknown target` error suggestions no longer advertise the `agent-skills` meta-target (which `apm targets` intentionally omits); the canonical set still accepts `agent-skills` via `--target` and `apm.yml`. (#1208, #1215) +- `apm outdated --help` now renders the missing one-line description. (#1216) +- `shared/apm.md` no longer wraps the `target` input in a `|| 'all'` fallback that broke gh-aw's expression-substitution regex and silently dropped consumer-supplied `target:` values. (#1185, #1186) +- GitLab monorepo marketplaces: `apm install plugin@marketplace` resolves plugins whose sources live in a subdirectory of the marketplace repository on GitLab-class hosts. (#1149) +- Protocol-fallback port warnings are deduplicated across parallel download workers via a threading lock, so each (host, repo, port) triple warns exactly once. (#1238) +- `apm marketplace add` accepts GitLab-class hosts; unsupported generic hosts now show separate recovery hints for GHES (`GITHUB_HOST`) and self-managed GitLab instead of only `GITHUB_HOST`. (#1149) +- Realigned the integration suite with current product contracts: copilot-target detection requires `.github/copilot-instructions.md` (post-#1154), `apm marketplace build` is removed in favor of `apm pack`, ADO virtual collections use the SUBDIRECTORY layout (post-#1094), and the `repo:` apm.yml key is replaced by `git:`. (#1257, #1261, #1264) +- `triage-panel` scheduled sweep now paginates oldest-first via GitHub MCP `search_issues` with `-label:status/triaged sort:created-asc`, so daily runs drain the untriaged backlog instead of processing one issue per cron tick. (#1193, #1194) ## [0.12.4] - 2026-05-07 diff --git a/pyproject.toml b/pyproject.toml index 9f10d6fc5..18ba68652 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "apm-cli" -version = "0.12.4" +version = "0.13.0" description = "MCP configuration tool" readme = "README.md" requires-python = ">=3.10" diff --git a/uv.lock b/uv.lock index 3caa863e2..cc14e22d8 100644 --- a/uv.lock +++ b/uv.lock @@ -179,7 +179,7 @@ wheels = [ [[package]] name = "apm-cli" -version = "0.12.4" +version = "0.13.0" source = { editable = "." } dependencies = [ { name = "click" },