April 2, 2026
11 pull requests merged across 2 repos
bahdotsh/wrkflw
- Rewrote main README from 610 lines to ~170 — removed duplicate sections, speculative roadmap, and verbose limitation disclaimers
- Removed 12 dead files: junk test files (
hello.cpp,hello.rs,test.py), duplicateAGENTS.md, verboseVERSION_MANAGEMENT.md,GITLAB_USAGE.md(merged into README),TESTING_PODMAN.md,README_SECURITY.md, 3 asciinema recordings, and 2 unused GIFs (~7MB) - Updated crate READMEs:
crates/README.mdnow lists all 14 crates with descriptions;tests/README.mdandexamples/secrets-demo/README.mdtrimmed
Net: 3,819 lines deleted, 197 added.
Test plan
- Verify all links in README.md resolve correctly (demo.gif still exists)
- Verify crate README table matches actual crates in
crates/ - Confirm no removed file is referenced by Cargo.toml or build scripts
- Adds post-step read-back of
GITHUB_OUTPUT,GITHUB_ENV,GITHUB_PATH, andGITHUB_STEP_SUMMARYfiles, enabling inter-step data flow in GHA emulation - Adds
${{ steps.<id>.outputs.<key> }}and${{ env.<name> }}expression substitution so step outputs and dynamic env vars are resolved in run commands - Restructures the
execute_job()step loop from an async-block wrapper to a deadline-based timeout, allowingjob_envmutation between steps
What works now
jobs:
test:
runs-on: ubuntu-latest
steps:
- id: set-output
run: echo "version=1.2.3" >> $GITHUB_OUTPUT
- run: echo "Got ${{ steps.set-output.outputs.version }}"
# ^ resolves to "Got 1.2.3"
- run: echo "MY_VAR=hello" >> $GITHUB_ENV
- run: echo "$MY_VAR"
# ^ sees "hello" in env
- run: echo "/custom/bin" >> $GITHUB_PATH
- run: echo "$PATH"
# ^ has /custom/bin prepended
Files changed
| File | Change |
|---|---|
crates/executor/src/github_env_files.rs | NEW — GHA key-value file parser (simple + heredoc format) with 11 tests |
crates/executor/src/engine.rs | Extended StepResult with outputs field, restructured step loops, added post-step read-back |
crates/executor/src/substitution.rs | Added steps.*.outputs.* and env.* regex patterns + substitution functions with 7 tests |
crates/executor/src/lib.rs | Module registration |
crates/ui/src/handlers/workflow.rs | Backward-compatible StepResult field addition |
Test plan
-
cargo fmt --all— clean -
cargo clippy --workspace— zero warnings -
cargo test --workspace— all 344 tests pass (18 new tests added) - Manual smoke test with a workflow using
$GITHUB_OUTPUTstep outputs
Implements four low-risk, well-scoped fixes to improve GitHub Actions emulation fidelity:
- **Expand
github.*context**: Adds 13 missingGITHUB_*env vars (CI,GITHUB_ACTIONS,GITHUB_REF_NAME,GITHUB_REF_TYPE,GITHUB_REPOSITORY_OWNER,GITHUB_RUN_ATTEMPT,GITHUB_SERVER_URL,GITHUB_API_URL,GITHUB_GRAPHQL_URL,GITHUB_HEAD_REF,GITHUB_BASE_REF,GITHUB_TRIGGERING_ACTOR,GITHUB_RETENTION_DAYS), improvesGITHUB_ACTORto derive fromgit config user.name/$USER, addsGITHUB_JOBper-job - Enforce
timeout-minutes: Parses job-leveltimeout-minutes, wraps job execution intokio::time::timeout(default 360 min per GHA spec), wraps step execution whenstep.timeout_minutesis set - Implement
defaults.run: AddsDefaults/DefaultsRunstructs, supportsdefaults.run.shellanddefaults.run.working-directoryat both workflow and job level with proper fallback chain (step > job defaults > workflow defaults > bash). Shell now uses-eflag for bash/sh matching real GHA behavior - Implement
hashFiles(): Addssha2/globdependencies, implements glob-based file matching with SHA-256 hashing, deterministic sort order, wired into the expression substitution pipeline
Test plan
-
cargo fmt --all— no issues -
cargo clippy --workspace— no warnings -
cargo test --workspace— all tests pass (164 executor, 21 parser, all others) - 11 new unit tests for environment helper functions
- 1 new parser test for
timeout-minutes - 1 new parser test for
defaults.run - 7 new unit tests for
hashFiles()(single/multiple patterns, no matches, recursive, deterministic, inline text, combined with matrix)
Replace monolithic build.yml with split ci.yml (parallel fmt, clippy, build+test jobs). Update all actions to modern versions (checkout@v4, dtolnay/rust-toolchain, rust-cache@v2). Overhaul release workflow with more build targets (musl, aarch64), simpler changelog, and crates.io publish step.
- Adds a job selection sub-view to the TUI Workflows tab — press Enter on a workflow to see its jobs, then Enter on a job to run just that one (with transitive deps),
ato run all, or Esc to go back - Reuses the existing
ExecutionConfig.target_jobandfilter_plan_to_job()machinery that the CLI--jobflag already uses - Updates status bar hints and help overlay to document the new keybindings
Test plan
-
cargo build --features tuicompiles cleanly -
cargo clippy --features tuipasses -
cargo test target_job— existing target_job tests pass - Manual:
wrkflw tui→ select workflow → Enter → see jobs listed → Enter on a job → only that job executes - Manual: In job selection mode, press
a→ all jobs execute - Manual: In job selection mode, press Esc → returns to workflow list
- Adds a
tuiCargo feature flag (enabled by default) towrkflw-uiand the main binary crate - When built with
--no-default-features,ratatui/crosstermare excluded, thetuisubcommand is removed, and running with no args prints help - All CLI subcommands (
validate,run,trigger,list) remain fully functional without TUI - Removes unused direct
crossterm/ratatuideps from the binary crate (they were only needed transitively viawrkflw-ui)
Usage
# Default build (TUI included, same as before)
cargo build
# Minimal build (no TUI, smaller binary)
cargo build --no-default-features
# Install without TUI
cargo install wrkflw --no-default-features
Test plan
-
cargo check— default build compiles -
cargo check --no-default-features— no-TUI build compiles -
cargo test— all tests pass with default features -
cargo test --no-default-features— all tests pass without TUI -
cargo clippy— clean on both feature configurations -
cargo fmt --check— formatting clean
- When a workflow step uses a local composite action (
uses: ./path),wrkflw validatenow reads the action'saction.yml, identifies inputs withrequired: true(and nodefault), and reports an error if they're missing from the step'swith:block. - Threads the repo root path through the validator chain (
evaluate_workflow_file→validate_jobs→validate_steps→validate_action_reference) so local action paths can be resolved. - Case-insensitive input matching (consistent with GitHub Actions behavior). Graceful degradation when repo root or action file is unavailable.
Closes #67
Test plan
- 10 new unit tests in
crates/validators/src/actions.rscovering: missing required input, provided input, required+default passes, no inputs section, missing action dir, missing action.yml, case-insensitive matching, multiple missing inputs,Nonerepo_root skips,.yamlextension - All existing tests updated for new signatures and passing
-
cargo test— full suite passes (273 tests, 0 failures) -
cargo fmt --check— clean -
cargo clippy --all-targets— no new warnings
- Add
--job <name>towrkflw runto execute a single job in isolation, for both GitHub workflows and GitLab pipelines - Add
--jobsflag towrkflw listto display job names within each workflow/pipeline file - If the specified job doesn't exist, error message lists all available jobs
Closes #68
Test plan
-
cargo build— compiles cleanly -
cargo test— all 243 tests pass -
wrkflw list --jobs— shows jobs grouped under each workflow file -
wrkflw run --job <name> <workflow>— runs only the specified job -
wrkflw run --job nonexistent <workflow>— errors with available job names
Setup actions like actions/setup-node and shivammathur/setup-php now correctly propagate their Docker images to subsequent run: steps instead of falling back to ubuntu:latest.
- Single setup action: Uses that runtime's image directly (e.g.,
node:20-slimforsetup-node) - Multiple setup actions: Builds a combined Docker image with all required runtimes (e.g., PHP + Node.js for Laravel workflows)
- No setup actions: Fully backward compatible — no behavior change
- Skips spurious
pull_imagefor locally-builtwrkflw-*images - Bumps
build_imagetimeout from 2min to 10min for combined image builds
Changes
crates/executor/src/engine.rs: Addeddetect_setup_runtimes(),build_combined_runtime_image(),resolve_runner_image(), andget_install_script(). Modifiedexecute_job()andexecute_matrix_job()to use the resolved image. Added 14 unit tests.crates/executor/src/docker.rs: Skip pull forwrkflw-*images, increase build timeout.tests/workflows/multi-runtime-test.yml: New test workflow with PHP + Node.js in same job.
Test plan
-
cargo fmt --all— clean -
cargo clippy— no warnings -
cargo test— all 219 tests pass (14 new) - Manual test: single Node.js workflow (
setup-node→run: node --version) — passes, usesnode:20-slim - Manual test: single PHP workflow (
setup-php→run: php --version) — passes, usescomposer:latest - Manual test: multi-runtime workflow (PHP + Node.js in same job) — passes, builds combined image
- Manual test: step failure stops job execution — confirmed working
— third-party Docker-based GitHub Actions (like super-linter/super-linter/slim@v7) were silently passing without ever actually running. The engine resolved the action image correctly but then ran echo 'Would execute GitHub action: ...' inside it regardless of runtime mode.
Root cause (two separate failures):
prepare_action()errored onActionType::DockerBuildwith "not yet supported", fell back todetermine_action_image(), and returnednode:20-slimfor super-linter- The
PreparedAction::Imageexecution branch had three sub-paths (is_docker, is_local, else) that all just ran echo commands — the resolved image was never actually used
Changes:
- Add
NativeDockervariant toPreparedAction— runs the image with its built-in ENTRYPOINT (no command override) - Implement
DockerBuildsupport: clone repo viashallow_clone, resolve Dockerfile path from action.yml, build it, return the tag - Fix
build_image_innerto tar the full context directory (not just the Dockerfile) soCOPYinstructions work - Allow empty
cmdinrun_containerto mean "use image's default ENTRYPOINT/CMD" - Emulation mode gracefully handles empty cmd instead of erroring
The existing PreparedAction::Image path with all special-cased action handling (actions-rs, checkout, etc.) is untouched.
Test plan
-
cargo build— compiles cleanly -
cargo test— all 171 tests pass -
cargo clippy— no warnings - Manual test: run a workflow with a Docker-based action (e.g.,
docker://alpine) and verify it actually executes - Manual test: run a workflow with a DockerBuild action (e.g., super-linter) and verify the Dockerfile is built and run