logs.gokuls.in

2 pull requests merged across 1 repo

bahdotsh/wrkflw

  • Composite actions that declare outputs: with value: expressions (e.g. ${{ steps.build-msg.outputs.msg }}) now correctly propagate those values back to the calling workflow
  • ${{ steps.<composite-id>.outputs.<key> }} previously resolved to empty string — now resolves to the evaluated value
  • Output propagation also runs on the early-return failure path so partial outputs are available when a composite step fails

Root cause

execute_composite_action() was running all internal composite steps and tracking their outputs in composite_step_outputs, but never reading the action's outputs: YAML section to evaluate and return those values. The last wire was simply never connected.

Approach

Added propagate_composite_outputs() which:

1. Reads the action's outputs: mapping from the parsed YAML

2. Evaluates each value: expression against the composite's internal step context

3. Writes results to the caller's GITHUB_OUTPUT file

The existing apply_step_environment_updates pipeline picks them up naturally — zero changes to StepResult or process_outcome.

Test plan

  • New unit test: propagate_composite_outputs_writes_to_github_output_file — verifies expression evaluation and file write
  • New unit test: propagate_composite_outputs_no_outputs_section_is_noop — verifies no-op when action has no outputs
  • All 335 executor tests pass (333 existing + 2 new)
  • Manual verification: ${{ steps.full-greet.outputs.message }} resolves to "Hi, World!" (was empty before)
  • Root cause: EmulationRuntime::run_container ignored the volumes parameter and silently rerouted the working directory to GITHUB_WORKSPACE (= the real project directory) when the container path didn't exist on the host. handle_upload_artifact still used ctx.working_dir (the per-job tempdir). Two workspaces diverged, so upload-artifact reported "No files found matching pattern" for any file a prior run: step had written. Same class of bug lived in SecureEmulationRuntime, which additionally copied files into a private sandbox workspace that the caller never saw.
  • Fix: Added container::resolve_host_working_dir — a pure function that rebases a container-visible path onto its host-side volume source via the longest component-boundary prefix match (uses Path::strip_prefix, so /github/workspace-foo is not matched by a /github/workspace mount). Both emulation runtimes now honor volumes, matching the bind-mount semantics docker/podman already provide. The sandbox runs commands in-place in the caller's workspace; the copy-files-to-a-private-directory layer was not a meaningful security boundary (validation comes from the command whitelist, dangerous-pattern regexes, and env-var filtering, all preserved) and it was the mechanism that broke the run-step/artifact-handler workspace invariant for secure emulation.
  • No back-compat shims: uncovered container paths now fail loudly with "not covered by any volume mount" instead of silently running in the wrong place. No GITHUB_WORKSPACE fallback, no implicit project copy.

Reproduces for any workflow with actions/upload-artifact + actions/download-artifact under --runtime emulation without an explicit actions/checkout.

Closes #88.

Test plan

  • New end-to-end regression test (run_step_upload_download_artifact_roundtrip_emulation) drives a real EmulationRuntime through run → upload-artifact → download-artifact and asserts the payload round-trips byte-for-byte. Verified it fails on main before the fix with the exact "No files found matching pattern" symptom.
  • 6 unit tests for resolve_host_working_dir: exact match, sub-path, longest-prefix-wins, no-match, empty volumes, component-boundary (the /github/workspace-foo edge case).
  • 2 new tests for SecureEmulationRuntime: volume rebase produces the correct host cwd, and a missing volume mapping hard-errors with a clear message.
  • Existing tests unchanged and still passing: 14/14 runtime, 333/333 executor. Deleted the orphaned sandbox::test_file_filtering test that only covered the removed copy path.
  • Manual smoke against a real workflow with upload-artifact/download-artifact under --runtime emulation on the reviewer's machine (the automated regression test covers the same code path, but a hands-on run is a good final sanity check).

Notes

  • Surfaces a pre-existing, orthogonal wrkflw quirk: ArtifactStore::upload feeds user patterns straight to the glob crate, which doesn't match files under a trailing (you need /*). The regression test uses an exact filename to dodge this; worth filing a follow-up for upload-artifact glob compatibility.
  • secure_emulation's allowed_read_paths / allowed_write_paths config fields are now unused in this PR but kept as public API for potential future use.