Files
wrkflw/crates/executor
Gokul e4752efa8e feat(evaluator): make toJSON(secrets) return secrets object (#100)
It turns out that toJSON(secrets) has been returning "null" this
whole time — same disease as env, steps, needs, and github before
it. The evaluator had no bare-`secrets` branch in
ExpressionContext::resolve, so resolving the identifier fell
through to the catch-all `_ => Null` and toJSON dutifully
serialised that. Fourth in a series after #96/#97/#98/#99.

The fix is the simplest of the bunch. secrets_context is already
a flat &HashMap<String, String> holding exactly the shape real
GHA exposes as the `secrets` context — no prefix strip, no
exclusion list (unlike #99's github arm), no nested outputs
sub-object (unlike steps/needs). Clone the entries into
ExprValue::String and wrap in ExprValue::Object. Done.

Values are returned in plaintext by design. That matches real
GHA — `toJSON(secrets)` there is not auto-redacted either, and
the common `fromJSON(toJSON(secrets))` pipe-through-an-action
pattern depends on the exact original values surviving the
round-trip. Masking stays where it belongs: the log boundary,
via wrkflw_secrets::SecretMasker, already wired up in engine.rs.

Pulling the masker into the evaluator would (a) break the
fromJSON round-trip, (b) diverge from GHA semantics, and (c)
duplicate a concern that already has one correct home. Please
don't do that.

Tests mirror the #96–#99 suites: populated secrets, empty
context, sorted keys, special-character values (quotes,
backslashes, PEM-style newlines), fromJSON(toJSON(secrets))
round-trip, bare-secrets truthiness, a regression guard that the
bare arm doesn't shadow the existing dotted-access arm, and a
plaintext-values test that pins the no-masking-here decision so
any future switch is deliberate rather than silent.

While at it, drop `secrets` from the lingering
`TODO: support other bare contexts` comment. Only `matrix`
remains.
2026-04-18 22:20:55 +05:30
..

wrkflw-executor

The execution engine that runs GitHub Actions workflows locally (Docker, Podman, or emulation).

  • Job graph execution with needs ordering and parallel independent jobs
  • Docker/Podman container steps and emulation mode
  • Run individual jobs via target_job / --job flag
  • GitHub Actions environment file support (GITHUB_OUTPUT, GITHUB_ENV, GITHUB_PATH, GITHUB_STEP_SUMMARY) with read-back
  • Docker-based action resolution (container, JavaScript, composite, local)
  • Job-level container: directive support
  • Used by: wrkflw CLI and TUI

API sketch

use wrkflw_executor::{execute_workflow, ExecutionConfig, RuntimeType};

let cfg = ExecutionConfig {
    runtime: RuntimeType::Docker,
    verbose: true,
    preserve_containers_on_failure: false,
    target_job: Some("build".to_string()), // run a single job
};

let workflow_path = std::path::Path::new(".github/workflows/ci.yml");
let result = execute_workflow(workflow_path, cfg).await?;
println!("workflow status: {:?}", result.summary_status);

Prefer using the wrkflw binary for a complete UX across validation, execution, and logs.