-
Notifications
You must be signed in to change notification settings - Fork 0
228 lines (218 loc) · 10.7 KB
/
Copy pathci.yml
File metadata and controls
228 lines (218 loc) · 10.7 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
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
name: CI
on:
push:
branches: [main]
tags: ["v*"]
pull_request:
branches: [main]
workflow_dispatch:
inputs:
full_matrix:
description: "Also run Windows and macOS (billed at 2x and 10x). Tags do this automatically."
type: boolean
default: false
permissions:
contents: read
jobs:
# Every PR commit must carry a Signed-off-by trailer (DCO -- see CONTRIBUTING.md).
dco:
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
persist-credentials: false
fetch-depth: 0
- name: Check Signed-off-by on every PR commit
env:
# No `git fetch` here: persist-credentials:false leaves the checkout without
# credentials, so fetching fails with "could not read Username". fetch-depth:0
# already put the base commit in the local history, so the event's base SHA is
# enough. Passed through the environment rather than interpolated into the
# shell, so no ${{ }} value is ever evaluated as code.
BASE_SHA: ${{ github.event.pull_request.base.sha }}
run: |
missing=0
while read -r sha; do
if ! git log -1 --format=%B "$sha" | grep -qi '^Signed-off-by: .\+ <.\+@.\+>'; then
echo "::error::commit $sha has no Signed-off-by trailer"
missing=1
fi
# --no-merges: on a pull_request event the checkout HEAD is GitHub's own
# synthetic merge commit (authored by web-flow), which can never carry a
# sign-off. DCO applies to authored commits, not the platform's merge.
done < <(git rev-list --no-merges "$BASE_SHA..HEAD")
exit "$missing"
# Commit messages and PR bodies are as public as the diff and much easier to leak
# into -- container paths, session identifiers, personal addresses. Checked here
# because the local test can only see the history it has.
no-private-data:
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
persist-credentials: false
fetch-depth: 0
- name: Scan commit messages for local or personal data
env:
BASE_SHA: ${{ github.event.pull_request.base.sha }}
run: |
local_data='/home/[A-Za-z0-9._-]+/|/Users/[A-Za-z0-9._-]+/|session_[0-9A-Za-z]{12,}|claude\.ai/code/session|/tmp/claude-[0-9]'
email='[A-Za-z0-9._%+-]+@[A-Za-z0-9-]+(\.[A-Za-z0-9-]+)*\.[A-Za-z]{2,24}'
# Suffixes that make a match a file path rather than an address.
not_mail='\.(json|ya?ml|md|txt|py|js|ts|tsx|sh|toml|cfg|ini|lock|log|csv|tsv|html?|svg|png|jpe?g|gif|pdf|zip|gz|tar|whl|so|dll|exe)$'
# Addresses that identify a service rather than a person.
service='@users\.noreply\.github\.com$|^noreply@anthropic\.com$|^support@github\.com$|^noreply@github\.com$'
# The maintainer's personal address, as a SHA-256 digest: writing it here in the
# clear would publish it more prominently than the commits this check guards.
blocked_digest='ecda4887099c1a8d73af02994c42938dfb0a1857674f6c3a6403af922360104f'
found=0
while read -r sha; do
msg=$(git log -1 --format=%B "$sha")
# Name the commit, never echo the match: a log line is a publication too.
if printf '%s' "$msg" | grep -qiE "$local_data"; then
echo "::error::commit $sha contains a local path or session identifier"
found=1
fi
# A sign-off is somebody's own attestation, so a contributor's address there is
# their choice and stays allowed. Anywhere else an address arrived by accident --
# pasted from a log, a stack trace, or a quoted report.
body=$(printf '%s' "$msg" | grep -viE '^(Signed-off-by|Co-Authored-By|Reported-by|Reviewed-by|Tested-by|Acked-by):')
if printf '%s' "$body" | grep -oiE "$email" | grep -ivE "$not_mail" | grep -qivE "$service"; then
echo "::error::commit $sha contains an email address outside a sign-off trailer"
found=1
fi
while read -r addr; do
[ -n "$addr" ] || continue
if [ "$(printf '%s' "$addr" | tr '[:upper:]' '[:lower:]' | sha256sum | cut -d' ' -f1)" = "$blocked_digest" ]; then
echo "::error::commit $sha contains the maintainer's personal address; sign off with the GitHub noreply address"
found=1
fi
done <<< "$(printf '%s' "$msg" | grep -oiE "$email")"
done < <(git rev-list --no-merges "$BASE_SHA..HEAD")
exit "$found"
brand-assets:
# docs/assets/social-preview.svg is derived from the package -- adapters, events,
# decision outcomes, version -- so adding an adapter or an event silently invalidates
# the committed card. Nothing on it is hand-typed (every count is len() of a list read
# at render time), but a stale image is wrong all the same, and the image is what a
# link preview shows to someone who has not read the repo yet.
#
# The check compares the SVG, not the PNG: the SVG is text derived only from
# repository data and is identical on any machine, while a PNG depends on the font
# installed on the renderer. Facts reach the SVG first.
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
persist-credentials: false
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7
with:
python-version: "3.12"
- name: Install the asset renderer
run: pip install --require-hashes -r .github/requirements/assets.txt
- name: Check the social preview matches the package
run: cd docs/assets && python gen_brand_assets.py --check
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
persist-credentials: false
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7
with:
python-version: "3.11"
- run: pip install --require-hashes -r .github/requirements/lint.txt
- run: ruff check .
- run: ruff format --check .
test:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
# Linux-only by default; Actions minutes are billed 2x on Windows and 10x on
# macOS. The other platforms are not deleted because the defects that justify
# them are platform-specific (path handling, line endings, shell invocation),
# and a release must never be cut having been tested on Linux alone -- so a
# v* tag always runs the full matrix without anyone having to remember.
os: [ubuntu-latest]
# 3.9 is the floor the package claims; 3.13 surfaces deprecations early.
python-version: ["3.9", "3.11", "3.13"]
include: >-
${{ fromJSON(
(startsWith(github.ref, 'refs/tags/v') || inputs.full_matrix)
&& '[{"os":"windows-latest","python-version":"3.11"},{"os":"macos-latest","python-version":"3.11"}]'
|| '[]') }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
persist-credentials: false
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7
with:
python-version: ${{ matrix.python-version }}
- run: pip install --require-hashes -r .github/requirements/dev.txt
# --no-deps because dev.txt already carries every dependency, hash-verified;
# the package itself declares none. Resolving here would reintroduce an
# unverified fetch on top of a verified set.
- run: pip install --no-deps -e .
- run: pytest -q
- name: CLI smoke
shell: bash
run: |
agentseam --version
agentseam agents
agentseam matrix | head -3
agentseam doctor --repo .
# The runtime path must stay importable with no third-party packages: adapters are
# copied verbatim into other projects, so an accidental dependency breaks exactly
# the consumers this library exists for. Cheaper to assert than to discover.
# The vendor example pages are generated from the real code paths, so a behaviour change
# that nobody regenerated leaves documentation asserting something the library no longer
# does. Same script the test suite runs, for the reason given on the stdlib-only job.
examples:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
persist-credentials: false
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7
with:
python-version: "3.11"
- name: Generated examples match the library
run: python examples/generate.py --check
stdlib-only:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
persist-credentials: false
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7
with:
python-version: "3.11"
# The same script the test suite runs, on purpose: the first version of this
# check was inline here, drifted from the local test, and shipped a red pipeline.
- name: Import with user site-packages disabled
env:
PYTHONNOUSERSITE: "1"
run: python tests/check_stdlib_only.py
# docs/figures/make_*.py render the README's figures FROM src/agentseam/matrix.py -- an
# agent or event added to the matrix, or a grade that shifts, changes a committed SVG.
# Regenerating and diffing here is the only thing that catches a figure nobody re-ran.
# No cairosvg install: the generators are stdlib-only plus docs/figures/palette.py, and
# social-card.png (which does use cairosvg, guarded by an ImportError fallback in
# make_social.py) is not reproduced here on purpose -- it depends on the renderer's
# installed fonts, so only the SVG it was rendered from is asserted against the package.
figures:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
persist-credentials: false
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7
with:
python-version: "3.12"
- name: Regenerate figures
run: cd docs/figures && for g in make_*.py; do python "$g"; done
- name: Fail on drift
run: git diff --exit-code -- docs/figures