Files
Victor Sollerhed 6d747ea8ff feat(manager): add apm manager for Agent Package Manager (#44390)
* feat(manager): add apm manager for Agent Package Manager

Add a new `apm` manager that updates git-pinned dependencies in APM
(Agent Package Manager) `apm.yml` manifests, as requested in
renovatebot/renovate#42507.

The manager parses the string entries under `dependencies.apm` and
`devDependencies.apm` (`[host/]owner/repo[/subpath]#<ref>`) and selects
the datasource dynamically from the git host, reusing the existing
github-tags, gitlab-tags and git-tags datasources. MCP entries carry no
pinnable version and are ignored.

When an `apm.lock.yaml` lockfile is present, it is refreshed by running
`apm install` via a new `apm` tool constraint (pypi `apm-cli`).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vfjt3BpqDriiVsvwqVe9M9

* fix(manager/apm): resolve apm tool from github releases

APM ships self-contained binaries as GitHub release assets
(apm-linux-<arch>.tar.gz under vX.Y.Z tags), not as the PyPI apm-cli
wheel. Point the apm tool constraint at the github-releases datasource so
the version Renovate resolves matches the artifact that actually gets
installed, stripping the leading `v` from the tag.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vfjt3BpqDriiVsvwqVe9M9

* docs(manager/apm): point to canonical microsoft/apm repository

The APM project's canonical home is github.com/microsoft/apm (originally
danielmeppiel/apm). Update the manager URL and readme accordingly.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vfjt3BpqDriiVsvwqVe9M9

* refactor(manager/apm): validate manifest with zod schema

Address review feedback on the apm manager:

- parse `apm.yml` with a dedicated zod schema via
  `parseSingleYaml(content, { customSchema })` instead of casting the
  parsed YAML to `ApmManifest`
- `LooseArray(z.string())` keeps only the string dependency entries, so
  MCP objects and other non-string values are dropped by the schema and
  the manual `Array.isArray`/`is.string` guards are no longer needed
- drop the `as SkipReason` casts now that the literals are inferred
- remove the now-unused `types.ts`

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UbMAQBiMRNkCNxc8EGQw1C

* Update schema.ts

Co-authored-by: Sebastian Poxhofer <secustor@users.noreply.github.com>

* Update readme.md

Co-authored-by: Sebastian Poxhofer <secustor@users.noreply.github.com>

* Update readme.md

Co-authored-by: Sebastian Poxhofer <secustor@users.noreply.github.com>

* refactor(manager/apm): simplify manifest schema and host detection

Address review feedback:

- default the `apm` array to `[]` in the schema (`LooseArray(...).catch([])`)
  since LooseArray already discards non-string entries, so downstream code
  no longer needs to handle `undefined`
- use `coerceArray` when mapping the optional dependency sections
- drop the redundant `host === 'github.com'` / `host === 'gitlab.com'`
  checks in datasource selection; `detectPlatform` already resolves those
  hosts (the explicit host comparison is only kept to decide registryUrls
  for self-hosted instances)
- document that APM only supports remote git references and that dots in
  repo names / subpaths are handled, with a regression test

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UbMAQBiMRNkCNxc8EGQw1C

* docs(manager/apm): fix readme trailing newline

Remove the trailing blank line left after the readme edits so it passes
`prettier` and `markdown-lint`.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UbMAQBiMRNkCNxc8EGQw1C

* refactor(manager/apm): parse dependency path with named capture groups

Replace the manual host/owner/repo splitting (and the standalone dot
regex) with a single named-capture regex, as suggested in review. `owner`
excludes dots so the optional hostname segment is unambiguous, while repo
names and subpaths may still contain dots.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UbMAQBiMRNkCNxc8EGQw1C

* test(manager/apm): align admin config with InternalGlobalConfigOptions

`localDir` moved out of `RepoGlobalConfig` into `InternalGlobalConfigOptions`
on main, so annotate the test admin config as
`RepoGlobalConfig & InternalGlobalConfigOptions`, matching the other
manager artifacts specs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UbMAQBiMRNkCNxc8EGQw1C

* fix(manager/apm): resolve GitLab nested groups and recognize SHA-pinned refs

- resolve nested GitLab project slugs (e.g. `group/subgroup/project`)
  instead of capping at `owner/repo`, splitting the project from any
  virtual-package subpath using APM's primitive-dir / virtual-file markers
- recognize the documented SHA-pinned form and skip it
  (`unversioned-reference`) rather than treating the SHA as a version or
  rewriting it

