Skip to content

fix(rg): cap passthru replacement matches #2990

fix(rg): cap passthru replacement matches

fix(rg): cap passthru replacement matches #2990

Workflow file for this run

# 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