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
39 changes: 38 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,42 @@ jobs:
env:
PROPTEST_CASES: 50

cpython-platforms:
# COFF rejects 64 KiB-aligned statics; Mach-O can lose their alignment at
# load time. Keep the actual embedding, module load and execution covered.
name: CPython (${{ matrix.os }}, Rust ${{ matrix.rust }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
rust: "1.95.0"
- os: macos-latest
rust: "1.95.0"
- os: windows-2025
rust: "1.96.0"
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false

- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@e081816240890017053eacbb1bdf337761dc5582 # 1.95.0
with:
toolchain: ${{ matrix.rust }}

- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2

- name: Probe embedding object generation and aligned storage
run: python -m unittest discover -s scripts/tests -p test_cpython_embedding.py -v

- name: Test CPython module loading and alignment
run: cargo test --locked -p bashkit-cpython-wasm --lib

- name: Execute CPython through the shell
run: cargo test --locked -p bashkit --features cpython --test integration -- cpython_integration_tests

windows-containment:
# THREAT[TM-ESC-033]: Windows path parsing has drive, UNC, device, and
# reparse-point semantics that cannot be exercised on a Unix runner.
Expand Down Expand Up @@ -624,14 +660,15 @@ jobs:
check:
name: Check
if: always()
needs: [lint, audit, test, windows-containment, test-windows, examples, fuzz-check, wasm, wasm-component, wasm-web]
needs: [lint, audit, test, cpython-platforms, windows-containment, test-windows, examples, fuzz-check, wasm, wasm-component, wasm-web]
runs-on: ubuntu-latest
steps:
- name: Verify all jobs passed
run: |
if [[ "${{ needs.lint.result }}" != "success" ]] || \
[[ "${{ needs.audit.result }}" != "success" ]] || \
[[ "${{ needs.test.result }}" != "success" ]] || \
[[ "${{ needs.cpython-platforms.result }}" != "success" ]] || \
[[ "${{ needs.windows-containment.result }}" != "success" ]] || \
[[ "${{ needs.test-windows.result }}" != "success" ]] || \
[[ "${{ needs.examples.result }}" != "success" ]] || \
Expand Down
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
# Changelog

## [0.20.1] - 2026-10-25

### Highlights

- **Windows CPython builds.** The embedded CPython module uses a plain static slice on Windows and caches a 64 KiB-aligned copy at runtime, avoiding the Rust compiler trap when generating Windows COFF objects. Python and the hosted integration catalog remain available ([#2777](https://github.com/everruns/bashkit/pull/2777)).
- **Complete npm runtime files.** The npm publish artifact includes `runtime-functions.js` and its declarations so importing the package succeeds ([#2776](https://github.com/everruns/bashkit/pull/2776)).

### What's Changed

- fix(cpython): avoid unsupported over-aligned statics on Windows (EVE-1311).
- fix(release): ship runtime-functions.js in the npm publish artifact ([#2776](https://github.com/everruns/bashkit/pull/2776)).

## [0.20.0] - 2026-10-25

### Highlights
Expand Down
22 changes: 11 additions & 11 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ resolver = "2"
members = ["crates/*"]

[workspace.package]
version = "0.20.0"
version = "0.20.1"
edition = "2024"
license = "MIT"
authors = ["Mykhailo Chalyi <mike@chaliy.name>", "Everruns"]
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -442,7 +442,7 @@ Enable the `sqlite` feature to embed [Turso](https://github.com/tursodatabase/tu

```toml
[dependencies]
bashkit = { version = "0.20.0", features = ["sqlite"] }
bashkit = { version = "0.20.1", features = ["sqlite"] }
```

```rust
Expand Down
2 changes: 1 addition & 1 deletion crates/bashkit-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ interactive = ["dep:rustyline", "dep:terminal_size", "dep:signal-hook"]
# `tzdata` is listed explicitly: an interactive shell must keep honouring
# named `TZ=` zones in `date`, which stopped being implied when chrono-tz
# moved behind a feature.
bashkit = { path = "../bashkit", version = "0.20.0", default-features = false, features = ["http_client", "ring", "git", "jq", "tzdata"] }
bashkit = { path = "../bashkit", version = "0.20.1", default-features = false, features = ["http_client", "ring", "git", "jq", "tzdata"] }
tokio = { workspace = true, features = ["macros", "net", "rt", "rt-multi-thread", "time", "io-std"] }
clap.workspace = true
anyhow.workspace = true
Expand Down
15 changes: 8 additions & 7 deletions crates/bashkit-cpython-wasm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,17 +32,18 @@ pub static STDLIB_ZIP: &[u8] = include_bytes!("../artifacts/python314.zip");

// Wasmtime maps data segments from immutable host-page-aligned bytes. Keep
// 64 KiB alignment for every supported host. Mach-O only preserves host-page
// alignment through linking/ASLR; do not form an over-aligned Rust reference
// there. Cache a padded, aligned copy once when the embedded slice needs one.
// alignment through linking/ASLR, and Windows COFF cannot encode a 64 KiB
// aligned static. Do not form an over-aligned Rust reference on those hosts.
// Cache a padded, aligned copy once when the embedded slice needs one.
const MODULE_ALIGNMENT: usize = 65536;

#[cfg(not(target_vendor = "apple"))]
#[cfg(not(any(target_vendor = "apple", windows)))]
#[repr(C, align(65536))]
struct Aligned<T: ?Sized>(T);

#[cfg(not(target_vendor = "apple"))]
#[cfg(not(any(target_vendor = "apple", windows)))]
static CWASM: &Aligned<[u8]> = &Aligned(*include_bytes!(concat!(env!("OUT_DIR"), "/python.cwasm")));
#[cfg(target_vendor = "apple")]
#[cfg(any(target_vendor = "apple", windows))]
static CWASM: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/python.cwasm"));

struct PaddedModule {
Expand Down Expand Up @@ -73,9 +74,9 @@ impl PaddedModule {
/// Precompiled module bytes (Pulley, or native with the `native` feature) (for diagnostics and size reporting).
/// The returned slice is immutable and 64 KiB aligned for the process lifetime.
pub fn cwasm_bytes() -> &'static [u8] {
#[cfg(not(target_vendor = "apple"))]
#[cfg(not(any(target_vendor = "apple", windows)))]
let bytes: &[u8] = &CWASM.0;
#[cfg(target_vendor = "apple")]
#[cfg(any(target_vendor = "apple", windows))]
let bytes = CWASM;
if bytes.as_ptr().align_offset(MODULE_ALIGNMENT) == 0 {
return bytes;
Expand Down
2 changes: 1 addition & 1 deletion crates/bashkit-js/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@everruns/bashkit",
"version": "0.20.0",
"version": "0.20.1",
"description": "Sandboxed bash interpreter for JavaScript/TypeScript",
"packageManager": "pnpm@10.33.0",
"main": "wrapper.js",
Expand Down
2 changes: 1 addition & 1 deletion crates/bashkit-scripted-tool/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ jq = ["bashkit/jq"]
tracing = ["dep:tracing"]

[dependencies]
bashkit = { path = "../bashkit", version = "0.20.0", default-features = false, features = ["bash_tool"] }
bashkit = { path = "../bashkit", version = "0.20.1", default-features = false, features = ["bash_tool"] }
tokio = { workspace = true }
async-trait = { workspace = true }
tower = { workspace = true }
Expand Down
2 changes: 1 addition & 1 deletion crates/bashkit-wasm/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@everruns/bashkit-wasm",
"version": "0.20.0",
"version": "0.20.1",
"description": "Sandboxed bash interpreter for the browser (WebAssembly). Single-threaded \u2014 no SharedArrayBuffer, no COOP/COEP headers.",
"type": "module",
"main": "index.js",
Expand Down
2 changes: 1 addition & 1 deletion crates/bashkit/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ monty = { version = "=0.0.19", optional = true }

# Real CPython 3.14 as a WASI guest, precompiled to Pulley (cpython feature).
# See knowledge/runtimes/cpython-wasm.md.
bashkit-cpython-wasm = { path = "../bashkit-cpython-wasm", version = "0.20.0", optional = true }
bashkit-cpython-wasm = { path = "../bashkit-cpython-wasm", version = "0.20.1", optional = true }
wasmtime = { workspace = true, optional = true, features = ["pooling-allocator"] }
monty-types = { version = "=0.0.19", optional = true }
# Monty's ruff AST uses compact_str 0.9; get-size2 >=0.10.2 only implements
Expand Down
4 changes: 2 additions & 2 deletions crates/bashkit/docs/cpython.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ Enable the `cpython` cargo feature and register the builtins:

```toml
[dependencies]
bashkit = { version = "0.20.0", features = ["cpython"] }
bashkit = { version = "0.20.1", features = ["cpython"] }
```

```rust
Expand Down Expand Up @@ -242,7 +242,7 @@ executable memory. Enable `cpython-native` instead of `cpython` to compile it
to machine code for your target at build time:

```toml
bashkit = { version = "0.20.0", features = ["cpython-native"] }
bashkit = { version = "0.20.1", features = ["cpython-native"] }
```

Calls get 4-10x faster (`print(1)` ~1 ms instead of ~4 ms; CPU-bound code
Expand Down
2 changes: 1 addition & 1 deletion crates/bashkit/docs/logging.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ Add the `logging` feature to your `Cargo.toml`:

```toml
[dependencies]
bashkit = { version = "0.20.0", features = ["logging"] }
bashkit = { version = "0.20.1", features = ["logging"] }
tracing-subscriber = "0.3" # or your preferred subscriber
```

Expand Down
2 changes: 1 addition & 1 deletion crates/bashkit/docs/sqlite.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ the `sqlite` feature. The engine is [Turso](https://github.com/tursodatabase/tur

```toml
# Cargo.toml
bashkit = { version = "0.20.0", features = ["sqlite"] }
bashkit = { version = "0.20.1", features = ["sqlite"] }
```

```rust,ignore
Expand Down
4 changes: 2 additions & 2 deletions crates/bashkit/fuzz/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions crates/bashkit/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,7 @@
//!
//! ```toml
//! [dependencies]
//! bashkit = { version = "0.20.0", features = ["git"] }
//! bashkit = { version = "0.20.1", features = ["git"] }
//! ```
//!
//! ```rust,ignore
Expand Down Expand Up @@ -354,7 +354,7 @@
//!
//! ```toml
//! [dependencies]
//! bashkit = { version = "0.20.0", features = ["python"] }
//! bashkit = { version = "0.20.1", features = ["python"] }
//! ```
//!
//! ```rust,ignore
Expand Down
2 changes: 1 addition & 1 deletion docs/builtin_typescript.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ Add the `typescript` feature to your `Cargo.toml`:

```toml
[dependencies]
bashkit = { version = "0.20.0", features = ["typescript"] }
bashkit = { version = "0.20.1", features = ["typescript"] }
```

Enable TypeScript in the builder:
Expand Down
4 changes: 2 additions & 2 deletions docs/migrating-scripted-tool.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ features = ["scripted_tool", "python", "typescript", "jq"]
After:

```toml
bashkit = { version = "0.20.0", features = ["python", "typescript", "jq"] }
bashkit-scripted-tool = { version = "0.20.0", features = ["python", "typescript", "jq"] }
bashkit = { version = "0.20.1", features = ["python", "typescript", "jq"] }
bashkit-scripted-tool = { version = "0.20.1", features = ["python", "typescript", "jq"] }
```

Keep `bashkit` only if you use it directly (`Bash`, `BashTool`, the `Tool`
Expand Down
2 changes: 1 addition & 1 deletion examples/hyperlight/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 9 additions & 2 deletions knowledge/runtimes/cpython-wasm.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,15 @@ call:
embed a byte slice rather than an over-aligned Rust reference. If its runtime
address is not 64 KiB aligned, one immutable padded `Vec` copy is cached for
the process lifetime; retaining the allocation preserves the chosen aligned
offset. Other hosts retain the aligned static fast path. The original
64 KiB pointer assertion plus byte-preservation tests cover this contract.
offset. Windows uses the same plain-slice path: COFF cannot encode 64 KiB
static alignment (Rust 1.96.0 traps during object generation). Reducing
the runtime alignment to COFF's 8 KiB limit would violate Wasmtime's
requirements. ELF hosts retain the aligned static fast path. The original
64 KiB pointer assertion plus byte-preservation tests cover this contract;
`scripts/tests/test_cpython_embedding.py` compiles the real embedding with
a tiny payload and checks object generation, byte preservation, alignment
and process-lifetime caching, including concurrent access. CI runs it and
actual module loading/Python execution on Windows, Apple and ELF hosts.
4. **Copy-on-write instances**: `memory_init_cow` +
`memory_guaranteed_dense_image_size(128 MB)` map the ~40 MB snapshot heap
instead of copying it (without the dense image every instantiation copied
Expand Down
Loading
Loading