* feat(manager/apm): support digest pinning for SHA-pinned deps

Recover the release tag from the trailing YAML comment of a SHA-pinned
entry (`owner/repo#<sha> # v2.0.0`) - which the structured parse strips -
so it extracts as a digest (`currentDigest` + `currentValue`) and updates
both via `pinDigests`, mirroring the github-actions manager. A bare SHA
with no tag comment (or a quoted form we can't rewrite verbatim) is left
skipped.

* fix(manager/apm): map SHA-pin tag recovery per line to handle duplicates

The raw-manifest scan matched the first line for the entry regardless of
section, so the same `owner/repo#<sha>` pinned in both `dependencies` and
`devDependencies` resolved to the first line's tag/replaceString. Consume
each line at most once (in file order) so duplicate pins map to distinct
lines.

* fix(manager/apm): recognize `.chatmode.md` virtual files

APM's VIRTUAL_FILE_EXTENSIONS includes `.chatmode.md`; add it to the
subpath-boundary regex so a `*.chatmode.md` virtual dependency on a
nested-group host (GitLab, Azure DevOps) is not folded into the repo path.

* test(manager/apm): cover SHA-pin digest updates on git-tags hosts

Lock in that a SHA-pinned dep on a non-github/gitlab host resolves via the
git-tags datasource (which supports `getDigest` through `git ls-remote`),
so the commit is preserved on a bump rather than dropped.

* fix(manager/apm): commit regenerated harness files, not just the lockfile

`apm install` re-deploys the harness directories APM consumers commit
(`.github/`, `.claude/`, ...), but updateArtifacts only returned
`apm.lock.yaml`, leaving instruction files stale after a bump. Capture
every file the install changed via `getRepoStatus` (additions +
deletions); the gitignored `apm_modules/` cache is excluded, and the
manifest stays with `updatedPackageFiles`.

* fix(manager/apm): declare lockFileMaintenanceIsDelegatedToPackageManager

main now requires managers with `supportsLockFileMaintenance` to set this
field. apm regenerates the lockfile via `apm install`, so it delegates to
the package manager (`true`).

* refactor(manager/apm): reuse shared regex and git-sha helpers

Adopt the shared utilities per review:

- use the exported `newlineRegex` from `util/regex` instead of a local copy
- use `isLongCommitSha` from `util/schema-utils/git` instead of a bespoke
  SHA regex
- filter path segments with `isTruthy` instead of `Boolean`

* test(manager/apm): align docker exec snapshot with current base-image wrapper

The shared `util/exec` docker wrapper now injects `-e CI` and single-quotes
the `bash -l -c` payload. Update the docker-mode expectation to match, so the
snapshot tracks the current wrapper output.

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Sebastian Poxhofer <secustor@users.noreply.github.com>
2026-09-02 16:17:15 +00:00

240 lines
9.3 KiB
TypeScript

import * as ansible from './ansible/index.ts';
import * as ansibleGalaxy from './ansible-galaxy/index.ts';
import * as ant from './ant/index.ts';
import * as apm from './apm/index.ts';
import * as argoCD from './argocd/index.ts';
import * as asdf from './asdf/index.ts';
import * as azurePipelines from './azure-pipelines/index.ts';
import * as batect from './batect/index.ts';
import * as batectWrapper from './batect-wrapper/index.ts';
import * as bazel from './bazel/index.ts';
import * as bazelModule from './bazel-module/index.ts';
import * as bazelisk from './bazelisk/index.ts';
import * as bicep from './bicep/index.ts';
import * as bitbucketPipelines from './bitbucket-pipelines/index.ts';
import * as bitrise from './bitrise/index.ts';
import * as buildkite from './buildkite/index.ts';
import * as buildpacks from './buildpacks/index.ts';
import * as bun from './bun/index.ts';
import * as bunVersion from './bun-version/index.ts';
import * as bundler from './bundler/index.ts';
import * as cake from './cake/index.ts';
import * as cargo from './cargo/index.ts';
import * as cdnurl from './cdnurl/index.ts';
import * as circleci from './circleci/index.ts';
import * as cloudbuild from './cloudbuild/index.ts';
import * as cocoapods from './cocoapods/index.ts';
import * as composer from './composer/index.ts';
import * as conan from './conan/index.ts';
import * as copier from './copier/index.ts';
import * as cpanfile from './cpanfile/index.ts';
import * as crossplane from './crossplane/index.ts';
import * as crow from './crow/index.ts';
import * as deno from './deno/index.ts';
import * as depsEdn from './deps-edn/index.ts';
import * as devbox from './devbox/index.ts';
import * as devContainer from './devcontainer/index.ts';
import * as dockerCompose from './docker-compose/index.ts';
import * as dockerfile from './dockerfile/index.ts';
import * as droneci from './droneci/index.ts';
import * as fleet from './fleet/index.ts';
import * as flux from './flux/index.ts';
import * as fvm from './fvm/index.ts';
import * as gitSubmodules from './git-submodules/index.ts';
import * as githubActions from './github-actions/index.ts';
import * as gitlabci from './gitlabci/index.ts';
import * as gitlabciInclude from './gitlabci-include/index.ts';
import * as glasskube from './glasskube/index.ts';
import * as gleam from './gleam/index.ts';
import * as gomod from './gomod/index.ts';
import * as gradle from './gradle/index.ts';
import * as gradleWrapper from './gradle-wrapper/index.ts';
import * as haskellCabal from './haskell-cabal/index.ts';
import * as helmRequirements from './helm-requirements/index.ts';
import * as helmValues from './helm-values/index.ts';
import * as helmfile from './helmfile/index.ts';
import * as helmsman from './helmsman/index.ts';
import * as helmv3 from './helmv3/index.ts';
import * as hermit from './hermit/index.ts';
import * as homeassistantManifest from './homeassistant-manifest/index.ts';
import * as homebrew from './homebrew/index.ts';
import * as html from './html/index.ts';
import * as jenkins from './jenkins/index.ts';
import * as jsonnetBundler from './jsonnet-bundler/index.ts';
import * as kotlinScript from './kotlin-script/index.ts';
import * as kubernetes from './kubernetes/index.ts';
import * as kustomize from './kustomize/index.ts';
import * as leiningen from './leiningen/index.ts';
import * as maven from './maven/index.ts';
import * as mavenWrapper from './maven-wrapper/index.ts';
import * as meteor from './meteor/index.ts';
import * as mint from './mint/index.ts';
import * as mise from './mise/index.ts';
import * as mix from './mix/index.ts';
import * as nix from './nix/index.ts';
import * as nodenv from './nodenv/index.ts';
import * as npm from './npm/index.ts';
import * as nuget from './nuget/index.ts';
import * as nvm from './nvm/index.ts';
import * as ocb from './ocb/index.ts';
import * as osgi from './osgi/index.ts';
import * as pep621 from './pep621/index.ts';
import * as pep723 from './pep723/index.ts';
import * as pip_requirements from './pip_requirements/index.ts';
import * as pip_setup from './pip_setup/index.ts';
import * as pipCompile from './pip-compile/index.ts';
import * as pipenv from './pipenv/index.ts';
import * as pixi from './pixi/index.ts';
import * as poetry from './poetry/index.ts';
import * as preCommit from './pre-commit/index.ts';
import * as proto from './proto/index.ts';
import * as pub from './pub/index.ts';
import * as puppet from './puppet/index.ts';
import * as pyenv from './pyenv/index.ts';
import * as quadlet from './quadlet/index.ts';
import * as renovateConfig from './renovate-config/index.ts';
import * as rubyVersion from './ruby-version/index.ts';
import * as runtimeVersion from './runtime-version/index.ts';
import * as rustToolchain from './rust-toolchain/index.ts';
import * as sbt from './sbt/index.ts';
import * as scalafmt from './scalafmt/index.ts';
import * as setupCfg from './setup-cfg/index.ts';
import * as smithy from './smithy/index.ts';
import * as sveltos from './sveltos/index.ts';
import * as swift from './swift/index.ts';
import * as tekton from './tekton/index.ts';
import * as terraform from './terraform/index.ts';
import * as terraformVersion from './terraform-version/index.ts';
import * as terragrunt from './terragrunt/index.ts';
import * as terragruntVersion from './terragrunt-version/index.ts';
import * as tflintPlugin from './tflint-plugin/index.ts';
import * as travis from './travis/index.ts';
import type { ManagerApi } from './types.ts';
import * as typst from './typst/index.ts';
import * as unity3d from './unity3d/index.ts';
import * as velaci from './velaci/index.ts';
import * as vendir from './vendir/index.ts';
import * as woodpecker from './woodpecker/index.ts';
import * as xcodegen from './xcodegen/index.ts';
const api = new Map<string, ManagerApi>();
export default api;
api.set('ant', ant);
api.set('ansible', ansible);
api.set('ansible-galaxy', ansibleGalaxy);
api.set('apm', apm);
api.set('argocd', argoCD);
api.set('asdf', asdf);
api.set('azure-pipelines', azurePipelines);
api.set('batect', batect);
api.set('batect-wrapper', batectWrapper);
api.set('bazel', bazel);
api.set('bazel-module', bazelModule);
api.set('bazelisk', bazelisk);
api.set('bicep', bicep);
api.set('bitbucket-pipelines', bitbucketPipelines);
api.set('bitrise', bitrise);
api.set('buildkite', buildkite);
api.set('buildpacks', buildpacks);
api.set('bun', bun);
api.set('bun-version', bunVersion);
api.set('bundler', bundler);
api.set('cake', cake);
api.set('cargo', cargo);
api.set('cdnurl', cdnurl);
api.set('circleci', circleci);
api.set('cloudbuild', cloudbuild);
api.set('cocoapods', cocoapods);
api.set('composer', composer);
api.set('conan', conan);
api.set('copier', copier);
api.set('cpanfile', cpanfile);
api.set('crossplane', crossplane);
api.set('crow', crow);
api.set('deno', deno);
api.set('deps-edn', depsEdn);
api.set('devbox', devbox);
api.set('devcontainer', devContainer);
api.set('docker-compose', dockerCompose);
api.set('dockerfile', dockerfile);
api.set('droneci', droneci);
api.set('fleet', fleet);
api.set('flux', flux);
api.set('fvm', fvm);
api.set('git-submodules', gitSubmodules);
api.set('github-actions', githubActions);
api.set('gitlabci', gitlabci);
api.set('gitlabci-include', gitlabciInclude);
api.set('glasskube', glasskube);
api.set('gleam', gleam);
api.set('gomod', gomod);
api.set('gradle', gradle);
api.set('gradle-wrapper', gradleWrapper);
api.set('haskell-cabal', haskellCabal);
api.set('helm-requirements', helmRequirements);
api.set('helm-values', helmValues);
api.set('helmfile', helmfile);
api.set('helmsman', helmsman);
api.set('helmv3', helmv3);
api.set('hermit', hermit);
api.set('homeassistant-manifest', homeassistantManifest);
api.set('homebrew', homebrew);
api.set('html', html);
api.set('jenkins', jenkins);
api.set('jsonnet-bundler', jsonnetBundler);
api.set('kotlin-script', kotlinScript);
api.set('kubernetes', kubernetes);
api.set('kustomize', kustomize);
api.set('leiningen', leiningen);
api.set('maven', maven);
api.set('maven-wrapper', mavenWrapper);
api.set('meteor', meteor);
api.set('mint', mint);
api.set('mise', mise);
api.set('mix', mix);
api.set('nix', nix);
api.set('nodenv', nodenv);
api.set('npm', npm);
api.set('nuget', nuget);
api.set('nvm', nvm);
api.set('ocb', ocb);
api.set('osgi', osgi);
api.set('pep621', pep621);
api.set('pep723', pep723);
api.set('pip-compile', pipCompile);
api.set('pip_requirements', pip_requirements);
api.set('pip_setup', pip_setup);
api.set('pipenv', pipenv);
api.set('pixi', pixi);
api.set('poetry', poetry);
api.set('pre-commit', preCommit);
api.set('proto', proto);
api.set('pub', pub);
api.set('puppet', puppet);
api.set('pyenv', pyenv);
api.set('quadlet', quadlet);
api.set('renovate-config', renovateConfig);
api.set('ruby-version', rubyVersion);
api.set('runtime-version', runtimeVersion);
api.set('rust-toolchain', rustToolchain);
api.set('sbt', sbt);
api.set('scalafmt', scalafmt);
api.set('setup-cfg', setupCfg);
api.set('smithy', smithy);
api.set('sveltos', sveltos);
api.set('swift', swift);
api.set('tekton', tekton);
api.set('terraform', terraform);
api.set('terraform-version', terraformVersion);
api.set('terragrunt', terragrunt);
api.set('terragrunt-version', terragruntVersion);
api.set('tflint-plugin', tflintPlugin);
api.set('travis', travis);
api.set('typst', typst);
api.set('unity3d', unity3d);
api.set('velaci', velaci);
api.set('vendir', vendir);
api.set('woodpecker', woodpecker);
api.set('xcodegen', xcodegen);