mirror of
https://github.com/bahdotsh/wrkflw.git
synced 2026-08-29 10:09:25 +02:00
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.
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.