Metadata-Version: 2.4
Name: retryflake
Version: 0.1.0
Summary: Run flaky commands with retries, exponential backoff, jitter, and timeouts
Author-email: AP <intertermux@gmail.com>
License: MIT
Project-URL: Homepage, https://github.com/intertermux-code/retryflake
Project-URL: Issues, https://github.com/intertermux-code/retryflake/issues
Keywords: retry,backoff,jitter,cli,reliability,devops
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: POSIX :: Linux
Classifier: Operating System :: MacOS
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: System :: Systems Administration
Classifier: Topic :: Utilities
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Dynamic: license-file

# retryflake

[![ci](https://github.com/intertermux-code/retryflake/actions/workflows/ci.yml/badge.svg)](https://github.com/intertermux-code/retryflake/actions)

Run flaky commands with retries, exponential backoff, and jitter. One
binary, zero dependencies — for the CI step that fails every third run, the
deploy script that hits a rate limit, the `pip install` that drops once a
week.

```sh
pip install retryflake

retryflake --retries 5 -- make test
retryflake --retries 5 --timeout 120 -- ./deploy.sh
retryflake --retry-on "rate limit" --retry-on "429" -- ./call-api.sh
retryflake --retries 0 -- ./must-pass-once.sh
```

Output streams live to your terminal exactly as the command produces it;
retryflake's own chatter (`attempt 2/4 failed (exit 1), retrying in 3.2s`) goes
to stderr, and `--quiet` silences it.

## Behavior

- Exit code is the command's exit code: `0` on success, the last attempt's
  code on failure, `124` if an attempt was killed by `--timeout`, `127`/`126`
  if the command can't start (no pointless retries), `130` on Ctrl-C
  (interrupts the child too, never retried).
- Backoff is `delay * 2^attempt`, capped at `--max-delay` (defaults: 1s base,
  60s cap), with full jitter on by default (`--no-jitter` disables it).
- `--timeout` kills the whole process group per attempt: SIGTERM, then
  SIGKILL after a 5s grace period. Timeouts always retry.
- `--retry-on` (repeatable) retries only when the attempt's output contains
  the substring — retry on `rate limit`, not on `invalid API key`. A bounded
  128 KiB tail is kept for matching, so memory stays flat on huge logs.

## Reference

| Flag | Default | What it does |
|---|---|---|
| `--retries N` | 3 | retries after the first attempt |
| `--delay S` | 1.0 | base backoff in seconds |
| `--max-delay S` | 60.0 | backoff cap in seconds |
| `--jitter` / `--no-jitter` | on | full jitter on the backoff |
| `--timeout S` | none | kill each attempt after S seconds |
| `--retry-on SUB` | none | only retry when output contains SUB |
| `--quiet` | off | suppress retryflake's stderr chatter |

POSIX only (Linux, macOS). Python 3.9+.

## License

MIT.
