Skip to content

Parse Co-authored-by trailers in commit messages#243

Draft
kraftbj wants to merge 4 commits into
WordPress:trunkfrom
kraftbj:coauthored-by-trailers
Draft

Parse Co-authored-by trailers in commit messages#243
kraftbj wants to merge 4 commits into
WordPress:trunkfrom
kraftbj:coauthored-by-trailers

Conversation

@kraftbj

@kraftbj kraftbj commented Apr 16, 2026

Copy link
Copy Markdown

What?

Parses Co-authored-by: trailers from PR commit messages so co-authors show up in the props output.

Fixes #86

Why?

Today the action only credits the direct commit author; any Co-authored-by: trailers in the commit message are ignored. That silently drops contributors from both the SVN props line and the Git co-authored-by block — especially painful when the PR is committed to SVN, where the full list is the authoritative record.

Per desrosj's comment on #86, parsing trailers and including them (SVN in particular) is the sensible first step, with the Git-side nuance handled by the existing squash-merge trailer behavior plus dedupe.

How?

  • src/utils.js — new parseCoAuthorTrailers(message) helper. Case-insensitive regex scan of the whole message returning { name, email } pairs. Emails lowercased for dedupe. Also adds a small escapeMarkdown(text) helper for safely interpolating attacker-controllable trailer text into the bot comment.
  • src/github.js::getContributorData — adds message to the GraphQL commit query.
  • src/github.js::getUsersByEmails — new method. Batch-resolves emails to GitHub users via a single GraphQL query with aliased search(type: USER, query: "in:email …") calls. The query value is built with JSON.stringify so backslashes, quotes, and control chars in trailer emails can't break out of the GraphQL string literal. On API failure the error is logged via core.warning and every email falls through as unresolved.
  • src/contribution-collector.js — after the normal commits loop, extracts trailers, dedupes against committer emails (so a PR author self-crediting doesn't double-count), and resolves via getUsersByEmails. Resolved trailer authors flow through the existing committer path (WPORG lookup, standard dedupe, SVN + Git formatting). Unresolved trailers land in a new unlinkedCoAuthors list on the output.
  • src/github.js::commentProps — renders unresolved trailers in a new "Co-authors from commit trailers" section (shown just after "Unlinked Accounts") so committers can credit them manually. Name and email are passed through escapeMarkdown so crafted trailers can't inject links, images, or @-mentions into a bot-authored comment. Unresolved entries do not enter the Git co-authored-by block, avoiding double-counting when the trailer is already in the squash-merge commit.
  • dist/index.js — rebuilt via ncc.

Testing Instructions

  1. Open a test PR against this action with commits that include a mix of trailers:
    • A trailer whose email is public on GitHub → contributor should appear in the committer list, SVN props, and Git co-authored-by block just like a direct committer.
    • A trailer with a private or unknown email (e.g. a …@users.noreply.github.com where the GitHub user has no public email) → should appear in the new "Co-authors from commit trailers" section of the props-bot comment.
    • A trailer matching the PR author's own email → should be deduped (not double-counted anywhere).
    • A trailer with markdown special characters in the name (e.g. Co-authored-by: [click me](https://evil.test) @octocat <foo@example.com> where foo@example.com isn't public) → the unresolved entry in the bot comment should render the bracketed text and @octocat literally, not as a link or mention.
  2. Confirm the props-bot comment renders each case.
  3. Open an unrelated PR with no trailers → no regressions, no new section appears.

Scans PR commit messages for `Co-authored-by: Name <email>` trailers and
attempts to resolve each email to a GitHub account via a user search.
Resolved co-authors flow through the existing committers path, so they
appear in the SVN props line and the Git co-authored-by block with the
usual dedupe. Unresolved trailers (private emails, mostly) surface as a
new "Co-authors from commit trailers" section in the PR comment so
committers can credit them manually.

Fixes WordPress#86
Comment thread src/github.js Fixed
kraftbj added 3 commits April 17, 2026 11:44
The previous implementation only escaped double quotes, so an email
containing a backslash could terminate the GraphQL string literal early
and inject query syntax. Use JSON.stringify for the whole query value,
which correctly handles backslashes, quotes, and control characters per
GraphQL/JSON string-literal rules.

Flagged by CodeQL as "Incomplete string escaping or encoding".
- Escape GitHub Flavored Markdown specials when interpolating trailer
  name/email into the "Co-authors from commit trailers" section so
  attacker-controlled commit messages cannot inject links, images, or
  @-mentions into a bot-authored PR comment.
- Raise getUsersByEmails error log from info to warning so a transient
  GraphQL failure (which silently demotes all trailers to the unlinked
  list) is visible in CI logs.
The noreply email format encodes the login directly (and, post-2017, the
numeric user ID). Parse it locally and add the contributor straight to
the committer list instead of round-tripping through a user-search query.
Falls back to getUsersByEmails() for anything else.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

"co-authored-by" tags in commits are ignored when collecting props

2 participants