fix: [CI-24318]: preserve multiline action output values - #28
Open
abhijeet-harness-io wants to merge 1 commit into
Open
fix: [CI-24318]: preserve multiline action output values#28abhijeet-harness-io wants to merge 1 commit into
abhijeet-harness-io wants to merge 1 commit into
Conversation
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.
Summary
Fixes CI-24318. GitHub Actions whose declared outputs contain multiline values (for example the
changelogfrommathieudutour/github-tag-action@v6.2when the repo has multiple commits since the last tag) currently lose all lines after the first when the plugin copies outputs into Harness's\$DRONE_OUTPUTfile. Downstream Harness steps that reference those outputs then see truncated or empty values, and features like automated GitHub Release creation fail.Root cause
utils/workflow.gosetOutputVariablesgenerated a singleecho "key=value" > \$DRONE_OUTPUTcommand per plugin run:When any value spans multiple lines, only the first line ends up on the
key=valueline. Remaining lines become bare lines with no=. Harness's delegate output reader (github.com/harness/godotenv) parses the file line by line askey=valueand drops the bare lines, leaving the pipeline with a truncated value.Fix
Emit each output as a single line in the form
key=__B64__<base64 payload>. Harness's delegate reader already has a__B64__prefix escape hatch (seelite-engine/pipeline/runtime/common.go) that base64-decodes the payload transparently before handing the environment map to the pipeline. Multiline values, values containing double quotes, dollar signs, backticks, and backslashes all round-trip cleanly because the payload is opaque to godotenv.Per output variable, the writer now emits:
Every line has a reason:
IFS= read -r -d '' __val <<'MARKER'. Ingests the substituted value into a bash variable. Quoted marker means bash performs no expansion on the value. Top-level heredoc (not inside\$(cat <<'EOF' ... EOF)) avoids a bash 3.2 parser bug where a"inside the heredoc body confuses the outer\$(...)scanner. That bug is real on macOS's default shell and could surface in any runsOn image that ships bash 3.2.|| true.read -d ''returns non-zero when it never finds the null delimiter, which is always the case here. The variable is still populated.__val="\${__val%\$'\n'}". A heredoc appends a trailing\nafter the last content line.readcaptures that trailing\n. This one line strips it so values do not gain a phantom trailing newline.printf '%s' "\$__val" | base64 | tr -d '\n'.printf '%s'avoids adding its own newline.tr -d '\n'removes GNU base64's default 76-column line wrapping. Portable across coreutils and BusyBox unlikebase64 -w 0.echo "<name>=__B64__\$__b64" >> outputFile. One line per variable, appended after the file has been truncated at the top of the writer.A random 8-byte hex EOF marker per plugin run makes marker collisions with value content vanishingly unlikely.
Reader side needs no change. The
__B64__decode logic already exists.Why not the GitHub-style heredoc format
The obvious first design was to use
name<<MARKER ... MARKERblocks (the same format GitHub Actions itself uses in\$GITHUB_OUTPUT). I tried it. It writes a well-formed file but Harness's delegate reader isgodotenv, which understands onlykey=valueand quoted string forms. On the first linename<<MARKERgodotenv logsCan't separate key from valueand abandons the whole file, so every declared output resolves to null downstream, not just multiline ones. Base64 keeps every value on one line, sidesteps that entirely, and uses an escape hatch the reader already supports.Test plan
Added to
utils/workflow_test.go. All 5 tests pass locally withgo test ./utils/....TestSetOutputVariables_MultilineValueRoundTrip. 3 line changelog and single line tag both round-trip through the generated bash and a mock of the delegate's__B64__decode.TestSetOutputVariables_MultilineValuePreservesBlankLines. Values with blank lines and consecutive blank lines survive.TestSetOutputVariables_ValuesWithSpecialChars. Values with",\$, backticks, and backslashes survive.TestSetOutputVariables. Writer emits per variable heredoc marker,__B64__prefix,>>append, truncation of file at the top of the writer, and gates the step off when there are no output variables.TestCreateWorkflowFile. Overall workflow YAML shape still valid.End-to-end verification on Harness Cloud
Two pipelines with identical everything except the plugin image. Same input action (a deterministic composite that emits a fixed 3 line changelog and a single line tag).
plugins/github-actions:latestchangelogin downstream stepwc -lonchangelognew_tagBuggy run resolved
<+steps.tag.output.outputVariables.changelog>to just* chore: bump internal dep C 57c830a. Fixed run resolved it to all three commit lines. New tag survived in both runs because it happens to be single line.Relation to open PR #27
Independent bug, no overlap. PR #27 touches
docker/Dockerfile.linux.amd64,plugin.go,utils/parse.go, andutils/parse_test.go. This PR only touchesutils/workflow.goandutils/workflow_test.go. Two fixes can merge in either order.