Skip to content

Commit

Permalink
Merge pull request #66 from runatlantis/comment-size
Browse files Browse the repository at this point in the history
Split GitHub comments into multiple if over max.
  • Loading branch information
lkysow committed Mar 14, 2018
2 parents 792df5c + b7e58e2 commit 28a9a1b
Show file tree
Hide file tree
Showing 3 changed files with 117 additions and 3 deletions.
52 changes: 50 additions & 2 deletions server/events/vcs/github_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package vcs
import (
"context"
"fmt"
"math"
"net/url"
"strings"

Expand Down Expand Up @@ -70,9 +71,20 @@ func (g *GithubClient) GetModifiedFiles(repo models.Repo, pull models.PullReques
}

// CreateComment creates a comment on the pull request.
// If comment length is greater than the max comment length we split into
// multiple comments.
func (g *GithubClient) CreateComment(repo models.Repo, pullNum int, comment string) error {
_, _, err := g.client.Issues.CreateComment(g.ctx, repo.Owner, repo.Name, pullNum, &github.IssueComment{Body: &comment})
return err
// maxCommentBodySize is derived from the error message when you go over
// this limit.
const maxCommentBodySize = 65536
comments := g.splitAtMaxChars(comment, maxCommentBodySize, "\ncontinued...\n")
for _, c := range comments {
_, _, err := g.client.Issues.CreateComment(g.ctx, repo.Owner, repo.Name, pullNum, &github.IssueComment{Body: &c})
if err != nil {
return err
}
}
return nil
}

// PullIsApproved returns true if the pull request was approved.
Expand Down Expand Up @@ -115,3 +127,39 @@ func (g *GithubClient) UpdateStatus(repo models.Repo, pull models.PullRequest, s
_, _, err := g.client.Repositories.CreateStatus(g.ctx, repo.Owner, repo.Name, pull.HeadCommit, status)
return err
}

// splitAtMaxChars splits comment into a slice with string up to max
// len separated by join which gets appended to the ends of the middle strings.
// If max <= len(join) we return an empty slice since this is an edge case we
// don't want to handle.
func (g *GithubClient) splitAtMaxChars(comment string, max int, join string) []string {
// If we're under the limit then no need to split.
if len(comment) <= max {
return []string{comment}
}

// If we can't fit the joining string in then this doesn't make sense.
if max <= len(join) {
return nil
}

var comments []string
maxSize := max - len(join)
numComments := int(math.Ceil(float64(len(comment)) / float64(maxSize)))
for i := 0; i < numComments; i++ {
upTo := g.min(len(comment), (i+1)*maxSize)
portion := comment[i*maxSize : upTo]
if i < numComments-1 {
portion += join
}
comments = append(comments, portion)
}
return comments
}

func (g *GithubClient) min(a, b int) int {
if a < b {
return a
}
return b
}
65 changes: 65 additions & 0 deletions server/events/vcs/github_client_internal_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package vcs

import (
"testing"

. "github.com/runatlantis/atlantis/testing"
)

func TestSplitAtMaxChars(t *testing.T) {
cases := []struct {
comment string
max int
exp []string
}{
// Test when comment is <= max length.
{
"",
5,
[]string{""},
},
{
"1",
5,
[]string{"1"},
},
{
"12345",
5,
[]string{"12345"},
},
// Now test when we need to join.
{
"123456",
5,
[]string{"1join", "2join", "3join", "4join", "5join", "6"},
},
{
"123456",
10,
[]string{"123456"},
},
{
"12345678901",
10,
[]string{"123456join", "78901"},
},
// Test the edge case of max < len("join")
{
"abc",
2,
nil,
},
{
"abcde",
4,
nil,
},
}
for _, c := range cases {
client := GithubClient{}
t.Run(c.comment, func(t *testing.T) {
Equals(t, c.exp, client.splitAtMaxChars(c.comment, c.max, "join"))
})
}
}
3 changes: 2 additions & 1 deletion server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -334,10 +334,11 @@ func (s *Server) Index(w http.ResponseWriter, _ *http.Request) {
Time: v.Time,
})
}
// nolint: errcheck
s.IndexTemplate.Execute(w, IndexData{
Locks: lockResults,
AtlantisVersion: s.AtlantisVersion,
}) // nolint: errcheck
})
}

// GetLockRoute is the GET /locks/{id} route. It renders the lock detail view.
Expand Down

0 comments on commit 28a9a1b

Please sign in to comment.