A GitHub Action that installs APM (Agent Package Manager) and deploys agent primitives (instructions, prompts, skills, agents) into your CI workflows. One line. Zero config.
📖 APM Documentation · Security Model · CI/CD Guide
- uses: microsoft/apm-action@v1This installs the APM CLI, reads your apm.yml, and runs apm install.
- uses: microsoft/apm-action@v1
with:
compile: 'true' # generate AGENTS.md after install
apm-version: '0.7.0' # pin a specific APM version
working-directory: './my-project' # custom working directory- uses: microsoft/apm-action@v1
with:
isolated: 'true'
dependencies: |
- microsoft/apm-sample-packageJust install the APM CLI and put it on PATH, like actions/setup-node. Run any apm command yourself in subsequent steps. No apm.yml required, no install step runs.
- uses: microsoft/apm-action@v1
id: apm
with:
setup-only: 'true'
apm-version: '0.11.0'
- run: apm --version
- run: apm pack -o build --format pluginsetup-only: true is mutually exclusive with pack, bundle, and bundles-file. The action will not read apm.yml, run apm install, or deploy primitives. Sets the apm-version and apm-path outputs so downstream steps can branch on the resolved CLI.
apm pack supports two layouts:
bundle-format: apm(default) -- produces an APM bundle containingapm.lock.yamland a.github/(or.claude/) tree. Restorable by this action viabundle:/bundles-file:. Use this when the consumer is anothermicrosoft/apm-actionstep.bundle-format: plugin-- produces a Claude Code plugin bundle withplugin.jsonat the root and flat primitive directories (agents/,skills/, ...). Use this when publishing to a Claude Code marketplace. Plugin bundles are not restorable by this action; restore them with your plugin tooling.
- uses: microsoft/apm-action@v1
with:
pack: 'true'
bundle-format: 'plugin' # opt-in; default is 'apm'The bundle-format output reflects the format of the produced or restored bundle.
Install dependencies, scan for hidden Unicode threats, and pack into a self-contained .tar.gz archive. Add audit-report to generate a SARIF report alongside the bundle:
- uses: microsoft/apm-action@v1
id: pack
with:
pack: 'true'
target: 'copilot'
audit-report: true
- uses: github/codeql-action/upload-sarif@v3
if: always() && steps.pack.outputs.audit-report-path
with:
sarif_file: ${{ steps.pack.outputs.audit-report-path }}
category: apm-audit
- uses: actions/upload-artifact@v4
with:
name: agent-bundle
path: ${{ steps.pack.outputs.bundle-path }}This works with all modes — isolated, inline dependencies, or from apm.yml.
When apm.yml declares an outputs: map (vendor-format marketplace files), forward the pack-time controls so CI emits exactly the right files for your release:
- uses: microsoft/apm-action@v1
id: pack
with:
pack: 'true'
archive: 'true'
marketplace: 'claude,codex' # which formats to emit (default: all from outputs:)
json-output: 'pack.json' # capture --json report for downstream steps
offline: 'true' # hermetic build using apm.lock.yaml
include-prerelease: 'false' # (default) skip pre-release tags
- name: Stage marketplace artifacts for the release
run: |
cat ${{ steps.pack.outputs.pack-json }}
# bundle-path is empty for marketplace-only projects; use pack-json
# to enumerate bundles + marketplace files + sidecars uniformly.marketplace-path overrides where each format file is written, useful when you need a vendor-expected filename in the release artifact set:
- uses: microsoft/apm-action@v1
with:
pack: 'true'
marketplace-path: |
claude=marketplace.json
codex=plugins.tomlVendor-neutral by design. This action does not assume which downstream CLI consumes the marketplace files. It produces the artifacts your apm.yml outputs: map declares; how consumers install them is a separate concern. See the apm marketplace init scaffold for guidance on which formats to declare for which consumer ecosystems.
Collapse the canonical release pipeline -- gate, matrix-pack, sha256 sidecars, marketplace.json drift detection, gh release create -- into a single step. Triggered by tag pushes; vendor-neutral underneath (the CLI primitives work identically in GitLab CI, Jenkins, ADO).
on:
push:
tags: ['v*']
jobs:
release:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4
- uses: microsoft/apm-action@v1
with:
mode: release
# release-tag defaults to GITHUB_REF_NAME
# release-prerelease: auto (detects -rc/-alpha/-beta/-pre suffix)
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}What it does, in order:
apm pack --check-versions --check-clean --json-- fails the job on misaligned per-package versions or uncommitted marketplace.json drift.- Detect repo shape (
aggregatorifplugins/<name>/apm.ymlfiles exist, otherwisesingle-plugin). - Matrix-pack every package with
apm pack --offline --archive-> tarballs indist/. - Write
<tarball>.sha256sidecars next to each tarball. - Stage
marketplace-<version>.jsonfor aggregator shapes. - Render a GitHub Step Summary table of the release contents.
gh release create <tag> <files...>(skipped ifrelease-skip-publish: true).
Outputs: packages (JSON), marketplace-drift, release-url, release-tag.
For the non-GitHub-Actions equivalent (the same primitives wrapped in .gitlab-ci.yml, Jenkinsfile, ADO azure-pipelines.yml), see producer/releasing-from-any-ci.md.
Restore primitives from a bundle. The action installs APM (cached across runs) and uses apm unpack for integrity verification — no Python, minimal network. Only files listed in the bundle's lockfile (deployed_files) are written to working-directory; the lockfile and apm.yml themselves are not, so the workspace stays clean for downstream steps such as git checkout.
- uses: actions/download-artifact@v4
with:
name: agent-bundle
- uses: microsoft/apm-action@v1
with:
bundle: './*.tar.gz'Why: when you fan out a pack job across N GitHub Apps (or N orgs, or N teams) you end up with N separate bundle artifacts. Without bundles-file, the consumer job has to call microsoft/apm-action@v1 N times in sequence, which adds latency and obscures which install came from which source. bundles-file lets a single restore step merge all N bundles into one workspace in caller-specified order. See issue #29 for the full rationale and diagrams.
Backward compatibility: existing single-bundle callers are unaffected. bundles-file is a new opt-in input; pack, bundle, and bundles-file are mutually exclusive (the action errors if more than one is set).
# In a downstream job that consumes all bundles:
- uses: actions/download-artifact@v4
with:
pattern: apm-*
path: /tmp/bundles
- run: find /tmp/bundles -name '*.tar.gz' | sort > /tmp/bundle-list.txt
- uses: microsoft/apm-action@v1
id: restore
with:
bundles-file: /tmp/bundle-list.txt
working-directory: /tmp/agent-workspace
- run: echo "Merged ${{ steps.restore.outputs.bundles-restored }} bundles into the workspace"The bundles-restored output reports the integer count of bundles successfully merged, which is convenient for assertions and logging in downstream steps.
Collision policy: bundles are applied in list order; on file conflicts, later bundles overwrite earlier bundles. The action logs an explicit warning naming the bundle count before the restore loop begins, so the policy is never silent. Per-file SHA-aware collision detection is planned for v1.6.0.
Pack once, restore everywhere — identical primitives across all consumer jobs.
jobs:
agent-config:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: microsoft/apm-action@v1
id: pack
with:
pack: 'true'
target: 'copilot'
- uses: actions/upload-artifact@v4
with:
name: agent-bundle
path: ${{ steps.pack.outputs.bundle-path }}
lint:
needs: agent-config
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
name: agent-bundle
- uses: microsoft/apm-action@v1
with:
bundle: './*.tar.gz'
# .github/ is ready — primitives deployed
deploy:
needs: agent-config
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
name: agent-bundle
- uses: microsoft/apm-action@v1
with:
bundle: './*.tar.gz'
# Same primitives, different job. Byte-identical.apm install automatically blocks packages with critical hidden-character findings — no configuration needed. Add audit-report for visibility: a SARIF report for Code Scanning annotations and a markdown summary in $GITHUB_STEP_SUMMARY. See the APM security model for details.
- uses: microsoft/apm-action@v1
id: apm
with:
audit-report: true
- uses: github/codeql-action/upload-sarif@v3
if: always() && steps.apm.outputs.audit-report-path
with:
sarif_file: ${{ steps.apm.outputs.audit-report-path }}
category: apm-auditBy default, github-token (which defaults to ${{ github.token }}) is automatically forwarded to APM as GITHUB_APM_PAT. This means same-org private repos work with zero config.
# Same-org private repos: zero config
- uses: microsoft/apm-action@v1For cross-org private repos, pass a PAT with broader scope via the github-token input:
# Cross-org private repos: pass a broader-scoped PAT
- uses: microsoft/apm-action@v1
with:
github-token: ${{ secrets.APM_PAT }}For multi-org or multi-platform scenarios, use the env: block for full control. An explicit GITHUB_APM_PAT in env: always wins over the auto-forwarded value. (For the matrix-based fan-out pattern that pairs one App per matrix replica with bundles-file:, see issue #29.)
# Multi-org / multi-platform: full control via env block
- uses: microsoft/apm-action@v1
env:
GITHUB_APM_PAT: ${{ secrets.APM_PAT }}
GITHUB_APM_PAT_CONTOSO: ${{ secrets.APM_PAT_CONTOSO }}
ADO_APM_PAT: ${{ secrets.ADO_PAT }}
ARTIFACTORY_APM_TOKEN: ${{ secrets.ARTIFACTORY_TOKEN }}Note: GitHub Actions forbids secrets named with the
GITHUB_prefix, so you cannot create a secret calledGITHUB_APM_PATdirectly. The auto-forward fromgithub-tokencovers the common case. For cross-org tokens, name your secret something likeAPM_PATand pass it viagithub-tokenorenv: GITHUB_APM_PAT.
| Input | Required | Default | Description |
|---|---|---|---|
working-directory |
No | . |
Working directory for execution. Must exist in non-isolated mode (with your apm.yml). In isolated, pack, or bundle modes the directory is created automatically. |
apm-version |
No | latest |
APM version to install |
github-token |
No | ${{ github.token }} |
GitHub token for API calls. Auto-forwarded as GITHUB_APM_PAT so same-org private repos work with zero config. Pass a broader-scoped PAT for cross-org access. |
script |
No | APM script to run after install | |
dependencies |
No | YAML array of extra dependencies to install (additive to apm.yml) | |
isolated |
No | false |
Ignore apm.yml and clear pre-existing primitive dirs — install only inline dependencies |
compile |
No | false |
Run apm compile after install to generate AGENTS.md |
pack |
No | false |
Pack a bundle after install (produces .tar.gz by default) |
bundle-format |
No | apm |
Bundle layout when pack: true. apm produces an APM bundle (with apm.lock.yaml and a .github/ tree, restorable by this action). plugin produces a Claude Code plugin bundle (with plugin.json at the root, intended for marketplace consumption). |
setup-only |
No | false |
Install the APM CLI and exit. No apm.yml is read, no apm install runs, no primitives are deployed. Mutually exclusive with pack, bundle, and bundles-file. |
bundle |
No | Restore from a bundle (local path or glob). Installs APM and unpacks via apm unpack (verified). |
|
bundles-file |
No | Path to a UTF-8 text file with one bundle path per line. Restores N bundles into a single workspace in caller-specified order (last wins on collisions). Mutually exclusive with pack and bundle. |
|
target |
No | Bundle target: copilot, vscode, claude, or all (used with pack: true) |
|
archive |
No | true |
Produce .tar.gz instead of directory (used with pack: true) |
marketplace |
No | Forwarded to apm pack --marketplace=<value> (used with pack: true). Comma-separated format list (claude,codex), all, or none. Defaults to whatever is configured in apm.yml's outputs: map. |
|
marketplace-path |
No | Forwarded to apm pack --marketplace-path FORMAT=PATH (used with pack: true). Repeatable: one FORMAT=PATH override per line. Newline is the only separator -- , is a legal filename character. Overrides where each marketplace format file is written. |
|
json-output |
No | Forwarded to apm pack --json (used with pack: true). When set, the action passes --json to the CLI and captures the JSON report to this path. Consume from downstream steps via the pack-json output. |
|
offline |
No | false |
Forwarded to apm pack --offline (used with pack: true). Skips network resolution of marketplace dependency refs. Useful in hermetic CI where versions are pinned in apm.lock.yaml. |
include-prerelease |
No | false |
Forwarded to apm pack --include-prerelease (used with pack: true). Considers pre-release version tags when resolving marketplace dependency refs. |
audit-report |
No | Generate a SARIF audit report (hidden Unicode scanning). apm install already blocks critical findings; this adds reporting for Code Scanning and a markdown summary in $GITHUB_STEP_SUMMARY. Set to true for default path, or provide a custom path. |
| Output | Description |
|---|---|
success |
Whether the action succeeded (true/false) |
apm-version |
Resolved APM CLI version (e.g. 0.11.0). Always set. |
apm-path |
Absolute path to the resolved apm binary. Resolved via tool-cache when the action installed APM, or via which apm when reusing a pre-existing CLI on PATH. |
bundle-format |
Format of the produced or restored bundle (apm or plugin). Set in pack and single-bundle restore modes. |
primitives-path |
Path where agent primitives were deployed (.github) |
bundle-path |
Path to the packed bundle (only set in pack mode). Now empty for marketplace-only projects (no dependencies: block in apm.yml) — consume pack-json to discover what was emitted. Pre-existing if: steps.pack.outputs.bundle-path != '' guards continue to work and will correctly skip downstream upload steps on marketplace-only projects. |
pack-json |
Path to the captured apm pack --json report. Set when the json-output input was provided. Source of truth for downstream steps that need to enumerate every artifact (bundles, marketplace files, sidecars) without globbing build/. |
audit-report-path |
Path to the generated SARIF audit report (if audit-report was set) |
bundles-restored |
Number of bundles successfully restored (multi-bundle mode only) |
This action bundles the following open-source packages (see dist/licenses.txt for full license texts):
- @actions/core — GitHub Actions toolkit (MIT)
- @actions/exec — GitHub Actions exec helpers (MIT)
- @actions/io — GitHub Actions I/O helpers (MIT)
- js-yaml — YAML parser (MIT)
See CONTRIBUTING.md for details.
This project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft trademarks or logos is subject to and must follow Microsoft's Trademark & Brand Guidelines. Use of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship. Any use of third-party trademarks or logos are subject to those third-party's policies.