Skip to content

ci(gh-aw): bump apm-action v1.5.0 -> v1.6.0 for plugin-bundle defenses #2218

ci(gh-aw): bump apm-action v1.5.0 -> v1.6.0 for plugin-bundle defenses

ci(gh-aw): bump apm-action v1.5.0 -> v1.6.0 for plugin-bundle defenses #2218

Workflow file for this run

name: CI
env:
PYTHON_VERSION: '3.12'
on:
pull_request:
branches: [ main ]
# Tier 1 also runs in merge queue context so the same unit + build checks
# execute against the tentative merge commit that the queue creates. See
# microsoft/apm#770 for the design.
merge_group:
branches: [ main ]
types: [ checks_requested ]
permissions:
contents: read
jobs:
# Fast lint gate -- runs in ~3s using Astral's ruff-action (no Python/uv setup needed).
# Fails fast on style, import, and complexity violations before the heavier build-and-test job.
lint:
name: Lint
runs-on: ubuntu-24.04
permissions:
contents: read
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v6
- name: Ruff lint
run: uv run --extra dev ruff check src/ tests/
- name: Ruff format check
run: uv run --extra dev ruff format --check src/ tests/
- name: Check YAML encoding safety
run: |
# Ensure YAML file I/O goes through yaml_io helpers.
# Catches yaml.dump/safe_dump writing to a file handle outside yaml_io.py.
VIOLATIONS=$(grep -rn --include='*.py' -P \
'yaml\.(safe_)?dump\(.+,\s*[a-zA-Z_]\w*\b' src/apm_cli/ \
| grep -v 'utils/yaml_io.py' \
| grep -v '# yaml-io-exempt' \
|| true)
if [ -n "$VIOLATIONS" ]; then
echo "::error::Direct yaml.dump() to file handle detected. Use yaml_io.dump_yaml() instead:"
echo "$VIOLATIONS"
exit 1
fi
- name: File length guardrail
run: |
# Ruff has no max-module-lines rule. This check prevents new files from
# exceeding the current worst case. Tighten the threshold over time.
MAX_LINES=2400 # current max: 2316 (github_downloader.py)
VIOLATIONS=$(find src/ -name '*.py' -print0 | xargs -0 -I{} awk -v max="$MAX_LINES" \
'END { if (NR > max) printf "%s: %d lines (max %d)\n", FILENAME, NR, max }' {})
if [ -n "$VIOLATIONS" ]; then
echo "::error::Source files exceed $MAX_LINES-line limit:"
echo "$VIOLATIONS"
exit 1
fi
- name: Lint - no raw str(relative_to) patterns
run: |
# Fail if any code uses str(x.relative_to(y)) instead of portable_relpath()
if grep -rn --include="*.py" -P 'str\([^)]*\.relative_to\(' src/apm_cli/ | grep -v portable_relpath | grep -v '\.pyc'; then
echo "::error::Found raw str(path.relative_to()) calls. Use portable_relpath() from apm_cli.utils.paths instead."
exit 1
fi
# Linux-only for PR feedback. Full platform matrix (incl. macOS + Windows) runs post-merge in build-release.yml.
# Combines unit tests + binary build into a single job to eliminate runner re-provisioning overhead.
build-and-test:
name: Build & Test (Linux)
runs-on: ubuntu-24.04
permissions:
contents: read
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Install uv
uses: astral-sh/setup-uv@v6
with:
enable-cache: true
- name: Install dependencies
run: uv sync --extra dev --extra build
- name: Run tests
run: uv run pytest tests/unit tests/test_console.py -n auto --dist worksteal
- name: Install UPX
run: |
sudo apt-get update
sudo apt-get install -y upx-ucl
- name: Build binary
run: |
chmod +x scripts/build-binary.sh
uv run ./scripts/build-binary.sh
- name: Upload binary as workflow artifact
uses: actions/upload-artifact@v4
with:
name: apm-linux-x86_64
# Scripts are included to preserve the artifact root at ./ (not ./dist/).
# Without a sibling directory, upload-artifact strips the dist/ prefix,
# breaking download paths in ci-integration.yml which expects dist/$BINARY_NAME/apm.
path: |
./dist/apm-linux-x86_64
./dist/apm-linux-x86_64.sha256
./scripts/test-release-validation.sh
./scripts/github-token-helper.sh
include-hidden-files: true
retention-days: 30
if-no-files-found: error
# Dogfood the two CI gates we ship and document to users:
# - Gate A (consumer-side): `apm audit --ci` -- lockfile / install fidelity.
# - Gate B (producer-side): regeneration drift -- did someone hand-edit
# a regenerated file under .github/ without updating canonical .apm/?
# See microsoft/apm#883 for context. Tier 1 (no secrets needed).
apm-self-check:
name: APM Self-Check
runs-on: ubuntu-24.04
permissions:
contents: read
steps:
- uses: actions/checkout@v4
# Installs the APM CLI (latest stable) and runs `apm install` against
# this repo's apm.yml. Auto-detects target from the existing .github/
# directory and re-integrates local .apm/ content, regenerating
# .github/instructions/, .github/agents/, .github/skills/, etc.
# Adds `apm` to PATH for subsequent steps.
- uses: microsoft/apm-action@v1
# Gate A: lockfile / install fidelity (consumer-side).
# Verifies every file in lockfile.deployed_files exists, ref consistency
# between apm.yml and apm.lock.yaml, no orphan packages, and
# content-integrity (hidden Unicode) on deployed package content.
# Does NOT verify deployed-file content vs lockfile (see #684).
- name: apm audit --ci
run: apm audit --ci
# Gate B: regeneration drift (producer-side).
# The action's `apm install` step re-integrated local .apm/ into
# .github/ via target auto-detection. If anything in the governed
# integration directories changed, someone edited the regenerated
# output without updating the canonical .apm/ source.
- name: Check APM integration drift
run: |
if [ -n "$(git status --porcelain -- .github/ .claude/ .cursor/ .opencode/)" ]; then
echo "::error::APM integration files are out of date."
echo "Run 'apm install' locally (with .github/ present) and commit the result."
git --no-pager diff -- .github/ .claude/ .cursor/ .opencode/
exit 1
fi