Self-hosted service
bahdotsh/wrkflw avatar
bahdotsh/wrkflw

wrkflw: validate and run GitHub Actions workflows on your own machine

Validate and Run GitHub Actions locally.

3,322 stars64 forksRustMIT

At a glance

What is it?
wrkflw is a Rust CLI and TUI that parses, validates and locally executes GitHub Actions workflows, with Docker, Podman, emulation and a sandboxed secure-emulation runtime. Its honest limitations list is the most useful part of the README.
Who is it for?
Adopt wrkflw if you write GitHub Actions YAML often and want a fast local syntax and structure check plus a container-based dry run before pushing. Do not adopt it if your workflows depend on service containers, concurrency groups or Windows and macOS runners, because the README states those are parsed but not enforced or not started.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 23 days ago.
What is it written in?
Mainly Rust, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 1, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The problem wrkflw targets: workflow YAML that only fails after you push

GitHub Actions configuration is a YAML dialect with its own schema, expression language and event model. A typo in an `on:` block, a `needs:` reference to a job that does not exist, or a composite action input that was renamed will not surface until the workflow is committed and a runner picks it up. The feedback loop runs through a remote service, and on a busy repository that loop can be minutes long. wrkflw is aimed at the part of that loop you can move onto your own machine: parsing and validating the workflow files, and then executing them against a local runtime. The intended audience is people who maintain .github/workflows directories, not people who only consume actions written by others. The README frames it plainly: test your workflows on your machine before pushing to GitHub. That framing is narrow, and it is the right one. This is not a general CI runner and it does not try to be a GitHub-hosted runner replacement; it is a pre-commit style check with an execution mode attached.

How wrkflw parses, filters and executes a workflow

The workspace layout in Cargo.toml shows the pipeline split into separate crates: wrkflw-parser, wrkflw-models, wrkflw-validators, wrkflw-evaluator, wrkflw-trigger-filter, wrkflw-matrix, wrkflw-secrets, wrkflw-runtime, wrkflw-executor and wrkflw-ui. That is a reasonable decomposition, and it tells you where behaviour lives. Parsing and schema validation happen first, backed by serde_yaml and jsonschema. The evaluator crate handles `${{ ... }}` expressions, and the README lists `toJSON`, `fromJSON`, `contains` and `startsWith` among the supported functions. The matrix crate covers `include`, `exclude`, `max-parallel` and `fail-fast`. The executor resolves job ordering from `needs` and runs independent jobs in parallel. Runtime selection is where the interesting design decision sits. wrkflw offers four modes: Docker via bollard, Podman, plain emulation with no containers, and secure emulation, which the README describes as sandboxed and container-free. Auto-detection tries Docker, then Podman, then emulation, which means a machine with no container runtime silently falls back to a mode that does not isolate the job. The trigger-filter crate implements diff-aware skipping: workflows whose `on:` block would not fire for the simulated event and changed file set are skipped. Strict mode is on by default, and the README states that `wrkflw run --event` without `--diff` or `--changed-files` is rejected up front rather than silently skipping every `paths:`-gated workflow. That default is a deliberate breaking change, and BREAKING_CHANGES.md is where the migration notes live.

Installing wrkflw and running your first workflow

The README gives three installation paths. The crate is published, so cargo works:

bash
cargo install wrkflw

Homebrew is also supported through the formula:

bash
brew install wrkflw

Building from source is the third option and produces a release binary in target/release:

bash
git clone https://github.com/bahdotsh/wrkflw.git
cd wrkflw
cargo build --release

Once installed, validation is the cheapest thing to try and needs no container runtime. Run it from the repository root and it picks up .github/workflows automatically:

bash
wrkflw validate

The README documents exit codes of 0 for all valid, 1 for validation failures and 2 for usage errors, with `--no-exit-code` to disable that behaviour. Those codes are what make it usable as a pre-push hook or a CI step. To see what wrkflw found before running anything, list the detected workflows and pipelines:

bash
wrkflw list

Execution is the next step. Running a single workflow with Docker selected explicitly avoids the auto-detection fallback:

bash
wrkflw run --runtime docker .github/workflows/ci.yml

If you want to know which jobs exist before committing to a run, the README provides a listing flag:

bash
wrkflw run --jobs .github/workflows/ci.yml

A single job can be isolated with `--job`, and `--preserve-containers-on-failure` keeps failed containers around for inspection. There is also a TUI, launched by running `wrkflw` with no arguments or `wrkflw tui`, with tabs for Workflows, Execution, DAG, Logs, Trigger, Secrets and Help. The DAG tab is the one worth opening first, because it shows the dependency graph the executor derived from `needs`.

What wrkflw does not do, according to its own README

