Skip to content
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

Hide "plan" comments #897

Merged
merged 12 commits into from
Apr 16, 2020
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
6 changes: 6 additions & 0 deletions cmd/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ const (
GitlabTokenFlag = "gitlab-token"
GitlabUserFlag = "gitlab-user"
GitlabWebhookSecretFlag = "gitlab-webhook-secret" // nolint: gosec
HidePrevPlanComments = "hide-prev-plan-comments"
LogLevelFlag = "log-level"
PortFlag = "port"
RepoConfigFlag = "repo-config"
Expand Down Expand Up @@ -250,6 +251,11 @@ var boolFlags = map[string]boolFlag{
description: "Disable \"atlantis apply\" command so a specific project/workspace/directory has to be specified for applies.",
defaultValue: false,
},
HidePrevPlanComments: {
description: "Hide previous plan comments will be hidden to reduce clutter in a PR. " +
"VCS support is limited to: Github",
defaultValue: false,
},
RequireApprovalFlag: {
description: "Require pull requests to be \"Approved\" before allowing the apply command to be run.",
defaultValue: false,
Expand Down
14 changes: 6 additions & 8 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module github.com/runatlantis/atlantis
go 1.13

require (
git.apache.org/thrift.git v0.12.0 // indirect
github.com/Laisky/graphql v1.0.4
github.com/Masterminds/semver v1.4.2 // indirect
github.com/Masterminds/sprig v2.15.0+incompatible
github.com/aokoli/goutils v1.0.1 // indirect
Expand All @@ -23,7 +23,6 @@ require (
github.com/google/uuid v0.0.0-20161128191214-064e2069ce9c // indirect
github.com/gorilla/context v0.0.0-20160226214623-1ea25387ff6f // indirect
github.com/gorilla/mux v1.6.2
github.com/grpc-ecosystem/grpc-gateway v1.6.2 // indirect
github.com/hashicorp/go-getter v1.4.0
github.com/hashicorp/go-version v1.2.0
github.com/hashicorp/hcl v0.0.0-20170914154624-68e816d1c783 // indirect
Expand All @@ -36,17 +35,17 @@ require (
github.com/microcosm-cc/bluemonday v1.0.1
github.com/mitchellh/colorstring v0.0.0-20150917214807-8631ce90f286
github.com/mitchellh/go-homedir v1.0.0
github.com/mitchellh/gox v1.0.1 // indirect
github.com/mitchellh/mapstructure v0.0.0-20170523030023-d0303fe80992 // indirect
github.com/mohae/deepcopy v0.0.0-20170603005431-491d3605edfb
github.com/nlopes/slack v0.1.0
github.com/onsi/ginkgo v1.9.0 // indirect
github.com/openzipkin/zipkin-go v0.1.3 // indirect
github.com/pelletier/go-buffruneio v0.2.0 // indirect
github.com/pelletier/go-toml v1.0.0 // indirect
github.com/petergtz/pegomock v2.5.0+incompatible
github.com/petergtz/pegomock v2.7.0+incompatible
github.com/pkg/errors v0.8.0
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829 // indirect
github.com/shurcooL/githubv4 v0.0.0-20191127044304-8f68eb5628d0
github.com/shurcooL/graphql v0.0.0-20181231061246-d48a9a75455f // indirect
github.com/sirupsen/logrus v1.2.0 // indirect
github.com/spf13/afero v0.0.0-20170901052352-ee1bd8ee15a1 // indirect
github.com/spf13/cast v1.1.0 // indirect
github.com/spf13/cobra v0.0.0-20170905172051-b78744579491
Expand All @@ -57,9 +56,8 @@ require (
github.com/urfave/cli v1.20.0
github.com/urfave/negroni v0.2.0
github.com/xanzy/go-gitlab v0.22.2-0.20191127083556-16a492660b8c
golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d // indirect
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5
golang.org/x/net v0.0.0-20191126235420-ef20fe5d7933 // indirect
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553 // indirect
golang.org/x/oauth2 v0.0.0-20191122200657-5d9234df094c // indirect
google.golang.org/appengine v1.6.5 // indirect
gopkg.in/go-playground/assert.v1 v1.2.1 // indirect
Expand Down
179 changes: 15 additions & 164 deletions go.sum

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions runatlantis.io/docs/server-configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,12 @@ Values are chosen in this order:
```
View help.

* ### `--hide-prev-plan-comments`
```bash
atlantis server --hide-prev-plan-comments
```
Hides previous plan comments to declutter PRs.

* ### `--log-level`
```bash
atlantis server --log-level="<debug|info|warn|error>"
Expand Down
11 changes: 11 additions & 0 deletions server/events/command_runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ type DefaultCommandRunner struct {
// this in our error message back to the user on a forked PR so they know
// how to enable this functionality.
AllowForkPRsFlag string
// HidePrevPlanComments will hide previous plan comments to declutter PRs.
HidePrevPlanComments bool
// SilenceForkPRErrors controls whether to comment on Fork PRs when AllowForkPRs = False
SilenceForkPRErrors bool
// SilenceForkPRErrorsFlag is the name of the flag that controls fork PR's. We use
Expand Down Expand Up @@ -421,6 +423,15 @@ func (c *DefaultCommandRunner) updatePull(ctx *CommandContext, command PullComma
ctx.Log.Warn(res.Failure)
}

// HideOldComments will hide old comments left from previous plan runs to reduce
// clutter in a pull/merge request. This will not delete the comment, since the
// comment trail may be useful in auditing or backtracing problems.
if c.HidePrevPlanComments {
if err := c.VCSClient.HideOldComments(ctx.BaseRepo, ctx.Pull.Num); err != nil {
ctx.Log.Err("unable to hide old comments: %s", err)
}
}

comment := c.MarkdownRenderer.Render(res, command.CommandName(), ctx.Log.History.String(), command.IsVerbose(), ctx.BaseRepo.VCSHost.Type)
if err := c.VCSClient.CreateComment(ctx.BaseRepo, ctx.Pull.Num, comment); err != nil {
ctx.Log.Err("unable to comment: %s", err)
Expand Down
4 changes: 4 additions & 0 deletions server/events/vcs/azuredevops_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,10 @@ func (g *AzureDevopsClient) CreateComment(repo models.Repo, pullNum int, comment
return nil
}

func (g *AzureDevopsClient) HideOldComments(repo models.Repo, pullNum int) error {
return nil
}

// PullIsApproved returns true if the merge request was approved.
// https://docs.microsoft.com/en-us/azure/devops/repos/git/branch-policies?view=azure-devops#require-a-minimum-number-of-reviewers
func (g *AzureDevopsClient) PullIsApproved(repo models.Repo, pull models.PullRequest) (bool, error) {
Expand Down
4 changes: 4 additions & 0 deletions server/events/vcs/bitbucketcloud/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,10 @@ func (b *Client) CreateComment(repo models.Repo, pullNum int, comment string) er
return err
}

func (b *Client) HideOldComments(repo models.Repo, pullNum int) error {
return nil
}

// PullIsApproved returns true if the merge request was approved.
func (b *Client) PullIsApproved(repo models.Repo, pull models.PullRequest) (bool, error) {
path := fmt.Sprintf("%s/2.0/repositories/%s/pullrequests/%d", b.BaseURL, repo.FullName, pull.Num)
Expand Down
4 changes: 4 additions & 0 deletions server/events/vcs/bitbucketserver/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,10 @@ func (b *Client) CreateComment(repo models.Repo, pullNum int, comment string) er
return nil
}

func (b *Client) HideOldComments(repo models.Repo, pullNum int) error {
return nil
}

// postComment actually posts the comment. It's a helper for CreateComment().
func (b *Client) postComment(repo models.Repo, pullNum int, comment string) error {
bodyBytes, err := json.Marshal(map[string]string{"text": comment})
Expand Down
1 change: 1 addition & 0 deletions server/events/vcs/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ type Client interface {
// relative to the repo root, e.g. parent/child/file.txt.
GetModifiedFiles(repo models.Repo, pull models.PullRequest) ([]string, error)
CreateComment(repo models.Repo, pullNum int, comment string) error
HideOldComments(repo models.Repo, pullNum int) error
PullIsApproved(repo models.Repo, pull models.PullRequest) (bool, error)
PullIsMergeable(repo models.Repo, pull models.PullRequest) (bool, error)
// UpdateStatus updates the commit status to state for pull. src is the
Expand Down
90 changes: 85 additions & 5 deletions server/events/vcs/github_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,13 @@ import (
"net/url"
"strings"

"github.com/runatlantis/atlantis/server/events/models"
"github.com/runatlantis/atlantis/server/events/vcs/common"

"github.com/Laisky/graphql"
"github.com/google/go-github/v28/github"
"github.com/pkg/errors"
"github.com/runatlantis/atlantis/server/events/models"
"github.com/shurcooL/githubv4"
)

// maxCommentLength is the maximum number of chars allowed in a single comment
Expand All @@ -32,8 +34,10 @@ const maxCommentLength = 65536

// GithubClient is used to perform GitHub actions.
type GithubClient struct {
client *github.Client
ctx context.Context
user string
client *github.Client
v4MutateClient *graphql.Client
ctx context.Context
}

// NewGithubClient returns a valid GitHub client.
Expand All @@ -43,6 +47,8 @@ func NewGithubClient(hostname string, user string, pass string) (*GithubClient,
Password: strings.TrimSpace(pass),
}
client := github.NewClient(tp.Client())
graphqlURL := "https://api.github.com/graphql"

// If we're using github.com then we don't need to do any additional configuration
// for the client. It we're using Github Enterprise, then we need to manually
// set the base url for the API.
Expand All @@ -53,11 +59,31 @@ func NewGithubClient(hostname string, user string, pass string) (*GithubClient,
return nil, errors.Wrapf(err, "Invalid github hostname trying to parse %s", baseURL)
}
client.BaseURL = base
graphqlURL = fmt.Sprintf("https://%s/graphql", hostname)
_, err = url.Parse(graphqlURL)
if err != nil {
return nil, errors.Wrapf(err, "Invalid GraphQL github hostname trying to parse %s", graphqlURL)
}
}

// shurcooL's githubv4 library has a client ctor, but it doesn't support schema
// previews, which need custom Accept headers (https://developer.github.com/v4/previews)
// So for now use the graphql client, since the githubv4 library was basically
// a simple wrapper around it. And instead of using shurcooL's graphql lib, use
// Laisky's, since shurcooL's doesn't support custom headers.
// Once the Minimize Comment schema is official, this can revert back to using
// shurcooL's libraries completely.
v4MutateClient := graphql.NewClient(
graphqlURL,
tp.Client(),
graphql.WithHeader("Accept", "application/vnd.github.queen-beryl-preview+json"),
)

return &GithubClient{
client: client,
ctx: context.Background(),
user: user,
client: client,
v4MutateClient: v4MutateClient,
ctx: context.Background(),
}, nil
}

Expand Down Expand Up @@ -113,6 +139,60 @@ func (g *GithubClient) CreateComment(repo models.Repo, pullNum int, comment stri
return nil
}

func (g *GithubClient) HideOldComments(repo models.Repo, pullNum int) error {
var allComments []*github.IssueComment
for page := 0; ; {
comments, resp, err := g.client.Issues.ListComments(g.ctx, repo.Owner, repo.Name, pullNum, &github.IssueListCommentsOptions{
Sort: "created",
Direction: "asc",
ListOptions: github.ListOptions{Page: page},
})
if err != nil {
return err
}
allComments = append(allComments, comments...)
if resp.NextPage == 0 {
break
}
page = resp.NextPage
}

for _, comment := range allComments {
if comment.User != nil && comment.User.GetLogin() != g.user {
Copy link
Contributor

@mhumeSF mhumeSF Apr 14, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was testing this out but running into issues with case sensitive username.

Suggested change
if comment.User != nil && comment.User.GetLogin() != g.user {
if comment.User != nil && !strings.EqualFold(comment.User.GetLogin(), g.user)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay I'll add it.

continue
}
// Crude filtering: The comment templates typically include the command name
// somewhere in the first line. It's a bit of an assumption, but seems like
// a reasonable one, given we've already filtered the comments by the
// configured Atlantis user.
body := strings.Split(comment.GetBody(), "\n")
firstLine := strings.ToLower(body[0])
if !strings.Contains(firstLine, models.PlanCommand.String()) {
continue
}
var m struct {
MinimizeComment struct {
MinimizedComment struct {
IsMinimized githubv4.Boolean
MinimizedReason githubv4.String
ViewerCanMinimize githubv4.Boolean
}
} `graphql:"minimizeComment(input:$input)"`
}
input := map[string]interface{}{
"input": githubv4.MinimizeCommentInput{
Classifier: githubv4.ReportedContentClassifiersOutdated,
SubjectID: comment.GetNodeID(),
},
}
if err := g.v4MutateClient.Mutate(g.ctx, &m, input); err != nil {
return errors.Wrapf(err, "minimize comment %s", comment.GetNodeID())
}
}

return nil
}

// PullIsApproved returns true if the pull request was approved.
func (g *GithubClient) PullIsApproved(repo models.Repo, pull models.PullRequest) (bool, error) {
nextPage := 0
Expand Down
Loading