Skip to content

Multi-line expressions unsupported in engine.env values #30204

@jeffhandley

Description

@jeffhandley

When engine.env values are written as multi-line YAML block scalars (e.g. >- with extra-indented continuation lines), goccy/go-yaml preserves the embedded newlines in the parsed string. Both emission sites in engine_helpers.go emitted env vars as single-line key: value pairs, so those newlines broke the compiled .lock.yml. PR #18017 added engine.env support but didn't account for this case.

Frontmatter Content

engine:
  id: copilot
  env:
    COPILOT_GITHUB_TOKEN: >-
      ${{ secrets.GH_AW_PAT_1 != '' && secrets.GH_AW_PAT_1 ||
          secrets.GH_AW_PAT_2 != '' && secrets.GH_AW_PAT_2 ||
          secrets.GH_AW_PAT_3 != '' && secrets.GH_AW_PAT_3 ||
          secrets.GH_AW_PAT_4 != '' && secrets.GH_AW_PAT_4 ||
          secrets.GH_AW_PAT_5 }}

Expected Result

engine:
  env:
    COPILOT_GITHUB_TOKEN: >-
      ${{ secrets.GH_AW_PAT_1 != '' && secrets.GH_AW_PAT_1 ||
          secrets.GH_AW_PAT_2 != '' && secrets.GH_AW_PAT_2 ||
          secrets.GH_AW_PAT_3 != '' && secrets.GH_AW_PAT_3 ||
          secrets.GH_AW_PAT_4 != '' && secrets.GH_AW_PAT_4 ||
          secrets.GH_AW_PAT_5 }}

Actual Result

generated lock file is not valid YAML: [166:5] non-map value is specified
  163 |         run: bash "${RUNNER_TEMP}/gh-aw/actions/validate_multi_secret.sh" COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
  164 |         env:
  165 |           COPILOT_GITHUB_TOKEN: ${{ secrets.GH_AW_PAT_1 != '' && secrets.GH_AW_PAT_1 ||
> 166 |     secrets.GH_AW_PAT_2 != '' && secrets.GH_AW_PAT_2 ||
            ^
  167 |     secrets.GH_AW_PAT_3 != '' && secrets.GH_AW_PAT_3 ||
  168 |     secrets.GH_AW_PAT_4 != '' && secrets.GH_AW_PAT_4 ||
  169 |     secrets.GH_AW_PAT_5 }}
  170 |
✗ compilation failed

Workaround

engine:
  id: copilot
  env:
    # We cannot use line breaks in this expression as it leads to a syntax error in the compiled workflow
    COPILOT_GITHUB_TOKEN: ${{ secrets.GH_AW_PAT_1 != '' && secrets.GH_AW_PAT_1 || secrets.GH_AW_PAT_2 != '' && secrets.GH_AW_PAT_2 || secrets.GH_AW_PAT_3 != '' && secrets.GH_AW_PAT_3 || secrets.GH_AW_PAT_4 != '' && secrets.GH_AW_PAT_4 || secrets.GH_AW_PAT_5 }}

Prototype Fix

I have a branch and a PR within my fork that should resolve this issue. The changes were authored via Copilot Coding Agent, human- and ai-reviewed, with tests running in a CCA session.

Changes

  • appendEnvVarLine helper — new shared function replacing direct fmt.Sprintf at both env var emission sites. Trims trailing \n (from | block scalars), then emits single-line values inline via yamlStringValue (unchanged path) or multi-line values as a YAML literal block scalar (|).
  • FormatStepWithCommandAndEnv and GenerateMultiSecretValidationStep — both now delegate to appendEnvVarLine.

Example

A five-PAT precedence chain written as a multi-line expression in the workflow frontmatter:

engine:
  id: copilot
  env:
    COPILOT_GITHUB_TOKEN: >-
      ${{ secrets.GH_AW_PAT_1 != '' && secrets.GH_AW_PAT_1 ||
          secrets.GH_AW_PAT_2 != '' && secrets.GH_AW_PAT_2 ||
          secrets.GH_AW_PAT_3 != '' && secrets.GH_AW_PAT_3 ||
          secrets.GH_AW_PAT_4 != '' && secrets.GH_AW_PAT_4 ||
          secrets.GH_AW_PAT_5 }}

The extra-indented continuation lines are treated as "more-indented" by the YAML spec, so the newlines are not folded to spaces — they land in the parsed string. The compiled output now emits this correctly as a literal block scalar:

  env:
    COPILOT_GITHUB_TOKEN: |
      ${{ secrets.GH_AW_PAT_1 != '' && secrets.GH_AW_PAT_1 ||
          secrets.GH_AW_PAT_2 != '' && secrets.GH_AW_PAT_2 ||
          ...
          secrets.GH_AW_PAT_5 }}

Metadata

Metadata

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions