From a34eae4b5d3789b1a8e53b015a2af5627f42796f Mon Sep 17 00:00:00 2001 From: Sion Kang Date: Wed, 22 Jul 2026 14:53:47 +0900 Subject: [PATCH 1/3] ci: pass quote-capable outputs to the backport workflow shell via env vars The "Save result to file" step interpolated the milestones result JSON inline into a single-quoted shell string, so any commit message containing an apostrophe broke the script (syntax error near unexpected token) and silently dropped the backport -- first triggered by #12985, whose title contains "aiohttp's". This is the third instance of the same defect class in this workflow (8bac28817, #7405), and #7405 fixed only the jq-argument site while leaving this step and the author/labels interpolations behind. Route every quote-capable value (result JSON, author, author email, labels) through env vars, matching the pattern #7405 established for the commit message, and write the result file with printf instead of echo. Reproduced the exact failure payload against both patterns: the old one fails identically, the new one round-trips the JSON intact. Past apostrophe-titled PRs never hit this because their milestones were absent (or set only after merge), so the vulnerable payload stayed empty. Co-Authored-By: Claude Opus 4.8 --- .github/workflows/backport.yml | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml index c06b771adb4..d8d4dcdfdff 100644 --- a/.github/workflows/backport.yml +++ b/.github/workflows/backport.yml @@ -75,6 +75,9 @@ jobs: env: GH_TOKEN: ${{ github.token }} COMMIT_MESSAGE: ${{ steps.commit.outputs.commit_message }} + COMMIT_AUTHOR: ${{ steps.commit.outputs.author }} + COMMIT_AUTHOR_EMAIL: ${{ steps.commit.outputs.author_email }} + COMMIT_LABELS: ${{ steps.commit.outputs.labels }} run: | target_milestone=$(gh pr view ${{ steps.commit.outputs.pr_number }} --json milestone --jq .milestone.title) @@ -114,9 +117,9 @@ jobs: --arg message "$COMMIT_MESSAGE" \ --arg pr "${{ steps.commit.outputs.pr_number }}" \ --arg release "${{ steps.commit.outputs.latest_release }}" \ - --arg author "${{ steps.commit.outputs.author }}" \ - --arg email "${{ steps.commit.outputs.author_email }}" \ - --arg labels "${{ steps.commit.outputs.labels }}" \ + --arg author "$COMMIT_AUTHOR" \ + --arg email "$COMMIT_AUTHOR_EMAIL" \ + --arg labels "$COMMIT_LABELS" \ '. += [{ commit: $commit, target_branch: $branch, @@ -132,9 +135,14 @@ jobs: echo "result=$result" >> $GITHUB_OUTPUT - name: Save result to file if: ${{ steps.commit.outputs.pr_number }} + env: + # The result JSON embeds the commit message and author, which may + # contain quotes -- inline ${{ }} interpolation into the shell + # breaks on them (e.g. an apostrophe in the PR title). + MILESTONES_RESULT: ${{ steps.milestones.outputs.result }} run: | mkdir -p /tmp/backport-results - echo '${{ steps.milestones.outputs.result }}' > /tmp/backport-results/${{ matrix.commits }}.json + printf '%s' "$MILESTONES_RESULT" > /tmp/backport-results/${{ matrix.commits }}.json - name: Upload result artifact if: ${{ steps.commit.outputs.pr_number }} uses: actions/upload-artifact@v4 @@ -185,9 +193,11 @@ jobs: - name: Cherry-pick env: COMMIT_MESSAGE: ${{ matrix.commit_message }} + COMMIT_AUTHOR: ${{ matrix.author }} + COMMIT_AUTHOR_EMAIL: ${{ matrix.author_email }} run: | - git config user.name "${{ matrix.author }}" - git config user.email "${{ matrix.author_email }}" + git config user.name "$COMMIT_AUTHOR" + git config user.email "$COMMIT_AUTHOR_EMAIL" target_commit="${{ matrix.commit }}" git fetch origin main "$target_commit" git cherry-pick --strategy=recursive --strategy-option=theirs "$target_commit" From f8a902ae14c68e243e202a053ac2730e696e2969 Mon Sep 17 00:00:00 2001 From: Sion Kang Date: Wed, 22 Jul 2026 14:54:06 +0900 Subject: [PATCH 2/3] docs: add changelog for #13021 Co-Authored-By: Claude Opus 4.8 --- changes/13021.fix.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 changes/13021.fix.md diff --git a/changes/13021.fix.md b/changes/13021.fix.md new file mode 100644 index 00000000000..5feabd5f949 --- /dev/null +++ b/changes/13021.fix.md @@ -0,0 +1 @@ +Fix the backport workflow silently dropping backports for commits whose message contains a quote character: the milestones result JSON (and author/label values) are now passed to the shell via environment variables instead of inline template interpolation. From d7be8965d1b37abf292a76fa17e310aee2af9bab Mon Sep 17 00:00:00 2001 From: Sion Kang Date: Wed, 22 Jul 2026 14:57:59 +0900 Subject: [PATCH 3/3] ci: name the env var after its payload (backport target entries) Co-Authored-By: Claude Opus 4.8 --- .github/workflows/backport.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml index d8d4dcdfdff..f2ba6831754 100644 --- a/.github/workflows/backport.yml +++ b/.github/workflows/backport.yml @@ -136,13 +136,14 @@ jobs: - name: Save result to file if: ${{ steps.commit.outputs.pr_number }} env: - # The result JSON embeds the commit message and author, which may + # Per-target backport entries (commit, target branch, commit + # message, author, ...). The embedded commit message and author may # contain quotes -- inline ${{ }} interpolation into the shell # breaks on them (e.g. an apostrophe in the PR title). - MILESTONES_RESULT: ${{ steps.milestones.outputs.result }} + BACKPORT_TARGETS_JSON: ${{ steps.milestones.outputs.result }} run: | mkdir -p /tmp/backport-results - printf '%s' "$MILESTONES_RESULT" > /tmp/backport-results/${{ matrix.commits }}.json + printf '%s' "$BACKPORT_TARGETS_JSON" > /tmp/backport-results/${{ matrix.commits }}.json - name: Upload result artifact if: ${{ steps.commit.outputs.pr_number }} uses: actions/upload-artifact@v4