diff --git a/cmd/kosli/reportEvidenceCommitJira.go b/cmd/kosli/reportEvidenceCommitJira.go index 7154ac591..749ea4227 100644 --- a/cmd/kosli/reportEvidenceCommitJira.go +++ b/cmd/kosli/reportEvidenceCommitJira.go @@ -22,6 +22,7 @@ type reportEvidenceCommitJiraOptions struct { pat string srcRepoRoot string payload JiraEvidencePayload + assert bool } const reportEvidenceCommitJiraShortDesc = `Report Jira evidence for a commit in Kosli flows.` @@ -75,6 +76,20 @@ kosli report evidence commit jira \ --api-token yourAPIToken \ --org yourOrgName \ --user-data /path/to/json/file.json + + +# fail if no issue reference is found, or the issue is not found in your jira instance +kosli report evidence commit jira \ + --commit yourGitCommitSha1 \ + --name yourEvidenceName \ + --jira-base-url https://kosli.atlassian.net \ + --jira-username user@domain.com \ + --jira-api-token yourJiraAPIToken \ + --flows yourFlowName \ + --build-url https://exampleci.com \ + --api-token yourAPIToken \ + --org yourOrgName \ + --assert ` func newReportEvidenceCommitJiraCmd(out io.Writer) *cobra.Command { @@ -117,6 +132,7 @@ func newReportEvidenceCommitJiraCmd(out io.Writer) *cobra.Command { cmd.Flags().StringVar(&o.srcRepoRoot, "repo-root", ".", repoRootFlag) cmd.Flags().StringVarP(&o.userDataFilePath, "user-data", "u", "", evidenceUserDataFlag) cmd.Flags().StringSliceVarP(&o.evidencePaths, "evidence-paths", "e", []string{}, evidencePathsFlag) + cmd.Flags().BoolVar(&o.assert, "assert", false, assertJiraEvidenceFlag) addDryRunFlag(cmd) err := RequireFlags(cmd, []string{"commit", "build-url", "name", "jira-base-url"}) @@ -145,6 +161,7 @@ func (o *reportEvidenceCommitJiraOptions) run(args []string) error { return err } + logger.Debug("Resolving commit reference: %s", o.payload.CommitSHA) o.payload.CommitSHA, err = gv.ResolveRevision(o.payload.CommitSHA) if err != nil { return err @@ -157,14 +174,19 @@ 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, err := gv.MatchPatternInCommitMessageORBranchName(jiraIssueKeyPattern, o.payload.CommitSHA) + issueIDs, commitInfo, err := gv.MatchPatternInCommitMessageORBranchName(jiraIssueKeyPattern, o.payload.CommitSHA) if err != nil { return err } - + logger.Debug("Checked for Jira issue references in Git commit %s on branch %s commit message:\n%s", commitInfo.Sha1, commitInfo.Branch, commitInfo.Message) logger.Debug("the following Jira references are found in commit message or branch name: %v", issueIDs) + if len(issueIDs) == 0 && o.assert { + return fmt.Errorf("no Jira references are found in commit message or branch name") + } + issueLog := "" + issueFoundCount := 0 for _, issueID := range issueIDs { result, err := jc.GetJiraIssueInfo(issueID) if err != nil { @@ -174,9 +196,13 @@ func (o *reportEvidenceCommitJiraOptions) run(args []string) error { issueExistLog := "issue not found" if result.IssueExists { issueExistLog = "issue found" + issueFoundCount++ } issueLog += fmt.Sprintf("\n\t%s: %s", result.IssueID, issueExistLog) } + if issueFoundCount != len(issueIDs) && o.assert { + return fmt.Errorf("missing Jira issues from references found in commit message or branch name%s", issueLog) + } form, cleanupNeeded, evidencePath, err := newEvidenceForm(o.payload, o.evidencePaths) // if we created a tar package, remove it after uploading it diff --git a/cmd/kosli/reportEvidenceCommitJira_test.go b/cmd/kosli/reportEvidenceCommitJira_test.go index 43248f2bb..003d6df06 100644 --- a/cmd/kosli/reportEvidenceCommitJira_test.go +++ b/cmd/kosli/reportEvidenceCommitJira_test.go @@ -173,6 +173,32 @@ func (suite *CommitEvidenceJiraCommandTestSuite) TestCommitEvidenceJiraCommandCm --build-url example.com %s`, suite.tmpDir, suite.defaultKosliArguments), golden: "Error: required flag(s) \"commit\" not set\n", }, + { + wantError: true, + name: "assert for non-existing Jira issue gives an error", + cmd: fmt.Sprintf(`report evidence commit jira --name jira-validation + --jira-base-url https://kosli-test.atlassian.net --jira-username tore@kosli.com + --repo-root %s + --assert + --build-url example.com %s`, suite.tmpDir, suite.defaultKosliArguments), + goldenRegex: "Error: missing Jira issues from references found in commit message or branch name.*", + additionalConfig: jiraTestsAdditionalConfig{ + commitMessage: "SAMI-1 test commit", + }, + }, + { + wantError: true, + name: "assert for no Jira issue reference gives an error", + cmd: fmt.Sprintf(`report evidence commit jira --name jira-validation + --jira-base-url https://kosli-test.atlassian.net --jira-username tore@kosli.com + --repo-root %s + --assert + --build-url example.com %s`, suite.tmpDir, suite.defaultKosliArguments), + goldenRegex: "Error: no Jira references are found in commit message or branch name", + additionalConfig: jiraTestsAdditionalConfig{ + commitMessage: "test commit without reference", + }, + }, } for _, test := range tests { funcName(test, suite) diff --git a/cmd/kosli/root.go b/cmd/kosli/root.go index 179447364..f669355ef 100644 --- a/cmd/kosli/root.go +++ b/cmd/kosli/root.go @@ -110,6 +110,7 @@ The service principal needs to have the following permissions: commitEvidenceFlag = "Git commit for which to verify and given evidence. (defaulted in some CIs: https://docs.kosli.com/ci-defaults )." repositoryFlag = "Git repository. (defaulted in some CIs: https://docs.kosli.com/ci-defaults )." assertPREvidenceFlag = "[optional] Exit with non-zero code if no pull requests found for the given commit." + assertJiraEvidenceFlag = "[optional] Exit with non-zero code if no jira issue reference found, or jira issue does not exist, for the given commit or branch." assertStatusFlag = "[optional] Exit with non-zero code if Kosli server is not responding." azureTokenFlag = "Azure Personal Access token." azureProjectFlag = "Azure project.(defaulted if you are running in Azure Devops pipelines: https://docs.kosli.com/ci-defaults )." diff --git a/internal/gitview/gitView.go b/internal/gitview/gitView.go index de776803c..ad8a1d6f5 100644 --- a/internal/gitview/gitView.go +++ b/internal/gitview/gitView.go @@ -193,23 +193,23 @@ func asCommitInfo(commit *object.Commit, branchName string) *CommitInfo { // 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, error) { +func (gv *GitView) MatchPatternInCommitMessageORBranchName(pattern, commitSHA string) ([]string, *CommitInfo, error) { commitInfo, err := gv.GetCommitInfoFromCommitSHA(commitSHA) if err != nil { - return []string{}, err + return []string{}, nil, err } re := regexp.MustCompile(pattern) matches := re.FindAllString(commitInfo.Message, -1) if matches != nil { - return matches, nil + return matches, commitInfo, nil } else { matches := re.FindAllString(commitInfo.Branch, -1) if matches != nil { - return matches, nil + return matches, commitInfo, nil } } - return []string{}, nil + return []string{}, commitInfo, nil } // ResolveRevision returns an explicit commit SHA1 from commit SHA or ref (e.g. HEAD~2) diff --git a/internal/gitview/gitView_test.go b/internal/gitview/gitView_test.go index d90729ade..723bd189c 100644 --- a/internal/gitview/gitView_test.go +++ b/internal/gitview/gitView_test.go @@ -327,7 +327,7 @@ func (suite *GitViewTestSuite) TestMatchPatternInCommitMessageORBranchName() { 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) require.True(suite.T(), (err != nil) == t.wantError) require.ElementsMatch(suite.T(), t.want, actual)