From 47846136bfec96106c3495f6dd28e6fb78de96f0 Mon Sep 17 00:00:00 2001 From: Yicong Huang <17627829+Yicong-Huang@users.noreply.github.com> Date: Sat, 2 May 2026 15:50:24 -0700 Subject: [PATCH 1/2] fix(ci): place "(backported from commit X)" before trailers in backport message Direct backports currently append the "(backported from commit X)" note at the very end of the commit message, which pushes it after the Co-Authored-By trailer block. Git's trailer parsing treats the message as having no trailers in that case, so tools that look at trailers (tags, hooks, anything calling `git interpret-trailers --parse`) see zero authors. Compose the message so the body comes first, then the "(backported from commit X)" note as its own paragraph, then the trailer block (Co-Authored-By, Signed-off-by, etc.) at the very end. A small Python step splits the original message at the trailer boundary using the standard `Key:\s` heuristic, then reassembles. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/direct-backport-push.yml | 32 ++++++++++++++++++++-- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/.github/workflows/direct-backport-push.yml b/.github/workflows/direct-backport-push.yml index c9fc6b20a12..743514ce8a9 100644 --- a/.github/workflows/direct-backport-push.yml +++ b/.github/workflows/direct-backport-push.yml @@ -221,8 +221,34 @@ jobs: git checkout -B "${TARGET_BRANCH}" "origin/${TARGET_BRANCH}" git cherry-pick --no-commit "${MERGE_SHA}" - { - printf '%s\n\n(backported from commit %s)\n' "${merge_message}" "${MERGE_SHA}" - } | git commit -F - --author="${original_author}" + # Compose the final commit message. The "(backported from commit X)" + # note goes between the message body and the trailer block (the + # trailing run of `Key: value` lines such as Co-Authored-By and + # Signed-off-by) so trailers stay contiguous at the bottom of the + # message — that's where git itself parses them. + new_message=$( + printf '%s' "${merge_message}" | \ + python3 -c ' +import re, sys +sha = sys.argv[1] +msg = sys.stdin.read().rstrip("\n") +lines = msg.split("\n") +trailer_re = re.compile(r"^[A-Za-z][A-Za-z0-9-]*:\s") +trailer_start = len(lines) +i = len(lines) - 1 +while i >= 0 and trailer_re.match(lines[i]): + trailer_start = i + i -= 1 +backport = f"(backported from commit {sha})" +if trailer_start == len(lines): + print(msg + "\n\n" + backport) +else: + body = "\n".join(lines[:trailer_start]).rstrip("\n") + trailers = "\n".join(lines[trailer_start:]) + print(body + "\n\n" + backport + "\n\n" + trailers) +' "${MERGE_SHA}" + ) + + printf '%s\n' "${new_message}" | git commit -F - --author="${original_author}" git push origin "HEAD:${TARGET_BRANCH}" From 48e63b02531d694a8ce998ef81e006e7fd80c6fc Mon Sep 17 00:00:00 2001 From: Yicong Huang <17627829+Yicong-Huang@users.noreply.github.com> Date: Sat, 2 May 2026 16:00:28 -0700 Subject: [PATCH 2/2] fix(ci): tighten trailer detection so subject lines are never mis-classified The first version scanned from the end and treated any contiguous run of `Key: value` lines as the trailer block. That mis-classifies a Conventional Commits subject like "docs: typo fix" (when the message has only a subject and no body) as a one-line trailer block, putting the backport note above the subject. Tighten the detection: a trailer block exists only if there is a blank line in the message and every line after the LAST blank line matches the trailer format. With that rule a subject without a body falls through to the no-trailer-block branch and the note is appended after the subject. Verified locally on five message shapes: - subject only, - subject + body without trailers, - subject + body + single trailer, - subject + body + multiple trailers, - body containing a `Word:` line that is not a trailer. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/direct-backport-push.yml | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/.github/workflows/direct-backport-push.yml b/.github/workflows/direct-backport-push.yml index 743514ce8a9..215a888996a 100644 --- a/.github/workflows/direct-backport-push.yml +++ b/.github/workflows/direct-backport-push.yml @@ -226,6 +226,12 @@ jobs: # trailing run of `Key: value` lines such as Co-Authored-By and # Signed-off-by) so trailers stay contiguous at the bottom of the # message — that's where git itself parses them. + # + # The trailer block, by git convention, is the run of `Key: value` + # lines after the LAST blank line in the message, and only counts + # if EVERY line after that blank line is in trailer format. This + # avoids mis-detecting a Conventional Commits subject like + # "feat: foo" or a body line like "References:" as a trailer. new_message=$( printf '%s' "${merge_message}" | \ python3 -c ' @@ -234,11 +240,19 @@ sha = sys.argv[1] msg = sys.stdin.read().rstrip("\n") lines = msg.split("\n") trailer_re = re.compile(r"^[A-Za-z][A-Za-z0-9-]*:\s") + +last_blank = -1 +for idx in range(len(lines) - 1, -1, -1): + if lines[idx] == "": + last_blank = idx + break + trailer_start = len(lines) -i = len(lines) - 1 -while i >= 0 and trailer_re.match(lines[i]): - trailer_start = i - i -= 1 +if last_blank != -1: + candidate = lines[last_blank + 1:] + if candidate and all(trailer_re.match(l) for l in candidate): + trailer_start = last_blank + 1 + backport = f"(backported from commit {sha})" if trailer_start == len(lines): print(msg + "\n\n" + backport)