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

Disable markdown folding on gitlab < 11.1 #366

Merged
merged 2 commits into from
Nov 30, 2018
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
14 changes: 12 additions & 2 deletions server/events/markdown_renderer.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,12 @@ var (
)

// MarkdownRenderer renders responses as markdown.
type MarkdownRenderer struct{}
type MarkdownRenderer struct {
// GitlabSupportsCommonMark is true if the version of GitLab we're
// using supports the CommonMark markdown format.
// If we're not configured with a GitLab client, this will be false.
GitlabSupportsCommonMark bool
}

// CommonData is data that all responses have.
type CommonData struct {
Expand Down Expand Up @@ -156,13 +161,18 @@ func (m *MarkdownRenderer) renderProjectResults(results []ProjectResult, common

// shouldUseWrappedTmpl returns true if we should use the wrapped markdown
// templates that collapse the output to make the comment smaller on initial
// load.
// load. Some VCS providers or versions of VCS providers don't support this
// syntax.
func (m *MarkdownRenderer) shouldUseWrappedTmpl(vcsHost models.VCSHostType, output string) bool {
// Bitbucket Cloud and Server don't support the folding markdown syntax.
if vcsHost == models.BitbucketServer || vcsHost == models.BitbucketCloud {
return false
}

if vcsHost == models.Gitlab && !m.GitlabSupportsCommonMark {
return false
}

return strings.Count(output, "\n") > maxUnwrappedLines
}

Expand Down
147 changes: 91 additions & 56 deletions server/events/markdown_renderer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -582,56 +582,74 @@ $$$
// VCS hosts during an error.
func TestRenderProjectResults_WrappedErr(t *testing.T) {
cases := []struct {
VCSHost models.VCSHostType
Output string
ShouldWrap bool
VCSHost models.VCSHostType
GitlabCommonMarkSupport bool
Output string
ShouldWrap bool
}{
{
models.Github,
strings.Repeat("line\n", 1),
false,
VCSHost: models.Github,
Output: strings.Repeat("line\n", 1),
ShouldWrap: false,
},
{
models.Github,
strings.Repeat("line\n", 13),
true,
VCSHost: models.Github,
Output: strings.Repeat("line\n", 13),
ShouldWrap: true,
},
{
models.Gitlab,
strings.Repeat("line\n", 1),
false,
VCSHost: models.Gitlab,
GitlabCommonMarkSupport: false,
Output: strings.Repeat("line\n", 1),
ShouldWrap: false,
},
{
models.Gitlab,
strings.Repeat("line\n", 13),
true,
VCSHost: models.Gitlab,
GitlabCommonMarkSupport: false,
Output: strings.Repeat("line\n", 13),
ShouldWrap: false,
},
{
models.BitbucketCloud,
strings.Repeat("line\n", 1),
false,
VCSHost: models.Gitlab,
GitlabCommonMarkSupport: true,
Output: strings.Repeat("line\n", 1),
ShouldWrap: false,
},
{
models.BitbucketCloud,
strings.Repeat("line\n", 13),
false,
VCSHost: models.Gitlab,
GitlabCommonMarkSupport: true,
Output: strings.Repeat("line\n", 13),
ShouldWrap: true,
},
{
models.BitbucketServer,
strings.Repeat("line\n", 1),
false,
VCSHost: models.BitbucketCloud,
Output: strings.Repeat("line\n", 1),
ShouldWrap: false,
},
{
models.BitbucketServer,
strings.Repeat("line\n", 13),
false,
VCSHost: models.BitbucketCloud,
Output: strings.Repeat("line\n", 13),
ShouldWrap: false,
},
{
VCSHost: models.BitbucketServer,
Output: strings.Repeat("line\n", 1),
ShouldWrap: false,
},
{
VCSHost: models.BitbucketServer,
Output: strings.Repeat("line\n", 13),
ShouldWrap: false,
},
}

mr := events.MarkdownRenderer{}
for _, c := range cases {
t.Run(fmt.Sprintf("%s_%v", c.VCSHost.String(), c.ShouldWrap),
func(t *testing.T) {
mr := events.MarkdownRenderer{
GitlabSupportsCommonMark: c.GitlabCommonMarkSupport,
}

rendered := mr.Render(events.CommandResult{
ProjectResults: []events.ProjectResult{
{
Expand Down Expand Up @@ -675,57 +693,74 @@ $$$
// VCS hosts for a single project.
func TestRenderProjectResults_WrapSingleProject(t *testing.T) {
cases := []struct {
VCSHost models.VCSHostType
Output string
ShouldWrap bool
VCSHost models.VCSHostType
GitlabCommonMarkSupport bool
Output string
ShouldWrap bool
}{
{
models.Github,
strings.Repeat("line\n", 1),
false,
VCSHost: models.Github,
Output: strings.Repeat("line\n", 1),
ShouldWrap: false,
},
{
models.Github,
strings.Repeat("line\n", 13),
true,
VCSHost: models.Github,
Output: strings.Repeat("line\n", 13),
ShouldWrap: true,
},
{
VCSHost: models.Gitlab,
GitlabCommonMarkSupport: false,
Output: strings.Repeat("line\n", 1),
ShouldWrap: false,
},
{
models.Gitlab,
strings.Repeat("line\n", 1),
false,
VCSHost: models.Gitlab,
GitlabCommonMarkSupport: false,
Output: strings.Repeat("line\n", 13),
ShouldWrap: false,
},
{
models.Gitlab,
strings.Repeat("line\n", 13),
true,
VCSHost: models.Gitlab,
GitlabCommonMarkSupport: true,
Output: strings.Repeat("line\n", 1),
ShouldWrap: false,
},
{
models.BitbucketCloud,
strings.Repeat("line\n", 1),
false,
VCSHost: models.Gitlab,
GitlabCommonMarkSupport: true,
Output: strings.Repeat("line\n", 13),
ShouldWrap: true,
},
{
models.BitbucketCloud,
strings.Repeat("line\n", 13),
false,
VCSHost: models.BitbucketCloud,
Output: strings.Repeat("line\n", 1),
ShouldWrap: false,
},
{
models.BitbucketServer,
strings.Repeat("line\n", 1),
false,
VCSHost: models.BitbucketCloud,
Output: strings.Repeat("line\n", 13),
ShouldWrap: false,
},
{
models.BitbucketServer,
strings.Repeat("line\n", 13),
false,
VCSHost: models.BitbucketServer,
Output: strings.Repeat("line\n", 1),
ShouldWrap: false,
},
{
VCSHost: models.BitbucketServer,
Output: strings.Repeat("line\n", 13),
ShouldWrap: false,
},
}

mr := events.MarkdownRenderer{}
for _, c := range cases {
for _, cmd := range []events.CommandName{events.PlanCommand, events.ApplyCommand} {
t.Run(fmt.Sprintf("%s_%s_%v", c.VCSHost.String(), cmd.String(), c.ShouldWrap),
func(t *testing.T) {
mr := events.MarkdownRenderer{
GitlabSupportsCommonMark: c.GitlabCommonMarkSupport,
}
var pr events.ProjectResult
switch cmd {
case events.PlanCommand:
Expand Down
104 changes: 104 additions & 0 deletions server/events/vcs/gitlab_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,75 @@ package vcs

import (
"fmt"
"net"
"net/url"
"strings"

"github.com/hashicorp/go-version"
"github.com/pkg/errors"
"github.com/runatlantis/atlantis/server/logging"

"github.com/lkysow/go-gitlab"
"github.com/runatlantis/atlantis/server/events/models"
)

type GitlabClient struct {
Client *gitlab.Client
// Version is set to the server version.
Version *version.Version
}

// commonMarkSupported is a version constraint that is true when this version of
// GitLab supports CommonMark, a markdown specification.
// See https://about.gitlab.com/2018/07/22/gitlab-11-1-released/
var commonMarkSupported = MustConstraint(">=11.1")

// gitlabClientUnderTest is true if we're running under go test.
var gitlabClientUnderTest = false

// NewGitlabClient returns a valid GitLab client.
func NewGitlabClient(hostname string, token string, logger *logging.SimpleLogger) (*GitlabClient, error) {
client := &GitlabClient{
Client: gitlab.NewClient(nil, token),
}

// If not using gitlab.com we need to set the URL to the API.
if hostname != "gitlab.com" {
u, err := url.Parse(hostname)
if err != nil {
return nil, errors.Wrapf(err, "parsing hostname %q", hostname)
}

// Warn if this hostname isn't resolvable. The GitLab client
// doesn't give good error messages in this case.
ips, err := net.LookupIP(u.Hostname())
if err != nil {
logger.Warn("unable to resolve %q: %s", u.Hostname(), err)
} else if len(ips) == 0 {
logger.Warn("found no IPs while resolving %q", u.Hostname())
}

scheme := "https"
if u.Scheme != "" {
scheme = u.Scheme
}
apiURL := fmt.Sprintf("%s://%s/api/v4/", scheme, u.Host)
if err := client.Client.SetBaseURL(apiURL); err != nil {
return nil, errors.Wrapf(err, "setting GitLab API URL: %s", apiURL)
}
}

// Determine which version of GitLab is running.
if !gitlabClientUnderTest {
var err error
client.Version, err = client.GetVersion()
if err != nil {
return nil, err
}
logger.Info("determined GitLab is running version %s", client.Version.String())
}

return client, nil
}

// GetModifiedFiles returns the names of files that were modified in the merge request.
Expand Down Expand Up @@ -103,3 +164,46 @@ func (g *GitlabClient) GetMergeRequest(repoFullName string, pullNum int) (*gitla
mr, _, err := g.Client.MergeRequests.GetMergeRequest(repoFullName, pullNum)
return mr, err
}

// GetVersion returns the version of the Gitlab server this client is using.
func (g *GitlabClient) GetVersion() (*version.Version, error) {
req, err := g.Client.NewRequest("GET", "/version", nil, nil)
if err != nil {
return nil, err
}
versionResp := new(gitlab.Version)
_, err = g.Client.Do(req, versionResp)
if err != nil {
return nil, err
}
// We need to strip any "-ee" or similar from the resulting version because go-version
// uses that in its constraints and it breaks the comparison we're trying
// to do for Common Mark.
split := strings.Split(versionResp.Version, "-")
parsedVersion, err := version.NewVersion(split[0])
if err != nil {
return nil, errors.Wrapf(err, "parsing response to /version: %q", versionResp.Version)
}
return parsedVersion, nil
}

// SupportsCommonMark returns true if the version of Gitlab this client is
// using supports the CommonMark markdown format.
func (g *GitlabClient) SupportsCommonMark() bool {
// This function is called even if we didn't construct a gitlab client
// so we need to handle that case.
if g == nil {
return false
}

return commonMarkSupported.Check(g.Version)
}

// MustConstraint returns a constraint. It panics on error.
func MustConstraint(constraint string) version.Constraints {
c, err := version.NewConstraint(constraint)
if err != nil {
panic(err)
}
return c
}
Loading