8000 Added bitbucket server (v1 api) support by shogo82148 · Pull Request #998 · reviewdog/reviewdog · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Added bitbucket server (v1 api) support #998

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### :sparkles: Release Note <!-- optional -->

### :rocket: Enhancements
- [#911](https://github.com/reviewdog/reviewdog/pull/911) Added bitbucket server (v1 api) support
- Added support for Bitbucket Pipes executed within Pipelines.

---
Expand Down
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ by diff.
* [Reporter: GitLab MergeRequest discussions (-reporter=gitlab-mr-discussion)](#reporter-gitlab-mergerequest-discussions--reportergitlab-mr-discussion)
* [Reporter: GitLab MergeRequest commit (-reporter=gitlab-mr-commit)](#reporter-gitlab-mergerequest-commit--reportergitlab-mr-commit)
* [Reporter: Bitbucket Code Insights Reports (-reporter=bitbucket-code-report)](#reporter-bitbucket-code-insights-reports--reporterbitbucket-code-report)
* [Reporter: Bitbucket Server PullRequest review comment (-reporter=bitbucket-pr-review)](#reporter-bitbucket-server-pullrequest-review-comment--reporterbitbucket-pr-review)
- [Supported CI services](#supported-ci-services)
* [GitHub Actions](#github-actions)
* [Travis CI](#travis-ci)
Expand Down Expand Up @@ -305,6 +306,7 @@ Note that not all reporters provide support of code suggestion.
| **`gitlab-mr-commit`** | NO [2] |
| **`gerrit-change-review`** | NO [1] |
| **`bitbucket-code-report`** | NO [2] |
| **`bitbucket-pr-review`** | NO [2] |

- [1] The reporter service support code suggestion feature, but reviewdog does not support it yet. See [#678](https://github.com/reviewdog/reviewdog/issues/678) for the status.
- [2] The reporter service itself doesn't support code suggestion feature.
Expand Down Expand Up @@ -552,6 +554,21 @@ $ export BITBUCKET_PASSWORD="my_password& 8000 quot;
$ reviewdog -reporter=bitbucket-code-report
```

### Reporter: Bitbucket Server PullRequest review comment (-reporter=bitbucket-pr-review)

bitbucket-pr-review reporter reports results to Bitbucket Server PullRequest review comments

- For Basic Auth you need to set following env variables:
`BITBUCKET_USER` and `BITBUCKET_PASSWORD`
- For AccessToken Auth you need to set `BITBUCKET_ACCESS_TOKEN`

```shell
$ export BITBUCKET_USER="my_user"
$ export BITBUCKET_PASSWORD="my_password"
$ export BITBUCKET_API="https://<bitbucket-host>/rest"
$ reviewdog -reporter=bitbucket-pr-review
```

## Supported CI services

### [GitHub Actions](https://github.com/features/actions)
Expand Down Expand Up @@ -885,6 +902,7 @@ so reviewdog will use [Check annotation](https://developer.github.com/v3/checks/
| **`gitlab-mr-commit`** | OK | Partially Supported [2] | Partially Supported [2] | Partially Supported [2] |
| **`gerrit-change-review`** | OK | OK? [3] | OK? [3] | Partially Supported? [2][3] |
| **`bitbucket-code-report`** | NO [4] | NO [4] | NO [4] | OK |
| **`bitbucket-pr-review`** | OK | OK | OK | Partially Supported [2] |

- [1] Report results which is outside diff context with Check annotation as fallback if it's running in GitHub actions instead of Review API (comments). All results will be reported to console as well.
- [2] Report results which is outside diff file to console.
Expand Down
56 changes: 56 additions & 0 deletions cmd/reviewdog/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"strings"
"text/tabwriter"

bitbucketv1 "github.com/gfleury/go-bitbucket-v1"
"golang.org/x/build/gerrit"
"golang.org/x/oauth2"

Expand Down Expand Up @@ -168,6 +169,13 @@ const (
- For AccessToken Auth you need to set BITBUCKET_ACCESS_TOKEN
Running on Bitbucket Server is not tested/supported yet.

"bitbucket-pr-review"
Report results to Bitbucket Server review comments.

1. Set BITBUCKET_USER and BITBUCKET_PASSWORD for basic authentication or
BITBUCKET_ACCESS_TOKEN for AccessToken Auth authentication.
2. Set BITBUCKET_API url

For GitHub Enterprise and self hosted GitLab, set
REVIEWDOG_INSECURE_SKIP_VERIFY to skip verifying SSL (please use this at your own risk)
$ export REVIEWDOG_INSECURE_SKIP_VERIFY=true
Expand Down Expand Up @@ -369,6 +377,26 @@ github-pr-check reporter as a fallback.
}
opt.filterMode = filter.ModeNoFilter
ds = &reviewdog.EmptyDiff{}
case "bitbucket-pr-review":
build, cli, err := bitbucketv1BuildWithClient(ctx)
if err != nil {
return err
}
if build.PullRequest == 0 {
fmt.Fprintln(os.Stderr, "this is not PullRequest build.")
return nil
}

gc, err := bbservice.NewPullRequestCommenter(cli, build.Owner, build.Repo, build.PullRequest, build.SHA)
if err != nil {
return err
}

cs = reviewdog.MultiCommentService(gc, cs)
ds, err = bbservice.NewPullRequestDiff(cli, build.Owner, build.Repo, build.PullRequest, build.SHA)
if err != nil {
return err
}
case "local":
if opt.diffCmd == "" && opt.filterMode == filter.ModeNoFilter {
ds = &reviewdog.EmptyDiff{}
Expand Down Expand Up @@ -632,6 +660,34 @@ func bitbucketBuildWithClient(ctx context.Context) (*cienv.BuildInfo, *bitbucket
return build, client, ctx, nil
}

func bitbucketv1BuildWithClient(ctx context.Context) (*cienv.BuildInfo, *bitbucketv1.APIClient, error) {
build, _, err := cienv.GetBuildInfo()
if err != nil {
return nil, nil, err
}

bbBasePath := os.Getenv("BITBUCKET_API")
if bbBasePath == "" {
return nil, nil, errors.New("cannot get bitbucket v1 api address from environment variable. Set BITBUCKET_API ?")
}

bbUser := os.Getenv("BITBUCKET_USER")
bbPass := os.Getenv("BITBUCKET_PASSWORD")
bbAccessToken := os.Getenv("BITBUCKET_ACCESS_TOKEN")

if bbUser != "" && bbPass != "" {
basicAuth := bitbucketv1.BasicAuth{UserName: bbUser, Password: bbPass}
ctx = context.WithValue(ctx, bitbucketv1.ContextBasicAuth, basicAuth)
}

if bbAccessToken != "" {
ctx = context.WithValue(ctx, bitbucketv1.ContextAccessToken, bbAccessToken)
}

client := bitbucketv1.NewAPIClient(ctx, bitbucketv1.NewConfiguration(bbBasePath))
return build, client, nil
}

func fetchMergeRequestIDFromCommit(cli *gitlab.Client, projectID, sha string) (id int, err error) {
// https://docs.gitlab.com/ce/api/merge_requests.html#list-project-merge-requests
opt := &gitlab.ListProjectMergeRequestsOptions{
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ require (
cloud.google.com/go/datastore v1.1.0
contrib.go.opencensus.io/exporter/stackdriver v0.13.8
github.com/bradleyfalzon/ghinstallation v1.1.1
github.com/gfleury/go-bitbucket-v1 v0.0.0-20210119103841-412cc3323e5e
github.com/golang/protobuf v1.5.2
github.com/google/go-cmp v0.5.6
github.com/google/go-github/v37 v37.0.0
Expand Down
5 changes: 4 additions & 1 deletion go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.m
github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
github.com/gfleury/go-bitbucket-v1 v0.0.0-20210119103841-412cc3323e5e h1:wl/vkmB2Qws0xYZPSgyO4m8MkxXXI/XopMJFvmQe08k=
github.com/gfleury/go-bitbucket-v1 v0.0.0-20210119103841-412cc3323e5e/go.mod h1:LB3osS9X2JMYmTzcCArHHLrndBAfcVLQAvUddfs+>
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
Expand Down Expand Up @@ -207,6 +209,8 @@ github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk=
github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
Expand All @@ -223,7 +227,6 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
Expand Down
147 changes: 147 additions & 0 deletions service/bitbucket/commenter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
package bitbucket

import (
"context"
"fmt"
"path/filepath"
"sync"

bbv1api "github.com/gfleury/go-bitbucket-v1"
"golang.org/x/sync/errgroup"

"github.com/reviewdog/reviewdog"
"github.com/reviewdog/reviewdog/service/commentutil"
"github.com/reviewdog/reviewdog/service/serviceutil"
)

var _ reviewdog.CommentService = &PullRequestCommenter{}

// PullRequestCommenter is a comment service for Bitbucket pull request discussion.
//
// API:
// https://docs.atlassian.com/bitbucket-server/rest/5.16.0/bitbucket-rest.html#idm8286336848
// POST /rest/api/1.0/projects/{projectKey}/repos/{repositorySlug}/pull-requests/{pullRequestId}/comments
type PullRequestCommenter struct {
cli *bbv1api.APIClient
pr int64
sha, owner, repo string

muComments sync.Mutex
postComments []*reviewdog.Comment

// wd is working directory relative to root of repository.
wd string
}

// NewPullRequestCommenter returns a new PullRequestCommenter service.
// PullRequestCommenter service needs git command in $PATH.
func NewPullRequestCommenter(cli *bbv1api.APIClient, owner, repo string, pr int, sha string) (*PullRequestCommenter,
error) {
workDir, err := serviceutil.GitRelWorkdir()
if err != nil {
return nil, fmt.Errorf("PullRequestCommenter needs 'git' command: %w", err)
}
return &PullRequestCommenter{
cli: cli,
pr: int64(pr),
sha: sha,
owner: owner,
repo: repo,
wd: workDir,
}, nil
}

// Post accepts a comment and holds it. Flush method actually posts comments to
// BitBucket in parallel.
func (g *PullRequestCommenter) Post(_ context.Context, c *reviewdog.Comment) error {
c.Result.Diagnostic.GetLocation().Path = filepath.ToSlash(
filepath.Join(g.wd, c.Result.Diagnostic.GetLocation().GetPath()))
g.muComments.Lock()
defer g.muComments.Unlock()
g.postComments = append(g.postComments, c)
return nil
}

// Flush posts comments which has not been posted yet.
func (g *PullRequestCommenter) Flush(ctx context.Context) error {
g.muComments.Lock()
defer g.muComments.Unlock()
postedcs, err := g.createPostedComments()
if err != nil {
return fmt.Errorf("failed to create posted comments: %w", err)
}
return g.postCommentsForEach(ctx, postedcs)
}

func (g *PullRequestCommenter) createPostedComments() (commentutil.PostedComments, error) {
postedcs := make(commentutil.PostedComments)
activities, err := listAllPullRequestActivities(g.cli, g.owner, g.repo, g.pr, map[string]interface{}{"limit": 100})
if err != nil {
return nil, fmt.Errorf("failed to list all pull request activities: %w", err)
}
for _, a := range activities {
if a.Action != bbv1api.ActionCommented || a.CommentAnchor.Line == 0 || a.Comment.Text == "" {
continue
}
postedcs.AddPostedComment(a.CommentAnchor.Path, a.CommentAnchor.Line, a.Comment.Text)
}
return postedcs, nil
}

func (g *PullRequestCommenter) postCommentsForEach(_ context.Context, postedcs commentutil.PostedComments) error {
var eg errgroup.Group
for _, c := range g.postComments {
c := c
loc := c.Result.Diagnostic.GetLocation()
lnum := int(loc.GetRange().GetStart().GetLine())
body := commentutil.BitBucketMarkdownComment(c)
if !c.Result.InDiffFile || lnum == 0 || postedcs.IsPosted(c, lnum, body) {
continue
}
eg.Go(func() error {
anchor := &bbv1api.Anchor{
DiffType: bbv1api.DiffTypeEffective,
Line: lnum,
LineType: bbv1api.LineTypeAdded,
FileType: bbv1api.FileTypeTo,
Path: loc.GetPath(),
SrcPath: c.Result.OldPath,
}
comment := bbv1api.Comment{
Text: body,
Anchor: anchor,
}
_, err := g.cli.DefaultApi.CreatePullRequestComment(g.owner, g.repo, int(g.pr), comment,
[]string{"application/json"})
if err != nil {
return fmt.Errorf("failed to create pull request comment: %w", err)
}
return nil
})
}
return eg.Wait()
}

func listAllPullRequestActivities(cli *bbv1api.APIClient, owner, repo string, pr int64, opts map[string]interface{}) (
[]bbv1api.Activity, error) {
resp, err := cli.DefaultApi.GetActivities(owner, repo, pr, opts)
if err != nil {
return nil, err
}
activities, err := bbv1api.GetActivitiesResponse(resp)
if err != nil {
return nil, err
}
if activities.IsLastPage {
return activities.Values, nil
}
newOpts := map[string]interface{}{
"start": activities.NextPageStart,
"limit": opts["limit"],
}
restActivities, err := listAllPullRequestActivities(cli, owner, repo, pr, newOpts)
if err != nil {
return nil, err
}
return append(activities.Values, restActivities...), nil
}
Loading
0