feat(jira): add --jira-trailer flag to extract Jira issue key from git trailer - #1109
feat(jira): add --jira-trailer flag to extract Jira issue key from git trailer#1109vidhu-balad wants to merge 23 commits into
Conversation
…iler When --jira-trailer <key> is set, the command reads lines of the form '<key>: <value>' from the commit message and uses those values as the sole source of Jira issue references, skipping the full commit message and branch name scan. This avoids false positives from other trailers (e.g. Ona-Environment-Id) whose values happen to match the Jira key pattern. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
Claude finished @vidhu-balad's task in 6m 39s —— View job Review — latest push (
|
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
@vidhu-balad there is still some feedback from the bot |
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
…curate help - Trim leading whitespace from trailer lines so editor-indented or git-log-formatted messages (4-space indent) match correctly - Strip trailing colon from --jira-trailer value so "Jira:" and "Jira" both produce the same prefix - Error when --jira-trailer and --jira-secondary-source are both set (they are mutually exclusive; secondary source is silently ignored in trailer mode) - Warn when --ignore-branch-match is set alongside --jira-trailer (it has no effect in trailer mode) - Warn when a trailer is found but contains no valid Jira issue keys - Thread issueSource through both --assert error messages so trailer mode names the trailer rather than "commit message or branch name" - Update Long description to document trailer mode, its interaction with --ignore-branch-match, and its use as the preferred CVE-collision fix - Add --jira-trailer example to attestJiraExample Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…canned test - TrimSpace the key in GetTrailerValues before TrimRight(key, ":") - Add unit test: key with surrounding whitespace still matches - Add integration test 30: --jira-trailer does not scan branch name Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
- Add test 32: bare Jira: line triggers TrailerKeyExists warning path - Rename old 32/33 → 33/34 - Fix example comment: replace "bypasses...entirely" with scoped claim matching the long desc caveat (trailer value still pattern-matched) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…r to audit spec - Add test 34: --ignore-branch-match warns it has no effect in trailer mode - Rename old 34 → 35 - Add jira-trailer to flags_to_test and flag_values in empty-flag-audit spec.json Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
AlexKantor87
left a comment
There was a problem hiding this comment.
Nice piece of work, Vidhu. The default path is untouched and the error messages now say which source was searched, which is a good touch. I pulled the branch down and had a proper dig through it. Two things I'd like sorted before it goes in, plus three smaller ones I've confirmed by running them.
1. It isn't really reading a git trailer
Git only treats the last block of a commit message as trailers. GetTrailerValues reads every line, so any line anywhere in the message that starts with Jira: counts. The comment on the function says this, but the flag help doesn't, and the flag help is what people will read.
Where it bites is revert commits. If someone reverts a commit and the old message gets quoted in the body, you get the old ticket as well as the new one. I ran this:
Revert "fix: thing"
This reverts commit abc123.
Original message was:
Jira: EX-999
Jira: EX-1
That gives back both EX-999 and EX-1. Git itself would only give you EX-1.
It's not as bad as it sounds, because a line has to start with Jira: to match. Prose like "we now require Jira: EX-42 in every commit" is correctly ignored, which I also checked. So it's really just the quoted-revert case. But this flag exists to stop wrong tickets being picked up, and the help text says it "confines scanning to the trailer value", which nobody would read as "and also anything quoted in the middle of the message". Either restrict it to the last block, or say the limitation in the flag help and the long description rather than only in the code comment.
2. The same error sentence is now written in two places
The sentence flag '--jira-trailer' was given an empty value gets produced in two different ways now.
root.go:504 builds that sentence for any flag, by slotting the flag's name into a template. That's the existing rule and it already covers every flag in the CLI.
Your new check in attestJira.go types the same sentence out again as a literal, with jira-trailer baked into it.
To be clear, your check is doing real work and I don't want it removed. The existing rule only catches a completely empty value, so --jira-trailer : and --jira-trailer " " genuinely do get past it and genuinely do need catching. The only problem is the duplicated sentence. If anyone reworded the message later, the generic one would change and yours wouldn't, and users would see two different sentences for the same problem. 2026-08-15-how-the-empty-value-rule-is-built.md calls this out and says the wording is worth settling in one place, partly because a lot of tests check for it.
The fix is small. Pull the sentence into one function, something like:
func emptyFlagValueError(name string) error {
return fmt.Errorf("flag '--%s' was given an empty value", name)
}then have reportEmptyFlagValue return emptyFlagValueError(invalid.GetFlag().Name) and your check return emptyFlagValueError("jira-trailer"). One new function, two lines changed.
Three smaller ones. I've run each of these, so they're confirmed rather than suspected:
-
A key with a colon in it splits in the wrong place. With
--jira-trailer "A:B"against a lineA:B: PROJ-1,GetTrailerValuesreturns"B: PROJ-1"instead of"PROJ-1". The ticket still comes out right in the end, becauseFindJiraIssueKeysfindsPROJ-1inside that string anyway, but the value shown in the debug log and the warning is wrong. Git trailer names can't contain colons or spaces, so rejecting those alongside the existing empty check would close it in a line or two. -
A missing trailer says nothing at all. Your test 28 pins this: commit with no trailer, no
--assert, and the expected output is just the success line. The golden check is an exact comparison of everything the command printed, so that really is silence. The attestation goes up non-compliant and nobody is told why. It matches the behaviour of the existing path so I won't insist, but a "trailer 'Jira' not found in commit message" warning would be kinder, and you've already gotTrailerKeyExistssitting there to do it. -
The "no valid Jira issue keys" warning fires when the key was valid.
FindJiraIssueKeys("EX-1", ["ABC"])returns nothing, so with--jira-project-key ABCand a commit carryingJira: EX-1, you gettrailer 'Jira' was found but contained no valid Jira issue keys: [EX-1].EX-1is a perfectly valid key, it just belongs to another project. The wording sends people looking for a formatting problem that isn't there.
Approving on the basis that 1 and 2 get picked up.
Dismissing: this was submitted as an approval by mistake. The review content asks for two changes before merge, so an approval was the wrong state. The findings still stand, see the review body and the changes-requested review below.
|
Good stuff, also to add to Alex's comments we sohuld update PR description, which is now stale (it still advertises 3 integration tests (27–29) and 6 unit tests, and don't mention the mutual exclusion with This branch carries 16 commits, most of them "fix the last round of bot feedback". Worth squashing per the slice guidance. |
- Restrict GetTrailerValues/TrailerKeyExists to the final paragraph of the commit message, matching git interpret-trailers semantics - Extract trailerBlock helper and scanTrailer to share one matcher - Extract emptyFlagValueError to avoid duplicating the error wording - Reject --jira-trailer keys containing colons or spaces (not valid in git) - Add unit test: key in commit body but not final paragraph is not matched - Add integration test 32: internal colon in key is rejected Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace "matching git interpret-trailers semantics" and similar phrasing with plain descriptions of what the code actually does: only the last block of lines is scanned (everything after the final blank line, or the whole message if there is no blank line). Also fix a misplaced doc comment for reportEmptyFlagValue/ emptyFlagValueError in root.go, and add a unit test case for a single-paragraph commit message with no blank lines. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Previously a single warning fired when TrailerKeyExists was true but no
issue IDs were found, which conflated three different situations and
produced misleading messages:
- Trailer absent entirely: now warns that the key was not found in the
last paragraph of the commit message, helping users diagnose cases
where their trailer line is in the message body rather than the footer.
- Key present but value empty: now says "had no value" instead of
"contained no valid Jira issue keys: []".
- Value present but yielding no IDs: distinguishes between a project-key
filter mismatch ("did not match project filter") and a format problem
("did not contain valid Jira issue keys"), so users are not told their
key format is wrong when it is actually filtered out.
Also update golden strings for tests 28, 33, 34, 36 and add test 37
for the project-key-filter case.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
"The commit message body is not scanned" contradicted the preceding sentence which says the last block of the commit message is scanned. Replace with "The rest of the commit message" to avoid the collision. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add TrailerKeyExistsAnywhere to gitview, which scans the whole commit message rather than only the final paragraph. Use it to split the "trailer not found" warning into two cases: - Key exists somewhere in the message but not in the last block: warns "a '<key>' line was found outside the last block of the commit message and was ignored" — names the actual mistake for squash-merge bodies where Jira: appears in the middle. - Key absent from the message entirely: warns "trailer '<key>' was not found in the commit message". Also fix test 29 whose golden was missed in da06c97 (same commit message as tests 28 and 36, so the warning fires there too), update tests 28, 29 and 36 to the new wording, and add test 38 for the outside-last-block case. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
"commit message body" contradicted the preceding phrase "final paragraph of the commit message". Use "rest of the commit message" to match the long description. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
TrailerKeyExistsAnywhere had its own copy of the line-matching loop, diverging from scanTrailer despite needing to agree on what "matching" means. Extract scanLines from scanTrailer; both scanTrailer and TrailerKeyExistsAnywhere now delegate to it, so a single change keeps all three exported helpers in sync. Also add the missing unit coverage: a "key present outside last block" case to TestTrailerKeyExists (must return false) and a new TestTrailerKeyExistsAnywhere table covering key in last block, key outside last block, and key absent entirely. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Summary
--jira-trailer <key>flag tokosli attest jira<key>: <value>from the commit message and uses those values as the sole source of Jira issue referencesOna-Environment-Id: ONA-456) whose values happen to match the Jira key patternChanges
internal/gitview/gitView.go— newGetTrailerValues(message, key string) []stringfunctioninternal/gitview/gitView_test.go— 6 unit tests covering no match, single match, case-insensitive key, multiple occurrences, non-matching trailers ignored, whitespace trimmingcmd/kosli/root.go—jiraTrailerFlagconstantcmd/kosli/attestJira.go—--jira-trailerflag wired into the issue-finding logiccmd/kosli/attestJira_test.go— 3 integration tests: trailer used successfully, trailer absent (non-compliant but reported), trailer absent with--assert(error)Test plan
make test_integration_single TARGET=AttestJiraCommandTestSuite— tests 27, 28, 29 cover the new flaggo test ./internal/gitview/... -run TestGitViewTestSuite/TestGetTrailerValues— unit tests forGetTrailerValuesmake lint— passes with 0 issues🤖 Generated with Claude Code