fix: replace dual process substitution with single pipe for output capture#548
Open
dghermanMI wants to merge 2 commits intoOP5dev:mainfrom
Open
fix: replace dual process substitution with single pipe for output capture#548dghermanMI wants to merge 2 commits intoOP5dev:mainfrom
dghermanMI wants to merge 2 commits intoOP5dev:mainfrom
Conversation
…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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
The current output capture pattern uses two process substitutions writing to the same file:
This creates two independent
teeprocesses that both opentf.console.txtfor 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 corruptedtf.console.txtfiles.When
tf.console.txtis empty, the summary extraction defaults to "View output." with an empty PR comment body:Fix
Replace all 5 occurrences with a single pipe:
This merges stderr into stdout and pipes through a single
teeprocess — no race, no file truncation. Exit codes are preserved correctly because GitHub Actions runs bash with-o pipefailby default.How we discovered this
We run
tf-via-prinside 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 inputwarning from bash during the post-processing command substitution, indicating corrupted interleaved writes totf.console.txt:Testing
We tested the fix across two environments with different runner configurations:
container: node:lts-alpineThe 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):
After the fix:
Notes
action.ymlonly)2>&1merge 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 unchangedpipefail(enabled by default in GHA's bash shell) preserves terraform's exit code through the pipe