https://github.com/qltysh/stet
Run only the tests affected by your changes. Open-source selective test execution inspired by Stripe.
https://github.com/qltysh/stet
bun ci developer-tools python ruby rust selective-testing test-selection testing typescript
Last synced: about 15 hours ago
JSON representation
Run only the tests affected by your changes. Open-source selective test execution inspired by Stripe.
- Host: GitHub
- URL: https://github.com/qltysh/stet
- Owner: qltysh
- License: mit
- Created: 2026-04-12T19:13:55.000Z (3 months ago)
- Default Branch: main
- Last Pushed: 2026-04-12T21:15:13.000Z (3 months ago)
- Last Synced: 2026-04-12T21:24:40.711Z (3 months ago)
- Topics: bun, ci, developer-tools, python, ruby, rust, selective-testing, test-selection, testing, typescript
- Language: Rust
- Homepage:
- Size: 445 KB
- Stars: 1
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# stet
*stet* — "let it stand." A proofreading term meaning the original text is correct and should not be changed. In the same way, `stet` identifies the tests that can stand as-is, and reruns only the ones affected by your changes.
`stet` is an open-source implementation of the selective test execution system described in Stripe's blog post [*Fast CI for a 50M-Line Ruby Monorepo*](https://stripe.dev/blog/selective-test-execution-at-stripe-fast-ci-for-a-50m-line-ruby-monorepo). It traces which files each test opens, builds a dependency graph, and uses it to select just the tests impacted by your code changes. It works with Ruby/Minitest, Python/Pytest, and Bun on macOS and Linux.
> **Research preview.** `stet` is functional and tested but is not yet battle-tested in production. APIs, config format, and on-disk storage may change without notice. Feedback and contributions are welcome.
## How it works
1. **Record** — `stet` runs your test suite with a shared library injected via `DYLD_INSERT_LIBRARIES` (macOS) or `LD_PRELOAD` (Linux) that intercepts file-open syscalls. This produces a map of which source files each test depends on.
2. **Select** — When files change, `stet` diffs the repo inventory against the recorded dependency graph and identifies which tests are affected.
3. **Run** — `stet` executes only the affected tests, re-traces their dependencies, and updates the dependency graph for next time. No full re-record needed.
## Install
Using the [GitHub CLI](https://cli.github.com/):
```bash
# macOS (Apple Silicon)
gh release download --repo qltysh/stet --pattern 'stet-aarch64-apple-darwin.tar.gz'
tar xzf stet-aarch64-apple-darwin.tar.gz
# Linux (x86_64)
gh release download --repo qltysh/stet --pattern 'stet-x86_64-unknown-linux-gnu.tar.gz'
tar xzf stet-x86_64-unknown-linux-gnu.tar.gz
```
Then add the extracted directory to your PATH. The archive contains both the `stet` binary and the `libstet_interceptor` library.
### Build from source
Alternatively, build from source with the [Rust toolchain](https://rustup.rs/):
```bash
git clone https://github.com/qltysh/stet.git
cd stet
bash scripts/setup.sh
```
## Quick start
Run `stet init` from your project root to auto-detect your test runner and generate a config:
```bash
stet init
```
This scans your repo for dependency files (Gemfile, pyproject.toml, package.json/bun.lockb) and test directories, then writes a commented `stet.toml` with the right settings. Add `.stet/` to your `.gitignore`.
Then record a baseline and start running:
```bash
# Record the baseline (runs your full test suite once with tracing)
stet record
# Make a code change, then run just the affected tests
stet run
```
`stet init` accepts `--runner minitest|pytest|bun_test` to skip detection, `--force` to overwrite an existing config, and `--config ` for a custom output location.
### Manual configuration
If you prefer to create `stet.toml` by hand:
**Ruby/Minitest:**
```toml
test_runner = "minitest"
test_globs = ["test/**/*_test.rb"]
test_args = ["-Itest"]
inventory_excludes = [".git/**", ".stet/**", "tmp/**"]
```
**Python/Pytest:**
```toml
test_runner = "pytest"
test_globs = ["tests/test_*.py", "tests/**/*_test.py"]
test_args = ["-m", "pytest"]
inventory_excludes = [".git/**", ".stet/**", "__pycache__/**", ".pytest_cache/**"]
```
**Bun:**
```toml
test_runner = "bun_test"
test_globs = ["test/**/*.test.ts"]
test_args = ["test"]
inventory_excludes = [".git/**", ".stet/**", "node_modules/**"]
```
`state_dir` defaults to `.stet`. `runtime` is inferred from `test_runner` (`ruby`, `python3`, or `bun`) and resolved from PATH. Both can be overridden explicitly.
`stet` looks for `stet.toml` in the current directory and auto-discovers the interceptor library next to its own binary. Use `--config` and `--interceptor` to override.
## Commands
### `stet init`
Auto-detects the test runner from your repo contents and generates a commented `stet.toml`. Supports `--runner` to skip detection, `--force` to overwrite, and `--config` for a custom output path.
### `stet record`
Runs every test with tracing and publishes a new dependency baseline under `.stet/bases/`. Use this for initial setup or to force a full re-record.
### `stet select`
Reads the latest baseline, diffs current file hashes against it, and outputs the list of affected test units. Doesn't run anything.
### `stet run`
Selects affected tests, runs them with tracing, and updates the baseline. If no baseline exists, bootstraps by running the full suite. This is the steady-state command for day-to-day development.
## Advanced configuration
### Mandatory tests
Tests that should always run regardless of what changed:
```toml
mandatory_test_globs = ["test/health/**/*_test.rb"]
```
### Root preload
A script that runs before tests to generate shared artifacts (e.g., schema files). Changes to files opened by the root preload will select all tests:
```toml
root_args = ["-Itest", "test/root_preload.rb"]
```
### Tool routes
Run additional commands when specific files change:
```toml
[[tool_routes]]
name = "yaml-lint"
changed_file_globs = ["config/**/*.yml"]
command = ["{runtime}", "script/changed_file_yaml_lint.rb", "{changed_files...}"]
```
### Parallelism
```toml
max_workers = 8 # defaults to available CPU cores
```
## Development
Try the interactive demo:
```bash
bash scripts/demo-fixture.sh --fixture ruby
bash scripts/demo-fixture.sh --fixture python
bash scripts/demo-fixture.sh --fixture bun
```
Run the test suite:
```bash
bash scripts/setup.sh
cargo test --workspace
```
Run clippy and format checks:
```bash
cargo clippy --workspace --all-targets -- -D warnings
cargo fmt --check
```
Benchmark:
```bash
bash scripts/benchmark-fixture.sh --fixture ruby
bash scripts/benchmark-fixture.sh --fixture python
bash scripts/benchmark-fixture.sh --fixture bun
```
Linux validation (requires Docker):
```bash
bash scripts/validate-linux-docker.sh
```
## License
MIT