Enforce in CI
apm install already runs the policy gate, the security scan, and drift
detection on every developer machine. CI re-runs the same gates on the
pull request itself. That is defence in depth: a developer can pass
--no-policy, --force, or APM_POLICY_DISABLE=1 locally; CI cannot.
This page is the recipe set. For the full schema and the rollout playbook, see Governance overview and apm-policy getting started.
The gate
Section titled “The gate”apm audit --ciOne command. It runs the eight baseline lockfile checks
(lockfile-exists, ref-consistency, deployed-files-present,
no-orphaned-packages, skill-subset-consistency, config-consistency,
content-integrity, includes-consent), the install-replay drift
check, and — if an apm-policy.yml is discovered — the org policy
checks. Exit code is 0 clean, 1 on any violation.
Useful flags:
--policy <source>— explicit policy ref (org, a path, a URL, or<owner>/<repo>). Without it, APM auto-discovers from your project’s git remote, mirroringapm install.--no-policy— skip policy discovery (baseline + drift only).--no-cache— force a fresh policy fetch. Recommended in CI so a cached file does not mask a same-day policy update.--no-fail-fast— run every check even after one fails. Useful for reports; default is stop at first failure.--no-drift— skip the install-replay. Reduces coverage; only use when CI minutes are the bottleneck.-f json/-f sarif— structured output. Markdown is not supported in--cimode.-o <path>— write the report to a file. The format is inferred from the extension (.sarif,.json).
Recipe: minimal GitHub Actions gate
Section titled “Recipe: minimal GitHub Actions gate”This is the smallest job that fails a PR on any APM violation.
name: APM auditon: pull_request: paths: - 'apm.yml' - 'apm.lock.yaml' - '.apm/**' - '.github/**' - '.claude/**' - '.cursor/**'
jobs: audit: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: microsoft/apm-action@v1 - run: apm audit --ci --no-cache env: GITHUB_APM_PAT: ${{ secrets.APM_PAT }}microsoft/apm-action@v1 runs apm install by default, so by the time
apm audit --ci runs, the lockfile and deployed files are present.
Make this job a required status check via
GitHub Rulesets and a violating PR cannot merge.
Recipe: SARIF for GitHub Code Scanning
Section titled “Recipe: SARIF for GitHub Code Scanning”Emit SARIF and upload it so each violation appears inline on the PR diff and in the repository’s Security tab.
jobs: audit: runs-on: ubuntu-latest permissions: contents: read security-events: write steps: - uses: actions/checkout@v4 - uses: microsoft/apm-action@v1 - name: Audit run: apm audit --ci --no-cache -o apm-audit.sarif env: GITHUB_APM_PAT: ${{ secrets.APM_PAT }} - name: Upload SARIF if: always() uses: github/codeql-action/upload-sarif@v3 with: sarif_file: apm-audit.sarif category: apm-auditif: always() matters: SARIF must upload even when the audit step
exited 1, otherwise the failing run produces no Code Scanning entry.
Recipe: scheduled drift sweep
Section titled “Recipe: scheduled drift sweep”Pull requests catch drift on the changed branch. A nightly job catches
drift on main — hand-edits, missing apm install runs after a
manual lockfile bump, or a stale deployed file that no PR touched.
on: schedule: - cron: '0 6 * * *' # 06:00 UTC daily workflow_dispatch:
jobs: drift-sweep: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: microsoft/apm-action@v1 - run: apm audit --ci --no-fail-fast -o drift.sarif - if: always() uses: github/codeql-action/upload-sarif@v3 with: sarif_file: drift.sarif category: apm-drift-sweep--no-fail-fast lets the sweep report every finding rather than the
first one. See drift detection for what the
replay actually checks and how to debug a finding locally.
When the gate blocks a PR
Section titled “When the gate blocks a PR”The fix path depends on which check failed.
lockfile-exists/ref-consistency/deployed-files-present. The author skippedapm installafter editingapm.yml. They runapm install, commitapm.lock.yamland the integrated files, and push.content-integrityor a hidden-Unicode finding. A primitive was hand-edited. The author runsapm audit --stripto clean it (or reverts the edit), thenapm installto refresh the lockfile.- Drift replay. A deployed file no longer matches what an install
would produce.
apm installis the fix. - Policy violation. The author either picks an allowed alternative,
or opens a change request against
<org>/.github/apm-policy.yml.--no-policydoes not work here — CI ignores the local bypass flag.
For genuine, time-boxed exceptions, two waiver shapes exist today:
- Amend
apm-policy.yml(allow-list the package, raisemax_depth, etc.) through the same review process as any other policy change. - Lower
enforcementfromblocktowarnfor that policy scope. Findings still appear in the SARIF report; they no longer fail the job. Treat this as a temporary state and track its removal.
There is no per-PR override flag and there will not be one. Bypass must be visible in the policy file’s history.
Next steps
Section titled “Next steps”- drift detection — what the replay actually catches and how to read its output.
- security and supply chain — the built-in install-time scan that complements the CI gate.
- github rulesets — make the audit job a required status check across an org.
- APM in CI/CD — deeper patterns for Azure Pipelines, GitLab, Jenkins, air-gapped runners, and bundle caching across jobs.