From 2ba76de7e3d4ff8bcd30d4040a643e24fe667531 Mon Sep 17 00:00:00 2001 From: Lukasz Mierzwa Date: Thu, 7 Dec 2023 18:12:20 +0000 Subject: [PATCH] Use tasks for high severity report on BitBucket --- docs/changelog.md | 2 + internal/reporter/bitbucket_api.go | 185 ++++++++++++++++++------ internal/reporter/bitbucket_api_test.go | 21 +-- internal/reporter/bitbucket_test.go | 8 +- 4 files changed, 160 insertions(+), 56 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index cc85dea7..076ce51f 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -17,6 +17,8 @@ To avoid any errors or slowdowns from scanning unrelated files you might need to add `ci` section to `.pint.hcl` with `include` and/or `exclude` options set. See [examples/ci.hcl](examples/ci.hcl) for an example config. +- `Bug` and `Fatal` severity problems are now reported as tasks when using + BitBucket. ## v0.50.1 diff --git a/internal/reporter/bitbucket_api.go b/internal/reporter/bitbucket_api.go index 8d5848f6..f57c1b94 100644 --- a/internal/reporter/bitbucket_api.go +++ b/internal/reporter/bitbucket_api.go @@ -124,10 +124,12 @@ type BitBucketFileDiffs struct { } type bitBucketComment struct { - id int - version int - text string - anchor BitBucketCommentAnchor + id int + version int + text string + severity string + anchor BitBucketCommentAnchor + replies int } type BitBucketCommentAuthor struct { @@ -135,11 +137,13 @@ type BitBucketCommentAuthor struct { } type BitBucketPullRequestComment struct { - ID int `json:"id"` - Version int `json:"version"` - State string `json:"state"` - Author BitBucketCommentAuthor `json:"author"` - Text string `json:"text"` + ID int `json:"id"` + Version int `json:"version"` + State string `json:"state"` + Author BitBucketCommentAuthor `json:"author"` + Text string `json:"text"` + Severity string `json:"severity"` + Comments []BitBucketPullRequestComment `json:"comments"` } type BitBucketCommentAnchor struct { @@ -181,10 +185,11 @@ type BitBucketPullRequestActivities struct { } type pendingComment struct { - text string - path string - line int - anchor checks.Anchor + severity string + text string + path string + line int + anchor checks.Anchor } func (pc pendingComment) toBitBucketComment(changes *bitBucketPRChanges) BitBucketPendingComment { @@ -197,7 +202,7 @@ func (pc pendingComment) toBitBucketComment(changes *bitBucketPRChanges) BitBuck FileType: "FROM", }, Text: pc.text, - Severity: "NORMAL", + Severity: pc.severity, } if pc.anchor == checks.AnchorBefore { @@ -233,6 +238,16 @@ type BitBucketPendingComment struct { Anchor BitBucketPendingCommentAnchor `json:"anchor"` } +type BitBucketCommentStateUpdate struct { + State string `json:"state"` + Version int `json:"version"` +} + +type BitBucketCommentSeverityUpdate struct { + Severity string `json:"severity"` + Version int `json:"version"` +} + func newBitBucketAPI(pintVersion, uri string, timeout time.Duration, token, project, repo string) *bitBucketAPI { return &bitBucketAPI{ pintVersion: pintVersion, @@ -538,10 +553,12 @@ func (bb bitBucketAPI) getPullRequestComments(pr *bitBucketPR) ([]bitBucketComme act.Comment.Author.Name == username && !act.CommentAnchor.Orphaned { comments = append(comments, bitBucketComment{ - id: act.Comment.ID, - version: act.Comment.Version, - text: act.Comment.Text, - anchor: act.CommentAnchor, + id: act.Comment.ID, + version: act.Comment.Version, + text: act.Comment.Text, + anchor: act.CommentAnchor, + severity: act.Comment.Severity, + replies: len(act.Comment.Comments), }) } } @@ -590,11 +607,21 @@ func (bb bitBucketAPI) makeComments(summary Summary, changes *bitBucketPRChanges buf.WriteString(reports[0].Problem.Reporter) buf.WriteString(".html).\n") + var severity string + // nolint:exhaustive + switch reports[0].Problem.Severity { + case checks.Bug, checks.Fatal: + severity = "BLOCKER" + default: + severity = "NORMAL" + } + pending := pendingComment{ - path: reports[0].ReportedPath, - line: reports[0].Problem.Lines[0], - text: buf.String(), - anchor: reports[0].Problem.Anchor, + severity: severity, + path: reports[0].ReportedPath, + line: reports[0].Problem.Lines[0], + text: buf.String(), + anchor: reports[0].Problem.Anchor, } comments = append(comments, pending.toBitBucketComment(changes)) } @@ -614,32 +641,104 @@ func (bb bitBucketAPI) pruneComments(pr *bitBucketPR, currentComments []bitBucke } } if !keep { - slog.Debug( - "Deleting stale comment", - slog.Int("id", cur.id), - slog.String("path", cur.anchor.Path), - slog.Int("line", cur.anchor.Line), - ) - _, err := bb.request( - http.MethodDelete, - fmt.Sprintf("/rest/api/1.0/projects/%s/repos/%s/pull-requests/%d/comments/%d?version=%d", - bb.project, bb.repo, - pr.ID, - cur.id, cur.version, - ), - nil, - ) - if err != nil { - slog.Error( - "Failed to delete stale BitBucket pull request comment", - slog.Int("id", cur.id), - slog.Any("err", err), - ) + switch { + case cur.replies == 0: + bb.deleteComment(pr, cur) + case cur.severity == "BLOCKER": + bb.resolveTask(pr, cur) + default: + bb.updateSeverity(pr, cur, "BLOCKER") + bb.resolveTask(pr, cur) } } } } +func (bb bitBucketAPI) deleteComment(pr *bitBucketPR, cur bitBucketComment) { + slog.Debug( + "Deleting stale comment", + slog.Int("id", cur.id), + slog.String("path", cur.anchor.Path), + slog.Int("line", cur.anchor.Line), + ) + _, err := bb.request( + http.MethodDelete, + fmt.Sprintf("/rest/api/1.0/projects/%s/repos/%s/pull-requests/%d/comments/%d?version=%d", + bb.project, bb.repo, + pr.ID, + cur.id, cur.version, + ), + nil, + ) + if err != nil { + slog.Error( + "Failed to delete stale BitBucket pull request comment", + slog.Int("id", cur.id), + slog.Any("err", err), + ) + } +} + +func (bb bitBucketAPI) resolveTask(pr *bitBucketPR, cur bitBucketComment) { + slog.Debug( + "Resolving stale blocker comment", + slog.Int("id", cur.id), + slog.String("path", cur.anchor.Path), + slog.Int("line", cur.anchor.Line), + ) + payload, _ := json.Marshal(BitBucketCommentStateUpdate{ + State: "RESOLVED", + Version: cur.version, + }) + _, err := bb.request( + http.MethodPut, + fmt.Sprintf("/rest/api/1.0/projects/%s/repos/%s/pull-requests/%d/comments/%d", + bb.project, bb.repo, + pr.ID, + cur.id, + ), + bytes.NewReader(payload), + ) + if err != nil { + slog.Error( + "Failed to resolve stale blocker BitBucket pull request comment", + slog.Int("id", cur.id), + slog.Any("err", err), + ) + } +} + +func (bb bitBucketAPI) updateSeverity(pr *bitBucketPR, cur bitBucketComment, severity string) { + slog.Debug( + "Updating comment severity", + slog.Int("id", cur.id), + slog.String("path", cur.anchor.Path), + slog.Int("line", cur.anchor.Line), + slog.String("from", cur.severity), + slog.String("to", severity), + ) + payload, _ := json.Marshal(BitBucketCommentSeverityUpdate{ + Severity: severity, + Version: cur.version, + }) + _, err := bb.request( + http.MethodPut, + fmt.Sprintf("/rest/api/1.0/projects/%s/repos/%s/pull-requests/%d/comments/%d", + bb.project, bb.repo, + pr.ID, + cur.id, + ), + bytes.NewReader(payload), + ) + if err != nil { + slog.Error( + "Failed to update BitBucket pull request comment severity", + slog.Int("id", cur.id), + slog.Any("err", err), + ) + } +} + func (bb bitBucketAPI) addComments(pr *bitBucketPR, currentComments []bitBucketComment, pendingComments []BitBucketPendingComment) error { var added int for _, pend := range pendingComments { diff --git a/internal/reporter/bitbucket_api_test.go b/internal/reporter/bitbucket_api_test.go index de3318d3..0a5ad2f1 100644 --- a/internal/reporter/bitbucket_api_test.go +++ b/internal/reporter/bitbucket_api_test.go @@ -22,9 +22,10 @@ func TestPendingCommentToBitBucketComment(t *testing.T) { { description: "nil changes", input: pendingComment{ - text: "this is text", - path: "foo.yaml", - line: 5, + severity: "NORMAL", + text: "this is text", + path: "foo.yaml", + line: 5, }, output: BitBucketPendingComment{ Text: "this is text", @@ -42,9 +43,10 @@ func TestPendingCommentToBitBucketComment(t *testing.T) { { description: "path not found in changes", input: pendingComment{ - text: "this is text", - path: "foo.yaml", - line: 5, + severity: "NORMAL", + text: "this is text", + path: "foo.yaml", + line: 5, }, output: BitBucketPendingComment{ Text: "this is text", @@ -65,9 +67,10 @@ func TestPendingCommentToBitBucketComment(t *testing.T) { { description: "path found in changes", input: pendingComment{ - text: "this is text", - path: "foo.yaml", - line: 5, + severity: "NORMAL", + text: "this is text", + path: "foo.yaml", + line: 5, }, output: BitBucketPendingComment{ Text: "this is text", diff --git a/internal/reporter/bitbucket_test.go b/internal/reporter/bitbucket_test.go index a811234b..73eb40ae 100644 --- a/internal/reporter/bitbucket_test.go +++ b/internal/reporter/bitbucket_test.go @@ -896,7 +896,7 @@ func TestBitBucketReporter(t *testing.T) { pullRequestComments: []reporter.BitBucketPendingComment{ { Text: ":stop_sign: **Bug** reported by [pint](https://cloudflare.github.io/pint/) **mock** check.\n\n------\n\nthis should be ignored, line is not part of the diff\n\n------\n\n:information_source: To see documentation covering this check and instructions on how to resolve it [click here](https://cloudflare.github.io/pint/checks/mock.html).\n", - Severity: "NORMAL", + Severity: "BLOCKER", Anchor: reporter.BitBucketPendingCommentAnchor{ Path: "foo.txt", Line: 1, @@ -907,7 +907,7 @@ func TestBitBucketReporter(t *testing.T) { }, { Text: ":stop_sign: **Fatal** reported by [pint](https://cloudflare.github.io/pint/) **mock** check.\n\n------\n\nbad name\n\n------\n\n:information_source: To see documentation covering this check and instructions on how to resolve it [click here](https://cloudflare.github.io/pint/checks/mock.html).\n", - Severity: "NORMAL", + Severity: "BLOCKER", Anchor: reporter.BitBucketPendingCommentAnchor{ Path: "foo.txt", Line: 2, @@ -918,7 +918,7 @@ func TestBitBucketReporter(t *testing.T) { }, { Text: ":stop_sign: **Bug** reported by [pint](https://cloudflare.github.io/pint/) **mock** check.\n\n------\n\nmock text\n\nmock details\n\n------\n\n:information_source: To see documentation covering this check and instructions on how to resolve it [click here](https://cloudflare.github.io/pint/checks/mock.html).\n", - Severity: "NORMAL", + Severity: "BLOCKER", Anchor: reporter.BitBucketPendingCommentAnchor{ Path: "foo.txt", Line: 2, @@ -1630,7 +1630,7 @@ func TestBitBucketReporter(t *testing.T) { pullRequestComments: []reporter.BitBucketPendingComment{ { Text: ":stop_sign: **Bug** reported by [pint](https://cloudflare.github.io/pint/) **mock** check.\n\n------\n\nthis should be ignored, line is not part of the diff\n\n------\n\nthis should be ignored, line is not part of the diff\n\n------\n\n:information_source: To see documentation covering this check and instructions on how to resolve it [click here](https://cloudflare.github.io/pint/checks/mock.html).\n", - Severity: "NORMAL", + Severity: "BLOCKER", Anchor: reporter.BitBucketPendingCommentAnchor{ Path: "foo.txt", Line: 1,