Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
188 changes: 188 additions & 0 deletions .github/workflows/publish-python.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
# PyPI publishing workflow for bashkit Python bindings
# Builds pre-compiled wheels for all major platforms and publishes to PyPI.
# Triggered alongside publish.yml on GitHub Release or manual dispatch.
# Adapted from https://github.com/pydantic/monty CI wheel-building pattern.
#
# Prerequisites:
# - PyPI trusted publisher configured for this repo + workflow
# - GitHub environment "release-python" created in repo settings

name: Publish Python

on:
release:
types: [published]
workflow_dispatch:

permissions:
contents: read

env:
CARGO_TERM_COLOR: always
PYTHON_VERSIONS: "3.9 3.10 3.11 3.12 3.13"

jobs:
# Source distribution (platform-independent)
build-sdist:
name: Build sdist
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6

- uses: actions/setup-python@v6
with:
python-version: "3.12"

- uses: PyO3/maturin-action@v1
with:
command: sdist
args: --out dist
rust-toolchain: stable
working-directory: crates/bashkit-python

- uses: actions/upload-artifact@v6
with:
name: pypi_files-sdist
path: crates/bashkit-python/dist

# Pre-compiled binary wheels for each platform
build:
name: Build wheel - ${{ matrix.os }} (${{ matrix.target }}, ${{ matrix.manylinux || 'auto' }})
strategy:
fail-fast: false
matrix:
include:
# Linux glibc
- os: linux
target: x86_64
runs-on: ubuntu-latest
- os: linux
target: aarch64
runs-on: ubuntu-latest

# Linux musl
- os: linux
target: x86_64
manylinux: musllinux_1_1
runs-on: ubuntu-latest
- os: linux
target: aarch64
manylinux: musllinux_1_1
runs-on: ubuntu-latest

# macOS
- os: macos
target: x86_64
runs-on: macos-latest
- os: macos
target: aarch64
runs-on: macos-latest

# Windows
- os: windows
target: x86_64
runs-on: windows-latest

runs-on: ${{ matrix.runs-on }}
steps:
- uses: actions/checkout@v6

- uses: actions/setup-python@v6
with:
python-version: "3.12"

- name: Build wheels
uses: PyO3/maturin-action@v1
with:
target: ${{ matrix.target }}
manylinux: ${{ matrix.manylinux || 'auto' }}
args: --release --out dist -i ${{ env.PYTHON_VERSIONS }}
rust-toolchain: stable
docker-options: -e CI
working-directory: crates/bashkit-python

- uses: actions/upload-artifact@v6
with:
name: pypi_files-${{ matrix.os }}-${{ matrix.target }}-${{ matrix.manylinux || 'manylinux' }}
path: crates/bashkit-python/dist

# Verify built artifacts
inspect:
name: Inspect artifacts
needs: [build, build-sdist]
runs-on: ubuntu-latest
steps:
- uses: actions/download-artifact@v7
with:
pattern: pypi_files-*
merge-multiple: true
path: dist

- name: List dist files
run: |
ls -lhR dist/
echo "---"
echo "Total files: $(ls -1 dist/ | wc -l)"

