-
Notifications
You must be signed in to change notification settings - Fork 18
285 lines (239 loc) · 10.4 KB
/
python.yml
File metadata and controls
285 lines (239 loc) · 10.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
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
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
# CI for bashkit Python package
# Builds the native extension via maturin and runs pytest on each PR.
# Complements publish-python.yml (release-only) with per-PR validation.
name: Python
on:
push:
branches: [main]
paths:
- "crates/bashkit-python/**"
- "crates/bashkit/**"
- "examples/*.py"
- "examples/*.ipynb"
- "Cargo.toml"
- "Cargo.lock"
- ".github/workflows/python.yml"
pull_request:
branches: [main]
paths:
- "crates/bashkit-python/**"
- "crates/bashkit/**"
- "examples/*.py"
- "examples/*.ipynb"
- "Cargo.toml"
- "Cargo.lock"
- ".github/workflows/python.yml"
workflow_call:
permissions:
contents: read
env:
CARGO_TERM_COLOR: always
RUST_BACKTRACE: 1
MATURIN_VERSION: ">=1.4,<2.0"
jobs:
lint:
name: Lint & Format
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7
- name: Ruff check
run: uvx ruff check crates/bashkit-python
- name: Ruff format
run: uvx ruff format --check crates/bashkit-python
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
with:
python-version: "3.12"
- name: Mypy type check
run: |
pip install mypy
mypy crates/bashkit-python/bashkit/ --ignore-missing-imports
test:
name: Test (Python ${{ matrix.python-version }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: ["3.9", "3.12", "3.13", "3.14"]
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
with:
python-version: ${{ matrix.python-version }}
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@e081816240890017053eacbb1bdf337761dc5582 # 1.95.0
- uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
- name: Install maturin
run: python -m pip install "maturin${MATURIN_VERSION}"
- name: Build wheel
working-directory: crates/bashkit-python
run: python -m maturin build --release --out dist -i python${{ matrix.python-version }}
- name: Install wheel and test dependencies
run: |
pip install bashkit --no-index --find-links crates/bashkit-python/dist --force-reinstall
pip install pytest pytest-asyncio langchain-core langgraph fastapi httpx maturin
- name: Build random filesystem fixture
run: |
python -m maturin build --release --out /tmp/bashkit-random-fs \
-m crates/bashkit-python/test-fixtures/random-fs/Cargo.toml \
-i python${{ matrix.python-version }}
pip install bashkit-random-fs --no-index --find-links /tmp/bashkit-random-fs --force-reinstall
- name: Run tests
working-directory: crates/bashkit-python
run: pytest tests/ -v
examples:
name: Examples
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
with:
python-version: "3.12"
- uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@e081816240890017053eacbb1bdf337761dc5582 # 1.95.0
- uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
- name: Install maturin
run: python -m pip install "maturin${MATURIN_VERSION}"
- name: Build wheel
working-directory: crates/bashkit-python
run: python -m maturin build --release --out dist -i python3.12
- name: Install local wheel
run: |
pip install bashkit --no-index --find-links crates/bashkit-python/dist --force-reinstall
pip install langchain-core langgraph fastapi httpx uvicorn nbconvert jupyter-client ipykernel maturin
- name: Build random filesystem fixture
run: |
python -m maturin build --release --out /tmp/bashkit-random-fs \
-m crates/bashkit-python/test-fixtures/random-fs/Cargo.toml \
-i python3.12
pip install bashkit-random-fs --no-index --find-links /tmp/bashkit-random-fs --force-reinstall
- name: Run examples
run: |
python crates/bashkit-python/examples/bash_basics.py
python crates/bashkit-python/examples/custom_filesystem_interop.py
python crates/bashkit-python/examples/k8s_orchestrator.py
python crates/bashkit-python/examples/data_pipeline.py
python crates/bashkit-python/examples/llm_tool.py
python crates/bashkit-python/examples/langgraph_async_tool.py
python crates/bashkit-python/examples/fastapi_async_tool.py
- name: Run notebooks
run: |
jupyter nbconvert --to notebook --execute --ExecutePreprocessor.timeout=120 \
examples/jupyter_basics.ipynb --output /tmp/jupyter_basics_out.ipynb
# Verify wheel builds and passes twine check
build-wheel:
name: Build wheel
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
with:
python-version: "3.12"
- name: Install maturin
run: python -m pip install "maturin${MATURIN_VERSION}"
- name: Build wheel
working-directory: crates/bashkit-python
run: python -m maturin build --release --out dist
- name: Verify wheel metadata
run: |
pip install twine
twine check crates/bashkit-python/dist/*
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: python-wheel
path: crates/bashkit-python/dist
retention-days: 5
# Build the reduced-feature Pyodide/Emscripten wheel and smoke-test it in a
# Pyodide venv. See specs/emscripten-wheels.md for the feature matrix and the
# pyodide-build <-> Emscripten <-> Python <-> Rust version lockstep.
wasm:
name: Build wheel (Pyodide/Emscripten)
runs-on: ubuntu-latest
env:
# Pinned for reproducibility: this trio must stay aligned on the wasm
# feature set + exception-handling ABI (see specs/emscripten-wheels.md
# "version triangle"). pyodide-build 0.34.x (under Python 3.13) selects
# Pyodide 0.29.x / Emscripten 4.0.9; the nightly's LLVM must match that
# binaryen. Bump all three together and re-verify the wheel imports.
RUST_NIGHTLY: "nightly-2026-05-29"
PYODIDE_BUILD_VERSION: "0.34.4"
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
# Python 3.13 selects pyodide-build's modern config (Pyodide 0.29.x,
# Emscripten 4.0.9), whose binaryen understands the wasm target-features
# modern LLVM emits and whose runtime supports wasm exception handling.
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
with:
python-version: "3.13"
# Pyodide passes `-Z link-native-libraries=no` (nightly-only). The nightly
# must satisfy our deps' MSRV (monty needs rustc 1.95) and edition 2024,
# so it has to be recent; its LLVM (19+) matches Emscripten 4.0.9's
# binaryen, which is what avoids the wasm-opt target-feature skew that
# older Emscripten (3.1.x) hit. See specs/emscripten-wheels.md.
- name: Install nightly Rust with the Emscripten target
# @nightly matches the repo's other nightly jobs (fuzz.yml, nightly.yml,
# ci.yml); the exact nightly is pinned via the toolchain: input below.
uses: dtolnay/rust-toolchain@5b842231ba77f5c045dba54ac5560fed2db780e2 # nightly
with:
toolchain: ${{ env.RUST_NIGHTLY }}
targets: wasm32-unknown-emscripten
- uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
# pyodide-build manages its own matching emsdk via the cross-build env, so
# no separate setup-emsdk step is needed.
- name: Install pyodide-build
run: pip install "pyodide-build==${PYODIDE_BUILD_VERSION}"
- name: Install pyodide cross-build environment
run: pyodide xbuildenv install
- name: Build Pyodide wheel
working-directory: crates/bashkit-python
env:
RUSTUP_TOOLCHAIN: ${{ env.RUST_NIGHTLY }}
run: pyodide build --outdir dist
- name: Smoke test in a Pyodide venv
working-directory: crates/bashkit-python
# Run the import test from a scratch dir: the crate's own `bashkit/`
# source package would otherwise shadow the installed extension module.
run: |
pyodide venv .venv-pyodide
.venv-pyodide/bin/pip install dist/*.whl
venv_python="$(pwd)/.venv-pyodide/bin/python"
cd "$(mktemp -d)"
"$venv_python" -c "
from bashkit import Bash
b = Bash(python=True)
r = b.execute_sync('echo hello && echo 1 | jq .')
print(r.stdout)
assert r.exit_code == 0, r
assert r.stdout == 'hello\n1\n', r.stdout
# Reduced-feature build: unavailable config must fail loudly.
try:
Bash(sqlite=True)
except RuntimeError:
pass
else:
raise AssertionError('expected sqlite=True to raise on wasm')
print('wasm smoke test OK')
"
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: python-wheel-emscripten
path: crates/bashkit-python/dist
retention-days: 5
# Gate job for branch protection
python-check:
name: Python Check
if: always()
needs: [lint, test, examples, build-wheel, wasm]
runs-on: ubuntu-latest
steps:
- name: Verify all jobs passed
run: |
if [[ "${{ needs.lint.result }}" != "success" ]] || \
[[ "${{ needs.test.result }}" != "success" ]] || \
[[ "${{ needs.examples.result }}" != "success" ]] || \
[[ "${{ needs.build-wheel.result }}" != "success" ]] || \
[[ "${{ needs.wasm.result }}" != "success" ]]; then
echo "One or more Python CI jobs failed"
exit 1
fi