Skip to content

feat(jira): add --jira-trailer flag to extract Jira issue key from git trailer - #1109

Open
vidhu-balad wants to merge 8 commits into
mainfrom
feat/jira-trailer-flag
Open

feat(jira): add --jira-trailer flag to extract Jira issue key from git trailer#1109
vidhu-balad wants to merge 8 commits into
mainfrom
feat/jira-trailer-flag

Conversation

@vidhu-balad

Copy link
Copy Markdown
Contributor

Summary

  • Adds a --jira-trailer <key> flag to kosli attest jira
  • When set, the command reads only lines of the form <key>: <value> from the commit message and uses those values as the sole source of Jira issue references
  • The full commit message body and branch name scan are skipped entirely, eliminating false positives from other trailers (e.g. Ona-Environment-Id: ONA-456) whose values happen to match the Jira key pattern
  • Key match is case-insensitive; multiple occurrences of the same trailer key are supported
  • All existing behaviour is unchanged for users who do not set the flag

Changes

  • internal/gitview/gitView.go — new GetTrailerValues(message, key string) []string function
  • internal/gitview/gitView_test.go — 6 unit tests covering no match, single match, case-insensitive key, multiple occurrences, non-matching trailers ignored, whitespace trimming
  • cmd/kosli/root.gojiraTrailerFlag constant
  • cmd/kosli/attestJira.go--jira-trailer flag wired into the issue-finding logic
  • cmd/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 flag
  • go test ./internal/gitview/... -run TestGitViewTestSuite/TestGetTrailerValues — unit tests for GetTrailerValues
  • make lint — passes with 0 issues

🤖 Generated with Claude Code

…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

claude Bot commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

Claude finished @vidhu-balad's task in 3m 38s —— View job


Review — round 4

The 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 --help text that contradicts the code) plus one small test gap.

Static analysis

Check Result
go build ./... ✅ clean
go vet ./... ✅ clean
go mod tidy + git diff --exit-code go.mod go.sum ✅ no changes
go test ./internal/gitview/... ⚠️ not run — go test is not in this job's allowed tools, so the unit and integration suites are unverified here

Findings (4 inline comments)

  1. attestJira.go:42–46 — the Long text contradicts the validation added in this same PR. It tells the user --jira-secondary-source "is not scanned" in trailer mode, but line 260 now makes passing both a hard error (pinned by test 30). Also still carries the dangling for Jira issue references of the form. on line 43.
  2. attestJira.go:66–70 — "bypasses pattern-scanning entirely" is still not true. Trailer values go through jira.FindJiraIssueKeys at line 345, so the key regex and the isPartialMultiSegment filter both still apply. This is the CVE-collision paragraph, so a CVE-project user who follows it and writes Jira: CVE-2026-41284 hits exactly the failure the paragraph promises to avoid.
  3. gitView.go:279–281 — doc comment is now behind the code. Empty trailer values are silently dropped (line 289): undocumented, untested, and it defeats the new warning at attestJira.go:347 — a bare Jira: line produces no key and no warning, which is the one case where the user clearly tried and typoed. The "trailer lines" wording also still implies git interpret-trailers semantics the function doesn't have.
  4. root.go:170jiraSecondarySourceFlag lacks the reciprocal mutual-exclusion note. Matters because bindFlags marks env/config-sourced flags as Changed, so KOSLI_JIRA_SECONDARY_SOURCE in a shared CI step + --jira-trailer on one job errors on a flag the user never typed.

Minor, not worth an inline: the mode switch is o.trailerKey != "" (attestJira.go:339) while the exclusion check is on flag.Changed, so --jira-trailer "" --jira-secondary-source X errors even though trailer mode is off. Harmless today, but the two disagreeing on what "set" means is the kind of thing that bites later.

What's improved since last round

  • Test 30 pins the MuXRequiredFlags error, correctly using an explicit --commit HEAD and no additionalConfig. Test 31 closes the branch-not-scanned half of the headline claim — EX-1 in the branch resolves under the old path, so wantError genuinely fails if branch scanning leaks in. Both gaps from the last two rounds are now covered.
  • issueSource threading (attestJira.go:356–359) means --assert failures no longer name sources the command deliberately skipped — this was the highest-impact finding and it's cleanly done, with the golden updated to match.
  • Key trimming in GetTrailerValues is in with unit cases for both the trailing-colon and surrounding-whitespace forms; the 9-case table is a nice, readable spread.
  • The two new logger.Warn branches (:341 and :347) turn two previously-silent failure modes into something a user can act on.
    · branch feat/jira-trailer-flag

Comment thread cmd/kosli/attestJira_test.go
Comment thread internal/gitview/gitView.go Outdated
Comment thread cmd/kosli/attestJira.go
Comment thread internal/gitview/gitView.go
Comment thread cmd/kosli/root.go Outdated
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Comment thread cmd/kosli/attestJira.go
@mbevc1

mbevc1 commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

@vidhu-balad there is still some feedback from the bot

@mbevc1 mbevc1 added the enhancement New feature or request label Aug 20, 2026
@github-actions github-actions Bot added go Pull requests that update go code feat labels Aug 25, 2026
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
Comment thread cmd/kosli/attestJira_test.go
…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>
Comment thread cmd/kosli/attestJira.go
Comment on lines 66 to +70
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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"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:

Suggested change
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.

Fix this →

Comment thread cmd/kosli/attestJira.go
Comment on lines +42 to +43
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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
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.

Comment thread internal/gitview/gitView.go Outdated
--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{

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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:

  1. 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.
  2. The branch-name half of the PR's headline claim — test 27 proves the commit body isn't scanned (ONA-999 would break the assert); nothing proves the branch isn't. execJiraTestCase already supports branchName (line 414).
  3. The two new logger.Warn branches (attestJira.go:338 and 347) — optional, but (2) below is nearly free.
Suggested change
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.

Fix this →

Comment thread cmd/kosli/root.go Outdated
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."

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
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."

Comment thread cmd/kosli/root.go Outdated
Comment on lines +170 to +172
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."

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
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>
Comment thread cmd/kosli/attestJira.go
Comment on lines +42 to +46
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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
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.

Comment thread cmd/kosli/attestJira.go
Comment on lines 66 to +70
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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"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:

Suggested change
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 interferenote the same pattern
rules still apply to the trailer value itself.
Alternatively, use ^--jira-secondary-source^ with a different identifier format.

Fix this →

additionalConfig: jiraTestsAdditionalConfig{
branchName: "EX-1-some-feature",
commitMessage: "fix: some change with no jira trailer",
},

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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:

Suggested change
},
{
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.

Fix this →

Comment thread cmd/kosli/root.go Outdated
Comment on lines +279 to +282
// 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 {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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:

  1. 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 bare Jira: line yields len(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.
  2. 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.
Suggested change
// 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>
Comment thread cmd/kosli/attestJira.go
Comment on lines +42 to +46
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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
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.

Fix this →

Comment thread cmd/kosli/attestJira.go
Comment on lines 66 to +70
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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"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:

Suggested change
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 interferenote the same pattern
rules still apply to the trailer value itself.
Alternatively, use ^--jira-secondary-source^ with a different identifier format.

Fix this →

Comment on lines +279 to +281
// 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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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:

  1. 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 bare Jira: line yields len(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.
  2. 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.
Suggested change
// 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.

Fix this →

Comment thread cmd/kosli/root.go
@@ -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 }}'"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
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."

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request feat go Pull requests that update go code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants