ripple-effect
Know your blast radius before you push.
Test a Python library against the downstream projects that depend on it — before you tag a release and find out the hard way.
Installation
pip install ripple-effect
# or, without installing:
uvx ripple-effect show
Usage
Create a ripple-effect.yml config file in your project root (this is the default config file name, so no --config flag needed):
# ripple-effect.yml
upstream: . # the library under test (current folder)
proving-grounds: ./build/ripple # where to clone git repos (default: build/ripple-check)
downstream-projects:
- https://github.com/example/project-b.git@main
- https://github.com/example/project-c.git@main
Then:
ripple-effect show # preview resolved config
ripple-effect prepare # clone repos, build venvs, inject your library
ripple-effect run # prepare + run all test commands, report results
ripple-effect run --clean # same, from an empty proving-grounds (as on CI)
ripple-effect will:
Clone (or update) each downstream project into proving-grounds
Create a venv for each using uv sync or requirements.txt (for pytest test commands)
Inject your library in editable mode (uv pip install -e .)
Run each project’s test commands: pytest ... from that venv, tox -r ... with a wheel of your library forced in its envs (via UV_OVERRIDE), verifying they really used your library
Report pass / fail per project with timing
Record the full output of the run in <proving-grounds>/last-run.log
Real example — this repo’s own ripple-effect.yml:
upstream: https://github.com/codrsquad/runez.git@main
proving-grounds: ./build/verify-runez
downstream-projects:
- https://github.com/zsimic/ripple-effect.git
- source-ref: https://github.com/codrsquad/portable-python.git
test: tox -re py,mypy,pyrefly,pyright,ty,zuban
$ ripple-effect run --clean
## Preparation
Cleaned 4 files from ~/github/ripple-effect/build/verify-runez
upstream: runez @ ~/github/ripple-effect/build/verify-runez/runez
Running: git clone https://github.com/codrsquad/runez.git --branch main ~/github/ripple-effect/build/verify-runez/runez
Running: uv build --wheel -o ~/github/ripple-effect/build/verify-runez/.ripple/upstream ~/github/ripple-effect/build/verify-runez/runez
...
uv overrides for tox runs: ~/github/ripple-effect/build/verify-runez/.ripple/overrides.txt
### ripple-effect
folder: ~/github/ripple-effect/build/verify-runez/ripple-effect
Running: git clone https://github.com/zsimic/ripple-effect.git ~/github/ripple-effect/build/verify-runez/ripple-effect
Running: uv sync
...
injecting runez (editable)
Running: uv pip install -e ~/github/ripple-effect/build/verify-runez/runez
### portable-python
folder: ~/github/ripple-effect/build/verify-runez/portable-python
Running: git clone https://github.com/codrsquad/portable-python.git ~/github/ripple-effect/build/verify-runez/portable-python
## Testing
### ripple-effect
Running: ~/github/ripple-effect/build/verify-runez/ripple-effect/.venv/bin/pytest
... 4 passed in 3.8s ...
### portable-python
UV_OVERRIDE=~/github/ripple-effect/build/verify-runez/.ripple/overrides.txt
Running: tox -re py,mypy,pyrefly,pyright,ty,zuban
...
congratulations :) (19.8 seconds)
## Outcome
ripple-effect: PASSED (4.4s)
portable-python: PASSED (19.9s)
Output is split into ## Preparation, ## Testing and ## Outcome sections (show prints ## Config), with a ### <project> sub-section per project. Config problems (and other errors found before running anything) are listed in an ## Errors section on stderr. Handy to pick out the part you need from a CI log.
Global flags
ripple-effect --dryrun run # show what would happen without doing it
ripple-effect --verbose run # show debug output
ripple-effect -c other.yml run # use a different config file
The config file is resolved in this order:
--config / -c flag
RIPPLE_CONFIG environment variable
ripple-effect.yml in the current working directory
Local iteration
Mix local checkouts with remote repos freely:
upstream: ~/github/mylib
proving-grounds: ~/dev/ripple-workspace
downstream-projects:
- https://github.com/example/project-b.git@main
- ~/dev/project-c # local checkout, no git operations
- source-ref: https://github.com/example/project-d.git@main
prepare: "uv sync --all-extras" # custom way to build its .venv/
test: "pytest tests/ -x"
Git URLs are cloned into proving-grounds, nothing outside of it is touched. Local folders are used in place though: their .venv/ gets your library injected (editable install), or is rebuilt if it has no working python, and tox -r recreates their .tox/ envs with a wheel of your library. Handy for quick iterations, but remember to recreate those envs (uv sync, tox -r…) once done.
Many downstream projects
Group projects that share settings, and use source-prefix to list repos by name:
upstream: ~/github/mylib
proving-grounds: ~/dev/ripple-workspace
source-prefix: https://github.com/example/ # applies to bare names below
test: tox -r -e py # default test command
downstream-projects:
- project-b # https://github.com/example/project-b
- description: Repos with a typecheck tox env
test: tox -r -e py,typecheck
sources: [project-c, project-d, project-e@main]
- description: Repos not using tox
test: pytest
source-prefix: https://git.example.com/corp/
sources: [lib-a, lib-b]
Config reference
Key |
Top level |
Group |
Project |
|
|---|---|---|---|---|
upstream |
required |
Library under test: local folder or git URL |
||
proving-grounds |
✓ |
Where to clone git repos (default: build/ripple-check) |
||
downstream-projects |
required |
Projects (and groups) to test |
||
source-ref |
✓ |
Local path, git URL, or bare name |
||
sources |
✓ |
Projects of a group (makes an entry a group) |
||
description |
✓ |
Shown by show and in the run summary |
||
source-prefix |
✓ |
✓ |
Prepended to bare names |
|
prepare |
✓ |
✓ |
✓ |
How to build .venv/ (default: auto, uses uv) |
test |
✓ |
✓ |
✓ |
Test command(s) (default pytest) |
Settings resolve per project first, then group, then top level. Relative paths are relative to the config file’s folder, ~ and env vars are expanded.
A project is a plain string (shorthand for source-ref: ...) or a mapping; groups don’t nest.
source-prefix is prepended as-is (nothing added) to bare names: refs that are neither a URL (://, git@) nor a local path (starting with ., ~ or /). It can be a local folder too (source-prefix: ~/dev/). A bare name without a prefix is an error.
prepare: auto uses requirements.txt / tests/requirements.txt if present (uv venv + uv pip install -r + the project itself), and uv sync otherwise. A .venv/ is built only for projects with a pytest test command.
The whole config is validated up front: unknown keys, wrong value types, missing required keys, unsupported test commands, duplicate projects… are all reported at once, each with its location (e.g. downstream-projects[2].sources[0]). show displays the config first, then the problems.
Test commands
test is a command, or a list of commands (all of them run, the project passes when they all pass). Default: pytest.
test:
- pytest tests/ -x
- tox -re typecheck
ripple-effect only runs test commands it can verify really test your library, so that a pass means something:
pytest ...: runs from the project’s .venv/, after checking that pytest and your library (editable) are installed there. Other ways of invoking pytest (.venv/bin/pytest, python -m pytest, uv run pytest) are rejected: state them as pytest ... (uv run would re-sync .venv/, replacing your library with its published version).
tox ... (also via a wrapper: uvx tox ..., my-wrapper tox ...): its first option must be -r / --recreate (tox -re py, tox -r -e py). A wheel of your library is built once per run, and forced into tox’s envs via UV_OVERRIDE (requires tox-uv). Afterwards, the recreated .tox/ envs are checked: an env that got your library from anywhere else (e.g. from PyPI) fails the project.
Anything else (e.g. hatch run test) is reported as not supported yet, contributions welcome.
Projects with only tox test commands don’t get a .venv/ built (tox uses its own envs).
Metadata
Release files for ripple-effect 1.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| ripple_effect-1.2.0.tar.gz | 39.3 kB | With provenance | Details for ripple_effect-1.2.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| ripple_effect-1.2.0-py3-none-any.whl | Python 3 | none | any | Details for ripple_effect-1.2.0-py3-none-any.whl |
Total release size: 57.5 kB
Release files / ripple_effect-1.2.0.tar.gz
| Download URL | ripple_effect-1.2.0.tar.gz |
|---|---|
| Size | 39.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
5c17b5842d15fb2d5a082bf595b28c474894f0240f18e1993320d93dda6f089a
|
|
BLAKE2b-256 checksum How to use checksums |
cd00433f63dd06c6c35a247e5704382269219eace811cf13f067c1c052f39212
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 20, 2026.
Transparency logRelease files / ripple_effect-1.2.0-py3-none-any.whl
| Download URL | ripple_effect-1.2.0-py3-none-any.whl |
|---|---|
| Size | 18.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
2f82c0b0a4af088af4e11ba15870f9e58f8d9a9c5e9b17a2a09be58d15fbc12c
|
|
BLAKE2b-256 checksum How to use checksums |
0ab3e098592ed4a5a31c549992b19b0ef9d9b5b5c8ba7a34dbf7670ac5ca0c50
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 20, 2026.
Transparency logSecurity for ripple-effect 1.2.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for ripple_effect-1.2.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 20, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.