Metadata-Version: 2.4
Name: reforge-build
Version: 0.1.0
Summary: Explicit, cached subprocess.run for ad-hoc build pipelines.
Project-URL: Homepage, https://github.com/winstonewert/reforge
Project-URL: Issues, https://github.com/winstonewert/reforge/issues
Author-email: Winston Ewert <winstonewert@gmail.com>
License: MIT
License-File: LICENSE
Keywords: build,cache,make,pipeline,subprocess
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Build Tools
Requires-Python: >=3.10
Description-Content-Type: text/markdown

# reforge

An explicit, cached `subprocess.run` for ad-hoc build pipelines.

Reforge is a tiny build helper: you write your pipeline as a plain Python
script that calls `run(...)` for each step, and reforge skips steps whose
declared inputs haven't changed. No DAG, no rules file, no DSL — just
`subprocess.run` with a content-addressed cache.

## Install

```sh
pip install reforge-build
# or
uv add reforge-build
```

The PyPI distribution name is `reforge-build` (the bare `reforge` name was
already taken on PyPI). The import name is still `reforge`.

## Usage

```python
from reforge import input, output, run

# Inputs and outputs must be declared explicitly.
run(
    "pandoc",
    input("docs/index.md"),
    "-o",
    output("build/index.html"),
)

# stdout/stderr can be captured to declared outputs.
run(
    "python",
    input("scripts/list_things.py"),
    stdout=output("build/things.tsv"),
)

# When an output is a directory containing a known sentinel file,
# pass the sentinel as the second argument so reforge can check it.
run(
    "iqtree2",
    "-s", input("data/aln.fasta"),
    "--prefix", output("build/iqtree/output", ".log"),
)
```

### Rules

- `run(...)` executes immediately.
- Inputs must be wrapped with `input(...)`; missing inputs raise `FileNotFoundError`.
- Outputs must be wrapped with `output(...)`; their parent directories are auto-created.
- A step is skipped only when **all** declared outputs exist *and* the fingerprint
  (command + content hash of every input) matches the cached fingerprint.
- A step with **no inputs always runs** — there is no way to fingerprint it safely.
- Commands run in a fresh temporary working directory, so they can't accidentally
  read or write relative paths in your tree.
- Cache metadata lives in `.reforge/` at the current working directory.

### Note on `input`

The module exports `input` and `output`, which shadow the Python builtin
`input()` when star-imported. If you need the builtin, import it explicitly:

```python
from reforge import input as rf_input, output, run
```

## License

MIT
