Repository navigation
234 lines (222 loc) · 9.4 KB
/
Copy pathci.yml
File metadata and controls
234 lines (222 loc) · 9.4 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
229
230
231
232
233
234
name: Test
on:
pull_request:
types:
- labeled
- opened
- reopened
- synchronize
- unlabeled
merge_group:
push:
branches:
- main
permissions:
contents: read
# One in-flight run per pull request. The `labeled` and `unlabeled` triggers
# above are needed, because the griffe step reads the `breaking` label off the
# event, but they also mean anything that touches a label re-runs the whole
# matrix — and a bot that toggles one produces a run per toggle, none of which
# supersede each other without this.
#
# Only pull request runs are cancelled. A merge queue run that loses its
# checks would fall out of the queue, and every commit on `main` is worth
# testing on its own.
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
jobs:
lint:
name: Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Fetch base branch for griffe comparison
if: github.event_name == 'pull_request'
env:
BASE_REF: ${{ github.event.pull_request.base.ref }}
run: git fetch origin "$BASE_REF"
- uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
- name: Check lockfile
run: uv lock --check
- name: Ruff lint
run: uv run ruff check . --output-format github
- name: Ruff format
run: uv run ruff format --diff .
- name: Ty type check
run: uv run ty check --output-format github
- name: Pyright type check
run: uv run pyright
- name: Griffe check
# Only run on PRs and allow breaking changes to be merged with a label
if: github.event_name == 'pull_request'
continue-on-error: ${{ contains(github.event.pull_request.labels.*.name, 'breaking') }}
env:
BASE_REF: ${{ github.event.pull_request.base.ref }}
run: uv run griffe check arcjet --search src --extensions tools/griffe_extensions.py:IgnoreProtobufDescriptors --format github --against "origin/${BASE_REF}"
test:
name: Run tests (py${{ matrix.python-version }})
strategy:
# Every version reports; one failure must not hide the others, because
# the interesting failures here are version-specific.
fail-fast: false
matrix:
os: [ubuntu-latest]
# `requires-python` is `>=3.10` with no upper bound, so these are the
# released versions the package installs on. The repo pins 3.10 in
# `.python-version`, so without this axis only the floor is exercised.
# Add each new Python here as it is released.
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
with:
python-version: ${{ matrix.python-version }}
- name: Tests
# `--python` rather than the environment alone, so the run cannot fall
# back to `.python-version` and silently retest the floor on every
# other cell.
run: uv run --python ${{ matrix.python-version }} pytest
# One check that stands for the whole matrix, so branch protection names a
# single context rather than one per interpreter. Adding a Python version
# above therefore needs no change to the ruleset, which cannot be edited from
# a pull request and so would otherwise drift behind the matrix.
#
# The name is the context `main` already requires. It predates the Python
# matrix, when the only axis was `os`. Windows and macOS report through
# their own aggregators below so this check stays Linux-only.
test-matrix:
name: Run tests (ubuntu-latest)
# Runs whether the matrix passed or failed, so the context always reports.
# `!cancelled()` rather than `always()`: a cancelled run is one the
# concurrency group superseded, and the run that replaced it reports
# instead.
if: ${{ !cancelled() }}
needs: test
runs-on: ubuntu-latest
steps:
- name: Check the matrix result
# `needs.test.result` is `success` only when every leg succeeded, so
# this fails for a failed, cancelled or skipped leg alike.
if: needs.test.result != 'success'
run: |
echo "::error::Test matrix result: ${{ needs.test.result }}"
exit 1
# Floor + current latest on each non-Linux OS. Ubuntu already covers every
# released interpreter; repeating that on windows-latest (2× minutes) and
# macos-latest (10×) would dominate the bill without catching much more than
# "does the locked wheel set install, and does WASM/TLS still work".
#
# Separate jobs — not extra cells on `test` — so a Windows or macOS failure
# cannot fail the required `Run tests (ubuntu-latest)` context. Each OS has
# its own aggregator with a stable name for a later ruleset addition.
test-windows:
name: Run tests (windows-latest, py${{ matrix.python-version }})
strategy:
fail-fast: false
matrix:
python-version: ["3.10", "3.14"]
runs-on: windows-latest
# First-run uv/Python/wheel fetch on Windows is slower than Ubuntu; a hung
# wasmtime or onnxruntime import must not sit on the 6h GitHub default.
timeout-minutes: 30
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
# The local-HTTPS transport tests shell out to `openssl`. The image ships
# it, but it is not always on PATH for PowerShell.
- name: Put OpenSSL on PATH
shell: pwsh
run: |
if (Get-Command openssl -ErrorAction SilentlyContinue) { exit 0 }
foreach ($dir in @(
"${env:ProgramFiles}\OpenSSL\bin",
"${env:ProgramFiles}\OpenSSL-Win64\bin",
"${env:ProgramFiles}\Git\usr\bin",
"${env:ProgramFiles}\Git\mingw64\bin"
)) {
if (Test-Path (Join-Path $dir "openssl.exe")) {
Add-Content -Path $env:GITHUB_PATH -Value $dir
exit 0
}
}
Write-Error "openssl.exe not found (required by tests/unit/test_transport.py)"
- uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
with:
python-version: ${{ matrix.python-version }}
- name: Tests
run: uv run --python ${{ matrix.python-version }} pytest
test-windows-matrix:
name: Run tests (windows-latest)
if: ${{ !cancelled() }}
needs: test-windows
runs-on: ubuntu-latest
steps:
- name: Check the matrix result
if: needs.test-windows.result != 'success'
run: |
echo "::error::Windows test matrix result: ${{ needs.test-windows.result }}"
exit 1
test-macos:
name: Run tests (macos-latest, py${{ matrix.python-version }})
strategy:
fail-fast: false
matrix:
python-version: ["3.10", "3.14"]
runs-on: macos-latest
timeout-minutes: 30
# macos-latest ships openssl on PATH (`/usr/bin/openssl`), so unlike
# Windows there is no extra PATH step. The TLS transport tests skip at
# collection if `shutil.which("openssl")` misses.
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
with:
python-version: ${{ matrix.python-version }}
- name: Tests
run: uv run --python ${{ matrix.python-version }} pytest
test-macos-matrix:
name: Run tests (macos-latest)
if: ${{ !cancelled() }}
needs: test-macos
runs-on: ubuntu-latest
steps:
- name: Check the matrix result
if: needs.test-macos.result != 'success'
run: |
echo "::error::macOS test matrix result: ${{ needs.test-macos.result }}"
exit 1
# The Claude Agent SDK integration tests need the optional
# `claude-agent-sdk` extra, which the default install leaves out, so the
# jobs above skip them. This job installs it from the lockfile. The CLI
# tests drive the CLI bundled with that SDK against a scripted, local
# Messages API, with no network access and no key. They are what catches a
# CLI release that changes a built-in tool's output shape: the CLI sends
# the original tool result to the model, without saying so, when a
# replacement's shape does not match.
test-claude-agent-sdk:
name: Run tests (claude-agent-sdk)
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
- name: Check the extra imports
# The tests skip when the extra is missing, so an install that left it
# out would otherwise pass with nothing run.
run: uv run --extra claude-agent-sdk python -c "import claude_agent_sdk"
- name: Tests
run: >-
uv run --extra claude-agent-sdk pytest --no-cov
tests/integration/guard/test_claude_agent_sdk.py
tests/integration/guard/test_claude_agent_sdk_cli.py