Skip to content

fix: replace dual process substitution with single pipe for output capture#548

Open
dghermanMI wants to merge 2 commits intoOP5dev:mainfrom
dghermanMI:fix/tee-process-substitution-race
Open

fix: replace dual process substitution with single pipe for output capture#548
dghermanMI wants to merge 2 commits intoOP5dev:mainfrom
dghermanMI:fix/tee-process-substitution-race

Conversation

@dghermanMI
Copy link
Copy Markdown

Problem

The current output capture pattern uses two process substitutions writing to the same file:

terraform plan ... 2> >(tee tf.console.txt) > >(tee tf.console.txt)

This creates two independent tee processes that both open tf.console.txt for writing (truncating). Whichever process opens the file last truncates the other's output. This is a race condition — the result depends on OS-level process scheduling, and it intermittently produces empty or corrupted tf.console.txt files.

When tf.console.txt is empty, the summary extraction defaults to "View output." with an empty PR comment body:

summary=$(awk '/^(Error:|Plan:|Apply complete!|No changes.|Success)/ ...' tf.console.txt)
# empty file → "View output."

Fix

Replace all 5 occurrences with a single pipe:

terraform plan ... 2>&1 | tee tf.console.txt

This merges stderr into stdout and pipes through a single tee process — no race, no file truncation. Exit codes are preserved correctly because GitHub Actions runs bash with -o pipefail by default.

How we discovered this

We run tf-via-pr inside Alpine containers on ARC (Actions Runner Controller) Kubernetes runners in a matrix strategy across 7 AWS regions. In our test runs, some regions produced full plan output while others consistently showed "View output." with empty comment bodies — despite terraform plan completing successfully (exit code 2, changes detected).

The CI logs confirmed the root cause — a null byte in input warning from bash during the post-processing command substitution, indicating corrupted interleaved writes to tf.console.txt:

/__w/_temp/ec67bbac-....sh: line 23: warning: command substitution: ignored null byte in input

Testing

We tested the fix across two environments with different runner configurations:

Environment Runner type Jobs Plan comments Empty outputs
AWS infra repo (ARC K8s, Alpine container) Self-hosted ARC + container: node:lts-alpine 7 7 0
GCP infra repo (ARC K8s, no container) Self-hosted ARC 139 139 0

The GCP test modified a shared Terraform module referenced by 139 root modules — every plan comment showed "Diff of 1 change." with full output. Zero empty comments across both environments.

Before the fix (same 7-region matrix on ARC):

  • Region A: "View output." — 495 chars, empty body
  • Region B: "View output." — 495 chars, empty body
  • Region C: "Diff of 255 changes." — 66K chars (race condition happened to preserve stdout)

After the fix:

  • Region A: "Diff of 170 changes." — 66K chars
  • Region B: "Diff of 85 changes." — 53K chars
  • Region C: "Diff of 255 changes." — 66K chars

Notes

  • This is a one-line change in 5 places (action.yml only)
  • The 2>&1 merge means stderr and stdout are interleaved in the output file rather than separated, but the prior pattern also wrote both to the same file — so the observable behavior is unchanged
  • No change in exit code handling — pipefail (enabled by default in GHA's bash shell) preserves terraform's exit code through the pipe

…pture

The current pattern uses two process substitutions writing to the same file:
  2> >(tee tf.console.txt) > >(tee tf.console.txt)

Two independent tee processes both truncate tf.console.txt on open,
creating a race condition that intermittently produces empty output.
Downstream summary extraction then defaults to "View output." with
an empty PR comment body.

Replaced with:
  2>&1 | tee tf.console.txt

Single pipe, single tee, no race. Exit codes preserved via pipefail
(enabled by default in GitHub Actions bash shell).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@dghermanMI dghermanMI marked this pull request as ready for review April 1, 2026 20:28
@dghermanMI dghermanMI requested a review from rdhar as a code owner April 1, 2026 20:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants