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
33 changes: 26 additions & 7 deletions cmd/kosli/attestJira.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,13 @@ type JiraAttestationPayload struct {

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

const attestJiraShortDesc = `Report a jira attestation to an artifact or a trail in a Kosli flow. `
Expand All @@ -41,6 +42,11 @@ The attestation is reported in all cases, and its compliance status depends on r
existing Jira issues.
If you have wrong Jira credentials or wrong Jira-base-url it will be reported as non existing Jira issue.
This is because Jira returns same 404 error code in all cases.

The ^--jira-issue-fields^ can be used to include fields from the jira issue. By default no fields
are included. ^*all^ will give all fields. Using ^--jira-issue-fields "*all" --dry-run^ will give you
the complete list so you can select the once you need. The issue fields uses the jira API that is documented here:
https://developer.atlassian.com/cloud/jira/platform/rest/v2/api-group-issues/#api-rest-api-2-issue-issueidorkey-get-request
` + attestationBindingDesc + `

` + commitDescription
Expand Down Expand Up @@ -81,6 +87,18 @@ kosli attest jira \
--api-token yourAPIToken \
--org yourOrgName

# report a jira attestation about a trail and include jira issue summary, description and creator:
kosli attest jira \
--name yourAttestationName \
--flow yourFlowName \
--trail yourTrailName \
--jira-base-url https://kosli.atlassian.net \
--jira-username user@domain.com \
--jira-api-token yourJiraAPIToken \
--jira-issue-fields "summary,description,creator"
--api-token yourAPIToken \
--org yourOrgName

# report a jira attestation about an artifact which has not been reported yet in a trail:
kosli attest jira \
--name yourTemplateArtifactName.yourAttestationName \
Expand Down Expand Up @@ -184,6 +202,7 @@ func newAttestJiraCmd(out io.Writer) *cobra.Command {
cmd.Flags().StringVar(&o.username, "jira-username", "", jiraUsernameFlag)
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().BoolVar(&o.assert, "assert", false, attestationAssertFlag)

err := RequireFlags(cmd, []string{"flow", "trail", "name", "commit", "jira-base-url"})
Expand Down Expand Up @@ -226,7 +245,7 @@ func (o *attestJiraOptions) run(args []string) error {
issueLog := ""
issueFoundCount := 0
for _, issueID := range issueIDs {
result, err := jc.GetJiraIssueInfo(issueID)
result, err := jc.GetJiraIssueInfo(issueID, o.issueFields)
if err != nil {
return err
}
Expand Down
11 changes: 11 additions & 0 deletions cmd/kosli/attestJira_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,17 @@ func (suite *AttestJiraCommandTestSuite) TestAttestJiraCmd() {
commitMessage: "EX-1 test commit",
},
},
{
name: "can attest jira against a trail with summary and description from jira issue fields",
cmd: fmt.Sprintf(`attest jira --name bar
--jira-base-url https://kosli-test.atlassian.net --jira-username tore@kosli.com
--jira-issue-fields "summary,description"
--repo-root %s %s`, suite.tmpDir, suite.defaultKosliArguments),
golden: "jira attestation 'bar' is reported to trail: test-123\n",
additionalConfig: jiraTestsAdditionalConfig{
commitMessage: "EX-1 test commit",
},
},
{
name: "can attest jira against a trail when name is not found in the trail template",
cmd: fmt.Sprintf(`attest jira --name additional
Expand Down
2 changes: 1 addition & 1 deletion cmd/kosli/reportEvidenceCommitJira.go
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ func (o *reportEvidenceCommitJiraOptions) run(args []string) error {
issueLog := ""
issueFoundCount := 0
for _, issueID := range issueIDs {
result, err := jc.GetJiraIssueInfo(issueID)
result, err := jc.GetJiraIssueInfo(issueID, "")
if err != nil {
return err
}
Expand Down
1 change: 1 addition & 0 deletions cmd/kosli/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ The ^.kosli_ignore^ will be treated as part of the artifact like any other file,
jiraUsernameFlag = "Jira username (for Jira Cloud)"
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."
envDescriptionFlag = "[optional] The environment description."
flowDescriptionFlag = "[optional] The Kosli flow description."
trailDescriptionFlag = "[optional] The Kosli trail description."
Expand Down
24 changes: 19 additions & 5 deletions internal/jira/jira.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@ type JiraConfig struct {
}

type JiraIssueInfo struct {
IssueID string `json:"issue_id"`
IssueURL string `json:"issue_url"`
IssueExists bool `json:"issue_exists"`
IssueID string `json:"issue_id"`
IssueURL string `json:"issue_url"`
IssueExists bool `json:"issue_exists"`
IssueFields *jira.IssueFields `json:"issue_fields,omitempty"`
}

// NewJiraConfig returns a new JiraConfig
Expand Down Expand Up @@ -61,7 +62,7 @@ func (jc *JiraConfig) NewJiraClient() (*jira.Client, error) {

// GetJiraIssueInfo retrieve Jira issue information
// if issue is not found, we still return a JiraIssueInfo object with IssueExists set to false
func (jc *JiraConfig) GetJiraIssueInfo(issueID string) (*JiraIssueInfo, error) {
func (jc *JiraConfig) GetJiraIssueInfo(issueID string, issueFields string) (*JiraIssueInfo, error) {
result := &JiraIssueInfo{
IssueID: issueID,
IssueExists: false,
Expand All @@ -72,13 +73,26 @@ func (jc *JiraConfig) GetJiraIssueInfo(issueID string) (*JiraIssueInfo, error) {
if err != nil {
return result, err
}
issue, response, err := jiraClient.Issue.Get(issueID, nil)

// API will return all fields if the Fields is empty so we default to a non-existing field.
// The user can use '*all' if they want all
if issueFields == "" {
issueFields = "non-existing-key-in-jira-fields"
}
queryOptions := jira.GetQueryOptions{
Fields: issueFields,
}

issue, response, err := jiraClient.Issue.Get(issueID, &queryOptions)
if err != nil && response.StatusCode != http.StatusNotFound {
return result, err
}

if issue != nil {
result.IssueExists = true
if issue.Fields != nil {
result.IssueFields = issue.Fields
}
}
return result, nil
}
Loading