A high-performance, open-source CI/CD pipeline runner written in Rust.
Define your build pipeline in YAML. Rusty Orchestrator runs independent tasks in parallel, skips unchanged work via content-addressable caching, and understands GitHub Actions workflow syntax natively — all from your terminal, with no external service required.
- Rusty Orchestrator
- Table of Contents
- Why Rusty Orchestrator?
- How it works
- Prerequisites
- Installation
- Quick start
- CLI reference
run— execute a pipelinerun-all— run all workflows in a directoryvalidate— check without runninglist— show execution ordergraph— ASCII dependency graphcache show— inspect the cachecache clean— clear the cacheinit— scaffold a new pipelineconnect— link to dashhy dashboarddisconnect— remove dashboard connectionstatus— show connection status
- Pipeline format
- Language examples
- Caching
- Features
- Contributing
- License
Most CI tools require you to push code before you can see whether your pipeline works. Rusty Orchestrator runs the same pipeline locally — including your existing GitHub Actions workflows — so you can iterate fast before ever opening a pull request.
| Problem | How Rusty Orchestrator solves it |
|---|---|
| "My CI is slow" | Parallel execution + smart caching skips unchanged tasks instantly |
| "I have to push to test my workflow" | Run .github/workflows/*.yml files locally with one command |
| "Debugging CI failures is painful" | Live TUI with per-task progress, output streaming, and debug logging |
| "I don't want another SaaS dependency" | Fully offline — no account, no network, no dashboard required |
pipeline.yaml ──► Parser ──► DAG Resolver ──► Scheduler ──► Worker Pool
│ │
Cycle check Tokio async tasks
Dep ordering Parallel execution
Content cache check
- Parse — Rusty reads your YAML (native format or GitHub Actions) and builds a list of tasks with dependencies.
- Resolve — A directed acyclic graph (DAG) is constructed. Circular dependencies are caught before anything runs.
- Schedule — Tasks are grouped into parallel stages. A task starts as soon as all its dependencies succeed.
- Cache — Each task is identified by a SHA-256 hash of its command, dependency IDs, and env values. If the hash matches a previous successful run, the task is skipped.
- Execute — Tasks run as shell subprocesses. Stdout and stderr are streamed live to the TUI or plain log output.
- Rust 1.70+ — only needed if installing via
cargo installor building from source. Not required for pre-built binaries.
cargo install rustyochestratorUpgrade to the latest version at any time:
cargo install rustyochestrator --forceDownload the binary for your platform from the latest GitHub release, then move it onto your PATH:
# macOS / Linux
tar xzf rustyochestrator-*.tar.gz
sudo mv rustyochestrator /usr/local/bin/
# verify
rustyochestrator --versioncurl -fsSL https://github.com/KodeSage/rustyochestrator/releases/latest/download/install.sh | shgit clone https://github.com/KodeSage/rustyochestrator
cd rustyochestrator
cargo build --release
./target/release/rustyochestrator --version1. Scaffold a pipeline in your project:
rustyochestrator initThis creates a pipeline.yaml in the current directory. No folder setup required.
2. Edit the generated file to match your project's build steps:
tasks:
- id: install
command: "npm install"
- id: lint
command: "npm run lint"
depends_on: [install]
- id: test
command: "npm test"
depends_on: [install]
- id: build
command: "npm run build"
depends_on: [lint, test]3. Run it:
rustyochestrator run pipeline.yaml4. Run it again — unchanged tasks are skipped from cache:
rustyochestrator run pipeline.yaml
# [CACHE HIT] Skipping task: install
# [CACHE HIT] Skipping task: lint
# [CACHE HIT] Skipping task: test
# [CACHE HIT] Skipping task: buildNote: Rusty Orchestrator only runs commands you define. If your project needs packages installed, declare it as a task (as shown above) — nothing is auto-installed.
rustyochestrator run <pipeline.yaml> [--concurrency <N>] [--no-tui]rustyochestrator run pipeline.yaml
rustyochestrator run pipeline.yaml --concurrency 4 # limit worker count
rustyochestrator run .github/workflows/ci.yml # GitHub Actions format
rustyochestrator run pipeline.yaml --no-tui # force plain log output
RUST_LOG=debug rustyochestrator run pipeline.yaml # verbose debug loggingWhen stdout is a TTY, the live TUI dashboard is shown automatically:
rustyochestrator — pipeline.yaml elapsed 00:00:12
✓ toolchain 0.8s [cached]
✓ fmt 1.2s
⠸ clippy 12s [running]
⠸ build-debug 9s [running]
◌ test [waiting]
◌ build-release [waiting]
◌ smoke-test [waiting]
████████░░░░░░░░░░░░░░░░ 2/7 2 done 2 running 3 pending 0 failed
In non-TTY environments (CI runners, | tee, > file) the TUI is suppressed automatically and plain log output is used. Use --no-tui to force plain output locally.
Discovers every .yml and .yaml file in the given directory and runs them all concurrently — just like GitHub Actions fires multiple workflow files in parallel. Each workflow's output is prefixed with its filename.
rustyochestrator run-all <dir> [--concurrency <N>]rustyochestrator run-all .github/workflows
rustyochestrator run-all ./my-pipelines
rustyochestrator run-all examples --concurrency 2Example output with two workflows running simultaneously:
INFO running workflows simultaneously count=2 dir=.github/workflows
[ci] Starting task: lint__cargo_fmt___check
[release] Starting task: build__Install_cross_...
[ci] Completed task: lint__cargo_fmt___check
[release] Completed task: build__Install_cross_...
- All pipelines are validated before any execution starts — parse errors surface immediately
- Each workflow runs its own independent DAG scheduler with its own cache
- Exit code is non-zero if any workflow fails
- Works with both native pipeline format and GitHub Actions format files in the same directory
Parses the file, resolves dependencies, checks for cycles. Exits non-zero on any error.
rustyochestrator validate pipeline.yaml 3 tasks
[ok] build
[ok] test (needs: build)
[ok] deploy (needs: test)
pipeline 'pipeline.yaml' is valid.
Groups tasks into parallel stages so you can see exactly what runs when.
rustyochestrator list pipeline.yamlExecution order for 'pipeline.yaml':
Stage 0 — 2 task(s) run in parallel:
1. toolchain
2. fmt
Stage 1 — 2 task(s) run in parallel:
3. clippy (after: fmt)
4. build-debug (after: fmt)
Stage 2 — 1 task(s) run in parallel:
5. test (after: build-debug, clippy)
rustyochestrator graph pipeline.yamlDependency graph for 'pipeline.yaml':
Stage 0 (no deps):
toolchain
fmt
Stage 1:
clippy ◄── [fmt]
build-debug ◄── [fmt]
Stage 2:
test ◄── [build-debug, clippy]
rustyochestrator cache show task status hash
─────────────────────────────────────────
build ok b3d10802f5217f42
clippy ok 9eeaf9f8bc055df3
fmt ok 810e75f0d3dee10e
test ok 257080d6e3e17348
4 cached task(s).
Forces every task to re-run on the next run.
rustyochestrator cache clean
# Cache cleared.Creates a starter pipeline.yaml (or a custom filename) in the current directory.
rustyochestrator init # creates pipeline.yaml
rustyochestrator init my-pipeline.yaml # custom filenameStream live pipeline events to Dashhy, the hosted monitoring UI built for Rusty Orchestrator.
rustyochestrator connect --token <jwt> --url <dashboard-url>Saves the connection to ~/.rustyochestrator/connect.json. All subsequent run commands will report live to the dashboard.
rustyochestrator disconnectrustyochestrator status
# Connected
# Dashboard : https://your-dashhy.vercel.app
# User : @your-github-usernametasks:
- id: build
command: "cargo build"
- id: lint
command: "cargo clippy -- -D warnings"
depends_on: [build]
- id: test
command: "cargo test"
depends_on: [build]
- id: package
command: "tar czf dist.tar.gz target/release/myapp"
depends_on: [lint, test]| Field | Required | Description |
|---|---|---|
id |
yes | Unique identifier for the task |
command |
yes | Shell command to run (executed via sh -c) |
depends_on |
no | List of task IDs that must succeed before this task starts |
env |
no | Map of environment variables scoped to this task |
Multi-line commands work with YAML block scalars:
tasks:
- id: report
command: |
echo "=== build info ==="
rustc --version
du -sh target/release/myapp
depends_on: [build]Declare env: at the pipeline level (applied to every task) or at the task level (overrides the pipeline-level value for that task only).
env:
NODE_ENV: production
API_URL: https://api.example.com
tasks:
- id: build
command: "npm run build"
- id: deploy
command: "npm run deploy"
env:
API_URL: https://staging.example.com # overrides pipeline-level value
API_KEY: "${{ secrets.DEPLOY_KEY }}" # read from shell environment at runtimeSecret references use ${{ secrets.NAME }} syntax. At runtime, the value is read from the process environment and passed to the task process — it is never written to disk.
.env file support: Rusty Orchestrator automatically loads a .env file from the current directory before running any pipeline. This means you can store secrets locally without exporting them to your shell every session:
# .env (add to .gitignore — never commit this file)
DEPLOY_KEY=ghp_yourtoken
DATABASE_URL=postgres://localhost/mydbrustyochestrator run pipeline.yaml # DEPLOY_KEY is resolved from .env automaticallyPrecedence rules:
- A variable already exported in your shell always wins over the
.envvalue .envwins over "not set at all"- Values may be quoted with
"..."or'...'; bothKEY=VALUEandexport KEY=VALUEare accepted
Pre-flight validation: all secrets are resolved before any task starts. If a referenced secret is missing from both the shell environment and .env, the run aborts immediately:
Error: secret 'DEPLOY_KEY' referenced by env key 'API_KEY' in task 'deploy' is not set in the environment
Automatic redaction: debug logging (RUST_LOG=debug) prints env keys but redacts values whose key contains SECRET, TOKEN, KEY, or PASSWORD (case-insensitive):
RUST_LOG=debug rustyochestrator run pipeline.yaml
# DEBUG task=deploy key=API_URL value=https://staging.example.com
# DEBUG task=deploy key=API_KEY value=***Cache invalidation: changing any env value (including secrets) invalidates the task's cache hash, forcing a re-run.
Rusty Orchestrator can run GitHub Actions workflow files directly — useful for local testing before pushing:
# .github/workflows/ci.yml
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Compile
run: cargo build --release
test:
runs-on: ubuntu-latest
needs: [build]
steps:
- name: Unit tests
run: cargo test --allrustyochestrator run .github/workflows/ci.ymlMapping rules:
- Each
run:step becomes one task - Steps within a job run sequentially
needs:wires the first step of a downstream job to the last step of each required jobuses:steps (third-party actions) are silently skippedenv:blocks at the workflow, job, and step levels are parsed and merged${{ secrets.NAME }}references are forwarded; other${{ }}expressions are dropped (they require a real Actions runner)
Rusty Orchestrator is completely language-agnostic. The command field runs anything your shell can execute.
tasks:
- id: install
command: "npm install"
- id: lint
command: "npm run lint"
depends_on: [install]
- id: test
command: "npm test"
depends_on: [install]
- id: build
command: "npm run build"
depends_on: [lint, test]tasks:
- id: install
command: "pip install -r requirements.txt"
- id: lint
command: "flake8 src/"
depends_on: [install]
- id: test
command: "pytest tests/"
depends_on: [install]
- id: docker-build
command: "docker build -t myapp:latest ."
depends_on: [lint, test]tasks:
- id: tf-init
command: "terraform init"
- id: tf-plan
command: "terraform plan -out=plan.tfplan"
depends_on: [tf-init]
- id: tf-apply
command: "terraform apply plan.tfplan"
depends_on: [tf-plan]tasks:
- id: backend-test
command: "cargo test"
- id: frontend-test
command: "npm test"
- id: build-image
command: "docker build -t myapp ."
depends_on: [backend-test, frontend-test]
- id: push-image
command: "docker push myapp:latest"
depends_on: [build-image]Any command that runs in sh -c works — shell scripts, Python scripts, Makefiles, Docker, cloud CLIs, or anything else.
Cache entries are stored in .rustyochestrator/cache.json.
A task is a cache hit when:
- Its SHA-256 hash of
command + dependency IDs + env key/value pairsmatches the stored entry - The previous run recorded
success: true
{
"entries": {
"build": {
"hash": "a3f1c2...",
"success": true
}
}
}To force a full re-run, clear the cache:
rustyochestrator cache clean
# or
rm -rf .rustyochestrator| Feature | Description |
|---|---|
| Parallel execution | Worker pool backed by Tokio; concurrency defaults to the number of logical CPUs |
| DAG scheduling | Dependencies resolved at runtime; tasks start as soon as their deps finish |
| Content-addressable cache | Tasks hashed by command + deps + env; unchanged tasks skipped instantly |
| GitHub Actions compatibility | Parse and run .github/workflows/*.yml files directly |
| Parallel workflow execution | run-all runs every workflow file in a directory simultaneously |
| Live TUI dashboard | Colour-coded per-task progress with spinners, elapsed time, and a summary bar |
| CI-friendly output | Auto-detects non-TTY environments and falls back to plain log output |
| Environment variables & secrets | Declare env: at pipeline or task level; secret refs resolved from shell env or .env file |
.env file support |
Automatically loads .env from the current directory; shell exports always take precedence |
| Pre-flight secret validation | All secrets validated before execution starts; missing secrets abort immediately |
| Retry logic | Failed tasks are retried up to 2 times before being marked failed |
| Failure propagation | When a task fails, its entire transitive dependent subtree is cancelled |
| Real-time output streaming | Stdout and stderr from every task streamed line-by-line as they run |
| Cycle detection | Circular dependencies caught and reported before execution starts |
| Dashboard integration | Optional Dashhy integration streams live pipeline events to a hosted monitoring UI |
Contributions are welcome. To get started:
git clone https://github.com/KodeSage/rustyochestrator
cd rustyochestrator
cargo build
cargo testUse cargo run -- <command> in place of the installed binary during development:
cargo run -- run examples/pipeline.yaml
cargo run -- validate examples/pipeline.yaml
cargo run -- list examples/pipeline.yaml
cargo run -- graph examples/pipeline.yaml
cargo run -- cache show
cargo run -- init
cargo run -- run-all .github/workflowsPlease open an issue before submitting large changes so we can align on the approach.
MIT — see LICENSE.