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 8 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 3m 38s —— View job Review — round 4The code side of this PR looks good to me now. Every behavioural point from the previous rounds is addressed; what's left is documentation accuracy (three of the four findings below are Static analysis
Findings (4 inline comments)
Minor, not worth an inline: the mode switch is What's improved since last round
|
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>
| Note: if your Jira project key collides with this pattern (e.g. a project key of ^CVE^), an | ||
| issue reference that happens to be the prefix of a longer hyphenated number (such as a CVE | ||
| identifier) will be filtered out. Use ^--jira-secondary-source^ with a different identifier | ||
| format as a workaround. | ||
| identifier) will be filtered out. Use ^--jira-trailer^ to read issue keys from a dedicated | ||
| git trailer line (e.g. ^Jira: CVE-42^), which bypasses pattern-scanning entirely. | ||
| Alternatively, use ^--jira-secondary-source^ with a different identifier format. |
There was a problem hiding this comment.
"bypasses pattern-scanning entirely" isn't true, and this paragraph is the one place a user acts on it.
Trailer values still go through jira.FindJiraIssueKeys (attestJira.go:344), which applies both the key regex and isPartialMultiSegment. --jira-trailer changes which text is scanned, not how it is parsed.
Concretely, a user with project key CVE who reads this and writes Jira: CVE-2026-41284 gets the regex match CVE-2026, whose only occurrence is followed by -4, so it is filtered out and the attestation is non-compliant — the exact outcome the paragraph promises to avoid. (The new logger.Warn at line 347 does at least surface it now, which is a good addition.)
The flag genuinely does help — it removes the surrounding commit text that causes most collisions — so the fix is just to scope the claim:
| Note: if your Jira project key collides with this pattern (e.g. a project key of ^CVE^), an | |
| issue reference that happens to be the prefix of a longer hyphenated number (such as a CVE | |
| identifier) will be filtered out. Use ^--jira-secondary-source^ with a different identifier | |
| format as a workaround. | |
| identifier) will be filtered out. Use ^--jira-trailer^ to read issue keys from a dedicated | |
| git trailer line (e.g. ^Jira: CVE-42^), which bypasses pattern-scanning entirely. | |
| Alternatively, use ^--jira-secondary-source^ with a different identifier format. | |
| Note: if your Jira project key collides with this pattern (e.g. a project key of ^CVE^), an | |
| issue reference that happens to be the prefix of a longer hyphenated number (such as a CVE | |
| identifier) will be filtered out. Use ^--jira-trailer^ to read issue keys from a dedicated | |
| git trailer line (e.g. ^Jira: CVE-42^), which narrows the scanned text to the trailer value | |
| so unrelated identifiers elsewhere in the commit cannot interfere. The same pattern rules | |
| still apply to the trailer value itself. | |
| Alternatively, use ^--jira-secondary-source^ with a different identifier format. |
| By default, parses the given commit's message, current branch name, or the content of the | ||
| ^--jira-secondary-source^ argument for Jira issue references of the form. |
There was a problem hiding this comment.
Leftover from splitting the sentence: line 43 ends for Jira issue references of the form. — the "of the form" now dangles, since the form is defined two lines later under its own heading. This is the first thing kosli attest jira --help prints.
| By default, parses the given commit's message, current branch name, or the content of the | |
| ^--jira-secondary-source^ argument for Jira issue references of the form. | |
| By default, parses the given commit's message, current branch name, or the content of the | |
| ^--jira-secondary-source^ argument for Jira issue references. |
| --assert | ||
| --repo-root %s %s`, suite.tmpDir, suite.defaultKosliArguments), | ||
| golden: "jira attestation 'bar' is reported to trail: test-123\nError: no Jira references are found in trailer 'Jira'\n", | ||
| additionalConfig: jiraTestsAdditionalConfig{ |
There was a problem hiding this comment.
Three behaviours added in the latest commit have no test, which is worth closing given the TDD discipline in CLAUDE.md — the first in particular is a new error path that changes what a valid command line is:
MuXRequiredFlags(cmd, []string{"jira-trailer", "jira-secondary-source"}, false)(attestJira.go:260) — nothing pins the error. A one-line golden case locks it in and documents the resolution of that thread.- The branch-name half of the PR's headline claim — test 27 proves the commit body isn't scanned (
ONA-999would break the assert); nothing proves the branch isn't.execJiraTestCasealready supportsbranchName(line 414). - The two new
logger.Warnbranches (attestJira.go:338and347) — optional, but (2) below is nearly free.
| additionalConfig: jiraTestsAdditionalConfig{ | |
| { | |
| wantError: true, | |
| name: "30 --jira-trailer and --jira-secondary-source are mutually exclusive", | |
| cmd: fmt.Sprintf(`attest jira --name bar --commit HEAD | |
| --jira-base-url https://kosli-test.atlassian.net | |
| --jira-trailer Jira | |
| --jira-secondary-source some-branch %s`, suite.defaultKosliArguments), | |
| golden: "Error: only one of --jira-trailer, --jira-secondary-source is allowed\n", | |
| }, | |
| { | |
| name: "31 --jira-trailer does not scan the branch name for issue references", | |
| cmd: fmt.Sprintf(`attest jira --name bar | |
| --jira-base-url https://kosli-test.atlassian.net | |
| --jira-trailer Jira | |
| --assert | |
| --repo-root %s %s`, suite.tmpDir, suite.defaultKosliArguments), | |
| golden: "jira attestation 'bar' is reported to trail: test-123\n", | |
| additionalConfig: jiraTestsAdditionalConfig{ | |
| branchName: "bugfix/ONA-999", | |
| commitMessage: "fix: some change\n\nJira: EX-1", | |
| }, | |
| }, | |
| } |
ONA-999 in the branch is the right probe for (2): it matches the Jira key pattern, so under the old scanning behaviour it becomes an unresolvable issue ID and issueFoundCount != len(issueIDs) fails the assert.
| jiraIssueFieldFlag = "[optional] The comma separated list of fields to include from the Jira issue. Default no fields are included. '*all' will give all fields." | ||
| jiraSecondarySourceFlag = "[optional] An optional string to search for Jira ticket reference, e.g. '--jira-secondary-source ${{ github.head_ref }}'" | ||
| ignoreBranchMatchFlag = "Ignore branch name when searching for Jira ticket reference." | ||
| jiraTrailerFlag = "[optional] The git trailer key to use as the sole source of Jira issue references (e.g. '--jira-trailer Jira' extracts the value of 'Jira: <issue-key>' lines from the commit message). When set, the commit message body and branch name are not scanned." |
There was a problem hiding this comment.
Now that --jira-trailer and --jira-secondary-source are mutually exclusive (attestJira.go:260), neither flag's help says so. Worth noting in both strings, because bindFlags sets flags from env/config via cmd.Flags().Set(...) (root.go:673), which marks them Changed — so an org that exports KOSLI_JIRA_SECONDARY_SOURCE in a shared CI step and then adds --jira-trailer Jira on the command line hits a hard error, not a silently-ignored flag.
That's the right behaviour (a silent no-op here is compliance-relevant, which is what the earlier thread was about) — it just needs to be discoverable from --help rather than from a failing pipeline.
| jiraTrailerFlag = "[optional] The git trailer key to use as the sole source of Jira issue references (e.g. '--jira-trailer Jira' extracts the value of 'Jira: <issue-key>' lines from the commit message). When set, the commit message body and branch name are not scanned." | |
| jiraSecondarySourceFlag = "[optional] An optional string to search for Jira ticket reference, e.g. '--jira-secondary-source ${{ github.head_ref }}'. Mutually exclusive with --jira-trailer." | |
| ignoreBranchMatchFlag = "Ignore branch name when searching for Jira ticket reference." | |
| jiraTrailerFlag = "[optional] The git trailer key to use as the sole source of Jira issue references (e.g. '--jira-trailer Jira' extracts the value of 'Jira: <issue-key>' lines from the commit message). When set, the commit message body and branch name are not scanned. Mutually exclusive with --jira-secondary-source." |
| jiraSecondarySourceFlag = "[optional] An optional string to search for Jira ticket reference, e.g. '--jira-secondary-source ${{ github.head_ref }}'" | ||
| ignoreBranchMatchFlag = "Ignore branch name when searching for Jira ticket reference." | ||
| jiraTrailerFlag = "[optional] The git trailer key to use as the sole source of Jira issue references (e.g. '--jira-trailer Jira' extracts the value of 'Jira: <issue-key>' lines from the commit message). When set, the commit message body and branch name are not scanned." |
There was a problem hiding this comment.
Corrected anchor for the suggestion in my comment just above — that one was anchored to line 172 alone, so its three-line block would have duplicated lines 170–171. This range is the right one; ignore the other block.
Now that --jira-trailer and --jira-secondary-source are mutually exclusive (attestJira.go:260), neither flag's help says so. Worth stating in both, because bindFlags sets flags from env/config via cmd.Flags().Set(...) (root.go:673), which marks them Changed — so an org that exports KOSLI_JIRA_SECONDARY_SOURCE in a shared CI step and then adds --jira-trailer Jira on the command line hits a hard error rather than a silently-ignored flag.
That's the right behaviour (a silent no-op here is compliance-relevant, which is what the earlier thread was about) — it just needs to be discoverable from --help instead of from a failing pipeline.
| jiraSecondarySourceFlag = "[optional] An optional string to search for Jira ticket reference, e.g. '--jira-secondary-source ${{ github.head_ref }}'" | |
| ignoreBranchMatchFlag = "Ignore branch name when searching for Jira ticket reference." | |
| jiraTrailerFlag = "[optional] The git trailer key to use as the sole source of Jira issue references (e.g. '--jira-trailer Jira' extracts the value of 'Jira: <issue-key>' lines from the commit message). When set, the commit message body and branch name are not scanned." | |
| jiraSecondarySourceFlag = "[optional] An optional string to search for Jira ticket reference, e.g. '--jira-secondary-source ${{ github.head_ref }}'. Mutually exclusive with --jira-trailer." | |
| ignoreBranchMatchFlag = "Ignore branch name when searching for Jira ticket reference." | |
| jiraTrailerFlag = "[optional] The git trailer key to use as the sole source of Jira issue references (e.g. '--jira-trailer Jira' extracts the value of 'Jira: <issue-key>' lines from the commit message). When set, the commit message body and branch name are not scanned. Mutually exclusive with --jira-secondary-source." |
…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>
| By default, parses the given commit's message, current branch name, or the content of the | ||
| ^--jira-secondary-source^ argument for Jira issue references of the form. | ||
| Use ^--jira-trailer^ to read issue keys exclusively from a named git trailer line instead | ||
| (e.g. ^Jira: PROJ-42^); when set, the commit message body, branch name, and | ||
| ^--jira-secondary-source^ are not scanned. |
There was a problem hiding this comment.
This paragraph now contradicts the validation added in this same commit. It tells the user that with --jira-trailer set, --jira-secondary-source "is not scanned" — but MuXRequiredFlags(cmd, []string{"jira-trailer", "jira-secondary-source"}, false) at line 260 makes passing both a hard error, not a silently-ignored flag. A user who reads this help and writes both gets Error: only one of --jira-trailer, --jira-secondary-source is allowed.
Also a leftover from the last round: line 43 still ends for Jira issue references of the form. — "of the form" dangles now that the form is defined two lines below under its own heading.
| By default, parses the given commit's message, current branch name, or the content of the | |
| ^--jira-secondary-source^ argument for Jira issue references of the form. | |
| Use ^--jira-trailer^ to read issue keys exclusively from a named git trailer line instead | |
| (e.g. ^Jira: PROJ-42^); when set, the commit message body, branch name, and | |
| ^--jira-secondary-source^ are not scanned. | |
| By default, parses the given commit's message, current branch name, or the content of the | |
| ^--jira-secondary-source^ argument for Jira issue references. | |
| Use ^--jira-trailer^ to read issue keys exclusively from a named git trailer line instead | |
| (e.g. ^Jira: PROJ-42^); when set, neither the commit message body nor the branch name is | |
| scanned, and ^--jira-secondary-source^ may not be used at the same time. |
| Note: if your Jira project key collides with this pattern (e.g. a project key of ^CVE^), an | ||
| issue reference that happens to be the prefix of a longer hyphenated number (such as a CVE | ||
| identifier) will be filtered out. Use ^--jira-secondary-source^ with a different identifier | ||
| format as a workaround. | ||
| identifier) will be filtered out. Use ^--jira-trailer^ to read issue keys from a dedicated | ||
| git trailer line (e.g. ^Jira: CVE-42^), which bypasses pattern-scanning entirely. | ||
| Alternatively, use ^--jira-secondary-source^ with a different identifier format. |
There was a problem hiding this comment.
"bypasses pattern-scanning entirely" is still not true — this was raised last round and the wording is unchanged.
Trailer values are joined and passed straight to jira.FindJiraIssueKeys at line 345, so both the key regex and the isPartialMultiSegment filter described in the paragraph above still apply. --jira-trailer changes which text is scanned, not how it is parsed.
This matters here specifically because this is the CVE-collision paragraph: a user with project key CVE who follows this advice and writes Jira: CVE-2026-41284 gets CVE-2026 matched and then filtered out (followed by -4), producing a non-compliant attestation — the exact outcome the paragraph promises the flag avoids. The new logger.Warn at line 348 does at least surface it now, which is a good addition.
The flag genuinely does help — it removes the surrounding commit text that causes most collisions — so it's just the scope of the claim that needs fixing:
| Note: if your Jira project key collides with this pattern (e.g. a project key of ^CVE^), an | |
| issue reference that happens to be the prefix of a longer hyphenated number (such as a CVE | |
| identifier) will be filtered out. Use ^--jira-secondary-source^ with a different identifier | |
| format as a workaround. | |
| identifier) will be filtered out. Use ^--jira-trailer^ to read issue keys from a dedicated | |
| git trailer line (e.g. ^Jira: CVE-42^), which bypasses pattern-scanning entirely. | |
| Alternatively, use ^--jira-secondary-source^ with a different identifier format. | |
| Note: if your Jira project key collides with this pattern (e.g. a project key of ^CVE^), an | |
| issue reference that happens to be the prefix of a longer hyphenated number (such as a CVE | |
| identifier) will be filtered out. Use ^--jira-trailer^ to read issue keys from a dedicated | |
| git trailer line (e.g. ^Jira: CVE-42^), which narrows the scanned text to the trailer value | |
| so unrelated identifiers elsewhere in the commit cannot interfere — note the same pattern | |
| rules still apply to the trailer value itself. | |
| Alternatively, use ^--jira-secondary-source^ with a different identifier format. |
| additionalConfig: jiraTestsAdditionalConfig{ | ||
| branchName: "EX-1-some-feature", | ||
| commitMessage: "fix: some change with no jira trailer", | ||
| }, |
There was a problem hiding this comment.
Test 30 is a good one — EX-1 in the branch resolves in Jira under the old scanning path, so wantError: true genuinely fails if branch scanning leaks into trailer mode. That closes the branch half of the PR's headline claim.
The one remaining gap is the new error path added in this commit: MuXRequiredFlags(cmd, []string{"jira-trailer", "jira-secondary-source"}, false) (attestJira.go:260) changes what a valid command line is, and nothing pins it. Per the TDD discipline in CLAUDE.md, a new error path should arrive with a test — and this one is a one-liner:
| }, | |
| { | |
| wantError: true, | |
| name: "31 --jira-trailer and --jira-secondary-source are mutually exclusive", | |
| cmd: fmt.Sprintf(`attest jira --name bar --commit HEAD | |
| --jira-base-url https://kosli-test.atlassian.net | |
| --jira-trailer Jira | |
| --jira-secondary-source some-branch %s`, suite.defaultKosliArguments), | |
| golden: "Error: only one of --jira-trailer, --jira-secondary-source is allowed\n", | |
| }, | |
| } |
Note this case must not carry additionalConfig, since execJiraTestCase appends --commit <sha> only when additionalConfig is non-nil (line 426) — hence the explicit --commit HEAD above.
| // GetTrailerValues extracts the values of all trailer lines in a commit message | ||
| // that match the given key. The key comparison is case-insensitive. Trailer lines | ||
| // have the format "<key>: <value>". Returns an empty (non-nil) slice if none are found. | ||
| func GetTrailerValues(message, key string) []string { |
There was a problem hiding this comment.
The key trimming from last round is in (TrimSpace + TrimRight(":"), both with unit cases — nice). Two doc items are still open, and one has a real behavioural consequence:
- Empty values are silently dropped (line 290) — a sensible choice, but undocumented and untested. It also quietly defeats the new warning in
attestJira.go:347: a commit with a bareJira:line yieldslen(trailerValues) == 0, so the user gets neither the issue key nor the "trailer found but no valid keys" warning — the one case where the user demonstrably tried to use the trailer and typoed it. - The name promises git-trailer semantics the implementation doesn't have — real trailers (per
git interpret-trailers) live only in the message's last paragraph; this matches<key>:on any line, including the subject. Harmless for this caller, but the comment should say so rather than the next caller assuming otherwise.
| // GetTrailerValues extracts the values of all trailer lines in a commit message | |
| // that match the given key. The key comparison is case-insensitive. Trailer lines | |
| // have the format "<key>: <value>". Returns an empty (non-nil) slice if none are found. | |
| func GetTrailerValues(message, key string) []string { | |
| // GetTrailerValues returns the values of every line in a commit message of the form | |
| // "<key>: <value>". The key comparison is case-insensitive; surrounding whitespace on | |
| // both the key and the line is ignored, and a trailing ":" on the key is tolerated. | |
| // Note this matches any such line anywhere in the message, not only trailers in the | |
| // final paragraph as `git interpret-trailers` defines them. Lines with an empty value | |
| // are skipped. Returns an empty (non-nil) slice if none are found. |
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
…exclusion Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
| By default, parses the given commit's message, current branch name, or the content of the | ||
| ^--jira-secondary-source^ argument for Jira issue references of the form. | ||
| Use ^--jira-trailer^ to read issue keys exclusively from a named git trailer line instead | ||
| (e.g. ^Jira: PROJ-42^); when set, the commit message body, branch name, and | ||
| ^--jira-secondary-source^ are not scanned. |
There was a problem hiding this comment.
This paragraph tells the user something the code now rejects. Line 46 says that when --jira-trailer is set, --jira-secondary-source "is not scanned" — but MuXRequiredFlags(cmd, []string{"jira-trailer", "jira-secondary-source"}, false) at line 260 (added in this same PR, and pinned by test 30) makes passing both a hard error. A user who reads this help and passes both gets Error: only one of --jira-trailer, --jira-secondary-source is allowed, not a quietly-ignored flag.
Also still open from the previous round: line 43 ends for Jira issue references of the form. — "of the form" dangles, since the form is now defined two lines down under its own heading. This is the first thing kosli attest jira --help prints.
| By default, parses the given commit's message, current branch name, or the content of the | |
| ^--jira-secondary-source^ argument for Jira issue references of the form. | |
| Use ^--jira-trailer^ to read issue keys exclusively from a named git trailer line instead | |
| (e.g. ^Jira: PROJ-42^); when set, the commit message body, branch name, and | |
| ^--jira-secondary-source^ are not scanned. | |
| By default, parses the given commit's message, current branch name, or the content of the | |
| ^--jira-secondary-source^ argument for Jira issue references. | |
| Use ^--jira-trailer^ to read issue keys exclusively from a named git trailer line instead | |
| (e.g. ^Jira: PROJ-42^); when set, neither the commit message body nor the branch name is | |
| scanned, and ^--jira-secondary-source^ may not be used at the same time. |
| Note: if your Jira project key collides with this pattern (e.g. a project key of ^CVE^), an | ||
| issue reference that happens to be the prefix of a longer hyphenated number (such as a CVE | ||
| identifier) will be filtered out. Use ^--jira-secondary-source^ with a different identifier | ||
| format as a workaround. | ||
| identifier) will be filtered out. Use ^--jira-trailer^ to read issue keys from a dedicated | ||
| git trailer line (e.g. ^Jira: CVE-42^), which bypasses pattern-scanning entirely. | ||
| Alternatively, use ^--jira-secondary-source^ with a different identifier format. |
There was a problem hiding this comment.
"bypasses pattern-scanning entirely" is still inaccurate — third round on this one, wording unchanged.
Trailer values are joined and handed to jira.FindJiraIssueKeys at line 345, exactly like the scanning path, so both the key regex and the isPartialMultiSegment filter described in the paragraph directly above still apply. --jira-trailer changes which text is scanned, not how it is parsed.
It matters here specifically because this is the CVE-collision paragraph — the one place the user acts on this claim. A user with project key CVE who follows the advice and writes Jira: CVE-2026-41284 gets CVE-2026 matched and then filtered out (its only occurrence is followed by -4), producing a non-compliant attestation: the exact outcome the paragraph promises the flag avoids. The new logger.Warn at line 348 does now surface it, which is a genuine improvement, but the prose still points the user at a workaround that doesn't work for this case.
The flag does help — it removes the surrounding commit text that causes most collisions — so only the scope of the claim needs fixing:
| Note: if your Jira project key collides with this pattern (e.g. a project key of ^CVE^), an | |
| issue reference that happens to be the prefix of a longer hyphenated number (such as a CVE | |
| identifier) will be filtered out. Use ^--jira-secondary-source^ with a different identifier | |
| format as a workaround. | |
| identifier) will be filtered out. Use ^--jira-trailer^ to read issue keys from a dedicated | |
| git trailer line (e.g. ^Jira: CVE-42^), which bypasses pattern-scanning entirely. | |
| Alternatively, use ^--jira-secondary-source^ with a different identifier format. | |
| Note: if your Jira project key collides with this pattern (e.g. a project key of ^CVE^), an | |
| issue reference that happens to be the prefix of a longer hyphenated number (such as a CVE | |
| identifier) will be filtered out. Use ^--jira-trailer^ to read issue keys from a dedicated | |
| git trailer line (e.g. ^Jira: CVE-42^), which narrows the scanned text to the trailer value | |
| so unrelated identifiers elsewhere in the commit cannot interfere — note the same pattern | |
| rules still apply to the trailer value itself. | |
| Alternatively, use ^--jira-secondary-source^ with a different identifier format. |
| // GetTrailerValues extracts the values of all trailer lines in a commit message | ||
| // that match the given key. The key comparison is case-insensitive. Trailer lines | ||
| // have the format "<key>: <value>". Returns an empty (non-nil) slice if none are found. |
There was a problem hiding this comment.
The key trimming is in and unit-tested (TrimSpace + TrimRight(":"), with cases at gitView_test.go:541 and :547) — that closes the last round's behavioural point. The doc comment didn't move with it, and one of the gaps has a user-visible consequence:
- Empty values are silently dropped (line 289) — a sensible choice, but undocumented and the only branch of this function with no unit case. It also quietly defeats the new warning in
attestJira.go:347: a commit with a bareJira:line yieldslen(trailerValues) == 0, so the user gets neither the issue key nor the "trailer found but no valid Jira issue keys" warning — and that's precisely the case where they demonstrably tried to use the trailer and got it wrong. Worth a unit case pinning the drop either way. - The name promises git-trailer semantics the implementation doesn't have — real trailers (per
git interpret-trailers) live only in the message's final paragraph; this matches<key>:on any line, including the subject. Harmless for this caller since values go through the Jira key regex anyway, but the comment should say so rather than leave the next caller to assume otherwise.
| // GetTrailerValues extracts the values of all trailer lines in a commit message | |
| // that match the given key. The key comparison is case-insensitive. Trailer lines | |
| // have the format "<key>: <value>". Returns an empty (non-nil) slice if none are found. | |
| // GetTrailerValues returns the values of every line in a commit message of the form | |
| // "<key>: <value>". The key comparison is case-insensitive; surrounding whitespace on | |
| // both the key and the line is ignored, and a trailing ":" on the key is tolerated. | |
| // Note this matches any such line anywhere in the message, not only trailers in the | |
| // final paragraph as `git interpret-trailers` defines them. Lines with an empty value | |
| // are skipped. Returns an empty (non-nil) slice if none are found. |
| @@ -169,6 +169,7 @@ The ^.kosli_ignore^ will be treated as part of the artifact like any other file, | |||
| jiraIssueFieldFlag = "[optional] The comma separated list of fields to include from the Jira issue. Default no fields are included. '*all' will give all fields." | |||
| jiraSecondarySourceFlag = "[optional] An optional string to search for Jira ticket reference, e.g. '--jira-secondary-source ${{ github.head_ref }}'" | |||
There was a problem hiding this comment.
jiraTrailerFlag now carries "Mutually exclusive with --jira-secondary-source." — thanks, that closes the main half of the earlier thread. The reciprocal sentence on jiraSecondarySourceFlag is still missing, so a user reading --jira-secondary-source's help alone has no way to discover the constraint.
It's worth the one line because bindFlags sets flags from env/config via cmd.Flags().Set(...) (root.go:673), which marks them Changed. An org that exports KOSLI_JIRA_SECONDARY_SOURCE in a shared CI step and then adds --jira-trailer Jira on one job's command line gets a hard error from a flag they never typed.
| jiraSecondarySourceFlag = "[optional] An optional string to search for Jira ticket reference, e.g. '--jira-secondary-source ${{ github.head_ref }}'" | |
| jiraSecondarySourceFlag = "[optional] An optional string to search for Jira ticket reference, e.g. '--jira-secondary-source ${{ github.head_ref }}'. Mutually exclusive with --jira-trailer." |
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