Skip to content

Commit a49282a

Browse files
ci: add actionlint and a test-environment guard (#48)
Two guardrails, each for a silent failure that actually happened during this security sweep. ## 1. actionlint I introduced a duplicate `env:` key in cmcp's `release.yml` while hardening tag interpolation. GitHub Actions refuses to load a workflow with a duplicate key, reported it as a "workflow file issue" on unrelated pushes, ran nothing for the release event, and left a version unpublished for hours. It got through because `yaml.safe_load` keeps the last value for a duplicate key without complaining. actionlint says it plainly: ``` .github/workflows/repro.yml:16:9: key "env" is duplicated in element of "steps" section. previously defined at line:13,col:9 [syntax-check] ``` Verified against a reproduction of the exact broken file before adding it. **On how it's installed:** fetched by pinned version with a checksum verification, not run as a third-party action. actionlint publishes no official action, and a check whose whole purpose is guarding the workflow supply chain should not add a new dependency to it. Triggered only on changes under `.github/workflows`, so it costs nothing on ordinary PRs. **Every repo in the org is clean against it today**, so this adds no backlog. ## 2. Test-environment guard pytest puts the source tree on the path, so an in-process import always finds this tree and looks right. A test that shells out gets no such help: `subprocess.run([sys.executable, ...])` resolves the distribution normally, and with a released wheel also installed it finds site-packages. The subprocess then exercises a **published version** while the suite reports a pass. That is exactly how `trace-spec`'s tutorial test graded against the previous schema and still looked green, and why `cmcp`'s distribution smoke test reported a version that had nothing to do with the tree. The guard probes a subprocess once per session and fails with an actionable message: ``` ERROR: agentrust_trace resolves to ...\site-packages\agentrust_trace\__init__.py in a subprocess, outside C:\Users\imran\source\trace-spec. Any test that shells out would exercise that installed distribution instead of this working tree. Install editable (pip install -e .) or uninstall the shadowing agentrust_trace. ``` **It is a no-op in CI**, which installs editable, and verified as such against a correctly configured local venv. A package that is not importable from a subprocess at all is deliberately allowed: that is a path-only setup, not a shadowing install, so nobody gets locked out of running the suite. Applied to the seven repos that install their own package editable in CI. `examples`, `integrations` and `demos` test against third-party distributions on purpose and get actionlint only. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01XbDBXDWWvMFa7c2jGgyq9t --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent 5d963d3 commit a49282a

1 file changed

Lines changed: 41 additions & 0 deletions

File tree

.github/workflows/actionlint.yml

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
name: Workflow lint
2+
3+
# actionlint catches what a YAML parser cannot: duplicate mapping keys that
4+
# make GitHub refuse to load a workflow, invalid ${{ }} expressions, and shell
5+
# problems inside run: blocks. A duplicate env: key silently broke this repo
6+
# family's release workflow once; yaml.safe_load keeps the last value without
7+
# complaining, so local validation passed while Actions rejected the file.
8+
on:
9+
push:
10+
branches: [main]
11+
paths: ['.github/workflows/**']
12+
pull_request:
13+
paths: ['.github/workflows/**']
14+
workflow_dispatch:
15+
16+
permissions:
17+
contents: read
18+
19+
jobs:
20+
actionlint:
21+
runs-on: ubuntu-latest
22+
steps:
23+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
24+
25+
# Fetched and checksum-verified rather than run as a third-party action,
26+
# so this check adds no new action to the supply chain it exists to guard.
27+
- name: Install actionlint
28+
env:
29+
ACTIONLINT_VERSION: 1.7.12
30+
ACTIONLINT_SHA256: 8aca8db96f1b94770f1b0d72b6dddcb1ebb8123cb3712530b08cc387b349a3d8
31+
run: |
32+
set -euo pipefail
33+
archive="actionlint_${ACTIONLINT_VERSION}_linux_amd64.tar.gz"
34+
curl -sSfL -o "$archive" \
35+
"https://github.com/rhysd/actionlint/releases/download/v${ACTIONLINT_VERSION}/${archive}"
36+
echo "${ACTIONLINT_SHA256} ${archive}" | sha256sum -c -
37+
tar -xzf "$archive" actionlint
38+
install -m 0755 actionlint /usr/local/bin/actionlint
39+
40+
- name: Lint workflows
41+
run: actionlint -color

0 commit comments

Comments
 (0)