Files
bahdotsh 31cf534625 feat(tui): live step view with streamed stdout/stderr
The execution tab used to stare back at you with a single progress
bar while the executor did... whatever it was doing. You found out
what happened when the whole workflow was over, and only then. Not
great for a tool whose entire point is running workflows locally.

The reason was structural. `execute_workflow` handed back one
`Vec<JobResult>` at the very end, `StepStatus` only had terminal
variants (Success/Failure/Skipped — no Running, no Pending), and
the container runtimes read logs *after* the container exited via
`wait_container` followed by `logs().collect()`. The TUI was doing
the best it could with a request/response it couldn't observe.

So: thread an optional event stream through the executor. New
`ExecutionEvent` enum (`JobStarted`, `StepStarted`, `StepLogChunk`,
`StepCompleted`, `JobCompleted`, plus `Execution*` bookends) emitted
via an `EventSink` that bundles an unbounded sender with a per-run
`JobId` allocator so matrix combinations get distinct ids. The TUI
spawns a receiver, drains it each tick, and paints the detail view
live — spinner on the running step, ✓/✖/⊘/○ everywhere else,
per-step duration, and a bottom pane that tails the step's log
buffer as bytes come off stdout/stderr. `f` re-engages auto-follow;
any manual j/k pins you where you are.

Log streaming required rewriting all four runtimes. Emulation,
secure_emulation and podman move to `tokio::process::Command` with
piped stdio drained via `AsyncBufReadExt::lines()` in parallel
tasks. Docker is the fun one: `logs(follow: true)` now runs
concurrently with `wait_container` under `tokio::join!`, instead of
the old sequential wait-then-read. Chunks mirror into the sink
*and* accumulate into the final `ContainerOutput` so both the live
view and the post-mortem `StepResult.output` stay truthful.

CLI path is unchanged — `ExecutionConfig.event_sink` defaults to
`None` and the whole thing short-circuits. Reusable workflows and
composite actions collapse to a single synthetic step in the event
stream for v1; nested forwarding is a separate problem for another
day.

`StepStatus` gained `Pending` and `Running` variants with
`#[non_exhaustive]` so we can add more without breaking callers
again. Per-step log buffer is capped at 10k lines with a truncation
marker; the Step Output pane pins to the bottom so newest-first is
always visible without caring about scroll state.

Two new integration tests assert event ordering (JobStarted before
any of its StepStarted, StepCompleted before JobCompleted, pairs
match by (job_id, step_idx)) and that \`StepLogChunk\` arrives
*between* \`StepStarted\` and \`StepCompleted\`. The sink-absent
path stays behaviorally identical.

Please don't put back the "collect everything then show it" pattern
when the executor grows new capabilities. The streaming seams are
there now; use them.
2026-04-20 00:14:33 +05:30
..
2025-09-05 08:22:15 +05:30

Wrkflw Crates

This directory contains the Rust crates that make up the wrkflw workspace.

Crate Structure

Crate Purpose
wrkflw CLI binary and library entry point
executor Workflow execution engine (Docker, Podman, emulation)
parser Workflow file parsing and JSON Schema validation
evaluator Structural evaluation of workflow files
validators Validation rules for jobs, steps, triggers, matrix
runtime Container management and emulation runtime
ui Terminal user interface (ratatui-based)
models Shared data structures (ValidationResult, GitLab models)
matrix Matrix expansion (include, exclude, fail-fast)
secrets Secrets management with multiple providers and encryption
github GitHub API integration (list/trigger workflows)
gitlab GitLab API integration (trigger pipelines)
logging Thread-safe in-memory logging for TUI/CLI
utils Workflow file detection and fd redirection helpers

Building

# Build everything
cargo build

# Build a specific crate
cargo build -p wrkflw-executor

Testing

# Run all tests
cargo test

# Run tests for a specific crate
cargo test -p wrkflw-executor

Each crate has its own Cargo.toml with dependencies managed through workspace inheritance in the root Cargo.toml.