Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 24 additions & 10 deletions cmd/kosli/attestJira.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,21 @@ type JiraAttestationPayload struct {

type attestJiraOptions struct {
*CommonAttestationOptions
baseURL string
username string
apiToken string
pat string
issueFields string
assert bool
payload JiraAttestationPayload
baseURL string
username string
apiToken string
pat string
issueFields string
secondarySource string
assert bool
payload JiraAttestationPayload
}

const attestJiraShortDesc = `Report a jira attestation to an artifact or a trail in a Kosli flow. `

const attestJiraLongDesc = attestJiraShortDesc + `
Parses the given commit's message or current branch name for Jira issue references of the
form:
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:
'at least 2 characters long, starting with an uppercase letter project key followed by
dash and one or more digits'.

Expand Down Expand Up @@ -134,6 +135,18 @@ kosli attest jira \
--api-token yourAPIToken \
--org yourOrgName \
--assert

# get jira reference from original branch name in a GitHub Pull Request merge job
kosli attest jira \
--name yourAttestationName \
--flow yourFlowName \
--trail yourTrailName \
--jira-secondary-source ${{ github.head_ref }} \
--jira-base-url https://kosli.atlassian.net \
--jira-username [email protected] \
--jira-api-token yourJiraAPIToken \
--api-token yourAPIToken \
--org yourOrgName
`

func newAttestJiraCmd(out io.Writer) *cobra.Command {
Expand Down Expand Up @@ -203,6 +216,7 @@ func newAttestJiraCmd(out io.Writer) *cobra.Command {
cmd.Flags().StringVar(&o.apiToken, "jira-api-token", "", jiraAPITokenFlag)
cmd.Flags().StringVar(&o.pat, "jira-pat", "", jiraPATFlag)
cmd.Flags().StringVar(&o.issueFields, "jira-issue-fields", "", jiraIssueFieldFlag)
cmd.Flags().StringVar(&o.secondarySource, "jira-secondary-source", "", jiraSecondarySourceFlag)
cmd.Flags().BoolVar(&o.assert, "assert", false, attestationAssertFlag)

err := RequireFlags(cmd, []string{"flow", "trail", "name", "commit", "jira-base-url"})
Expand Down Expand Up @@ -235,7 +249,7 @@ func (o *attestJiraOptions) run(args []string) error {
// more info: https://support.atlassian.com/jira-software-cloud/docs/what-is-an-issue/#Workingwithissues-Projectandissuekeys
jiraIssueKeyPattern := `[A-Z][A-Z0-9]{1,9}-[0-9]+`

issueIDs, commitInfo, err := gv.MatchPatternInCommitMessageORBranchName(jiraIssueKeyPattern, o.payload.Commit.Sha1)
issueIDs, commitInfo, err := gv.MatchPatternInCommitMessageORBranchName(jiraIssueKeyPattern, o.payload.Commit.Sha1, o.secondarySource)
if err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/kosli/reportEvidenceCommitJira.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ func (o *reportEvidenceCommitJiraOptions) run(args []string) error {
// more info: https://support.atlassian.com/jira-software-cloud/docs/what-is-an-issue/#Workingwithissues-Projectandissuekeys
jiraIssueKeyPattern := `[A-Z][A-Z0-9]{1,9}-[0-9]+`

issueIDs, commitInfo, err := gv.MatchPatternInCommitMessageORBranchName(jiraIssueKeyPattern, o.payload.CommitSHA)
issueIDs, commitInfo, err := gv.MatchPatternInCommitMessageORBranchName(jiraIssueKeyPattern, o.payload.CommitSHA, "")
if err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/kosli/reportEvidenceCommitJira_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ func (suite *CommitEvidenceJiraCommandTestSuite) TestCommitEvidenceJiraCommandCm
},
},
{
name: "report existing and non existing Jira commit evidence with reference in midle of line works",
name: "report existing and non existing Jira commit evidence with reference in middle of line works",
cmd: fmt.Sprintf(`report evidence commit jira --name jira-validation
--jira-base-url https://kosli-test.atlassian.net --jira-username [email protected]
--repo-root %s
Expand Down
1 change: 1 addition & 0 deletions cmd/kosli/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ The ^.kosli_ignore^ will be treated as part of the artifact like any other file,
jiraAPITokenFlag = "Jira API token (for Jira Cloud)"
jiraPATFlag = "Jira personal access token (for self-hosted Jira)"
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 }}'"
envDescriptionFlag = "[optional] The environment description."
flowDescriptionFlag = "[optional] The Kosli flow description."
trailDescriptionFlag = "[optional] The Kosli trail description."
Expand Down
35 changes: 25 additions & 10 deletions internal/gitview/gitView.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"
"net/url"
"regexp"
"sort"
"strings"

"github.com/go-git/go-git/v5"
Expand Down Expand Up @@ -271,23 +272,37 @@ func getCommitURL(repoURL, commitHash string) string {
// MatchPatternInCommitMessageORBranchName returns a slice of strings matching a pattern in a commit message or branch name
// matches lookup happens in the commit message first, and if none is found, matching against the branch name is done
// if no matches are found in both the commit message and the branch name, an empty slice is returned
func (gv *GitView) MatchPatternInCommitMessageORBranchName(pattern, commitSHA string) ([]string, *CommitInfo, error) {
func (gv *GitView) MatchPatternInCommitMessageORBranchName(pattern, commitSHA, secondarySource string) ([]string, *CommitInfo, error) {
commitInfo, err := gv.GetCommitInfoFromCommitSHA(commitSHA, true, []string{})
if err != nil {
return []string{}, nil, err
}

re := regexp.MustCompile(pattern)
matches := re.FindAllString(commitInfo.Message, -1)
if matches != nil {
return matches, commitInfo, nil
} else {
matches := re.FindAllString(commitInfo.Branch, -1)
if matches != nil {
return matches, commitInfo, nil
}
commitMatches := re.FindAllString(commitInfo.Message, -1)
branchMatches := re.FindAllString(commitInfo.Branch, -1)
secondaryMatches := re.FindAllString(secondarySource, -1)

// Use a map to remove duplicates
uniqueMatches := make(map[string]struct{})
for _, match := range commitMatches {
uniqueMatches[match] = struct{}{}
}
for _, match := range branchMatches {
uniqueMatches[match] = struct{}{}
}
for _, match := range secondaryMatches {
uniqueMatches[match] = struct{}{}
}
return []string{}, commitInfo, nil

// Convert map keys back to a slice
matches := make([]string, 0, len(uniqueMatches))
for match := range uniqueMatches {
matches = append(matches, match)
}
sort.Strings(matches)

return matches, commitInfo, nil
}

// ResolveRevision returns an explicit commit SHA1 from commit SHA or ref (e.g. HEAD~2)
Expand Down
71 changes: 64 additions & 7 deletions internal/gitview/gitView_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -361,12 +361,14 @@ func (suite *GitViewTestSuite) TestMatchPatternInCommitMessageORBranchName() {
require.NoError(suite.T(), err)

for _, t := range []struct {
name string
pattern string
commitMessage string
wantError bool
want []string
commitSha string
name string
pattern string
commitMessage string
secondarySource string
wantError bool
want []string
commitSha string
branchName string
}{
{
name: "One Jira reference found",
Expand All @@ -389,6 +391,55 @@ func (suite *GitViewTestSuite) TestMatchPatternInCommitMessageORBranchName() {
want: []string{},
wantError: false,
},
{
name: "Jira references found in branch name",
pattern: "[A-Z][A-Z0-9]{1,9}-[0-9]+",
commitMessage: "some test commit",
branchName: "EX-5-cool-branch",
want: []string{"EX-5"},
wantError: false,
},
{
name: "Jira references found in secondary source",
pattern: "[A-Z][A-Z0-9]{1,9}-[0-9]+",
commitMessage: "some test commit",
secondarySource: "EX-1-test-commit",
want: []string{"EX-1"},
wantError: false,
},
{
name: "Jira references found in commit and secondary source",
pattern: "[A-Z][A-Z0-9]{1,9}-[0-9]+",
commitMessage: "EX-1 some test commit",
secondarySource: "EX-2-test-commit",
want: []string{"EX-1", "EX-2"},
wantError: false,
},
{
name: "Jira references found in commit and branch name",
pattern: "[A-Z][A-Z0-9]{1,9}-[0-9]+",
commitMessage: "EX-1 some test commit",
branchName: "EX-2-test-commit",
want: []string{"EX-1", "EX-2"},
wantError: false,
},
{
name: "Same Jira references found in commit and branch name is not duplicated",
pattern: "[A-Z][A-Z0-9]{1,9}-[0-9]+",
commitMessage: "DUP-1 some test commit",
branchName: "DUP-1-test-commit",
want: []string{"DUP-1"},
wantError: false,
},
{
name: "Jira references found in commit, branch name and secondary source",
pattern: "[A-Z][A-Z0-9]{1,9}-[0-9]+",
commitMessage: "ALL-1 some test commit",
branchName: "ALL-2-test-commit",
secondarySource: "ALL-3-some-things",
want: []string{"ALL-1", "ALL-2", "ALL-3"},
wantError: false,
},
{
name: "No Jira references found, despite something that looks similar to Jira reference",
pattern: "[A-Z][A-Z0-9]{1,9}-[0-9]+",
Expand Down Expand Up @@ -417,10 +468,16 @@ func (suite *GitViewTestSuite) TestMatchPatternInCommitMessageORBranchName() {
require.NoError(suite.T(), err)
}

if t.branchName != "" {
err := testHelpers.CheckoutNewBranch(workTree, t.branchName)
require.NoError(suite.T(), err)
defer testHelpers.CheckoutMaster(workTree, suite.T())
}

gitView, err := New(suite.tmpDir)
require.NoError(suite.T(), err)

actual, _, err := gitView.MatchPatternInCommitMessageORBranchName(t.pattern, t.commitSha)
actual, _, err := gitView.MatchPatternInCommitMessageORBranchName(t.pattern, t.commitSha, t.secondarySource)
require.True(suite.T(), (err != nil) == t.wantError)
require.ElementsMatch(suite.T(), t.want, actual)

Expand Down
Loading