Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 32 additions & 40 deletions .github/workflows/apm-audit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,23 @@ name: apm-audit

# Reusable workflow shipped by zava-agent-config for downstream APM consumers.
#
# v5.1.0+: the reusable workflow now runs `apm audit --ci --policy org`
# explicitly. v5.0.x called microsoft/apm-action with audit-report only,
# which generated SARIF but did NOT enforce the org policy or fail the
# job on violations — drift-only check, no policy gate. This regression
# was caught during the D2 governance demo wiring (see DevExpGbb/.github
# apm-policy.yml v2.0.0) where required-packages, dependency-denylist,
# and unmanaged-files violations passed CI silently.
# v5.1.2: switched apm-action to setup-only and removed `apm install` from
# the audit path. Reason: install RE-DEPLOYS files from upstream, OVERWRITING
# any in-PR tamper of a deployed managed file. That silently disabled drift
# detection in CI (caught during D2 demo Beat 5 wiring). Audit now runs
# directly against the contents of the PR, so content-integrity / drift /
# unmanaged-files all see what the PR author committed.
#
# v5.1.1: auto-detect repo apm-policy.yml override and use it as policy
# source so `extends: org` actually layers (vs --policy org which loads
# only the org layer).
#
# v5.1.0: explicit `apm audit --ci --policy ...` — the apm-action default
# only generates SARIF and does not enforce policy.
#
# jobs:
# audit:
# uses: DevExpGbb/zava-agent-config/.github/workflows/apm-audit.yml@v5.1.0
#
# Pinning the workflow to a tag means the consumer's audit step is part of
# the supply chain they audit — no implicit "latest" reusable workflow.
# uses: DevExpGbb/zava-agent-config/.github/workflows/apm-audit.yml@v5.1.2

on:
workflow_call:
Expand Down Expand Up @@ -49,26 +52,18 @@ jobs:
echo "::notice::No APM dependencies declared — audit is a no-op."
fi

# Install + run apm install via the action. setup-only would skip install,
# but we WANT install to also run so policy enforcement at install time
# (denylist on uninstalled new deps) catches violations even before audit.
- name: Install APM + dependencies
# setup-only installs the apm CLI on PATH and skips `apm install`.
# We must NOT run install here: it would re-deploy primitives from
# upstream and overwrite any tampered file in the PR, masking drift.
- name: Install APM CLI
if: steps.detect.outputs.has_deps == 'true'
uses: microsoft/apm-action@v1
with:
audit-report: true
setup-only: true

# Explicit policy enforcement. The action generates SARIF but does NOT
# call `--ci` or `--policy` — it runs drift-only. This step closes the
# gap: `--policy org` resolves the consumer org's apm-policy.yml from
# GitHub (DevExpGbb/.github/apm-policy.yml in our case) and `--ci`
# makes block-enforcement violations exit non-zero.
# Detect repo-level override. apm policy supports tighten-only override
# layering via `extends: org` in a repo apm-policy.yml. When present,
# use the repo file as policy source so the audit gates on the EFFECTIVE
# policy (org ∪ repo override). Falls back to `--policy org` otherwise.
- name: Detect repo policy override
id: policy
if: steps.detect.outputs.has_deps == 'true'
run: |
if [ -f apm-policy.yml ]; then
echo "source=./apm-policy.yml" >> "$GITHUB_OUTPUT"
Expand All @@ -77,32 +72,29 @@ jobs:
echo "source=org" >> "$GITHUB_OUTPUT"
fi

- name: Enforce APM policy (org + repo override)
- name: Enforce APM policy + drift (apm audit --ci)
if: steps.detect.outputs.has_deps == 'true'
env:
GITHUB_APM_PAT: ${{ github.token }}
APM_FAIL_ON_WARN: ${{ inputs.fail-on-warn }}
POLICY_SOURCE: ${{ steps.policy.outputs.source }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -e
set -euo pipefail
if [ "$APM_FAIL_ON_WARN" = "true" ]; then
apm audit --ci --policy "$POLICY_SOURCE" --fail-on-warn
apm audit --ci --policy "$POLICY_SOURCE" --fail-on-warning
else
apm audit --ci --policy "$POLICY_SOURCE"
fi

- name: Summarize on PR
if: always() && github.event_name == 'pull_request'
- name: Step summary
if: always() && steps.detect.outputs.has_deps == 'true'
run: |
{
echo "## 🛡️ APM Audit"
echo "## APM audit"
echo ""
echo "- Policy source: \`${{ steps.policy.outputs.source }}\`"
echo "- Layered enforcement: repo override (\`extends: org\`) when present, else org floor"
echo "- Checks: lockfile-exists, ref-consistency, deployed-files-present, no-orphaned-packages, skill-subset-consistency, config-consistency, content-integrity, drift, required-packages, dependency-allowlist, dependency-denylist, unmanaged-files"
echo ""
if [ "${{ steps.detect.outputs.has_deps }}" = "true" ]; then
echo "Ran \`apm audit --ci --policy org\` against the consumer's org policy via \`microsoft/apm-action@v1\`."
echo "(Org policy resolved from \`<consumer-org>/.github/apm-policy.yml\` automatically.)"
echo ""
echo "SARIF report uploaded by apm-action; see job logs and Code scanning tab."
else
echo "No APM dependencies declared in \`apm.yml\` — audit skipped."
fi
echo "Ran \`apm audit --ci --policy $POLICY_SOURCE\` against PR contents (no \`apm install\` — drift detection sees the as-committed file state)."
} >> "$GITHUB_STEP_SUMMARY"
Loading