- uses: astral-sh/setup-uv@v7
- run: uvx twine check dist/*

# Smoke-test wheels on each OS
test-builds:
name: Test wheel on ${{ matrix.os }}
needs: [build]
strategy:
fail-fast: false
matrix:
include:
- os: linux
runs-on: ubuntu-latest
- os: macos
runs-on: macos-latest
- os: windows
runs-on: windows-latest
runs-on: ${{ matrix.runs-on }}
steps:
- uses: actions/setup-python@v6
with:
python-version: "3.12"

- uses: actions/download-artifact@v7
with:
pattern: pypi_files-${{ matrix.os }}-*
merge-multiple: true
path: dist

- name: Install from wheel
run: pip install bashkit --no-index --find-links dist --force-reinstall

- name: Smoke test
run: python -c "from bashkit import BashTool; t = BashTool(); r = t.execute_sync('echo hello'); print(r); assert r.exit_code == 0"

# Publish to PyPI using trusted publishing (OIDC)
publish:
name: Publish to PyPI
needs: [inspect, test-builds]
if: success()
runs-on: ubuntu-latest

environment:
name: release-python

permissions:
id-token: write

steps:
- uses: actions/download-artifact@v7
with:
pattern: pypi_files-*
merge-multiple: true
path: dist

- name: List dist files
run: ls -lhR dist/

- uses: astral-sh/setup-uv@v7

- name: Publish to PyPI
run: uv publish --trusted-publishing always dist/*
3 changes: 2 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,14 @@ Fix root cause. Unsure: read more code; if stuck, ask w/ short options. Unrecogn
| 007-parallel-execution | Threading model, Arc usage |
| 008-documentation | Rustdoc guides, embedded markdown |
| 008-posix-compliance | POSIX design rationale, security exclusions |
| 008-release-process | Version tagging, crates.io publishing |
| 008-release-process | Version tagging, crates.io + PyPI publishing |
| 009-implementation-status | Feature status, test coverage, limitations |
| 009-tool-contract | Public LLM Tool trait contract |
| 010-git-support | Sandboxed git operations on VFS |
| 011-python-builtin | Embedded Python via Monty, security, resource limits |
| 012-eval | LLM evaluation harness, dataset format, scoring |
| 012-maintenance | Pre-release maintenance checklist |
| 013-python-package | Python bindings, PyPI wheels, platform matrix |

### Documentation

Expand Down
3 changes: 2 additions & 1 deletion crates/bashkit-python/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "maturin"

[project]
name = "bashkit"
version = "0.1.2"
dynamic = ["version"]
description = "Python bindings for Bashkit - a sandboxed bash interpreter for AI agents"
readme = "README.md"
license = { text = "MIT" }
Expand All @@ -19,6 +19,7 @@ classifiers = [
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Rust",
"Topic :: Software Development :: Interpreters",
"Topic :: Security",
Expand Down
38 changes: 37 additions & 1 deletion specs/008-release-process.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ Example:

- `bashkit` on crates.io (core library)
- `bashkit-cli` on crates.io (CLI tool)
- `bashkit` on PyPI (Python bindings, pre-built wheels)

## Publishing Order

Expand All @@ -153,7 +154,9 @@ Crates must be published in dependency order:
1. `bashkit` (core library, no internal deps)
2. `bashkit-cli` (depends on bashkit)

The CI workflow handles this with a dependency chain and wait for index update.
Python wheels are published independently (no crates.io dependency).

The CI workflows handle this automatically on GitHub Release.

## Workflows

Expand All @@ -170,6 +173,33 @@ The CI workflow handles this with a dependency chain and wait for index update.
- **File**: `.github/workflows/publish.yml`
- **Secret required**: `CARGO_REGISTRY_TOKEN`

### publish-python.yml

- **Trigger**: GitHub Release published (runs in parallel with publish.yml)
- **Actions**: Builds pre-compiled wheels for all platforms, smoke-tests, publishes to PyPI
- **File**: `.github/workflows/publish-python.yml`
- **Auth**: PyPI trusted publishing (OIDC, no secrets needed)
- **Environment**: `release-python` (must exist in GitHub repo settings)

#### Wheel matrix

| OS | Architecture | Variant |
|----|-------------|---------|
| Linux | x86_64 | manylinux (glibc) |
| Linux | aarch64 | manylinux (glibc) |
| Linux | x86_64 | musllinux |
| Linux | aarch64 | musllinux |
| macOS | x86_64 | universal |
| macOS | aarch64 (Apple Silicon) | universal |
| Windows | x86_64 | MSVC |

Python versions: 3.9, 3.10, 3.11, 3.12, 3.13

#### Version sync

Python package version is read dynamically from `Cargo.toml` via maturin
(`dynamic = ["version"]` in pyproject.toml). No manual version sync needed.

## Authentication

**Required Secrets** (GitHub Settings > Secrets > Actions):
Expand All @@ -178,6 +208,11 @@ The CI workflow handles this with a dependency chain and wait for index update.
- Generate at: https://crates.io/settings/tokens
- Scope: Publish new crates, Publish updates

**PyPI Trusted Publishing** (no secret needed):

- Configure at: https://pypi.org/manage/project/bashkit/settings/publishing/
- Add publisher: GitHub, repo `everruns/bashkit`, workflow `publish-python.yml`, environment `release-python`

## Example Conversation

```
Expand Down Expand Up @@ -220,3 +255,4 @@ Each release includes:

- **GitHub Release**: Tag, release notes, source archives
- **crates.io**: Published crates for `cargo add bashkit`
- **PyPI**: Pre-built wheels for `pip install bashkit`
Loading
Loading