-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1 from owenrumney/initial-commit
Initial commit
Showing
10 changed files
with
373 additions
and
212 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,77 @@ | ||
# go-github-pr-commenter | ||
Library for adding comments to github PRs | ||
|
||
## What is it? | ||
|
||
A convenience libary that wraps the [go-github](https://github.com/google/go-github) library and allows you to quickly add comments to the lines of changes in a comment. | ||
|
||
The intention is this is used with CI tools to automatically comment on new Github pull requests when static analysis checks are failing. | ||
|
||
For an example of this in use, see the [tfsec-pr-commenter-action](https://github.com/tfsec/tfsec-pr-commenter-action). This Github action will run against your Terraform and report any security issues that are present in the code before it is committed. | ||
|
||
## How do I use it? | ||
|
||
The intention is to keep the interface as clean as possible; steps are | ||
|
||
- create a commenter for a repo and PR | ||
- write comments to the commenter | ||
- comments which exist will not be written | ||
- comments that aren't appropriate (not part of the PR) will not be written | ||
|
||
### Expected Errors | ||
|
||
The following errors can be handled - I hope these are self explanatory | ||
|
||
```go | ||
type PrDoesNotExistError struct { | ||
owner string | ||
repo string | ||
prNumber int | ||
} | ||
|
||
type NotPartOfPrError struct { | ||
filepath string | ||
} | ||
|
||
type CommentAlreadyWrittenError struct { | ||
filepath string | ||
comment string | ||
} | ||
|
||
type CommentNotValidError struct { | ||
filepath string | ||
lineNo int | ||
} | ||
``` | ||
|
||
### Basic Usage Example | ||
|
||
```go | ||
package main | ||
|
||
import ( | ||
commenter "github.com/owenrumney/go-github-pr-commenter" | ||
log "github.com/sirupsen/logrus" | ||
"os" | ||
) | ||
|
||
|
||
// Create the commenter | ||
token := os.Getenv("GITHUB_TOKEN") | ||
|
||
c, err := commenter.NewCommenter(token, "tfsec", "tfsec-example-project", 8) | ||
if err != nil { | ||
fmt.Println(err.Error()) | ||
} | ||
|
||
// process whatever static analysis results you've gathered | ||
for _, result := myResults { | ||
err = c.WriteMultiLineComment(result.Path, result.Comment, result.StartLine, result.EndLine) | ||
if err != nil { | ||
if errors.Is(err, commenter.CommentNotValidError{}) { | ||
log.Debugf("result not relevant for commit. %s", err.Error()) | ||
} else { | ||
log.Errorf("an error occurred writing the comment: %s", err.Error()) | ||
} | ||
} | ||
} | ||
``` |
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package go_github_pr_commenter | ||
|
||
type CommitFileInfo struct { | ||
FileName string | ||
hunkStart int | ||
hunkEnd int | ||
sha string | ||
} | ||
|
||
func (cfi CommitFileInfo) CommentRequired(filename string, startLine int) bool { | ||
return filename == cfi.FileName && startLine > cfi.hunkStart && startLine < cfi.hunkEnd | ||
} | ||
|
||
func (cfi CommitFileInfo) CalculatePosition(line int) *int { | ||
position := line - cfi.hunkStart | ||
return &position | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
package go_github_pr_commenter | ||
|
||
import ( | ||
"context" | ||
"github.com/google/go-github/v32/github" | ||
"golang.org/x/oauth2" | ||
) | ||
|
||
type connector struct { | ||
prs *github.PullRequestsService | ||
owner string | ||
repo string | ||
prNumber int | ||
} | ||
|
||
type existingComment struct { | ||
filename *string | ||
comment *string | ||
} | ||
|
||
func createConnector(token, owner, repo string, prNumber int) *connector { | ||
ctx := context.Background() | ||
ts := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: token}) | ||
tc := oauth2.NewClient(ctx, ts) | ||
|
||
client := github.NewClient(tc) | ||
|
||
return &connector{ | ||
prs: client.PullRequests, | ||
owner: owner, | ||
repo: repo, | ||
prNumber: prNumber, | ||
} | ||
} | ||
|
||
func (c *connector) writeReviewComment(block *github.PullRequestComment) error { | ||
ctx := context.Background() | ||
|
||
var _, _, err = c.prs.CreateComment(ctx, c.owner, c.repo, c.prNumber, block) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func (c *connector) getFilesForPr() ([]*github.CommitFile, error) { | ||
files, _, err := c.prs.ListFiles(context.Background(), c.owner, c.repo, c.prNumber, nil) | ||
if err != nil { | ||
return nil, err | ||
} | ||
var commitFiles []*github.CommitFile | ||
for _, file := range files { | ||
if *file.Status != "deleted" { | ||
commitFiles = append(commitFiles, file) | ||
} | ||
} | ||
return commitFiles, nil | ||
} | ||
|
||
func (c *connector) getExistingComments() ([]*existingComment, error) { | ||
ctx := context.Background() | ||
|
||
comments, _, err := c.prs.ListComments(ctx, c.owner, c.repo, c.prNumber, &github.PullRequestListCommentsOptions{}) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
var existingComments []*existingComment | ||
for _, comment := range comments { | ||
existingComments = append(existingComments, &existingComment{ | ||
filename: comment.Path, | ||
comment: comment.Body, | ||
}) | ||
} | ||
return existingComments, nil | ||
} | ||
|
||
func (c *connector) prExists() bool { | ||
ctx := context.Background() | ||
|
||
_, _, err := c.prs.Get(ctx, c.owner, c.repo, c.prNumber) | ||
return err == nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
package go_github_pr_commenter | ||
|
||
import "fmt" | ||
|
||
type NotPartOfPrError struct { | ||
filepath string | ||
} | ||
|
||
type CommentAlreadyWrittenError struct { | ||
filepath string | ||
comment string | ||
} | ||
|
||
type CommentNotValidError struct { | ||
filepath string | ||
lineNo int | ||
} | ||
|
||
type PrDoesNotExistError struct { | ||
owner string | ||
repo string | ||
prNumber int | ||
} | ||
|
||
func newNotPartOfPrError(filepath string) NotPartOfPrError { | ||
return NotPartOfPrError{ | ||
filepath: filepath, | ||
} | ||
} | ||
|
||
func (e NotPartOfPrError) Error() string { | ||
return fmt.Sprintf("The file [%s] provided is not part of the PR", e.filepath) | ||
} | ||
|
||
func newCommentAlreadyWrittenError(filepath, comment string) CommentAlreadyWrittenError { | ||
return CommentAlreadyWrittenError{ | ||
filepath: filepath, | ||
comment: comment, | ||
} | ||
} | ||
|
||
func (e CommentAlreadyWrittenError) Error() string { | ||
return fmt.Sprintf("The file [%s] already has the comment written [%s]", e.filepath, e.comment) | ||
} | ||
|
||
func newCommentNotValidError(filepath string, line int) CommentNotValidError { | ||
return CommentNotValidError{ | ||
filepath: filepath, | ||
lineNo: line, | ||
} | ||
} | ||
|
||
func (e CommentNotValidError) Error() string { | ||
return fmt.Sprintf("There is nothing to comment on at line [%d] in file [%s]", e.lineNo, e.filepath) | ||
} | ||
|
||
func newPrDoesNotExistError(c *connector) PrDoesNotExistError { | ||
return PrDoesNotExistError{ | ||
owner: c.owner, | ||
repo: c.repo, | ||
prNumber: c.prNumber, | ||
} | ||
} | ||
|
||
func (e PrDoesNotExistError) Error() string { | ||
return fmt.Sprintf("PR number [%d] not found for %s/%s", e.prNumber, e.owner, e.repo) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters