Skip to main content

Compile declarative CI recipes into fully-wired GitHub Actions workflows with typed data-flow validation.

Project description

reci

Compile declarative CI recipes into fully-wired GitHub Actions workflows with typed data-flow validation.

To install: pip install reci

What it does

Write a short recipe YAML describing your CI pipeline as a DAG of GitHub Actions. reci introspects each action's action.yml for typed input/output contracts, infers data-flow edges from variable names, injects config from pyproject.toml, and compiles the whole thing into a complete workflow YAML — with all the cross-job output wiring generated for you.

Quick start

from reci import parse_recipe_string, compile_recipe, dump_workflow, ActionSpec, InputSpec, OutputSpec

recipe = parse_recipe_string("""
name: CI
on: [push, pull_request]

jobs:
  test:
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: '${{ config.python_version }}'
      - id: run_tests
        run: pytest
""")

# Action specs can be fetched from GitHub or provided manually
specs = {
    "actions/checkout@v4": ActionSpec(ref="actions/checkout@v4"),
    "actions/setup-python@v5": ActionSpec(
        ref="actions/setup-python@v5",
        inputs={"python_version": InputSpec(name="python_version", default="3.x")},
    ),
}

config = {"python_version": "3.12"}
workflow = compile_recipe(recipe, specs, config=config)
print(dump_workflow(workflow))

The compiler:

  • Injects a setup job that exports config values as job outputs
  • Rewrites ${{ config.* }} to ${{ needs.setup.outputs.* }}
  • Auto-wires needs: edges from data-flow dependencies
  • Generates the cross-job output forwarding ceremony (step output -> job output -> needs consumption)

CLI

# Inspect an action's input/output contract
reci inspect actions/setup-python@v5

# Compile a recipe to workflow YAML
reci compile recipe.yml --output .github/workflows/ci.yml

# Validate a recipe + config
reci validate --recipe recipe.yml --format cli

# Scaffold a config skeleton from a recipe
reci scaffold recipe.yml

Five-level input resolution

For each action input, the compiler resolves its value using this precedence:

  1. Explicit with: in the recipe — used verbatim
  2. Upstream output match${{ steps.<id>.outputs.<name> }}
  3. Config value (scoped action__key, then shared key) — ${{ needs.setup.outputs.<key> }}
  4. Default from action.yml — omitted (GitHub uses the default)
  5. Required + no source — validation error

Validation

ESLint-style severity (error/warning/info) with ruff-style rule prefixes:

Rule Category What it catches
DAG001 Structure Cycle detected
DAG002 Structure Duplicate output name in a job
FLOW001 Data flow Required input has no source
FLOW002 Data flow Ambiguous wiring (multiple upstream matches)
FLOW006 Data flow Matrix job output consumed downstream (non-deterministic)
CONF001 Config Required config key missing
PURE001 Purity run: step breaks typed contract
ACT002 Action Referenced action not found

Config adapters

Read CI config from your project file of choice:

Adapter Section Library
pyproject [tool.ci] tomlkit (round-trip)
wads [tool.wads.ci] tomlkit
package-json "ci" key json
yaml .ci.yml ruamel.yaml

The recipe format

A recipe looks like a GitHub Actions workflow with reci extensions:

  • ${{ config.* }} — references to config values (resolved from pyproject.toml etc.)
  • bind: — input renaming (bind: {tag_name: version} wires upstream output version to input tag_name)
  • outputs: on run: steps — manual output annotation for untyped steps
name: Python CI
on: [push, pull_request]

jobs:
  test:
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: '${{ config.python_version }}'
      - run: pytest

  publish:
    needs: [test]
    steps:
      - uses: actions/checkout@v4
      - id: bump
        uses: i2mint/isee/actions/bump-version-number@master
      - uses: i2mint/wads/actions/git-tag@master
        bind:
          tag_name: version  # wire bump's "version" output to git-tag's "tag_name" input

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

reci-0.0.3.tar.gz (68.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

reci-0.0.3-py3-none-any.whl (30.5 kB view details)

Uploaded Python 3

File details

Details for the file reci-0.0.3.tar.gz.

File metadata

  • Download URL: reci-0.0.3.tar.gz
  • Upload date:
  • Size: 68.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.2 {"installer":{"name":"uv","version":"0.11.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for reci-0.0.3.tar.gz
Algorithm Hash digest
SHA256 d432dc40e751b291dfa9fefcc6261169ef6b3892d38b68aea61825cf39936bd0
MD5 4732b5da6c47620e3eb3fe4c9db90d91
BLAKE2b-256 66dd695f45b615401fe06f4d854c3f8c6e2d934c692d776336338758ba93527b

See more details on using hashes here.

File details

Details for the file reci-0.0.3-py3-none-any.whl.

File metadata

  • Download URL: reci-0.0.3-py3-none-any.whl
  • Upload date:
  • Size: 30.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.2 {"installer":{"name":"uv","version":"0.11.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for reci-0.0.3-py3-none-any.whl
Algorithm Hash digest
SHA256 31c4a77f644b388479bfa81de07bc1b36fb73ed4fe9cd4dd88186835897ef595
MD5 c018e551babe1fd209c79681ee47b303
BLAKE2b-256 cf4b47738926028e78022e776f9c2b59d8148e7393a428be0bf00aeedd4cbc36

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page