-
Notifications
You must be signed in to change notification settings - Fork 161
173 lines (150 loc) · 6.5 KB
/
ci.yml
File metadata and controls
173 lines (150 loc) · 6.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
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).
# NOTE: Once `apm-action` ships a CLI version that includes the
# default-on `apm audit` drift detection (issue #1071), this entire
# step becomes redundant -- Gate A above already catches the same
# divergence via install-replay. Keep this bash check until then as
# a defense-in-depth fallback.
#
# 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 (legacy bash fallback, see #1071)
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