Ready-to-run automations with a Python escape hatch
Project description
Stepyard
YAML pipelines. Python plugins. Runs on your machine - no server to set up, no cloud account needed.
pip install stepyard
stepyard init my-automations && cd my-automations
stepyard run hello
Quick example
Build a container, smoke-test it, summarise the result with an LLM, and post to Slack. One YAML file, no glue scripts.
name: deploy
description: Build the image, smoke-test it, and post an AI summary to Slack.
steps:
- id: build
uses: shell.run
with:
command: docker build -t myapp:${{ env.GIT_SHA }} .
- id: smoke_test
uses: http.request
with:
method: GET
url: https://staging.myapp.com/healthz
- id: summary
uses: llm.generate # built-in - reads OPENAI_API_KEY from the env
with:
model: gpt-4o-mini
prompt: |
Write a one-line Slack message about this deploy.
Build exit code: ${{ steps.build.output.code }}
Health check HTTP status: ${{ steps.smoke_test.output.status }}
- id: notify
uses: http.request
with:
method: POST
url: ${{ env.SLACK_WEBHOOK }}
json_body:
text: ${{ steps.summary.output.output }}
Run it:
GIT_SHA=$(git rev-parse --short HEAD) stepyard run deploy
✓ build 12.4s
✓ smoke_test 0.3s
✓ summary 0.9s
✓ notify 0.2s
Flow completed in 13.8s
Reading step outputs. Each node has a documented output shape, referenced as
${{ steps.<id>.output... }}:
Node outputshapeshell.run{ stdout, stderr, code }http.request{ status, headers, body }llm.generate{ output, usage, model, provider }(use${{ steps.<id>.output.output }}for text)Full reference:
docs/nodes/builtin.md.
Why Stepyard?
- Flows are YAML files in your repo. Steps, conditions, loops, and retries are plain keys. Version-control them alongside your code; no proprietary DSL to learn.
- Extend with plain Python. One
@nodedecorator turns any function into a reusable step. Inputs are type-validated automatically; plugin dependencies run in an isolated virtualenv, so they never clash with Stepyard's own. - Nothing leaves your machine. State is stored in a local SQLite database. Data only goes out if a step in your flow explicitly sends it.
- Every run is its own process. Each flow executes in a dedicated OS
subprocess, so a crash, timeout, or
sys.exitin one run cannot take down the scheduler or a sibling run. See Execution model. - Built-in scheduler, no hosted service. Add a
trigger:block withcron,interval, orstartup, runstepyard service start, and flows execute on schedule without a control plane.
Install
# from PyPI
pip install stepyard
# or, for development, with uv
git clone https://github.com/rorlikowski/stepyard && cd stepyard
uv pip install -e ".[dev,docs]"
uv run stepyard doctor # verify the install
Requires Python 3.10+. Works on macOS, Linux, and Windows (WSL).
The 60-second tour
stepyard init my-automations # scaffold flows/ + .gitignore + .stepyard/
cd my-automations
stepyard run hello # run a flow now (in its own subprocess)
stepyard status # see every flow and its latest run
stepyard show <run-id> # drill into the steps of one run
stepyard logs <run-id> # stream the captured logs
stepyard validate --all # type-check your YAML without running it
Schedule it instead of running by hand - add a trigger and start the daemon:
name: nightly_backup
trigger:
uses: cron
with:
schedule: "0 3 * * *" # every day at 03:00
steps:
- id: dump
uses: shell.run
with:
command: pg_dump ${{ env.DATABASE_URL }} | gzip > /tmp/backup.sql.gz
stepyard service start # run the scheduler in the background
stepyard service status
Execution model
Stepyard is deliberately process-isolated:
stepyard run <flow>spawns a fresh subprocess (python -m stepyard.engine.runner) for that single run. Its stdout/stderr are captured to.stepyard/logs/.- The scheduler daemon (
stepyard service start) runs separately. It evaluates triggers, enqueues runs in SQLite, and a worker spawns one subprocess per run (bounded bySTEPYARD_MAX_CONCURRENT_FLOWS, default4). - Inside a run, steps execute sequentially. Built-in nodes run in-process; plugin nodes installed into an isolated virtualenv run in a second short-lived subprocess that talks JSON over stdin/stdout - so a plugin's dependencies can never clash with Stepyard's own.
The practical upshot: one misbehaving flow or plugin cannot corrupt the scheduler or sibling runs.
Write your own step
# my_plugin/nodes.py
from stepyard.sdk import node, NodeResult, NodeStatus
@node(name="math.add")
def add(a: int, b: int) -> int:
return a + b
@node(name="files.archive")
def archive(path: str) -> NodeResult:
if not path:
return NodeResult(status=NodeStatus.FAILED, error="path is required")
# ... do the work ...
return NodeResult(status=NodeStatus.SUCCESS, output={"archived": path})
Register it via an entry point and it becomes available as uses: math.add in
every flow. See docs/plugins/creating.md.
Documentation
| Section | What's inside |
|---|---|
| Getting Started | Install, quickstart, two tutorials |
| Core Concepts | Flows, expressions, control flow, triggers, errors |
| How-to Guides | Scheduling, secrets, approvals, debugging |
| Built-in Nodes | Every node that ships with Stepyard |
| Plugin Development | Write, test, and publish plugins |
| CLI Reference | Every command and flag |
The full documentation is published at rorlikowski.github.io/stepyard.
Browse the docs locally with uv run mkdocs serve.
License
MIT - see LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file stepyard-0.1.2.tar.gz.
File metadata
- Download URL: stepyard-0.1.2.tar.gz
- Upload date:
- Size: 1.5 MB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fae515974453efb4a460366047debef97fd8756e01538857d3d52c5af8888993
|
|
| MD5 |
59e04c949178fe045b51e681324c76f5
|
|
| BLAKE2b-256 |
fb8d007b6b6bcdaeffea3ab80553b22e7dac7dc11f972d60d6c63880539867c0
|
Provenance
The following attestation bundles were made for stepyard-0.1.2.tar.gz:
Publisher:
release.yml on rorlikowski/stepyard
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
stepyard-0.1.2.tar.gz -
Subject digest:
fae515974453efb4a460366047debef97fd8756e01538857d3d52c5af8888993 - Sigstore transparency entry: 1820005918
- Sigstore integration time:
-
Permalink:
rorlikowski/stepyard@1f6d7f672a1429397b4585ee52cb5f891ad67321 -
Branch / Tag:
refs/tags/v0.1.2 - Owner: https://github.com/rorlikowski
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@1f6d7f672a1429397b4585ee52cb5f891ad67321 -
Trigger Event:
push
-
Statement type:
File details
Details for the file stepyard-0.1.2-py3-none-any.whl.
File metadata
- Download URL: stepyard-0.1.2-py3-none-any.whl
- Upload date:
- Size: 127.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
78788e741d89187ecc19bc2f0c7c1f81a336c2c5d3440d36ccc09fd8daf38d6c
|
|
| MD5 |
2383acebee4677f17ad15027da53363c
|
|
| BLAKE2b-256 |
a9098c9eed84f753e1299fe3eeba21b1e2a4bb1a1bcc8e7c317a78d98b9d7611
|
Provenance
The following attestation bundles were made for stepyard-0.1.2-py3-none-any.whl:
Publisher:
release.yml on rorlikowski/stepyard
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
stepyard-0.1.2-py3-none-any.whl -
Subject digest:
78788e741d89187ecc19bc2f0c7c1f81a336c2c5d3440d36ccc09fd8daf38d6c - Sigstore transparency entry: 1820005937
- Sigstore integration time:
-
Permalink:
rorlikowski/stepyard@1f6d7f672a1429397b4585ee52cb5f891ad67321 -
Branch / Tag:
refs/tags/v0.1.2 - Owner: https://github.com/rorlikowski
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@1f6d7f672a1429397b4585ee52cb5f891ad67321 -
Trigger Event:
push
-
Statement type: