forked from duolahypercho/codex-router
-
Notifications
You must be signed in to change notification settings - Fork 0
267 lines (252 loc) · 11.8 KB
/
Copy pathpython-lock.yml
File metadata and controls
267 lines (252 loc) · 11.8 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
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
# CI never installed the Python gateway. `ci.yml` syntax-checks the shell
# scripts, runs `./install.sh --help`, and parses the PowerShell — none of which
# reaches `pip`. So `requirements/python.txt` was verified by exactly one local
# macOS install; Linux and Windows were only *resolved*, with `uv pip install
# --dry-run`. A missing wheel, a marker that excludes something the gateway
# imports, or a hash that does not match the file that platform's pip picks
# would all reach users before anything caught them.
#
# This workflow closes that: it installs the lock for real, with the installer's
# own command, on both platforms and through both resolvers.
#
# It lives in its own file rather than as a job in `ci.yml` because `paths:`
# filters are per-workflow. Gating from inside `ci.yml` would mean either
# gating `test` and `desktop` too, or adding a third-party changed-files action
# to a repository that currently uses only first-party ones. The lists below
# are duplicated rather than shared through a YAML anchor because GitHub
# Actions does not resolve anchors in workflow files.
name: Python lock
"on":
push:
branches: [main]
paths:
- "requirements/**"
- "bin/install"
- "bin/lock-python"
- "install.ps1"
- "src/install-plan.mjs"
- "scripts/verify-python-lock.py"
- "scripts/verify-zai-litellm-usage.mjs"
- "src/api-forwarder.mjs"
- "src/zai-cache-usage.mjs"
- ".github/workflows/python-lock.yml"
pull_request:
paths:
- "requirements/**"
- "bin/install"
- "bin/lock-python"
- "install.ps1"
- "src/install-plan.mjs"
- "scripts/verify-python-lock.py"
- "scripts/verify-zai-litellm-usage.mjs"
- "src/api-forwarder.mjs"
- "src/zai-cache-usage.mjs"
- ".github/workflows/python-lock.yml"
# The lock is static but PyPI is not: a yanked file, or a pip that changes
# how it selects one, breaks an unchanged lock. The paths filter above would
# never fire for that, so the same job also runs weekly against main. Without
# this, the filter's real cost — a job that silently never runs — would go
# unmitigated.
schedule:
- cron: "17 6 * * 1"
workflow_dispatch: {}
permissions:
contents: read
concurrency:
group: python-lock-${{ github.ref }}
cancel-in-progress: true
jobs:
install:
name: ${{ matrix.os }} / ${{ matrix.tool }} / python ${{ matrix.python }}
runs-on: ${{ matrix.os }}
timeout-minutes: 25
strategy:
fail-fast: false
# Both resolvers, because `bin/install` and `install.ps1` each have a uv
# branch and a pip branch and the two select distributions independently.
# The Python versions are not arbitrary either: the uv branch pins 3.12
# (`uv venv --python 3.12`), while the pip branch takes whatever the
# machine has, down to the documented floor of 3.10 — which is also the
# only way to exercise the lock's `python_full_version < '3.11'` marker
# arm, where a wrong marker would hide a package the gateway needs.
#
# macOS is deliberately absent. litellm 1.95.0 ships manylinux and
# win_amd64 wheels only, so a macOS run builds it from the sdist through
# maturin and Rust: minutes of compile time, a toolchain to provision,
# and a failure mode that belongs to the sdist rather than to the lock.
# It is also the one platform whose install was already verified by hand.
# Add it the day a macOS wheel exists, or the day the sdist build itself
# needs guarding — not as a tax on every lock change.
matrix:
include:
- os: ubuntu-latest
tool: uv
python: "3.12"
- os: ubuntu-latest
tool: pip
python: "3.10"
- os: windows-latest
tool: uv
python: "3.12"
- os: windows-latest
tool: pip
python: "3.10"
steps:
- uses: actions/checkout@v7
# Node reads only `src/install-plan.mjs`, which imports nothing outside
# the standard library, so this job skips `npm ci` entirely.
- uses: actions/setup-node@v7
with:
node-version: "24"
- uses: actions/setup-python@v7
with:
python-version: ${{ matrix.python }}
# Deliberately no pip or uv cache. A cache keyed on the lock can only hit
# when the lock is unchanged — the one case that needs no speed — and uv
# serves already-unpacked wheels from its cache, so a hit can bypass the
# very hash check this job exists to perform. A job that can pass from
# cache is worth less than the minutes it saves.
- name: Install uv
if: matrix.tool == 'uv'
shell: bash
run: python -m pip install uv
- name: Report the toolchain
shell: bash
run: |
python --version
node --version
if [ "${{ matrix.tool }}" = uv ]; then uv --version; fi
# The command is not written here. It is extracted from the shipped
# installer by `src/install-plan.mjs`, using the same matcher the drift
# test uses, so this job cannot pass while `bin/install` or `install.ps1`
# fails and cannot drift away from either.
- name: Read the installer's own install command
id: installer
shell: bash
run: |
# Spelled as an if rather than `[ ... ] && platform=windows`, whose
# exit status would be the step's if it ever became the last line.
if [ "${{ runner.os }}" = Windows ]; then platform=windows; else platform=posix; fi
install_command=$(node src/install-plan.mjs python-install-command "${{ matrix.tool }}" "$platform")
echo "Extracted from the installer: $install_command"
echo "command=$install_command" >> "$GITHUB_OUTPUT"
node src/install-plan.mjs requirements > declared-requirements.txt
cat declared-requirements.txt
- name: Create the environment the way the installer does
shell: bash
run: |
if [ "${{ matrix.tool }}" = uv ]; then
uv venv --python 3.12 .venv
elif [ "${{ runner.os }}" = Windows ]; then
# install.ps1 prefers `py -3` and falls back to `python`; the
# fallback is what pins this leg to the matrix Python version.
python -m venv .venv
.venv/Scripts/python.exe -m pip install --upgrade pip
else
python3 -m venv .venv
.venv/bin/python -m pip install --upgrade pip
fi
# A negative control, because every step below would still pass if
# `--require-hashes` quietly stopped being enforced — a green job would
# then mean nothing at all.
#
# It runs here, against the environment while it is still empty, and that
# ordering is load-bearing rather than tidy: once the tree is installed,
# `uv pip install --require-hashes` on an already-satisfied requirement
# answers "Audited 1 package" and exits 0 without ever looking at a hash,
# so the same control placed after the install silently stops testing
# anything. It is the same reason this job caches nothing.
#
# The requirement is one entry lifted straight out of the lock with its
# `--hash=` lines dropped, so the control introduces no package the lock
# does not already vouch for, and the first marker-free entry is used so
# the resolver cannot skip it as inapplicable to this platform. It is a
# file of its own rather than the whole lock appended to, because a
# hundred-package resolution can fail for a dozen unrelated reasons and a
# control that passes on the wrong failure is not a control.
#
# Hence both halves of the assertion: the command must fail, *and* the
# failure must be about hashes. pip says "Hashes are required in
# --require-hashes mode"; uv says "In `--require-hashes` mode, all
# requirements must have a hash". Anything else is a different bug.
- name: Prove hash checking is enforced
shell: bash
env:
INSTALL_COMMAND: ${{ steps.installer.outputs.command }}
run: |
# `|| true` because the step runs under `-o pipefail`: a grep that
# matches nothing would otherwise kill the step before the explicit
# message below could say why.
entry=$(grep -m1 -E '^[A-Za-z0-9][A-Za-z0-9._-]*==[^ ;]+ \\$' requirements/python.txt | sed 's/ \\$//' || true)
if [ -z "$entry" ]; then
echo "::error::No marker-free pin found in requirements/python.txt to build the control from."
exit 1
fi
mkdir -p tampered
printf '%s\n' "$entry" > tampered/python.txt
control=$(printf '%s' "$INSTALL_COMMAND" | sed 's|requirements/python.txt|tampered/python.txt|')
if [ "${{ runner.os }}" = Windows ]; then
# Reduce the PowerShell line to its POSIX equivalent: drop the call
# operator and resolve the $Python the pwsh install step binds.
control=$(printf '%s' "$control" | sed 's|^& ||; s|[$]Python|.venv/Scripts/python.exe|')
fi
echo "Negative control on $entry (unhashed): $control"
if sh -c "$control" >control.log 2>&1; then
cat control.log
echo "::error::An unhashed requirement installed anyway; --require-hashes is not enforced."
exit 1
fi
cat control.log
if ! grep -qi 'hash' control.log; then
echo "::error::The control failed, but not because of hashes; hash enforcement is unproven."
exit 1
fi
echo "Rejected the unhashed requirement, as it must."
# Nothing piped, no `|| true`, no summarising: a hash mismatch is
# diagnosable only from the resolver's own stderr, so it goes straight to
# the log and its exit status straight to the step.
- name: Install the lock (POSIX installer command)
if: runner.os != 'Windows'
shell: bash
env:
INSTALL_COMMAND: ${{ steps.installer.outputs.command }}
run: sh -c "$INSTALL_COMMAND"
- name: Install the lock (PowerShell installer command)
if: runner.os == 'Windows'
shell: pwsh
env:
INSTALL_COMMAND: ${{ steps.installer.outputs.command }}
run: |
# install.ps1 defines $Python exactly this way, and the extracted line
# refers to it, so binding it here keeps the command verbatim.
$Python = Join-Path $PWD ".venv\Scripts\python.exe"
Invoke-Expression $env:INSTALL_COMMAND
if ($LASTEXITCODE -ne 0) { throw "The locked install failed with $LASTEXITCODE." }
# `pip exited 0` is not the claim under test. This asserts the pinned
# versions, that both top-level packages import, that every
# `litellm[proxy]` extra requirement is installed and satisfies its
# specifier, and that the proxy answers /health/liveliness — the endpoint
# `src/start.mjs` blocks on before it will call the router healthy.
- name: Verify the installed gateway
shell: bash
run: |
if [ "${{ runner.os }}" = Windows ]; then
venv_python=.venv/Scripts/python.exe
else
venv_python=.venv/bin/python
fi
set --
while IFS= read -r requirement; do
[ -n "$requirement" ] || continue
set -- "$@" --requirement "$requirement"
done < declared-requirements.txt
"$venv_python" scripts/verify-python-lock.py --venv .venv "$@"
- name: Verify Z.ai streaming usage survives LiteLLM
shell: bash
run: |
if [ "${{ runner.os }}" = Windows ]; then
venv_python=.venv/Scripts/python.exe
else
venv_python=.venv/bin/python
fi
node scripts/verify-zai-litellm-usage.mjs "$venv_python"