The "Not yet supported" section is unusually candid, and it is the part to read before adopting anything. Service containers are the biggest gap: the README states that `services:` is parsed but never started, in any runtime. Any workflow that relies on a Postgres or Redis sidecar will not behave locally the way it does on GitHub, and the failure will be a connection error inside the job rather than a clear message from wrkflw. Concurrency groups and `cancel-in-progress` are parsed but not enforced, so a workflow that depends on serialising runs will run concurrently under wrkflw. Runner OS handling is the second sharp edge. The README says `runs-on: windows-*` and `macos-*` are silently mapped to a container image, with macOS mapped to a Linux image and Windows to a Windows container that will not run on Linux or macOS hosts. Worse, `${{ runner.os }}` reflects the host OS rather than `runs-on`. A workflow that branches on `runner.os` will therefore take a different path locally than it does on GitHub, and it will do so without warning. Private repositories are also unsupported for remote `uses:` references, because reusable workflows clone over unauthenticated HTTPS. GitHub encrypted secrets and fine-grained permissions are listed as unsupported too. Treat the emulation runtime as a convenience for workflows that mostly run shell commands, not as a faithful GitHub runner.

wrkflw versus act, and when the difference matters

The obvious comparison is nektos/act, which also runs GitHub Actions locally and has been the default answer to this problem for years. The difference in approach shows up in two places. First, wrkflw treats validation as a first-class command with its own exit codes, separate from execution; act's centre of gravity is execution. If what you want is a fast schema and structure check in a pre-push hook, wrkflw's `wrkflw validate` with exit code 1 on failure maps directly onto that, while act would require you to run the workflow and interpret the result. Second, wrkflw ships a container-free execution path and a sandboxed variant of it, so it can run on a machine without Docker or Podman installed. That is a real deployment difference: a laptop or a locked-down CI box with no container runtime can still execute some workflows. The trade-off is fidelity. Running without containers means the job sees your host environment rather than a clean image, which is exactly the kind of difference that hides bugs. For workflows that are mostly `run:` steps invoking a compiler or a test suite, that is acceptable. For workflows that install system packages or depend on a specific base image, it is not. wrkflw also covers GitLab pipelines for validation and triggering, which act does not; if your repository has both .github/workflows and .gitlab-ci.yml, one tool covers both.

Maintenance, licence and the cost of upgrading

The repository is not archived, and the last push was on 2026-09-08. The most recent release listed is v0.8.0 from 2026-04-21, preceded by v0.7.3 and v0.7.2 in August 2025. The gap between the 0.7.x releases and 0.8.0 is roughly eight months, so releases are not frequent, but they are not abandoned either. The workspace version in Cargo.toml is 0.8.0, which matches the release tag, and the internal crates are versioned in lockstep with it. That lockstep versioning means the crates.io package and the internal crate versions move together, which simplifies pinning. The upgrade cost is concentrated in one area: the trigger-aware execution feature introduced strict mode as the default and shipped a BREAKING_CHANGES.md to document it. Any script that calls `wrkflw run --event` without `--diff` or `--changed-files` will be rejected under strict mode and needs either those flags or `--no-strict-filter`. That is a migration you do once, and the file exists to make it mechanical. The project is MIT licensed, which permits commercial and private use and modification; the repository ships a LICENSE file and the Cargo.toml declares `license = "MIT"`. If you redistribute a modified binary, the usual MIT obligation to include the copyright notice and permission notice applies. This is a description of the licence text, not legal advice; check the LICENSE file for the exact terms.

Editorial conclusion

Adopt wrkflw if you write GitHub Actions YAML often and want a fast local syntax and structure check plus a container-based dry run before pushing. Do not adopt it if your workflows depend on service containers, concurrency groups or Windows and macOS runners, because the README states those are parsed but not enforced or not started. Before trusting it on a real pipeline, run wrkflw validate on your existing .github/workflows directory, then wrkflw run --jobs on the workflow you care about and compare the job list against what GitHub itself reports.

Frequently asked questions

What are the three types of GitHub Actions that wrkflw can run?

The README lists Docker container actions, JavaScript actions and composite actions, with composite actions supporting output propagation. Local actions are supported as well. The README does not describe them as three types, so treat this as the set of action kinds wrkflw handles rather than an official taxonomy.

Is wrkflw free to use?

Yes. The project is MIT licensed, the Cargo.toml declares license = "MIT", and the source is published on GitHub and crates.io. MIT permits commercial and private use, and redistribution requires keeping the copyright and permission notice.

What are the disadvantages of using wrkflw?

The README's "Not yet supported" section lists service containers that are parsed but never started, concurrency groups that are parsed but not enforced, Windows and macOS runners that are silently mapped to container images, and private repositories for remote uses: references. It also states that GitHub encrypted secrets and fine-grained permissions are not supported.

Official sources

  1. bahdotsh/wrkflw on GitHub
  2. License: MIT
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/bahdotsh-wrkflw.svg)](https://hysenlabs.com/projects/bahdotsh-wrkflw)