Skip to content

ci: pass workflow inputs to run steps via env vars - #8860

Merged
gustavodiaz7722 merged 1 commit into
eksctl-io:mainfrom
sapphirew:ci/pass-workflow-inputs-via-env
Sep 11, 2026
Merged

ci: pass workflow inputs to run steps via env vars#8860
gustavodiaz7722 merged 1 commit into
eksctl-io:mainfrom
sapphirew:ci/pass-workflow-inputs-via-env

Conversation

@sapphirew

Copy link
Copy Markdown
Collaborator

Description

${{ }} expressions are substituted into a run: block as raw text before bash parses it, so an interpolated value has to survive shell parsing rather than simply being used as data. Two workflow steps relied on that. This passes the values through env: and references them as quoted variables instead.

release-drafter.yaml — "Copy release notes from Draft"

tag_name=${{ steps.draft.outputs.tag_name }}
echo "${{ steps.draft.outputs.body }}" > docs/release_notes/${tag_name:1}.md

steps.draft.outputs.body is a rendered list of merged PR titles (change-template: '- $TITLE (#$NUMBER)'), so characters that are meaningful to bash — ", $, (, ), ; — land in the script itself. change-title-escapes in .github/release-drafter.yml is set to '\<*_&#@`', which does not cover them, so a title containing a double quote produces a broken script and a garbled or missing release-notes file.

Now:

        env:
          TAG_NAME: ${{ steps.draft.outputs.tag_name }}
          RELEASE_BODY: ${{ steps.draft.outputs.body }}
        run: |
          printf '%s\n' "$RELEASE_BODY" > "docs/release_notes/${TAG_NAME#v}.md"
  • printf '%s\n' rather than echo, so backslash sequences in the notes are written verbatim.
  • ${TAG_NAME#v} replaces ${tag_name:1} — same result for a vX.Y.Z tag, but it strips the prefix by name rather than by offset.
  • The output path is quoted.

Also set persist-credentials: false on this job's checkout. The following Upsert pull request step authenticates with its own token: input — create-pull-request configures the git extraheader itself and unsets any persisted one — so nothing in the job needs the credential checkout otherwise leaves behind in the git config.

homebrew-update.yml — "Create update issue"

The request body was a single-quoted JSON literal with ${{ }} values pasted in, which emits malformed JSON if any value contains a quote or backslash. It is now built with jq -n --arg from env: values, which handles the escaping, and piped to curl with -d @-. Added -sSf so a failed API call fails the step instead of passing silently, and moved the token into env: so it is not part of the script text.

Checklist

  • Added tests that cover your change (n/a — GitHub Actions workflow config)
  • Added/modified documentation as required (n/a)
  • Manually tested
  • Made sure the title of the PR is a good description that can go into the release notes
  • (Core team) Added labels for change area (e.g. area/nodegroup) and target version (e.g. version/0.12.0)

Testing

Ran both rewritten run: bodies locally with representative env values.

release-drafter.yaml — a body containing - evil"; whoami; echo " (#124) plus \n, $VAR and backticks writes to docs/release_notes/0.220.0.md with every character intact and no expansion:

# Release v0.220.0

- normal PR title (#123)
- evil"; whoami; echo " (#124)
- backslash \n and $VAR and `cmd`

Under the previous version the same body produced a truncated file. For an ordinary body the output is byte-identical.

homebrew-update.ymljq output for TAG_NAME=v0.220.0 matches the previous payload exactly:

{
  "title": "Update eksctl to v0.220.0",
  "body": "New eksctl release v0.220.0 is available.\n\nRelease URL: https://github.com/eksctl-io/eksctl/releases/tag/v0.220.0",
  "labels": ["eksctl-update"]
}

Both files parse as valid YAML.

`${{ }}` expressions are substituted into a `run:` block as raw text
before bash parses it, so any interpolated value has to survive shell
parsing rather than just being used as data. Two steps relied on that.

release-drafter.yaml, "Copy release notes from Draft": the drafter body
is a rendered list of merged PR titles, so characters that are
meaningful to bash (`"`, `$`, `(`, `)`, `;`) end up in the script
itself. `change-title-escapes` in .github/release-drafter.yml escapes
`\<*_&#@` and a backtick, which does not cover those. Move `tag_name`
and `body` into `env:` and reference them as `"$TAG_NAME"` /
`"$RELEASE_BODY"`, and use `printf '%s\n'` so backslashes in the notes
are written verbatim. `${TAG_NAME#v}` replaces `${tag_name:1}` — same
result for a `vX.Y.Z` tag, but it strips the prefix by name instead of
by offset.

Also set `persist-credentials: false` on the checkout in that job. The
following "Upsert pull request" step authenticates with its own `token:`
input (create-pull-request configures the git extraheader itself, and
unsets any persisted one), so nothing in the job needs the credential
that checkout otherwise leaves in the git config.

homebrew-update.yml: the request body was a single-quoted JSON literal
with `${{ }}` values pasted in, which breaks if a value contains a quote
or backslash and would emit malformed JSON. Build it with `jq -n --arg`
from `env:` values instead, which handles the escaping, and pipe it to
curl via `-d @-`. Added `-sSf` so an API error fails the step instead of
passing silently. The token also moves to `env:` so it is not part of
the script text.

No behaviour change for well-formed inputs; verified the rendered
filename, note contents, and JSON payload are byte-identical for a
normal `v0.220.0` release.
@gustavodiaz7722
gustavodiaz7722 merged commit b1854da into eksctl-io:main Sep 11, 2026
8 of 10 checks passed
@sapphirew
sapphirew deleted the ci/pass-workflow-inputs-via-env branch September 11, 2026 22:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants