Gitlab initial implementation
This commit is contained in:
Luke Kysow
2017-11-16 13:22:28 -08:00
committed by GitHub
parent dd39715976
commit fa43febf60
121 changed files with 15731 additions and 1031 deletions

8
Gopkg.lock generated
View File

@@ -85,6 +85,12 @@
revision = "76626ae9c91c4f2a10f34cad8ce83ea42c93bb75"
version = "v1.0"
[[projects]]
branch = "master"
name = "github.com/lkysow/go-gitlab"
packages = ["."]
revision = "5aba7769280ffa5408f98e1b95290989cc1428c5"
[[projects]]
name = "github.com/magiconair/properties"
packages = ["."]
@@ -232,6 +238,6 @@
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "ff26f498317e49b0c36b98f2ec0b91a1b7c9ccfb4b04777a3f1e8f7e143828b7"
inputs-digest = "ae2f51ff70354344e93c471a531b44a57822ea9dcc57dddbb8b476d0e1f1d3a8"
solver-name = "gps-cdcl"
solver-version = 1

View File

@@ -88,3 +88,7 @@
[[constraint]]
branch = "v2"
name = "gopkg.in/yaml.v2"
[[constraint]]
branch = "master"
name = "github.com/lkysow/go-gitlab"

View File

@@ -1,5 +1,5 @@
// Package cmd holds all our cli commands.
// These are different from the commands that get run via GitHub comments
// These are different from the commands that get run via pull request comments.
package cmd
import (

View File

@@ -25,6 +25,10 @@ const (
GHTokenFlag = "gh-token"
GHUserFlag = "gh-user"
GHWebHookSecret = "gh-webhook-secret"
GitlabHostnameFlag = "gitlab-hostname"
GitlabTokenFlag = "gitlab-token"
GitlabUserFlag = "gitlab-user"
GitlabWebHookSecret = "gitlab-webhook-secret"
LogLevelFlag = "log-level"
PortFlag = "port"
RequireApprovalFlag = "require-approval"
@@ -49,19 +53,42 @@ var stringFlags = []stringFlag{
description: "Hostname of your Github Enterprise installation. If using github.com, no need to set.",
value: "github.com",
},
{
name: GHUserFlag,
description: "GitHub username of API user.",
},
{
name: GHTokenFlag,
description: "[REQUIRED] GitHub token of API user. Can also be specified via the ATLANTIS_GH_TOKEN environment variable.",
description: "GitHub token of API user. Can also be specified via the ATLANTIS_GH_TOKEN environment variable.",
env: "ATLANTIS_GH_TOKEN",
},
{
name: GHUserFlag,
description: "[REQUIRED] GitHub username of API user.",
name: GHWebHookSecret,
description: "Optional secret used to validate GitHub webhooks (see https://developer.github.com/webhooks/securing/)." +
" If not specified, Atlantis won't be able to validate that the incoming webhook call came from GitHub. " +
"Can also be specified via the ATLANTIS_GH_WEBHOOK_SECRET environment variable.",
env: "ATLANTIS_GH_WEBHOOK_SECRET",
},
{
name: GHWebHookSecret,
description: "Optional secret used for GitHub webhooks (see https://developer.github.com/webhooks/securing/). If not specified, Atlantis won't validate the incoming webhook call.",
env: "ATLANTIS_GH_WEBHOOK_SECRET",
name: GitlabHostnameFlag,
description: "Hostname of your GitLab Enterprise installation. If using gitlab.com, no need to set.",
value: "gitlab.com",
},
{
name: GitlabUserFlag,
description: "GitLab username of API user.",
},
{
name: GitlabTokenFlag,
description: "GitLab token of API user. Can also be specified via the ATLANTIS_GITLAB_TOKEN environment variable.",
env: "ATLANTIS_GITLAB_TOKEN",
},
{
name: GitlabWebHookSecret,
description: "Optional secret used to validate GitLab webhooks." +
" If not specified, Atlantis won't be able to validate that the incoming webhook call came from GitLab. " +
"Can also be specified via the ATLANTIS_GITLAB_WEBHOOK_SECRET environment variable.",
env: "ATLANTIS_GITLAB_WEBHOOK_SECRET",
},
{
name: LogLevelFlag,
@@ -206,7 +233,7 @@ func (s *ServerCmd) run() error {
if err := setDataDir(&config); err != nil {
return err
}
sanitizeGithubUser(&config)
trimAtSymbolFromUsers(&config)
// Config looks good. Start the server.
server, err := s.ServerCreator.NewServer(config)
@@ -221,11 +248,23 @@ func validate(config server.Config) error {
if logLevel != "debug" && logLevel != "info" && logLevel != "warn" && logLevel != "error" {
return errors.New("invalid log level: not one of debug, info, warn, error")
}
if config.GithubUser == "" {
return fmt.Errorf("--%s must be set", GHUserFlag)
vcsErr := fmt.Errorf("--%s/--%s or --%s/--%s must be set", GHUserFlag, GHTokenFlag, GitlabUserFlag, GitlabTokenFlag)
// The following combinations are valid.
// 1. github user and token
// 2. gitlab user and token
// 3. all 4 set
// We validate using contradiction (I think).
if config.GithubUser != "" && config.GithubToken == "" || config.GithubToken != "" && config.GithubUser == "" {
return vcsErr
}
if config.GithubToken == "" {
return fmt.Errorf("--%s must be set", GHTokenFlag)
if config.GitlabUser != "" && config.GitlabToken == "" || config.GitlabToken != "" && config.GitlabUser == "" {
return vcsErr
}
// At this point, we know that there can't be a single user/token without
// its pair, but we haven't checked if any user/token is set at all.
if config.GithubUser == "" && config.GitlabUser == "" {
return vcsErr
}
return nil
}
@@ -256,9 +295,10 @@ func setDataDir(config *server.Config) error {
return nil
}
// sanitizeGithubUser trims @ from the front of the github username if it exists.
func sanitizeGithubUser(config *server.Config) {
// trimAtSymbolFromUsers trims @ from the front of the github and gitlab usernames
func trimAtSymbolFromUsers(config *server.Config) {
config.GithubUser = strings.TrimPrefix(config.GithubUser, "@")
config.GitlabUser = strings.TrimPrefix(config.GitlabUser, "@")
}
// withErrPrint prints out any errors to a terminal in red.

View File

@@ -90,42 +90,111 @@ func TestExecute_InvalidConfig(t *testing.T) {
Assert(t, strings.Contains(err.Error(), "unmarshal errors"), "should be an unmarshal error")
}
func TestExecute_Validation(t *testing.T) {
cases := []struct {
description string
flags map[string]interface{}
expErr string
}{
{
"Should validate log level.",
map[string]interface{}{
func TestExecute_ValidateLogLevel(t *testing.T) {
t.Log("Should validate log level.")
c := setup(map[string]interface{}{
cmd.LogLevelFlag: "invalid",
cmd.GHUserFlag: "user",
cmd.GHTokenFlag: "token",
},
"invalid log level: not one of debug, info, warn, error",
})
err := c.Execute()
Assert(t, err != nil, "should be an error")
Equals(t, "invalid log level: not one of debug, info, warn, error", err.Error())
}
func TestExecute_ValidateVCSConfig(t *testing.T) {
expErr := "--gh-user/--gh-token or --gitlab-user/--gitlab-token must be set"
cases := []struct {
description string
flags map[string]interface{}
expectError bool
}{
{
"no config set",
nil,
true,
},
{
"Should ensure github user is set.",
"just github token set",
map[string]interface{}{
cmd.GHTokenFlag: "token",
},
"--gh-user must be set",
true,
},
{
"Should ensure github token is set.",
"just gitlab token set",
map[string]interface{}{
cmd.GitlabTokenFlag: "token",
},
true,
},
{
"just github user set",
map[string]interface{}{
cmd.GHUserFlag: "user",
},
"--gh-token must be set",
true,
},
{
"just gitlab user set",
map[string]interface{}{
cmd.GitlabUserFlag: "user",
},
true,
},
{
"github user and gitlab token set",
map[string]interface{}{
cmd.GHUserFlag: "user",
cmd.GitlabTokenFlag: "token",
},
true,
},
{
"gitlab user and github token set",
map[string]interface{}{
cmd.GitlabUserFlag: "user",
cmd.GHTokenFlag: "token",
},
true,
},
{
"github user and github token set and should be successful",
map[string]interface{}{
cmd.GHUserFlag: "user",
cmd.GHTokenFlag: "token",
},
false,
},
{
"gitlab user and gitlab token set and should be successful",
map[string]interface{}{
cmd.GitlabUserFlag: "user",
cmd.GitlabTokenFlag: "token",
},
false,
},
{
"github and gitlab user and github and gitlab token set and should be successful",
map[string]interface{}{
cmd.GHUserFlag: "user",
cmd.GHTokenFlag: "token",
cmd.GitlabUserFlag: "user",
cmd.GitlabTokenFlag: "token",
},
false,
},
}
for _, testCase := range cases {
t.Log(testCase.description)
t.Log("Should validate vcs config when " + testCase.description)
c := setup(testCase.flags)
err := c.Execute()
if testCase.expectError {
Assert(t, err != nil, "should be an error")
Equals(t, testCase.expErr, err.Error())
Equals(t, expErr, err.Error())
} else {
Ok(t, err)
}
}
}
@@ -134,6 +203,8 @@ func TestExecute_Defaults(t *testing.T) {
c := setup(map[string]interface{}{
cmd.GHUserFlag: "user",
cmd.GHTokenFlag: "token",
cmd.GitlabUserFlag: "gitlab-user",
cmd.GitlabTokenFlag: "gitlab-token",
})
err := c.Execute()
Ok(t, err)
@@ -141,6 +212,9 @@ func TestExecute_Defaults(t *testing.T) {
Equals(t, "user", passedConfig.GithubUser)
Equals(t, "token", passedConfig.GithubToken)
Equals(t, "", passedConfig.GithubWebHookSecret)
Equals(t, "gitlab-user", passedConfig.GitlabUser)
Equals(t, "gitlab-token", passedConfig.GitlabToken)
Equals(t, "", passedConfig.GitlabWebHookSecret)
// Get our hostname since that's what gets defaulted to
hostname, err := os.Hostname()
Ok(t, err)
@@ -151,6 +225,7 @@ func TestExecute_Defaults(t *testing.T) {
Ok(t, err)
Equals(t, dataDir, passedConfig.DataDir)
Equals(t, "github.com", passedConfig.GithubHostname)
Equals(t, "gitlab.com", passedConfig.GitlabHostname)
Equals(t, "info", passedConfig.LogLevel)
Equals(t, false, passedConfig.RequireApproval)
Equals(t, 4141, passedConfig.Port)
@@ -183,6 +258,18 @@ func TestExecute_GithubUser(t *testing.T) {
Equals(t, "user", passedConfig.GithubUser)
}
func TestExecute_GitlabUser(t *testing.T) {
t.Log("Should remove the @ from the gitlab username if it's passed.")
c := setup(map[string]interface{}{
cmd.GitlabUserFlag: "@user",
cmd.GitlabTokenFlag: "token",
})
err := c.Execute()
Ok(t, err)
Equals(t, "user", passedConfig.GitlabUser)
}
func TestExecute_Flags(t *testing.T) {
t.Log("Should use all flags that are set.")
c := setup(map[string]interface{}{
@@ -192,6 +279,10 @@ func TestExecute_Flags(t *testing.T) {
cmd.GHUserFlag: "user",
cmd.GHTokenFlag: "token",
cmd.GHWebHookSecret: "secret",
cmd.GitlabHostnameFlag: "gitlab-hostname",
cmd.GitlabUserFlag: "gitlab-user",
cmd.GitlabTokenFlag: "gitlab-token",
cmd.GitlabWebHookSecret: "gitlab-secret",
cmd.LogLevelFlag: "debug",
cmd.PortFlag: 8181,
cmd.RequireApprovalFlag: true,
@@ -205,6 +296,10 @@ func TestExecute_Flags(t *testing.T) {
Equals(t, "user", passedConfig.GithubUser)
Equals(t, "token", passedConfig.GithubToken)
Equals(t, "secret", passedConfig.GithubWebHookSecret)
Equals(t, "gitlab-hostname", passedConfig.GitlabHostname)
Equals(t, "gitlab-user", passedConfig.GitlabUser)
Equals(t, "gitlab-token", passedConfig.GitlabToken)
Equals(t, "gitlab-secret", passedConfig.GitlabWebHookSecret)
Equals(t, "debug", passedConfig.LogLevel)
Equals(t, 8181, passedConfig.Port)
Equals(t, true, passedConfig.RequireApproval)
@@ -219,6 +314,10 @@ gh-hostname: "ghhostname"
gh-user: "user"
gh-token: "token"
gh-webhook-secret: "secret"
gitlab-hostname: "gitlab-hostname"
gitlab-user: "gitlab-user"
gitlab-token: "gitlab-token"
gitlab-webhook-secret: "gitlab-secret"
log-level: "debug"
port: 8181
require-approval: true`)
@@ -235,6 +334,10 @@ require-approval: true`)
Equals(t, "user", passedConfig.GithubUser)
Equals(t, "token", passedConfig.GithubToken)
Equals(t, "secret", passedConfig.GithubWebHookSecret)
Equals(t, "gitlab-hostname", passedConfig.GitlabHostname)
Equals(t, "gitlab-user", passedConfig.GitlabUser)
Equals(t, "gitlab-token", passedConfig.GitlabToken)
Equals(t, "gitlab-secret", passedConfig.GitlabWebHookSecret)
Equals(t, "debug", passedConfig.LogLevel)
Equals(t, 8181, passedConfig.Port)
Equals(t, true, passedConfig.RequireApproval)
@@ -279,6 +382,24 @@ func TestExecute_FlagEnvVarOverride(t *testing.T) {
Equals(t, "override", passedConfig.GithubToken)
}
func TestExecute_EnvVars(t *testing.T) {
t.Log("Setting flags by env var should work.")
os.Setenv("ATLANTIS_GH_TOKEN", "gh-token") // nolint: errcheck
os.Setenv("ATLANTIS_GH_WEBHOOK_SECRET", "gh-webhook") // nolint: errcheck
os.Setenv("ATLANTIS_GITLAB_TOKEN", "gitlab-token") // nolint: errcheck
os.Setenv("ATLANTIS_GITLAB_WEBHOOK_SECRET", "gitlab-webhook") // nolint: errcheck
c := setup(map[string]interface{}{
cmd.GHUserFlag: "user",
cmd.GitlabUserFlag: "user",
})
err := c.Execute()
Ok(t, err)
Equals(t, "gh-token", passedConfig.GithubToken)
Equals(t, "gh-webhook", passedConfig.GithubWebHookSecret)
Equals(t, "gitlab-token", passedConfig.GitlabToken)
Equals(t, "gitlab-webhook", passedConfig.GitlabWebHookSecret)
}
func setup(flags map[string]interface{}) *cobra.Command {
viper := viper.New()
for k, v := range flags {

View File

@@ -8,14 +8,14 @@ import (
"path/filepath"
"github.com/hootsuite/atlantis/server/events/github"
"github.com/hootsuite/atlantis/server/events/models"
"github.com/hootsuite/atlantis/server/events/run"
"github.com/hootsuite/atlantis/server/events/terraform"
"github.com/hootsuite/atlantis/server/events/vcs"
)
type ApplyExecutor struct {
Github github.Client
VCSClient vcs.ClientProxy
Terraform *terraform.Client
RequireApproval bool
Run *run.Run
@@ -25,7 +25,7 @@ type ApplyExecutor struct {
func (a *ApplyExecutor) Execute(ctx *CommandContext) CommandResponse {
if a.RequireApproval {
approved, err := a.Github.PullIsApproved(ctx.BaseRepo, ctx.Pull)
approved, err := a.VCSClient.PullIsApproved(ctx.BaseRepo, ctx.Pull, ctx.VCSHost)
if err != nil {
return CommandResponse{Error: errors.Wrap(err, "checking if pull request was approved")}
}

View File

@@ -2,6 +2,7 @@ package events
import (
"github.com/hootsuite/atlantis/server/events/models"
"github.com/hootsuite/atlantis/server/events/vcs"
"github.com/hootsuite/atlantis/server/logging"
)
@@ -12,4 +13,5 @@ type CommandContext struct {
User models.User
Command *Command
Log *logging.SimpleLogger
VCSHost vcs.Host
}

View File

@@ -3,15 +3,31 @@ package events
import (
"fmt"
"github.com/hootsuite/atlantis/server/events/github"
"github.com/google/go-github/github"
"github.com/hootsuite/atlantis/server/events/models"
"github.com/hootsuite/atlantis/server/events/vcs"
"github.com/hootsuite/atlantis/server/logging"
"github.com/hootsuite/atlantis/server/recovery"
"github.com/lkysow/go-gitlab"
"github.com/pkg/errors"
)
//go:generate pegomock generate --use-experimental-model-gen --package mocks -o mocks/mock_command_runner.go CommandRunner
type CommandRunner interface {
ExecuteCommand(ctx *CommandContext)
ExecuteCommand(baseRepo models.Repo, headRepo models.Repo, user models.User, pullNum int, cmd *Command, vcsHost vcs.Host)
}
//go:generate pegomock generate --use-experimental-model-gen --package mocks -o mocks/mock_github_pull_getter.go GithubPullGetter
type GithubPullGetter interface {
GetPullRequest(repo models.Repo, pullNum int) (*github.PullRequest, error)
}
//go:generate pegomock generate --use-experimental-model-gen --package mocks -o mocks/mock_gitlab_merge_request_getter.go GitlabMergeRequestGetter
type GitlabMergeRequestGetter interface {
GetMergeRequest(repoFullName string, pullNum int) (*gitlab.MergeRequest, error)
}
// CommandHandler is the first step when processing a comment command.
@@ -20,48 +36,90 @@ type CommandHandler struct {
ApplyExecutor Executor
HelpExecutor Executor
LockURLGenerator LockURLGenerator
GHClient github.Client
GHStatus GHStatusUpdater
VCSClient vcs.ClientProxy
GithubPullGetter GithubPullGetter
GitlabMergeRequestGetter GitlabMergeRequestGetter
CommitStatusUpdater CommitStatusUpdater
EventParser EventParsing
EnvLocker EnvLocker
GHCommentRenderer *GithubCommentRenderer
Logger *logging.SimpleLogger
MarkdownRenderer *MarkdownRenderer
Logger logging.SimpleLogging
}
func (c *CommandHandler) ExecuteCommand(ctx *CommandContext) {
// Set up logger specific to this command.
// It's safe to reuse the underlying logger.
src := fmt.Sprintf("%s/pull/%d", ctx.BaseRepo.FullName, ctx.Pull.Num)
ctx.Log = logging.NewSimpleLogger(src, c.Logger.Logger, true, c.Logger.Level)
defer c.logPanics(ctx)
// ExecuteCommand executes the command
func (c *CommandHandler) ExecuteCommand(baseRepo models.Repo, headRepo models.Repo, user models.User, pullNum int, cmd *Command, vcsHost vcs.Host) {
var err error
var pull models.PullRequest
if vcsHost == vcs.Github {
pull, headRepo, err = c.getGithubData(baseRepo, pullNum)
} else if vcsHost == vcs.Gitlab {
pull, err = c.getGitlabData(baseRepo.FullName, pullNum)
}
// Get additional data from the pull request.
ghPull, _, err := c.GHClient.GetPullRequest(ctx.BaseRepo, ctx.Pull.Num)
log := c.buildLogger(baseRepo.FullName, pullNum)
if err != nil {
ctx.Log.Err("making pull request API call to GitHub: %s", err)
log.Err(err.Error())
return
}
if ghPull.GetState() != "open" {
ctx.Log.Info("command was run on closed pull request")
c.GHClient.CreateComment(ctx.BaseRepo, ctx.Pull, "Atlantis commands can't be run on closed pull requests") // nolint: errcheck
return
ctx := &CommandContext{
User: user,
Log: log,
Pull: pull,
HeadRepo: headRepo,
Command: cmd,
VCSHost: vcsHost,
BaseRepo: baseRepo,
}
pull, headRepo, err := c.EventParser.ExtractPullData(ghPull)
if err != nil {
ctx.Log.Err("extracting required fields from comment data: %s", err)
return
}
ctx.Pull = pull
ctx.HeadRepo = headRepo
c.run(ctx)
}
func (c *CommandHandler) getGithubData(baseRepo models.Repo, pullNum int) (models.PullRequest, models.Repo, error) {
if c.GithubPullGetter == nil {
return models.PullRequest{}, models.Repo{}, errors.New("Atlantis not configured to support GitHub")
}
ghPull, err := c.GithubPullGetter.GetPullRequest(baseRepo, pullNum)
if err != nil {
return models.PullRequest{}, models.Repo{}, errors.Wrap(err, "making pull request API call to GitHub")
}
pull, repo, err := c.EventParser.ParseGithubPull(ghPull)
if err != nil {
return pull, repo, errors.Wrap(err, "extracting required fields from comment data")
}
return pull, repo, nil
}
func (c *CommandHandler) getGitlabData(repoFullName string, pullNum int) (models.PullRequest, error) {
if c.GitlabMergeRequestGetter == nil {
return models.PullRequest{}, errors.New("Atlantis not configured to support GitLab")
}
mr, err := c.GitlabMergeRequestGetter.GetMergeRequest(repoFullName, pullNum)
if err != nil {
return models.PullRequest{}, errors.Wrap(err, "making merge request API call to GitLab")
}
pull := c.EventParser.ParseGitlabMergeRequest(mr)
return pull, nil
}
func (c *CommandHandler) buildLogger(repoFullName string, pullNum int) *logging.SimpleLogger {
src := fmt.Sprintf("%s#%d", repoFullName, pullNum)
return logging.NewSimpleLogger(src, c.Logger.Underlying(), true, c.Logger.GetLevel())
}
func (c *CommandHandler) SetLockURL(f func(id string) (url string)) {
c.LockURLGenerator.SetLockURL(f)
}
func (c *CommandHandler) run(ctx *CommandContext) {
c.GHStatus.Update(ctx.BaseRepo, ctx.Pull, Pending, ctx.Command) // nolint: errcheck
log := c.buildLogger(ctx.BaseRepo.FullName, ctx.Pull.Num)
ctx.Log = log
defer c.logPanics(ctx)
if ctx.Pull.State != models.Open {
ctx.Log.Info("command was run on closed pull request")
c.VCSClient.CreateComment(ctx.BaseRepo, ctx.Pull, "Atlantis commands can't be run on closed pull requests", ctx.VCSHost) // nolint: errcheck
return
}
c.CommitStatusUpdater.Update(ctx.BaseRepo, ctx.Pull, vcs.Pending, ctx.Command, ctx.VCSHost) // nolint: errcheck
if !c.EnvLocker.TryLock(ctx.BaseRepo.FullName, ctx.Command.Environment, ctx.Pull.Num) {
errMsg := fmt.Sprintf(
"The %s environment is currently locked by another"+
@@ -97,17 +155,17 @@ func (c *CommandHandler) updatePull(ctx *CommandContext, res CommandResponse) {
}
// Update the pull request's status icon and comment back.
c.GHStatus.UpdateProjectResult(ctx, res) // nolint: errcheck
comment := c.GHCommentRenderer.Render(res, ctx.Command.Name, ctx.Log.History.String(), ctx.Command.Verbose)
c.GHClient.CreateComment(ctx.BaseRepo, ctx.Pull, comment) // nolint: errcheck
c.CommitStatusUpdater.UpdateProjectResult(ctx, res) // nolint: errcheck
comment := c.MarkdownRenderer.Render(res, ctx.Command.Name, ctx.Log.History.String(), ctx.Command.Verbose)
c.VCSClient.CreateComment(ctx.BaseRepo, ctx.Pull, comment, ctx.VCSHost) // nolint: errcheck
}
// logPanics logs and creates a comment on the pull request for panics
func (c *CommandHandler) logPanics(ctx *CommandContext) {
if err := recover(); err != nil {
stack := recovery.Stack(3)
c.GHClient.CreateComment(ctx.BaseRepo, ctx.Pull, // nolint: errcheck
fmt.Sprintf("**Error: goroutine panic. This is a bug.**\n```\n%s\n%s```", err, stack))
c.VCSClient.CreateComment(ctx.BaseRepo, ctx.Pull, // nolint: errcheck
fmt.Sprintf("**Error: goroutine panic. This is a bug.**\n```\n%s\n%s```", err, stack), ctx.VCSHost)
ctx.Log.Err("PANIC: %s\n%s", err, stack)
}
}

View File

@@ -1,145 +1,164 @@
package events_test
import (
"bytes"
"errors"
"testing"
"log"
"reflect"
"strings"
"testing"
"github.com/google/go-github/github"
"github.com/hootsuite/atlantis/server/events"
gh "github.com/hootsuite/atlantis/server/events/github/fixtures"
ghmocks "github.com/hootsuite/atlantis/server/events/github/mocks"
"github.com/hootsuite/atlantis/server/events/mocks"
"github.com/hootsuite/atlantis/server/events/models"
"github.com/hootsuite/atlantis/server/events/models/fixtures"
"github.com/hootsuite/atlantis/server/logging"
"github.com/hootsuite/atlantis/server/events/vcs"
vcsmocks "github.com/hootsuite/atlantis/server/events/vcs/mocks"
logmocks "github.com/hootsuite/atlantis/server/logging/mocks"
. "github.com/hootsuite/atlantis/testing"
"github.com/mohae/deepcopy"
. "github.com/petergtz/pegomock"
)
var applier *mocks.MockExecutor
var helper *mocks.MockExecutor
var planner *mocks.MockPlanner
var planner *mocks.MockExecutor
var eventParsing *mocks.MockEventParsing
var ghClient *ghmocks.MockClient
var ghStatus *mocks.MockGHStatusUpdater
var vcsClient *vcsmocks.MockClientProxy
var ghStatus *mocks.MockCommitStatusUpdater
var githubGetter *mocks.MockGithubPullGetter
var gitlabGetter *mocks.MockGitlabMergeRequestGetter
var envLocker *mocks.MockEnvLocker
var ch events.CommandHandler
var logBytes *bytes.Buffer
func setup(t *testing.T) {
RegisterMockTestingT(t)
applier = mocks.NewMockExecutor()
helper = mocks.NewMockExecutor()
planner = mocks.NewMockPlanner()
planner = mocks.NewMockExecutor()
eventParsing = mocks.NewMockEventParsing()
ghClient = ghmocks.NewMockClient()
ghStatus = mocks.NewMockGHStatusUpdater()
ghStatus = mocks.NewMockCommitStatusUpdater()
envLocker = mocks.NewMockEnvLocker()
vcsClient = vcsmocks.NewMockClientProxy()
githubGetter = mocks.NewMockGithubPullGetter()
gitlabGetter = mocks.NewMockGitlabMergeRequestGetter()
logger := logmocks.NewMockSimpleLogging()
logBytes = new(bytes.Buffer)
When(logger.Underlying()).ThenReturn(log.New(logBytes, "", 0))
ch = events.CommandHandler{
PlanExecutor: planner,
ApplyExecutor: applier,
HelpExecutor: helper,
GHClient: ghClient,
GHStatus: ghStatus,
VCSClient: vcsClient,
CommitStatusUpdater: ghStatus,
EventParser: eventParsing,
EnvLocker: envLocker,
GHCommentRenderer: &events.GithubCommentRenderer{},
Logger: logging.NewNoopLogger(),
MarkdownRenderer: &events.MarkdownRenderer{},
GithubPullGetter: githubGetter,
GitlabMergeRequestGetter: gitlabGetter,
Logger: logger,
}
}
func TestExecuteCommand_LogPanics(t *testing.T) {
t.Log("if there is a panic it is commented back on the pull request")
setup(t)
When(ghClient.GetPullRequest(fixtures.Repo, fixtures.Pull.Num)).ThenPanic("panic")
ch.ExecuteCommand(&events.CommandContext{
BaseRepo: fixtures.Repo,
Pull: fixtures.Pull,
})
_, _, comment := ghClient.VerifyWasCalledOnce().CreateComment(AnyRepo(), AnyPullRequest(), AnyString()).GetCapturedArguments()
When(ghStatus.Update(fixtures.Repo, fixtures.Pull, vcs.Pending, nil, vcs.Github)).ThenPanic("panic")
ch.ExecuteCommand(fixtures.Repo, fixtures.Repo, fixtures.User, 1, nil, vcs.Github)
_, _, comment, _ := vcsClient.VerifyWasCalledOnce().CreateComment(AnyRepo(), AnyPullRequest(), AnyString(), AnyVCSHost()).GetCapturedArguments()
Assert(t, strings.Contains(comment, "Error: goroutine panic"), "comment should be about a goroutine panic")
}
func TestExecuteCommand_PullErr(t *testing.T) {
t.Log("if getting the pull request fails nothing should continue")
func TestExecuteCommand_NoGithubPullGetter(t *testing.T) {
t.Log("if CommandHandler was constructed with a nil GithubPullGetter an error should be logged")
setup(t)
When(ghClient.GetPullRequest(fixtures.Repo, fixtures.Pull.Num)).ThenReturn(nil, nil, errors.New("err"))
ch.ExecuteCommand(&events.CommandContext{
BaseRepo: fixtures.Repo,
Pull: fixtures.Pull,
})
ch.GithubPullGetter = nil
ch.ExecuteCommand(fixtures.Repo, fixtures.Repo, fixtures.User, 1, nil, vcs.Github)
Equals(t, "[ERROR] hootsuite/atlantis#1: Atlantis not configured to support GitHub\n", logBytes.String())
}
func TestExecuteCommand_ExtractErr(t *testing.T) {
t.Log("if extracting data from the pull request fails nothing should continue")
func TestExecuteCommand_NoGitlabMergeGetter(t *testing.T) {
t.Log("if CommandHandler was constructed with a nil GitlabMergeRequestGetter an error should be logged")
setup(t)
pull := deepcopy.Copy(gh.Pull).(github.PullRequest)
pull.State = github.String("open")
When(ghClient.GetPullRequest(fixtures.Repo, fixtures.Pull.Num)).ThenReturn(&pull, nil, nil)
When(eventParsing.ExtractPullData(&pull)).ThenReturn(fixtures.Pull, fixtures.Repo, errors.New("err"))
ch.GitlabMergeRequestGetter = nil
ch.ExecuteCommand(fixtures.Repo, fixtures.Repo, fixtures.User, 1, nil, vcs.Gitlab)
Equals(t, "[ERROR] hootsuite/atlantis#1: Atlantis not configured to support GitLab\n", logBytes.String())
}
ch.ExecuteCommand(&events.CommandContext{
BaseRepo: fixtures.Repo,
Pull: fixtures.Pull,
})
func TestExecuteCommand_GithubPullErr(t *testing.T) {
t.Log("if getting the github pull request fails an error should be logged")
setup(t)
When(githubGetter.GetPullRequest(fixtures.Repo, fixtures.Pull.Num)).ThenReturn(nil, errors.New("err"))
ch.ExecuteCommand(fixtures.Repo, fixtures.Repo, fixtures.User, fixtures.Pull.Num, nil, vcs.Github)
Equals(t, "[ERROR] hootsuite/atlantis#1: Making pull request API call to GitHub: err\n", logBytes.String())
}
func TestExecuteCommand_GitlabMergeRequestErr(t *testing.T) {
t.Log("if getting the gitlab merge request fails an error should be logged")
setup(t)
When(gitlabGetter.GetMergeRequest(fixtures.Repo.FullName, fixtures.Pull.Num)).ThenReturn(nil, errors.New("err"))
ch.ExecuteCommand(fixtures.Repo, fixtures.Repo, fixtures.User, fixtures.Pull.Num, nil, vcs.Gitlab)
Equals(t, "[ERROR] hootsuite/atlantis#1: Making merge request API call to GitLab: err\n", logBytes.String())
}
func TestExecuteCommand_GithubPullParseErr(t *testing.T) {
t.Log("if parsing the returned github pull request fails an error should be logged")
setup(t)
var pull github.PullRequest
When(githubGetter.GetPullRequest(fixtures.Repo, fixtures.Pull.Num)).ThenReturn(&pull, nil)
When(eventParsing.ParseGithubPull(&pull)).ThenReturn(fixtures.Pull, fixtures.Repo, errors.New("err"))
ch.ExecuteCommand(fixtures.Repo, fixtures.Repo, fixtures.User, fixtures.Pull.Num, nil, vcs.Github)
Equals(t, "[ERROR] hootsuite/atlantis#1: Extracting required fields from comment data: err\n", logBytes.String())
}
func TestExecuteCommand_ClosedPull(t *testing.T) {
t.Log("if a command is run on a closed pull request atlantis should" +
" comment saying that this is not allowed")
setup(t)
pull := deepcopy.Copy(gh.Pull).(github.PullRequest)
pull.State = github.String("closed")
When(ghClient.GetPullRequest(fixtures.Repo, fixtures.Pull.Num)).ThenReturn(&pull, nil, nil)
pull := &github.PullRequest{
State: github.String("closed"),
}
modelPull := models.PullRequest{State: models.Closed}
When(githubGetter.GetPullRequest(fixtures.Repo, fixtures.Pull.Num)).ThenReturn(pull, nil)
When(eventParsing.ParseGithubPull(pull)).ThenReturn(modelPull, fixtures.Repo, nil)
ch.ExecuteCommand(&events.CommandContext{
BaseRepo: fixtures.Repo,
User: fixtures.User,
Pull: fixtures.Pull,
Command: &events.Command{
Name: events.Plan,
},
})
ghClient.VerifyWasCalledOnce().CreateComment(fixtures.Repo, fixtures.Pull, "Atlantis commands can't be run on closed pull requests")
ch.ExecuteCommand(fixtures.Repo, fixtures.Repo, fixtures.User, fixtures.Pull.Num, nil, vcs.Github)
vcsClient.VerifyWasCalledOnce().CreateComment(fixtures.Repo, modelPull, "Atlantis commands can't be run on closed pull requests", vcs.Github)
}
func TestExecuteCommand_EnvLocked(t *testing.T) {
t.Log("if the environment is locked, should comment back on the pull")
setup(t)
pull := deepcopy.Copy(gh.Pull).(github.PullRequest)
pull.State = github.String("open")
pull := &github.PullRequest{
State: github.String("closed"),
}
cmd := events.Command{
Name: events.Plan,
Environment: "env",
}
baseCtx := events.CommandContext{
BaseRepo: fixtures.Repo,
User: fixtures.User,
Pull: fixtures.Pull,
Command: &cmd,
}
When(ghClient.GetPullRequest(fixtures.Repo, fixtures.Pull.Num)).ThenReturn(&pull, nil, nil)
When(eventParsing.ExtractPullData(&pull)).ThenReturn(fixtures.Pull, fixtures.Repo, nil)
When(githubGetter.GetPullRequest(fixtures.Repo, fixtures.Pull.Num)).ThenReturn(pull, nil)
When(eventParsing.ParseGithubPull(pull)).ThenReturn(fixtures.Pull, fixtures.Repo, nil)
When(envLocker.TryLock(fixtures.Repo.FullName, cmd.Environment, fixtures.Pull.Num)).ThenReturn(false)
ch.ExecuteCommand(&baseCtx)
ch.ExecuteCommand(fixtures.Repo, fixtures.Repo, fixtures.User, fixtures.Pull.Num, &cmd, vcs.Github)
msg := "The env environment is currently locked by another" +
" command that is running for this pull request." +
" Wait until the previous command is complete and try again."
ghStatus.VerifyWasCalledOnce().Update(fixtures.Repo, fixtures.Pull, events.Pending, &cmd)
ghStatus.VerifyWasCalledOnce().UpdateProjectResult(&baseCtx, events.CommandResponse{Failure: msg})
ghClient.VerifyWasCalledOnce().CreateComment(fixtures.Repo, fixtures.Pull,
"**Plan Failed**: "+msg+"\n\n")
ghStatus.VerifyWasCalledOnce().Update(fixtures.Repo, fixtures.Pull, vcs.Pending, &cmd, vcs.Github)
_, response := ghStatus.VerifyWasCalledOnce().UpdateProjectResult(AnyCommandContext(), AnyCommandResponse()).GetCapturedArguments()
Equals(t, msg, response.Failure)
vcsClient.VerifyWasCalledOnce().CreateComment(fixtures.Repo, fixtures.Pull,
"**Plan Failed**: "+msg+"\n\n", vcs.Github)
}
func TestExecuteCommand_FullRun(t *testing.T) {
t.Log("when running a plan, apply or help should comment")
pull := deepcopy.Copy(gh.Pull).(github.PullRequest)
pull.State = github.String("open")
pull := &github.PullRequest{
State: github.String("closed"),
}
cmdResponse := events.CommandResponse{}
for _, c := range []events.CommandName{events.Help, events.Plan, events.Apply} {
setup(t)
@@ -147,14 +166,8 @@ func TestExecuteCommand_FullRun(t *testing.T) {
Name: c,
Environment: "env",
}
baseCtx := events.CommandContext{
BaseRepo: fixtures.Repo,
User: fixtures.User,
Pull: fixtures.Pull,
Command: &cmd,
}
When(ghClient.GetPullRequest(fixtures.Repo, fixtures.Pull.Num)).ThenReturn(&pull, nil, nil)
When(eventParsing.ExtractPullData(&pull)).ThenReturn(fixtures.Pull, fixtures.Repo, nil)
When(githubGetter.GetPullRequest(fixtures.Repo, fixtures.Pull.Num)).ThenReturn(pull, nil)
When(eventParsing.ParseGithubPull(pull)).ThenReturn(fixtures.Pull, fixtures.Repo, nil)
When(envLocker.TryLock(fixtures.Repo.FullName, cmd.Environment, fixtures.Pull.Num)).ThenReturn(true)
switch c {
case events.Help:
@@ -165,11 +178,12 @@ func TestExecuteCommand_FullRun(t *testing.T) {
When(applier.Execute(AnyCommandContext())).ThenReturn(cmdResponse)
}
ch.ExecuteCommand(&baseCtx)
ch.ExecuteCommand(fixtures.Repo, fixtures.Repo, fixtures.User, fixtures.Pull.Num, &cmd, vcs.Github)
ghStatus.VerifyWasCalledOnce().Update(fixtures.Repo, fixtures.Pull, events.Pending, &cmd)
ghStatus.VerifyWasCalledOnce().UpdateProjectResult(&baseCtx, cmdResponse)
ghClient.VerifyWasCalledOnce().CreateComment(AnyRepo(), AnyPullRequest(), AnyString())
ghStatus.VerifyWasCalledOnce().Update(fixtures.Repo, fixtures.Pull, vcs.Pending, &cmd, vcs.Github)
_, response := ghStatus.VerifyWasCalledOnce().UpdateProjectResult(AnyCommandContext(), AnyCommandResponse()).GetCapturedArguments()
Equals(t, cmdResponse, response)
vcsClient.VerifyWasCalledOnce().CreateComment(AnyRepo(), AnyPullRequest(), AnyString(), AnyVCSHost())
envLocker.VerifyWasCalledOnce().Unlock(fixtures.Repo.FullName, cmd.Environment, fixtures.Pull.Num)
}
}
@@ -178,3 +192,13 @@ func AnyCommandContext() *events.CommandContext {
RegisterMatcher(NewAnyMatcher(reflect.TypeOf(&events.CommandContext{})))
return &events.CommandContext{}
}
func AnyVCSHost() vcs.Host {
RegisterMatcher(NewAnyMatcher(reflect.TypeOf(vcs.Github)))
return vcs.Github
}
func AnyCommandResponse() events.CommandResponse {
RegisterMatcher(NewAnyMatcher(reflect.TypeOf(events.CommandResponse{})))
return events.CommandResponse{}
}

View File

@@ -0,0 +1,48 @@
package events
import (
"fmt"
"strings"
"github.com/hootsuite/atlantis/server/events/models"
"github.com/hootsuite/atlantis/server/events/vcs"
)
//go:generate pegomock generate --use-experimental-model-gen --package mocks -o mocks/mock_commit_status_updater.go CommitStatusUpdater
type CommitStatusUpdater interface {
Update(repo models.Repo, pull models.PullRequest, status vcs.CommitStatus, cmd *Command, host vcs.Host) error
UpdateProjectResult(ctx *CommandContext, res CommandResponse) error
}
type DefaultCommitStatusUpdater struct {
Client vcs.ClientProxy
}
func (d *DefaultCommitStatusUpdater) Update(repo models.Repo, pull models.PullRequest, status vcs.CommitStatus, cmd *Command, host vcs.Host) error {
description := fmt.Sprintf("%s %s", strings.Title(cmd.Name.String()), strings.Title(status.String()))
return d.Client.UpdateStatus(repo, pull, status, description, host)
}
func (d *DefaultCommitStatusUpdater) UpdateProjectResult(ctx *CommandContext, res CommandResponse) error {
var status vcs.CommitStatus
if res.Error != nil || res.Failure != "" {
status = vcs.Failed
} else {
var statuses []vcs.CommitStatus
for _, p := range res.ProjectResults {
statuses = append(statuses, p.Status())
}
status = d.worstStatus(statuses)
}
return d.Update(ctx.BaseRepo, ctx.Pull, status, ctx.Command, ctx.VCSHost)
}
func (d *DefaultCommitStatusUpdater) worstStatus(ss []vcs.CommitStatus) vcs.CommitStatus {
for _, s := range ss {
if s == vcs.Failed {
return vcs.Failed
}
}
return vcs.Success
}

View File

@@ -1,31 +1,30 @@
package events_test
import (
"testing"
"errors"
"strings"
"testing"
"github.com/hootsuite/atlantis/server/events"
"github.com/hootsuite/atlantis/server/events/github/mocks"
"github.com/hootsuite/atlantis/server/events/models"
"github.com/hootsuite/atlantis/server/events/vcs"
"github.com/hootsuite/atlantis/server/events/vcs/mocks"
. "github.com/hootsuite/atlantis/testing"
. "github.com/petergtz/pegomock"
)
var repoModel = models.Repo{}
var pullModel = models.PullRequest{}
var status = events.Success
var status = vcs.Success
var cmd = events.Command{
Name: events.Plan,
}
func TestStatus_String(t *testing.T) {
cases := map[events.Status]string{
events.Pending: "pending",
events.Success: "success",
events.Failure: "failure",
events.Error: "error",
cases := map[vcs.CommitStatus]string{
vcs.Pending: "pending",
vcs.Success: "success",
vcs.Failed: "failed",
}
for k, v := range cases {
Equals(t, v, k.String())
@@ -34,11 +33,11 @@ func TestStatus_String(t *testing.T) {
func TestUpdate(t *testing.T) {
RegisterMockTestingT(t)
client := mocks.NewMockClient()
s := events.GithubStatus{Client: client}
err := s.Update(repoModel, pullModel, status, &cmd)
client := mocks.NewMockClientProxy()
s := events.DefaultCommitStatusUpdater{Client: client}
err := s.Update(repoModel, pullModel, status, &cmd, vcs.Github)
Ok(t, err)
client.VerifyWasCalledOnce().UpdateStatus(repoModel, pullModel, "success", "Plan Success", "Atlantis")
client.VerifyWasCalledOnce().UpdateStatus(repoModel, pullModel, status, "Plan Success", vcs.Github)
}
func TestUpdateProjectResult_Error(t *testing.T) {
@@ -47,12 +46,13 @@ func TestUpdateProjectResult_Error(t *testing.T) {
BaseRepo: repoModel,
Pull: pullModel,
Command: &events.Command{Name: events.Plan},
VCSHost: vcs.Github,
}
client := mocks.NewMockClient()
s := events.GithubStatus{Client: client}
client := mocks.NewMockClientProxy()
s := events.DefaultCommitStatusUpdater{Client: client}
err := s.UpdateProjectResult(ctx, events.CommandResponse{Error: errors.New("err")})
Ok(t, err)
client.VerifyWasCalledOnce().UpdateStatus(repoModel, pullModel, events.Error.String(), "Plan Error", "Atlantis")
client.VerifyWasCalledOnce().UpdateStatus(repoModel, pullModel, vcs.Failed, "Plan Failed", vcs.Github)
}
func TestUpdateProjectResult_Failure(t *testing.T) {
@@ -61,12 +61,13 @@ func TestUpdateProjectResult_Failure(t *testing.T) {
BaseRepo: repoModel,
Pull: pullModel,
Command: &events.Command{Name: events.Plan},
VCSHost: vcs.Github,
}
client := mocks.NewMockClient()
s := events.GithubStatus{Client: client}
client := mocks.NewMockClientProxy()
s := events.DefaultCommitStatusUpdater{Client: client}
err := s.UpdateProjectResult(ctx, events.CommandResponse{Failure: "failure"})
Ok(t, err)
client.VerifyWasCalledOnce().UpdateStatus(repoModel, pullModel, events.Failure.String(), "Plan Failure", "Atlantis")
client.VerifyWasCalledOnce().UpdateStatus(repoModel, pullModel, vcs.Failed, "Plan Failed", vcs.Github)
}
func TestUpdateProjectResult(t *testing.T) {
@@ -77,31 +78,40 @@ func TestUpdateProjectResult(t *testing.T) {
BaseRepo: repoModel,
Pull: pullModel,
Command: &events.Command{Name: events.Plan},
VCSHost: vcs.Github,
}
cases := []struct {
Statuses []string
Expected string
Expected vcs.CommitStatus
}{
{
[]string{"success", "failure", "error"},
"error",
vcs.Failed,
},
{
[]string{"failure", "error", "success"},
"error",
vcs.Failed,
},
{
[]string{"success", "failure"},
"failure",
vcs.Failed,
},
{
[]string{"success", "error"},
"error",
vcs.Failed,
},
{
[]string{"failure", "error"},
"error",
vcs.Failed,
},
{
[]string{"success"},
vcs.Success,
},
{
[]string{"success", "success"},
vcs.Success,
},
}
@@ -121,10 +131,10 @@ func TestUpdateProjectResult(t *testing.T) {
}
resp := events.CommandResponse{ProjectResults: results}
client := mocks.NewMockClient()
s := events.GithubStatus{Client: client}
client := mocks.NewMockClientProxy()
s := events.DefaultCommitStatusUpdater{Client: client}
err := s.UpdateProjectResult(ctx, resp)
Ok(t, err)
client.VerifyWasCalledOnce().UpdateStatus(repoModel, pullModel, c.Expected, "Plan "+strings.Title(c.Expected), "Atlantis")
client.VerifyWasCalledOnce().UpdateStatus(repoModel, pullModel, c.Expected, "Plan "+strings.Title(c.Expected.String()), vcs.Github)
}
}

View File

@@ -7,8 +7,12 @@ import (
"github.com/google/go-github/github"
"github.com/hootsuite/atlantis/server/events/models"
"github.com/hootsuite/atlantis/server/events/vcs"
"github.com/lkysow/go-gitlab"
)
const gitlabPullOpened = "opened"
//go:generate pegomock generate --use-experimental-model-gen --package mocks -o mocks/mock_event_parsing.go EventParsing
type Command struct {
@@ -19,20 +23,25 @@ type Command struct {
}
type EventParsing interface {
DetermineCommand(comment *github.IssueCommentEvent) (*Command, error)
ExtractCommentData(comment *github.IssueCommentEvent) (baseRepo models.Repo, user models.User, pull models.PullRequest, err error)
ExtractPullData(pull *github.PullRequest) (models.PullRequest, models.Repo, error)
ExtractRepoData(ghRepo *github.Repository) (models.Repo, error)
DetermineCommand(comment string, vcsHost vcs.Host) (*Command, error)
ParseGithubIssueCommentEvent(comment *github.IssueCommentEvent) (baseRepo models.Repo, user models.User, pullNum int, err error)
ParseGithubPull(pull *github.PullRequest) (models.PullRequest, models.Repo, error)
ParseGithubRepo(ghRepo *github.Repository) (models.Repo, error)
ParseGitlabMergeEvent(event gitlab.MergeEvent) (models.PullRequest, models.Repo)
ParseGitlabMergeCommentEvent(event gitlab.MergeCommentEvent) (baseRepo models.Repo, headRepo models.Repo, user models.User)
ParseGitlabMergeRequest(mr *gitlab.MergeRequest) models.PullRequest
}
type EventParser struct {
GithubUser string
GithubToken string
GitlabUser string
GitlabToken string
}
// DetermineCommand parses the comment as an atlantis command. If it succeeds,
// it returns the command. Otherwise it returns error.
func (e *EventParser) DetermineCommand(comment *github.IssueCommentEvent) (*Command, error) {
func (e *EventParser) DetermineCommand(comment string, vcsHost vcs.Host) (*Command, error) {
// valid commands contain:
// the initial "executable" name, 'run' or 'atlantis' or '@GithubUser' where GithubUser is the api user atlantis is running as
// then a command, either 'plan', 'apply', or 'help'
@@ -44,12 +53,8 @@ func (e *EventParser) DetermineCommand(comment *github.IssueCommentEvent) (*Comm
// @GithubUser plan staging
// atlantis plan staging --verbose
// atlantis plan staging --verbose -key=value -key2 value2
commentBody := comment.Comment.GetBody()
if commentBody == "" {
return nil, errors.New("comment.body is null")
}
err := errors.New("not an Atlantis command")
args := strings.Fields(commentBody)
args := strings.Fields(comment)
if len(args) < 2 {
return nil, err
}
@@ -58,7 +63,11 @@ func (e *EventParser) DetermineCommand(comment *github.IssueCommentEvent) (*Comm
verbose := false
var flags []string
if !e.stringInSlice(args[0], []string{"run", "atlantis", "@" + e.GithubUser}) {
vcsUser := e.GithubUser
if vcsHost == vcs.Gitlab {
vcsUser = e.GitlabUser
}
if !e.stringInSlice(args[0], []string{"run", "atlantis", "@" + vcsUser}) {
return nil, err
}
if !e.stringInSlice(args[1], []string{"plan", "apply", "help"}) {
@@ -99,41 +108,28 @@ func (e *EventParser) DetermineCommand(comment *github.IssueCommentEvent) (*Comm
return c, nil
}
func (e *EventParser) ExtractCommentData(comment *github.IssueCommentEvent) (baseRepo models.Repo, user models.User, pull models.PullRequest, err error) {
baseRepo, err = e.ExtractRepoData(comment.Repo)
func (e *EventParser) ParseGithubIssueCommentEvent(comment *github.IssueCommentEvent) (baseRepo models.Repo, user models.User, pullNum int, err error) {
baseRepo, err = e.ParseGithubRepo(comment.Repo)
if err != nil {
return
}
pullNum := comment.Issue.GetNumber()
if comment.Comment == nil || comment.Comment.User.GetLogin() == "" {
err = errors.New("comment.user.login is null")
return
}
commentorUsername := comment.Comment.User.GetLogin()
user = models.User{
Username: commentorUsername,
}
pullNum = comment.Issue.GetNumber()
if pullNum == 0 {
err = errors.New("issue.number is null")
return
}
pullCreator := comment.Issue.User.GetLogin()
if pullCreator == "" {
err = errors.New("issue.user.login is null")
return
}
htmlURL := comment.Issue.GetHTMLURL()
if htmlURL == "" {
err = errors.New("issue.html_url is null")
return
}
commentorUsername := comment.Comment.User.GetLogin()
if commentorUsername == "" {
err = errors.New("comment.user.login is null")
return
}
user = models.User{
Username: commentorUsername,
}
pull = models.PullRequest{
Num: pullNum,
}
return
}
func (e *EventParser) ExtractPullData(pull *github.PullRequest) (models.PullRequest, models.Repo, error) {
func (e *EventParser) ParseGithubPull(pull *github.PullRequest) (models.PullRequest, models.Repo, error) {
var pullModel models.PullRequest
var headRepoModel models.Repo
@@ -141,10 +137,6 @@ func (e *EventParser) ExtractPullData(pull *github.PullRequest) (models.PullRequ
if commit == "" {
return pullModel, headRepoModel, errors.New("head.sha is null")
}
base := pull.Base.GetSHA()
if base == "" {
return pullModel, headRepoModel, errors.New("base.sha is null")
}
url := pull.GetHTMLURL()
if url == "" {
return pullModel, headRepoModel, errors.New("html_url is null")
@@ -162,22 +154,27 @@ func (e *EventParser) ExtractPullData(pull *github.PullRequest) (models.PullRequ
return pullModel, headRepoModel, errors.New("number is null")
}
headRepoModel, err := e.ExtractRepoData(pull.Head.Repo)
headRepoModel, err := e.ParseGithubRepo(pull.Head.Repo)
if err != nil {
return pullModel, headRepoModel, err
}
pullState := models.Closed
if pull.GetState() == "open" {
pullState = models.Open
}
return models.PullRequest{
BaseCommit: base,
Author: authorUsername,
Branch: branch,
HeadCommit: commit,
URL: url,
Num: num,
State: pullState,
}, headRepoModel, nil
}
func (e *EventParser) ExtractRepoData(ghRepo *github.Repository) (models.Repo, error) {
func (e *EventParser) ParseGithubRepo(ghRepo *github.Repository) (models.Repo, error) {
var repo models.Repo
repoFullName := ghRepo.GetFullName()
if repoFullName == "" {
@@ -196,7 +193,7 @@ func (e *EventParser) ExtractRepoData(ghRepo *github.Repository) (models.Repo, e
return repo, errors.New("repository.clone_url is null")
}
// construct HTTPS repo clone url string with username and password
// Construct HTTPS repo clone url string with username and password.
repoCloneURL := strings.Replace(repoSanitizedCloneURL, "https://", fmt.Sprintf("https://%s:%s@", e.GithubUser, e.GithubToken), -1)
return models.Repo{
@@ -208,6 +205,100 @@ func (e *EventParser) ExtractRepoData(ghRepo *github.Repository) (models.Repo, e
}, nil
}
func (e *EventParser) ParseGitlabMergeEvent(event gitlab.MergeEvent) (models.PullRequest, models.Repo) {
modelState := models.Closed
if event.ObjectAttributes.State == gitlabPullOpened {
modelState = models.Open
}
// GitLab also has a "merged" state, but we map that to Closed so we don't
// need to check for it.
pull := models.PullRequest{
URL: event.ObjectAttributes.URL,
Author: event.User.Username,
Num: event.ObjectAttributes.IID,
HeadCommit: event.ObjectAttributes.LastCommit.ID,
Branch: event.ObjectAttributes.SourceBranch,
State: modelState,
}
cloneURL := e.addGitlabAuth(event.Project.GitHTTPURL)
// Get owner and name from PathWithNamespace because the fields
// event.Project.Name and event.Project.Owner can have capitals.
owner, name := e.getOwnerAndName(event.Project.PathWithNamespace)
repo := models.Repo{
FullName: event.Project.PathWithNamespace,
Name: name,
SanitizedCloneURL: event.Project.GitHTTPURL,
Owner: owner,
CloneURL: cloneURL,
}
return pull, repo
}
// addGitlabAuth adds gitlab username/password to the cloneURL.
// We support http and https URLs because GitLab's docs have http:// URLs whereas
// their API responses have https://.
// Ex. https://gitlab.com/owner/repo.git => https://uname:pass@gitlab.com/owner/repo.git
func (e *EventParser) addGitlabAuth(cloneURL string) string {
httpsReplaced := strings.Replace(cloneURL, "https://", fmt.Sprintf("https://%s:%s@", e.GitlabUser, e.GitlabToken), -1)
return strings.Replace(httpsReplaced, "http://", fmt.Sprintf("http://%s:%s@", e.GitlabUser, e.GitlabToken), -1)
}
// getOwnerAndName takes pathWithNamespace that should look like "owner/repo"
// and returns "owner", "repo"
func (e *EventParser) getOwnerAndName(pathWithNamespace string) (string, string) {
pathSplit := strings.Split(pathWithNamespace, "/")
if len(pathSplit) > 1 {
return pathSplit[0], pathSplit[1]
}
return "", ""
}
// ParseGitlabMergeCommentEvent creates Atlantis models out of a GitLab event.
func (e *EventParser) ParseGitlabMergeCommentEvent(event gitlab.MergeCommentEvent) (baseRepo models.Repo, headRepo models.Repo, user models.User) {
// Get owner and name from PathWithNamespace because the fields
// event.Project.Name and event.Project.Owner can have capitals.
owner, name := e.getOwnerAndName(event.Project.PathWithNamespace)
baseRepo = models.Repo{
FullName: event.Project.PathWithNamespace,
Name: name,
SanitizedCloneURL: event.Project.GitHTTPURL,
Owner: owner,
CloneURL: e.addGitlabAuth(event.Project.GitHTTPURL),
}
user = models.User{
Username: event.User.Username,
}
owner, name = e.getOwnerAndName(event.MergeRequest.Source.PathWithNamespace)
headRepo = models.Repo{
FullName: event.MergeRequest.Source.PathWithNamespace,
Name: name,
SanitizedCloneURL: event.MergeRequest.Source.GitHTTPURL,
Owner: owner,
CloneURL: e.addGitlabAuth(event.MergeRequest.Source.GitHTTPURL),
}
return
}
func (e *EventParser) ParseGitlabMergeRequest(mr *gitlab.MergeRequest) models.PullRequest {
pullState := models.Closed
if mr.State == gitlabPullOpened {
pullState = models.Open
}
// GitLab also has a "merged" state, but we map that to Closed so we don't
// need to check for it.
return models.PullRequest{
URL: mr.WebURL,
Author: mr.Author.Username,
Num: mr.IID,
HeadCommit: mr.SHA,
Branch: mr.SourceBranch,
State: pullState,
}
}
func (e *EventParser) stringInSlice(a string, list []string) bool {
for _, b := range list {
if b == a {

View File

@@ -6,22 +6,23 @@ import (
"errors"
"strings"
"encoding/json"
"github.com/google/go-github/github"
"github.com/hootsuite/atlantis/server/events"
. "github.com/hootsuite/atlantis/server/events/github/fixtures"
"github.com/hootsuite/atlantis/server/events/models"
"github.com/hootsuite/atlantis/server/events/vcs"
. "github.com/hootsuite/atlantis/server/events/vcs/fixtures"
. "github.com/hootsuite/atlantis/testing"
"github.com/lkysow/go-gitlab"
"github.com/mohae/deepcopy"
)
var parser = events.EventParser{
GithubUser: "user",
GithubToken: "token",
}
func TestDetermineCommandNoBody(t *testing.T) {
_, err := parser.DetermineCommand(&github.IssueCommentEvent{})
Equals(t, errors.New("comment.body is null"), err)
GithubUser: "github-user",
GithubToken: "github-token",
GitlabUser: "gitlab-user",
GitlabToken: "gitlab-token",
}
func TestDetermineCommandInvalid(t *testing.T) {
@@ -30,17 +31,17 @@ func TestDetermineCommandInvalid(t *testing.T) {
// just the executable, no command
"run",
"atlantis",
"@user",
"@github-user",
// invalid command
"run slkjd",
"atlantis slkjd",
"@user slkjd",
"@github-user slkjd",
"atlantis plans",
// misc
"related comment mentioning atlantis",
}
for _, c := range comments {
_, e := parser.DetermineCommand(buildComment(c))
_, e := parser.DetermineCommand(c, vcs.Github)
Assert(t, e != nil, "expected error for comment: "+c)
}
}
@@ -50,18 +51,18 @@ func TestDetermineCommandHelp(t *testing.T) {
comments := []string{
"run help",
"atlantis help",
"@user help",
"@github-user help",
"atlantis help --verbose",
}
for _, c := range comments {
command, e := parser.DetermineCommand(buildComment(c))
command, e := parser.DetermineCommand(c, vcs.Github)
Ok(t, e)
Equals(t, events.Help, command.Name)
}
}
func TestDetermineCommandPermutations(t *testing.T) {
execNames := []string{"run", "atlantis", "@user"}
execNames := []string{"run", "atlantis", "@github-user", "@gitlab-user"}
commandNames := []events.CommandName{events.Plan, events.Apply}
envs := []string{"", "default", "env", "env-dash", "env_underscore", "camelEnv"}
flagCases := [][]string{
@@ -86,7 +87,15 @@ func TestDetermineCommandPermutations(t *testing.T) {
for _, lineEnding := range []string{"", "\r\n"} {
comment := strings.Join(append([]string{exec, name.String(), env}, flags...), " ") + lineEnding
t.Log("testing comment: " + comment)
c, err := parser.DetermineCommand(buildComment(comment))
// In order to test gitlab without fully refactoring this test
// we're just detecting if we're using the gitlab user as the
// exec name.
vcsHost := vcs.Github
if exec == "@gitlab-user" {
vcsHost = vcs.Gitlab
}
c, err := parser.DetermineCommand(comment, vcsHost)
Ok(t, err)
Equals(t, name, c.Name)
if env == "" {
@@ -114,42 +123,42 @@ func TestDetermineCommandPermutations(t *testing.T) {
}
}
func TestExtractRepoData(t *testing.T) {
func TestParseGithubRepo(t *testing.T) {
testRepo := Repo
testRepo.FullName = nil
_, err := parser.ExtractRepoData(&testRepo)
_, err := parser.ParseGithubRepo(&testRepo)
Equals(t, errors.New("repository.full_name is null"), err)
testRepo = Repo
testRepo.Owner = nil
_, err = parser.ExtractRepoData(&testRepo)
_, err = parser.ParseGithubRepo(&testRepo)
Equals(t, errors.New("repository.owner.login is null"), err)
testRepo = Repo
testRepo.Name = nil
_, err = parser.ExtractRepoData(&testRepo)
_, err = parser.ParseGithubRepo(&testRepo)
Equals(t, errors.New("repository.name is null"), err)
testRepo = Repo
testRepo.CloneURL = nil
_, err = parser.ExtractRepoData(&testRepo)
_, err = parser.ParseGithubRepo(&testRepo)
Equals(t, errors.New("repository.clone_url is null"), err)
t.Log("should replace https clone with user/pass")
{
r, err := parser.ExtractRepoData(&Repo)
r, err := parser.ParseGithubRepo(&Repo)
Ok(t, err)
Equals(t, models.Repo{
Owner: "owner",
FullName: "owner/repo",
CloneURL: "https://user:token@github.com/lkysow/atlantis-example.git",
CloneURL: "https://github-user:github-token@github.com/lkysow/atlantis-example.git",
SanitizedCloneURL: Repo.GetCloneURL(),
Name: "repo",
}, r)
}
}
func TestExtractCommentData(t *testing.T) {
func TestParseGithubIssueCommentEvent(t *testing.T) {
comment := github.IssueCommentEvent{
Repo: &Repo,
Issue: &github.Issue{
@@ -163,109 +172,169 @@ func TestExtractCommentData(t *testing.T) {
}
testComment := deepcopy.Copy(comment).(github.IssueCommentEvent)
testComment.Repo = nil
_, _, _, err := parser.ExtractCommentData(&testComment)
_, _, _, err := parser.ParseGithubIssueCommentEvent(&testComment)
Equals(t, errors.New("repository.full_name is null"), err)
testComment = deepcopy.Copy(comment).(github.IssueCommentEvent)
testComment.Issue = nil
_, _, _, err = parser.ExtractCommentData(&testComment)
Equals(t, errors.New("issue.number is null"), err)
testComment.Comment = nil
_, _, _, err = parser.ParseGithubIssueCommentEvent(&testComment)
Equals(t, errors.New("comment.user.login is null"), err)
testComment = deepcopy.Copy(comment).(github.IssueCommentEvent)
testComment.Issue.User = nil
_, _, _, err = parser.ExtractCommentData(&testComment)
Equals(t, errors.New("issue.user.login is null"), err)
testComment = deepcopy.Copy(comment).(github.IssueCommentEvent)
testComment.Issue.HTMLURL = nil
_, _, _, err = parser.ExtractCommentData(&testComment)
Equals(t, errors.New("issue.html_url is null"), err)
testComment.Comment.User = nil
_, _, _, err = parser.ParseGithubIssueCommentEvent(&testComment)
Equals(t, errors.New("comment.user.login is null"), err)
testComment = deepcopy.Copy(comment).(github.IssueCommentEvent)
testComment.Comment.User.Login = nil
_, _, _, err = parser.ExtractCommentData(&testComment)
_, _, _, err = parser.ParseGithubIssueCommentEvent(&testComment)
Equals(t, errors.New("comment.user.login is null"), err)
testComment = deepcopy.Copy(comment).(github.IssueCommentEvent)
testComment.Issue = nil
_, _, _, err = parser.ParseGithubIssueCommentEvent(&testComment)
Equals(t, errors.New("issue.number is null"), err)
// this should be successful
repo, user, pull, err := parser.ExtractCommentData(&comment)
repo, user, pullNum, err := parser.ParseGithubIssueCommentEvent(&comment)
Ok(t, err)
Equals(t, models.Repo{
Owner: *comment.Repo.Owner.Login,
FullName: *comment.Repo.FullName,
CloneURL: "https://user:token@github.com/lkysow/atlantis-example.git",
CloneURL: "https://github-user:github-token@github.com/lkysow/atlantis-example.git",
SanitizedCloneURL: *comment.Repo.CloneURL,
Name: "repo",
}, repo)
Equals(t, models.User{
Username: *comment.Comment.User.Login,
}, user)
Equals(t, models.PullRequest{
Num: *comment.Issue.Number,
}, pull)
Equals(t, *comment.Issue.Number, pullNum)
}
func TestExtractPullData(t *testing.T) {
func TestParseGithubPull(t *testing.T) {
testPull := deepcopy.Copy(Pull).(github.PullRequest)
testPull.Head.SHA = nil
_, _, err := parser.ExtractPullData(&testPull)
_, _, err := parser.ParseGithubPull(&testPull)
Equals(t, errors.New("head.sha is null"), err)
testPull = deepcopy.Copy(Pull).(github.PullRequest)
testPull.Base.SHA = nil
_, _, err = parser.ExtractPullData(&testPull)
Equals(t, errors.New("base.sha is null"), err)
testPull = deepcopy.Copy(Pull).(github.PullRequest)
testPull.HTMLURL = nil
_, _, err = parser.ExtractPullData(&testPull)
_, _, err = parser.ParseGithubPull(&testPull)
Equals(t, errors.New("html_url is null"), err)
testPull = deepcopy.Copy(Pull).(github.PullRequest)
testPull.Head.Ref = nil
_, _, err = parser.ExtractPullData(&testPull)
_, _, err = parser.ParseGithubPull(&testPull)
Equals(t, errors.New("head.ref is null"), err)
testPull = deepcopy.Copy(Pull).(github.PullRequest)
testPull.User.Login = nil
_, _, err = parser.ExtractPullData(&testPull)
_, _, err = parser.ParseGithubPull(&testPull)
Equals(t, errors.New("user.login is null"), err)
testPull = deepcopy.Copy(Pull).(github.PullRequest)
testPull.Number = nil
_, _, err = parser.ExtractPullData(&testPull)
_, _, err = parser.ParseGithubPull(&testPull)
Equals(t, errors.New("number is null"), err)
testPull = deepcopy.Copy(Pull).(github.PullRequest)
testPull.Head.Repo = nil
_, _, err = parser.ExtractPullData(&testPull)
_, _, err = parser.ParseGithubPull(&testPull)
Equals(t, errors.New("repository.full_name is null"), err)
PullRes, repoRes, err := parser.ExtractPullData(&Pull)
pullRes, repoRes, err := parser.ParseGithubPull(&Pull)
Ok(t, err)
Equals(t, models.PullRequest{
BaseCommit: Pull.Base.GetSHA(),
URL: Pull.GetHTMLURL(),
Author: Pull.User.GetLogin(),
Branch: Pull.Head.GetRef(),
HeadCommit: Pull.Head.GetSHA(),
Num: Pull.GetNumber(),
}, PullRes)
State: models.Open,
}, pullRes)
Equals(t, models.Repo{
Owner: "owner",
FullName: "owner/repo",
CloneURL: "https://user:token@github.com/lkysow/atlantis-example.git",
CloneURL: "https://github-user:github-token@github.com/lkysow/atlantis-example.git",
SanitizedCloneURL: Repo.GetCloneURL(),
Name: "repo",
}, repoRes)
}
func buildComment(c string) *github.IssueCommentEvent {
return &github.IssueCommentEvent{
Comment: &github.IssueComment{
Body: github.String(c),
},
func TestParseGitlabMergeEvent(t *testing.T) {
t.Log("should properly parse a gitlab merge event")
var event *gitlab.MergeEvent
err := json.Unmarshal([]byte(mergeEventJSON), &event)
Ok(t, err)
pull, repo := parser.ParseGitlabMergeEvent(*event)
Equals(t, models.PullRequest{
URL: "http://example.com/diaspora/merge_requests/1",
Author: "root",
Num: 1,
HeadCommit: "da1560886d4f094c3e6c9ef40349f7d38b5d27d7",
Branch: "ms-viewport",
State: models.Open,
}, pull)
Equals(t, models.Repo{
FullName: "gitlabhq/gitlab-test",
Name: "gitlab-test",
SanitizedCloneURL: "https://example.com/gitlabhq/gitlab-test.git",
Owner: "gitlabhq",
CloneURL: "https://gitlab-user:gitlab-token@example.com/gitlabhq/gitlab-test.git",
}, repo)
t.Log("If the state is closed, should set field correctly.")
event.ObjectAttributes.State = "closed"
pull, _ = parser.ParseGitlabMergeEvent(*event)
Equals(t, models.Closed, pull.State)
}
func TestParseGitlabMergeRequest(t *testing.T) {
t.Log("should properly parse a gitlab merge request")
var event *gitlab.MergeRequest
err := json.Unmarshal([]byte(mergeRequestJSON), &event)
Ok(t, err)
pull := parser.ParseGitlabMergeRequest(event)
Equals(t, models.PullRequest{
URL: "https://gitlab.com/lkysow/atlantis-example/merge_requests/8",
Author: "lkysow",
Num: 8,
HeadCommit: "0b4ac85ea3063ad5f2974d10cd68dd1f937aaac2",
Branch: "abc",
State: models.Open,
}, pull)
t.Log("If the state is closed, should set field correctly.")
event.State = "closed"
pull = parser.ParseGitlabMergeRequest(event)
Equals(t, models.Closed, pull.State)
}
func TestParseGitlabMergeCommentEvent(t *testing.T) {
t.Log("should properly parse a gitlab merge comment event")
var event *gitlab.MergeCommentEvent
err := json.Unmarshal([]byte(mergeCommentEventJSON), &event)
Ok(t, err)
baseRepo, headRepo, user := parser.ParseGitlabMergeCommentEvent(*event)
Equals(t, models.Repo{
FullName: "gitlabhq/gitlab-test",
Name: "gitlab-test",
SanitizedCloneURL: "https://example.com/gitlabhq/gitlab-test.git",
Owner: "gitlabhq",
CloneURL: "https://gitlab-user:gitlab-token@example.com/gitlabhq/gitlab-test.git",
}, baseRepo)
Equals(t, models.Repo{
FullName: "gitlab-org/gitlab-test",
Name: "gitlab-test",
SanitizedCloneURL: "https://example.com/gitlab-org/gitlab-test.git",
Owner: "gitlab-org",
CloneURL: "https://gitlab-user:gitlab-token@example.com/gitlab-org/gitlab-test.git",
}, headRepo)
Equals(t, models.User{
Username: "root",
}, user)
}
func containsVerbose(list []string) bool {
@@ -276,3 +345,310 @@ func containsVerbose(list []string) bool {
}
return false
}
var mergeEventJSON = `{
"object_kind": "merge_request",
"user": {
"name": "Administrator",
"username": "root",
"avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=40\u0026d=identicon"
},
"project": {
"id": 1,
"name":"Gitlab Test",
"description":"Aut reprehenderit ut est.",
"web_url":"http://example.com/gitlabhq/gitlab-test",
"avatar_url":null,
"git_ssh_url":"git@example.com:gitlabhq/gitlab-test.git",
"git_http_url":"https://example.com/gitlabhq/gitlab-test.git",
"namespace":"GitlabHQ",
"visibility_level":20,
"path_with_namespace":"gitlabhq/gitlab-test",
"default_branch":"master",
"homepage":"http://example.com/gitlabhq/gitlab-test",
"url":"https://example.com/gitlabhq/gitlab-test.git",
"ssh_url":"git@example.com:gitlabhq/gitlab-test.git",
"http_url":"https://example.com/gitlabhq/gitlab-test.git"
},
"repository": {
"name": "Gitlab Test",
"url": "https://example.com/gitlabhq/gitlab-test.git",
"description": "Aut reprehenderit ut est.",
"homepage": "http://example.com/gitlabhq/gitlab-test"
},
"object_attributes": {
"id": 99,
"target_branch": "master",
"source_branch": "ms-viewport",
"source_project_id": 14,
"author_id": 51,
"assignee_id": 6,
"title": "MS-Viewport",
"created_at": "2013-12-03T17:23:34Z",
"updated_at": "2013-12-03T17:23:34Z",
"st_commits": null,
"st_diffs": null,
"milestone_id": null,
"state": "opened",
"merge_status": "unchecked",
"target_project_id": 14,
"iid": 1,
"description": "",
"source": {
"name":"Awesome Project",
"description":"Aut reprehenderit ut est.",
"web_url":"http://example.com/awesome_space/awesome_project",
"avatar_url":null,
"git_ssh_url":"git@example.com:awesome_space/awesome_project.git",
"git_http_url":"http://example.com/awesome_space/awesome_project.git",
"namespace":"Awesome Space",
"visibility_level":20,
"path_with_namespace":"awesome_space/awesome_project",
"default_branch":"master",
"homepage":"http://example.com/awesome_space/awesome_project",
"url":"http://example.com/awesome_space/awesome_project.git",
"ssh_url":"git@example.com:awesome_space/awesome_project.git",
"http_url":"http://example.com/awesome_space/awesome_project.git"
},
"target": {
"name":"Awesome Project",
"description":"Aut reprehenderit ut est.",
"web_url":"http://example.com/awesome_space/awesome_project",
"avatar_url":null,
"git_ssh_url":"git@example.com:awesome_space/awesome_project.git",
"git_http_url":"http://example.com/awesome_space/awesome_project.git",
"namespace":"Awesome Space",
"visibility_level":20,
"path_with_namespace":"awesome_space/awesome_project",
"default_branch":"master",
"homepage":"http://example.com/awesome_space/awesome_project",
"url":"http://example.com/awesome_space/awesome_project.git",
"ssh_url":"git@example.com:awesome_space/awesome_project.git",
"http_url":"http://example.com/awesome_space/awesome_project.git"
},
"last_commit": {
"id": "da1560886d4f094c3e6c9ef40349f7d38b5d27d7",
"message": "fixed readme",
"timestamp": "2012-01-03T23:36:29+02:00",
"url": "http://example.com/awesome_space/awesome_project/commits/da1560886d4f094c3e6c9ef40349f7d38b5d27d7",
"author": {
"name": "GitLab dev user",
"email": "gitlabdev@dv6700.(none)"
}
},
"work_in_progress": false,
"url": "http://example.com/diaspora/merge_requests/1",
"action": "open",
"assignee": {
"name": "User1",
"username": "user1",
"avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=40\u0026d=identicon"
}
},
"labels": [{
"id": 206,
"title": "API",
"color": "#ffffff",
"project_id": 14,
"created_at": "2013-12-03T17:15:43Z",
"updated_at": "2013-12-03T17:15:43Z",
"template": false,
"description": "API related issues",
"type": "ProjectLabel",
"group_id": 41
}],
"changes": {
"updated_by_id": [null, 1],
"updated_at": ["2017-09-15 16:50:55 UTC", "2017-09-15 16:52:00 UTC"],
"labels": {
"previous": [{
"id": 206,
"title": "API",
"color": "#ffffff",
"project_id": 14,
"created_at": "2013-12-03T17:15:43Z",
"updated_at": "2013-12-03T17:15:43Z",
"template": false,
"description": "API related issues",
"type": "ProjectLabel",
"group_id": 41
}],
"current": [{
"id": 205,
"title": "Platform",
"color": "#123123",
"project_id": 14,
"created_at": "2013-12-03T17:15:43Z",
"updated_at": "2013-12-03T17:15:43Z",
"template": false,
"description": "Platform related issues",
"type": "ProjectLabel",
"group_id": 41
}]
}
}
}`
var mergeCommentEventJSON = `{
"object_kind": "note",
"user": {
"name": "Administrator",
"username": "root",
"avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=40\u0026d=identicon"
},
"project_id": 5,
"project":{
"id": 5,
"name":"Gitlab Test",
"description":"Aut reprehenderit ut est.",
"web_url":"http://example.com/gitlabhq/gitlab-test",
"avatar_url":null,
"git_ssh_url":"git@example.com:gitlabhq/gitlab-test.git",
"git_http_url":"https://example.com/gitlabhq/gitlab-test.git",
"namespace":"Gitlab Org",
"visibility_level":10,
"path_with_namespace":"gitlabhq/gitlab-test",
"default_branch":"master",
"homepage":"http://example.com/gitlabhq/gitlab-test",
"url":"https://example.com/gitlabhq/gitlab-test.git",
"ssh_url":"git@example.com:gitlabhq/gitlab-test.git",
"http_url":"https://example.com/gitlabhq/gitlab-test.git"
},
"repository":{
"name": "Gitlab Test",
"url": "http://localhost/gitlab-org/gitlab-test.git",
"description": "Aut reprehenderit ut est.",
"homepage": "http://example.com/gitlab-org/gitlab-test"
},
"object_attributes": {
"id": 1244,
"note": "This MR needs work.",
"noteable_type": "MergeRequest",
"author_id": 1,
"created_at": "2015-05-17",
"updated_at": "2015-05-17",
"project_id": 5,
"attachment": null,
"line_code": null,
"commit_id": "",
"noteable_id": 7,
"system": false,
"st_diff": null,
"url": "http://example.com/gitlab-org/gitlab-test/merge_requests/1#note_1244"
},
"merge_request": {
"id": 7,
"target_branch": "markdown",
"source_branch": "master",
"source_project_id": 5,
"author_id": 8,
"assignee_id": 28,
"title": "Tempora et eos debitis quae laborum et.",
"created_at": "2015-03-01 20:12:53 UTC",
"updated_at": "2015-03-21 18:27:27 UTC",
"milestone_id": 11,
"state": "opened",
"merge_status": "cannot_be_merged",
"target_project_id": 5,
"iid": 1,
"description": "Et voluptas corrupti assumenda temporibus. Architecto cum animi eveniet amet asperiores. Vitae numquam voluptate est natus sit et ad id.",
"position": 0,
"source":{
"name":"Gitlab Test",
"description":"Aut reprehenderit ut est.",
"web_url":"http://example.com/gitlab-org/gitlab-test",
"avatar_url":null,
"git_ssh_url":"git@example.com:gitlab-org/gitlab-test.git",
"git_http_url":"https://example.com/gitlab-org/gitlab-test.git",
"namespace":"Gitlab Org",
"visibility_level":10,
"path_with_namespace":"gitlab-org/gitlab-test",
"default_branch":"master",
"homepage":"http://example.com/gitlab-org/gitlab-test",
"url":"https://example.com/gitlab-org/gitlab-test.git",
"ssh_url":"git@example.com:gitlab-org/gitlab-test.git",
"http_url":"https://example.com/gitlab-org/gitlab-test.git",
"git_http_url":"https://example.com/gitlab-org/gitlab-test.git"
},
"target": {
"name":"Gitlab Test",
"description":"Aut reprehenderit ut est.",
"web_url":"http://example.com/gitlabhq/gitlab-test",
"avatar_url":null,
"git_ssh_url":"git@example.com:gitlabhq/gitlab-test.git",
"git_http_url":"https://example.com/gitlabhq/gitlab-test.git",
"namespace":"Gitlab Org",
"visibility_level":10,
"path_with_namespace":"gitlabhq/gitlab-test",
"default_branch":"master",
"homepage":"http://example.com/gitlabhq/gitlab-test",
"url":"https://example.com/gitlabhq/gitlab-test.git",
"ssh_url":"git@example.com:gitlabhq/gitlab-test.git",
"http_url":"https://example.com/gitlabhq/gitlab-test.git"
},
"last_commit": {
"id": "562e173be03b8ff2efb05345d12df18815438a4b",
"message": "Merge branch 'another-branch' into 'master'\n\nCheck in this test\n",
"timestamp": "2002-10-02T10:00:00-05:00",
"url": "http://example.com/gitlab-org/gitlab-test/commit/562e173be03b8ff2efb05345d12df18815438a4b",
"author": {
"name": "John Smith",
"email": "john@example.com"
}
},
"work_in_progress": false,
"assignee": {
"name": "User1",
"username": "user1",
"avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=40\u0026d=identicon"
}
}
}`
var mergeRequestJSON = `{
"id":6056811,
"iid":8,
"project_id":4580910,
"title":"Update main.tf",
"description":"",
"state":"opened",
"created_at":"2017-11-13T19:33:42.704Z",
"updated_at":"2017-11-13T23:35:26.200Z",
"target_branch":"master",
"source_branch":"abc",
"upvotes":0,
"downvotes":0,
"author":{
"id":1755902,
"name":"Luke Kysow",
"username":"lkysow",
"state":"active",
"avatar_url":"https://secure.gravatar.com/avatar/25fd57e71590fe28736624ff24d41c5f?s=80\u0026d=identicon",
"web_url":"https://gitlab.com/lkysow"
},
"assignee":null,
"source_project_id":4580910,
"target_project_id":4580910,
"labels":[
],
"work_in_progress":false,
"milestone":null,
"merge_when_pipeline_succeeds":false,
"merge_status":"can_be_merged",
"sha":"0b4ac85ea3063ad5f2974d10cd68dd1f937aaac2",
"merge_commit_sha":null,
"user_notes_count":10,
"approvals_before_merge":null,
"discussion_locked":null,
"should_remove_source_branch":null,
"force_remove_source_branch":false,
"squash":false,
"web_url":"https://gitlab.com/lkysow/atlantis-example/merge_requests/8",
"time_stats":{
"time_estimate":0,
"total_time_spent":0,
"human_time_estimate":null,
"human_total_time_spent":null
}
}`

View File

@@ -1,79 +0,0 @@
package events
import (
"fmt"
"strings"
"github.com/hootsuite/atlantis/server/events/github"
"github.com/hootsuite/atlantis/server/events/models"
)
type Status int
const (
statusContext = "Atlantis"
)
const (
Pending Status = iota
Success
Failure
Error
)
//go:generate pegomock generate --use-experimental-model-gen --package mocks -o mocks/mock_github_status.go GHStatusUpdater
type GHStatusUpdater interface {
Update(repo models.Repo, pull models.PullRequest, status Status, cmd *Command) error
UpdateProjectResult(ctx *CommandContext, res CommandResponse) error
}
type GithubStatus struct {
Client github.Client
}
func (s Status) String() string {
switch s {
case Pending:
return "pending"
case Success:
return "success"
case Failure:
return "failure"
case Error:
return "error"
}
return "error"
}
func (g *GithubStatus) Update(repo models.Repo, pull models.PullRequest, status Status, cmd *Command) error {
description := fmt.Sprintf("%s %s", strings.Title(cmd.Name.String()), strings.Title(status.String()))
return g.Client.UpdateStatus(repo, pull, status.String(), description, statusContext)
}
func (g *GithubStatus) UpdateProjectResult(ctx *CommandContext, res CommandResponse) error {
var status Status
if res.Error != nil {
status = Error
} else if res.Failure != "" {
status = Failure
} else {
var statuses []Status
for _, p := range res.ProjectResults {
statuses = append(statuses, p.Status())
}
status = g.worstStatus(statuses)
}
return g.Update(ctx.BaseRepo, ctx.Pull, status, ctx.Command)
}
func (g *GithubStatus) worstStatus(ss []Status) Status {
worst := Success
for _, s := range ss {
if s > worst {
worst = s
}
}
return worst
}

View File

@@ -4,9 +4,10 @@
package mocks
import (
"reflect"
models "github.com/hootsuite/atlantis/server/events/models"
pegomock "github.com/petergtz/pegomock"
"reflect"
)
type MockBackend struct {

View File

@@ -4,10 +4,11 @@
package mocks
import (
"reflect"
locking "github.com/hootsuite/atlantis/server/events/locking"
models "github.com/hootsuite/atlantis/server/events/models"
pegomock "github.com/petergtz/pegomock"
"reflect"
)
type MockLocker struct {

View File

@@ -63,8 +63,8 @@ var failureTmpl = template.Must(template.New("").Parse(failureTmplText))
var failureWithLogTmpl = template.Must(template.New("").Parse(failureTmplText + logTmpl))
var logTmpl = "{{if .Verbose}}\n<details><summary>Log</summary>\n <p>\n\n```\n{{.Log}}```\n</p></details>{{end}}\n"
// GithubCommentRenderer renders responses as GitHub comments
type GithubCommentRenderer struct{}
// MarkdownRenderer renders responses as markdown
type MarkdownRenderer struct{}
type CommonData struct {
Command string
@@ -89,7 +89,7 @@ type ResultData struct {
// Render formats the data into a string that can be commented back to GitHub.
// nolint: interfacer
func (g *GithubCommentRenderer) Render(res CommandResponse, cmdName CommandName, log string, verbose bool) string {
func (g *MarkdownRenderer) Render(res CommandResponse, cmdName CommandName, log string, verbose bool) string {
if cmdName == Help {
return g.renderTemplate(helpTmpl, nil)
}
@@ -104,7 +104,7 @@ func (g *GithubCommentRenderer) Render(res CommandResponse, cmdName CommandName,
return g.renderProjectResults(res.ProjectResults, common)
}
func (g *GithubCommentRenderer) renderProjectResults(pathResults []ProjectResult, common CommonData) string {
func (g *MarkdownRenderer) renderProjectResults(pathResults []ProjectResult, common CommonData) string {
results := make(map[string]string)
for _, result := range pathResults {
if result.Error != nil {
@@ -141,7 +141,7 @@ func (g *GithubCommentRenderer) renderProjectResults(pathResults []ProjectResult
return g.renderTemplate(tmpl, ResultData{results, common})
}
func (g *GithubCommentRenderer) renderTemplate(tmpl *template.Template, data interface{}) string {
func (g *MarkdownRenderer) renderTemplate(tmpl *template.Template, data interface{}) string {
buf := &bytes.Buffer{}
if err := tmpl.Execute(buf, data); err != nil {
return fmt.Sprintf("Failed to render template, this is a bug: %v", err)

View File

@@ -30,7 +30,7 @@ func TestRenderErr(t *testing.T) {
},
}
r := events.GithubCommentRenderer{}
r := events.MarkdownRenderer{}
for _, c := range cases {
res := events.CommandResponse{
Error: c.Error,
@@ -68,7 +68,7 @@ func TestRenderFailure(t *testing.T) {
},
}
r := events.GithubCommentRenderer{}
r := events.MarkdownRenderer{}
for _, c := range cases {
res := events.CommandResponse{
Failure: c.Failure,
@@ -87,7 +87,7 @@ func TestRenderFailure(t *testing.T) {
func TestRenderErrAndFailure(t *testing.T) {
t.Log("if there is an error and a failure, the error should be printed")
r := events.GithubCommentRenderer{}
r := events.MarkdownRenderer{}
res := events.CommandResponse{
Error: errors.New("error"),
Failure: "failure",
@@ -225,7 +225,7 @@ func TestRenderProjectResults(t *testing.T) {
},
}
r := events.GithubCommentRenderer{}
r := events.MarkdownRenderer{}
for _, c := range cases {
res := events.CommandResponse{
ProjectResults: c.ProjectResults,

View File

@@ -4,9 +4,12 @@
package mocks
import (
events "github.com/hootsuite/atlantis/server/events"
pegomock "github.com/petergtz/pegomock"
"reflect"
events "github.com/hootsuite/atlantis/server/events"
models "github.com/hootsuite/atlantis/server/events/models"
vcs "github.com/hootsuite/atlantis/server/events/vcs"
pegomock "github.com/petergtz/pegomock"
)
type MockCommandRunner struct {
@@ -17,8 +20,8 @@ func NewMockCommandRunner() *MockCommandRunner {
return &MockCommandRunner{fail: pegomock.GlobalFailHandler}
}
func (mock *MockCommandRunner) ExecuteCommand(ctx *events.CommandContext) {
params := []pegomock.Param{ctx}
func (mock *MockCommandRunner) ExecuteCommand(baseRepo models.Repo, headRepo models.Repo, user models.User, pullNum int, cmd *events.Command, vcsHost vcs.Host) {
params := []pegomock.Param{baseRepo, headRepo, user, pullNum, cmd, vcsHost}
pegomock.GetGenericMockFrom(mock).Invoke("ExecuteCommand", params, []reflect.Type{})
}
@@ -40,8 +43,8 @@ type VerifierCommandRunner struct {
inOrderContext *pegomock.InOrderContext
}
func (verifier *VerifierCommandRunner) ExecuteCommand(ctx *events.CommandContext) *CommandRunner_ExecuteCommand_OngoingVerification {
params := []pegomock.Param{ctx}
func (verifier *VerifierCommandRunner) ExecuteCommand(baseRepo models.Repo, headRepo models.Repo, user models.User, pullNum int, cmd *events.Command, vcsHost vcs.Host) *CommandRunner_ExecuteCommand_OngoingVerification {
params := []pegomock.Param{baseRepo, headRepo, user, pullNum, cmd, vcsHost}
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "ExecuteCommand", params)
return &CommandRunner_ExecuteCommand_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations}
}
@@ -51,17 +54,37 @@ type CommandRunner_ExecuteCommand_OngoingVerification struct {
methodInvocations []pegomock.MethodInvocation
}
func (c *CommandRunner_ExecuteCommand_OngoingVerification) GetCapturedArguments() *events.CommandContext {
ctx := c.GetAllCapturedArguments()
return ctx[len(ctx)-1]
func (c *CommandRunner_ExecuteCommand_OngoingVerification) GetCapturedArguments() (models.Repo, models.Repo, models.User, int, *events.Command, vcs.Host) {
baseRepo, headRepo, user, pullNum, cmd, vcsHost := c.GetAllCapturedArguments()
return baseRepo[len(baseRepo)-1], headRepo[len(headRepo)-1], user[len(user)-1], pullNum[len(pullNum)-1], cmd[len(cmd)-1], vcsHost[len(vcsHost)-1]
}
func (c *CommandRunner_ExecuteCommand_OngoingVerification) GetAllCapturedArguments() (_param0 []*events.CommandContext) {
func (c *CommandRunner_ExecuteCommand_OngoingVerification) GetAllCapturedArguments() (_param0 []models.Repo, _param1 []models.Repo, _param2 []models.User, _param3 []int, _param4 []*events.Command, _param5 []vcs.Host) {
params := pegomock.GetGenericMockFrom(c.mock).GetInvocationParams(c.methodInvocations)
if len(params) > 0 {
_param0 = make([]*events.CommandContext, len(params[0]))
_param0 = make([]models.Repo, len(params[0]))
for u, param := range params[0] {
_param0[u] = param.(*events.CommandContext)
_param0[u] = param.(models.Repo)
}
_param1 = make([]models.Repo, len(params[1]))
for u, param := range params[1] {
_param1[u] = param.(models.Repo)
}
_param2 = make([]models.User, len(params[2]))
for u, param := range params[2] {
_param2[u] = param.(models.User)
}
_param3 = make([]int, len(params[3]))
for u, param := range params[3] {
_param3[u] = param.(int)
}
_param4 = make([]*events.Command, len(params[4]))
for u, param := range params[4] {
_param4[u] = param.(*events.Command)
}
_param5 = make([]vcs.Host, len(params[5]))
for u, param := range params[5] {
_param5[u] = param.(vcs.Host)
}
}
return

View File

@@ -0,0 +1,136 @@
// Automatically generated by pegomock. DO NOT EDIT!
// Source: github.com/hootsuite/atlantis/server/events (interfaces: CommitStatusUpdater)
package mocks
import (
events "github.com/hootsuite/atlantis/server/events"
models "github.com/hootsuite/atlantis/server/events/models"
vcs "github.com/hootsuite/atlantis/server/events/vcs"
pegomock "github.com/petergtz/pegomock"
"reflect"
)
type MockCommitStatusUpdater struct {
fail func(message string, callerSkip ...int)
}
func NewMockCommitStatusUpdater() *MockCommitStatusUpdater {
return &MockCommitStatusUpdater{fail: pegomock.GlobalFailHandler}
}
func (mock *MockCommitStatusUpdater) Update(repo models.Repo, pull models.PullRequest, status vcs.CommitStatus, cmd *events.Command, host vcs.Host) error {
params := []pegomock.Param{repo, pull, status, cmd, host}
result := pegomock.GetGenericMockFrom(mock).Invoke("Update", params, []reflect.Type{reflect.TypeOf((*error)(nil)).Elem()})
var ret0 error
if len(result) != 0 {
if result[0] != nil {
ret0 = result[0].(error)
}
}
return ret0
}
func (mock *MockCommitStatusUpdater) UpdateProjectResult(ctx *events.CommandContext, res events.CommandResponse) error {
params := []pegomock.Param{ctx, res}
result := pegomock.GetGenericMockFrom(mock).Invoke("UpdateProjectResult", params, []reflect.Type{reflect.TypeOf((*error)(nil)).Elem()})
var ret0 error
if len(result) != 0 {
if result[0] != nil {
ret0 = result[0].(error)
}
}
return ret0
}
func (mock *MockCommitStatusUpdater) VerifyWasCalledOnce() *VerifierCommitStatusUpdater {
return &VerifierCommitStatusUpdater{mock, pegomock.Times(1), nil}
}
func (mock *MockCommitStatusUpdater) VerifyWasCalled(invocationCountMatcher pegomock.Matcher) *VerifierCommitStatusUpdater {
return &VerifierCommitStatusUpdater{mock, invocationCountMatcher, nil}
}
func (mock *MockCommitStatusUpdater) VerifyWasCalledInOrder(invocationCountMatcher pegomock.Matcher, inOrderContext *pegomock.InOrderContext) *VerifierCommitStatusUpdater {
return &VerifierCommitStatusUpdater{mock, invocationCountMatcher, inOrderContext}
}
type VerifierCommitStatusUpdater struct {
mock *MockCommitStatusUpdater
invocationCountMatcher pegomock.Matcher
inOrderContext *pegomock.InOrderContext
}
func (verifier *VerifierCommitStatusUpdater) Update(repo models.Repo, pull models.PullRequest, status vcs.CommitStatus, cmd *events.Command, host vcs.Host) *CommitStatusUpdater_Update_OngoingVerification {
params := []pegomock.Param{repo, pull, status, cmd, host}
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "Update", params)
return &CommitStatusUpdater_Update_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations}
}
type CommitStatusUpdater_Update_OngoingVerification struct {
mock *MockCommitStatusUpdater
methodInvocations []pegomock.MethodInvocation
}
func (c *CommitStatusUpdater_Update_OngoingVerification) GetCapturedArguments() (models.Repo, models.PullRequest, vcs.CommitStatus, *events.Command, vcs.Host) {
repo, pull, status, cmd, host := c.GetAllCapturedArguments()
return repo[len(repo)-1], pull[len(pull)-1], status[len(status)-1], cmd[len(cmd)-1], host[len(host)-1]
}
func (c *CommitStatusUpdater_Update_OngoingVerification) GetAllCapturedArguments() (_param0 []models.Repo, _param1 []models.PullRequest, _param2 []vcs.CommitStatus, _param3 []*events.Command, _param4 []vcs.Host) {
params := pegomock.GetGenericMockFrom(c.mock).GetInvocationParams(c.methodInvocations)
if len(params) > 0 {
_param0 = make([]models.Repo, len(params[0]))
for u, param := range params[0] {
_param0[u] = param.(models.Repo)
}
_param1 = make([]models.PullRequest, len(params[1]))
for u, param := range params[1] {
_param1[u] = param.(models.PullRequest)
}
_param2 = make([]vcs.CommitStatus, len(params[2]))
for u, param := range params[2] {
_param2[u] = param.(vcs.CommitStatus)
}
_param3 = make([]*events.Command, len(params[3]))
for u, param := range params[3] {
_param3[u] = param.(*events.Command)
}
_param4 = make([]vcs.Host, len(params[4]))
for u, param := range params[4] {
_param4[u] = param.(vcs.Host)
}
}
return
}
func (verifier *VerifierCommitStatusUpdater) UpdateProjectResult(ctx *events.CommandContext, res events.CommandResponse) *CommitStatusUpdater_UpdateProjectResult_OngoingVerification {
params := []pegomock.Param{ctx, res}
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "UpdateProjectResult", params)
return &CommitStatusUpdater_UpdateProjectResult_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations}
}
type CommitStatusUpdater_UpdateProjectResult_OngoingVerification struct {
mock *MockCommitStatusUpdater
methodInvocations []pegomock.MethodInvocation
}
func (c *CommitStatusUpdater_UpdateProjectResult_OngoingVerification) GetCapturedArguments() (*events.CommandContext, events.CommandResponse) {
ctx, res := c.GetAllCapturedArguments()
return ctx[len(ctx)-1], res[len(res)-1]
}
func (c *CommitStatusUpdater_UpdateProjectResult_OngoingVerification) GetAllCapturedArguments() (_param0 []*events.CommandContext, _param1 []events.CommandResponse) {
params := pegomock.GetGenericMockFrom(c.mock).GetInvocationParams(c.methodInvocations)
if len(params) > 0 {
_param0 = make([]*events.CommandContext, len(params[0]))
for u, param := range params[0] {
_param0[u] = param.(*events.CommandContext)
}
_param1 = make([]events.CommandResponse, len(params[1]))
for u, param := range params[1] {
_param1[u] = param.(events.CommandResponse)
}
}
return
}

View File

@@ -4,8 +4,9 @@
package mocks
import (
pegomock "github.com/petergtz/pegomock"
"reflect"
pegomock "github.com/petergtz/pegomock"
)
type MockEnvLocker struct {

View File

@@ -4,11 +4,14 @@
package mocks
import (
"reflect"
github "github.com/google/go-github/github"
events "github.com/hootsuite/atlantis/server/events"
models "github.com/hootsuite/atlantis/server/events/models"
vcs "github.com/hootsuite/atlantis/server/events/vcs"
go_gitlab "github.com/lkysow/go-gitlab"
pegomock "github.com/petergtz/pegomock"
"reflect"
)
type MockEventParsing struct {
@@ -19,8 +22,8 @@ func NewMockEventParsing() *MockEventParsing {
return &MockEventParsing{fail: pegomock.GlobalFailHandler}
}
func (mock *MockEventParsing) DetermineCommand(comment *github.IssueCommentEvent) (*events.Command, error) {
params := []pegomock.Param{comment}
func (mock *MockEventParsing) DetermineCommand(comment string, vcsHost vcs.Host) (*events.Command, error) {
params := []pegomock.Param{comment, vcsHost}
result := pegomock.GetGenericMockFrom(mock).Invoke("DetermineCommand", params, []reflect.Type{reflect.TypeOf((**events.Command)(nil)).Elem(), reflect.TypeOf((*error)(nil)).Elem()})
var ret0 *events.Command
var ret1 error
@@ -35,12 +38,12 @@ func (mock *MockEventParsing) DetermineCommand(comment *github.IssueCommentEvent
return ret0, ret1
}
func (mock *MockEventParsing) ExtractCommentData(comment *github.IssueCommentEvent) (models.Repo, models.User, models.PullRequest, error) {
func (mock *MockEventParsing) ParseGithubIssueCommentEvent(comment *github.IssueCommentEvent) (models.Repo, models.User, int, error) {
params := []pegomock.Param{comment}
result := pegomock.GetGenericMockFrom(mock).Invoke("ExtractCommentData", params, []reflect.Type{reflect.TypeOf((*models.Repo)(nil)).Elem(), reflect.TypeOf((*models.User)(nil)).Elem(), reflect.TypeOf((*models.PullRequest)(nil)).Elem(), reflect.TypeOf((*error)(nil)).Elem()})
result := pegomock.GetGenericMockFrom(mock).Invoke("ParseGithubIssueCommentEvent", params, []reflect.Type{reflect.TypeOf((*models.Repo)(nil)).Elem(), reflect.TypeOf((*models.User)(nil)).Elem(), reflect.TypeOf((*int)(nil)).Elem(), reflect.TypeOf((*error)(nil)).Elem()})
var ret0 models.Repo
var ret1 models.User
var ret2 models.PullRequest
var ret2 int
var ret3 error
if len(result) != 0 {
if result[0] != nil {
@@ -50,7 +53,7 @@ func (mock *MockEventParsing) ExtractCommentData(comment *github.IssueCommentEve
ret1 = result[1].(models.User)
}
if result[2] != nil {
ret2 = result[2].(models.PullRequest)
ret2 = result[2].(int)
}
if result[3] != nil {
ret3 = result[3].(error)
@@ -59,9 +62,9 @@ func (mock *MockEventParsing) ExtractCommentData(comment *github.IssueCommentEve
return ret0, ret1, ret2, ret3
}
func (mock *MockEventParsing) ExtractPullData(pull *github.PullRequest) (models.PullRequest, models.Repo, error) {
func (mock *MockEventParsing) ParseGithubPull(pull *github.PullRequest) (models.PullRequest, models.Repo, error) {
params := []pegomock.Param{pull}
result := pegomock.GetGenericMockFrom(mock).Invoke("ExtractPullData", params, []reflect.Type{reflect.TypeOf((*models.PullRequest)(nil)).Elem(), reflect.TypeOf((*models.Repo)(nil)).Elem(), reflect.TypeOf((*error)(nil)).Elem()})
result := pegomock.GetGenericMockFrom(mock).Invoke("ParseGithubPull", params, []reflect.Type{reflect.TypeOf((*models.PullRequest)(nil)).Elem(), reflect.TypeOf((*models.Repo)(nil)).Elem(), reflect.TypeOf((*error)(nil)).Elem()})
var ret0 models.PullRequest
var ret1 models.Repo
var ret2 error
@@ -79,9 +82,9 @@ func (mock *MockEventParsing) ExtractPullData(pull *github.PullRequest) (models.
return ret0, ret1, ret2
}
func (mock *MockEventParsing) ExtractRepoData(ghRepo *github.Repository) (models.Repo, error) {
func (mock *MockEventParsing) ParseGithubRepo(ghRepo *github.Repository) (models.Repo, error) {
params := []pegomock.Param{ghRepo}
result := pegomock.GetGenericMockFrom(mock).Invoke("ExtractRepoData", params, []reflect.Type{reflect.TypeOf((*models.Repo)(nil)).Elem(), reflect.TypeOf((*error)(nil)).Elem()})
result := pegomock.GetGenericMockFrom(mock).Invoke("ParseGithubRepo", params, []reflect.Type{reflect.TypeOf((*models.Repo)(nil)).Elem(), reflect.TypeOf((*error)(nil)).Elem()})
var ret0 models.Repo
var ret1 error
if len(result) != 0 {
@@ -95,6 +98,54 @@ func (mock *MockEventParsing) ExtractRepoData(ghRepo *github.Repository) (models
return ret0, ret1
}
func (mock *MockEventParsing) ParseGitlabMergeEvent(event go_gitlab.MergeEvent) (models.PullRequest, models.Repo) {
params := []pegomock.Param{event}
result := pegomock.GetGenericMockFrom(mock).Invoke("ParseGitlabMergeEvent", params, []reflect.Type{reflect.TypeOf((*models.PullRequest)(nil)).Elem(), reflect.TypeOf((*models.Repo)(nil)).Elem()})
var ret0 models.PullRequest
var ret1 models.Repo
if len(result) != 0 {
if result[0] != nil {
ret0 = result[0].(models.PullRequest)
}
if result[1] != nil {
ret1 = result[1].(models.Repo)
}
}
return ret0, ret1
}
func (mock *MockEventParsing) ParseGitlabMergeCommentEvent(event go_gitlab.MergeCommentEvent) (models.Repo, models.Repo, models.User) {
params := []pegomock.Param{event}
result := pegomock.GetGenericMockFrom(mock).Invoke("ParseGitlabMergeCommentEvent", params, []reflect.Type{reflect.TypeOf((*models.Repo)(nil)).Elem(), reflect.TypeOf((*models.Repo)(nil)).Elem(), reflect.TypeOf((*models.User)(nil)).Elem()})
var ret0 models.Repo
var ret1 models.Repo
var ret2 models.User
if len(result) != 0 {
if result[0] != nil {
ret0 = result[0].(models.Repo)
}
if result[1] != nil {
ret1 = result[1].(models.Repo)
}
if result[2] != nil {
ret2 = result[2].(models.User)
}
}
return ret0, ret1, ret2
}
func (mock *MockEventParsing) ParseGitlabMergeRequest(mr *go_gitlab.MergeRequest) models.PullRequest {
params := []pegomock.Param{mr}
result := pegomock.GetGenericMockFrom(mock).Invoke("ParseGitlabMergeRequest", params, []reflect.Type{reflect.TypeOf((*models.PullRequest)(nil)).Elem()})
var ret0 models.PullRequest
if len(result) != 0 {
if result[0] != nil {
ret0 = result[0].(models.PullRequest)
}
}
return ret0
}
func (mock *MockEventParsing) VerifyWasCalledOnce() *VerifierEventParsing {
return &VerifierEventParsing{mock, pegomock.Times(1), nil}
}
@@ -113,8 +164,8 @@ type VerifierEventParsing struct {
inOrderContext *pegomock.InOrderContext
}
func (verifier *VerifierEventParsing) DetermineCommand(comment *github.IssueCommentEvent) *EventParsing_DetermineCommand_OngoingVerification {
params := []pegomock.Param{comment}
func (verifier *VerifierEventParsing) DetermineCommand(comment string, vcsHost vcs.Host) *EventParsing_DetermineCommand_OngoingVerification {
params := []pegomock.Param{comment, vcsHost}
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "DetermineCommand", params)
return &EventParsing_DetermineCommand_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations}
}
@@ -124,39 +175,43 @@ type EventParsing_DetermineCommand_OngoingVerification struct {
methodInvocations []pegomock.MethodInvocation
}
func (c *EventParsing_DetermineCommand_OngoingVerification) GetCapturedArguments() *github.IssueCommentEvent {
comment := c.GetAllCapturedArguments()
return comment[len(comment)-1]
func (c *EventParsing_DetermineCommand_OngoingVerification) GetCapturedArguments() (string, vcs.Host) {
comment, vcsHost := c.GetAllCapturedArguments()
return comment[len(comment)-1], vcsHost[len(vcsHost)-1]
}
func (c *EventParsing_DetermineCommand_OngoingVerification) GetAllCapturedArguments() (_param0 []*github.IssueCommentEvent) {
func (c *EventParsing_DetermineCommand_OngoingVerification) GetAllCapturedArguments() (_param0 []string, _param1 []vcs.Host) {
params := pegomock.GetGenericMockFrom(c.mock).GetInvocationParams(c.methodInvocations)
if len(params) > 0 {
_param0 = make([]*github.IssueCommentEvent, len(params[0]))
_param0 = make([]string, len(params[0]))
for u, param := range params[0] {
_param0[u] = param.(*github.IssueCommentEvent)
_param0[u] = param.(string)
}
_param1 = make([]vcs.Host, len(params[1]))
for u, param := range params[1] {
_param1[u] = param.(vcs.Host)
}
}
return
}
func (verifier *VerifierEventParsing) ExtractCommentData(comment *github.IssueCommentEvent) *EventParsing_ExtractCommentData_OngoingVerification {
func (verifier *VerifierEventParsing) ParseGithubIssueCommentEvent(comment *github.IssueCommentEvent) *EventParsing_ParseGithubIssueCommentEvent_OngoingVerification {
params := []pegomock.Param{comment}
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "ExtractCommentData", params)
return &EventParsing_ExtractCommentData_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations}
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "ParseGithubIssueCommentEvent", params)
return &EventParsing_ParseGithubIssueCommentEvent_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations}
}
type EventParsing_ExtractCommentData_OngoingVerification struct {
type EventParsing_ParseGithubIssueCommentEvent_OngoingVerification struct {
mock *MockEventParsing
methodInvocations []pegomock.MethodInvocation
}
func (c *EventParsing_ExtractCommentData_OngoingVerification) GetCapturedArguments() *github.IssueCommentEvent {
func (c *EventParsing_ParseGithubIssueCommentEvent_OngoingVerification) GetCapturedArguments() *github.IssueCommentEvent {
comment := c.GetAllCapturedArguments()
return comment[len(comment)-1]
}
func (c *EventParsing_ExtractCommentData_OngoingVerification) GetAllCapturedArguments() (_param0 []*github.IssueCommentEvent) {
func (c *EventParsing_ParseGithubIssueCommentEvent_OngoingVerification) GetAllCapturedArguments() (_param0 []*github.IssueCommentEvent) {
params := pegomock.GetGenericMockFrom(c.mock).GetInvocationParams(c.methodInvocations)
if len(params) > 0 {
_param0 = make([]*github.IssueCommentEvent, len(params[0]))
@@ -167,23 +222,23 @@ func (c *EventParsing_ExtractCommentData_OngoingVerification) GetAllCapturedArgu
return
}
func (verifier *VerifierEventParsing) ExtractPullData(pull *github.PullRequest) *EventParsing_ExtractPullData_OngoingVerification {
func (verifier *VerifierEventParsing) ParseGithubPull(pull *github.PullRequest) *EventParsing_ParseGithubPull_OngoingVerification {
params := []pegomock.Param{pull}
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "ExtractPullData", params)
return &EventParsing_ExtractPullData_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations}
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "ParseGithubPull", params)
return &EventParsing_ParseGithubPull_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations}
}
type EventParsing_ExtractPullData_OngoingVerification struct {
type EventParsing_ParseGithubPull_OngoingVerification struct {
mock *MockEventParsing
methodInvocations []pegomock.MethodInvocation
}
func (c *EventParsing_ExtractPullData_OngoingVerification) GetCapturedArguments() *github.PullRequest {
func (c *EventParsing_ParseGithubPull_OngoingVerification) GetCapturedArguments() *github.PullRequest {
pull := c.GetAllCapturedArguments()
return pull[len(pull)-1]
}
func (c *EventParsing_ExtractPullData_OngoingVerification) GetAllCapturedArguments() (_param0 []*github.PullRequest) {
func (c *EventParsing_ParseGithubPull_OngoingVerification) GetAllCapturedArguments() (_param0 []*github.PullRequest) {
params := pegomock.GetGenericMockFrom(c.mock).GetInvocationParams(c.methodInvocations)
if len(params) > 0 {
_param0 = make([]*github.PullRequest, len(params[0]))
@@ -194,23 +249,23 @@ func (c *EventParsing_ExtractPullData_OngoingVerification) GetAllCapturedArgumen
return
}
func (verifier *VerifierEventParsing) ExtractRepoData(ghRepo *github.Repository) *EventParsing_ExtractRepoData_OngoingVerification {
func (verifier *VerifierEventParsing) ParseGithubRepo(ghRepo *github.Repository) *EventParsing_ParseGithubRepo_OngoingVerification {
params := []pegomock.Param{ghRepo}
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "ExtractRepoData", params)
return &EventParsing_ExtractRepoData_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations}
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "ParseGithubRepo", params)
return &EventParsing_ParseGithubRepo_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations}
}
type EventParsing_ExtractRepoData_OngoingVerification struct {
type EventParsing_ParseGithubRepo_OngoingVerification struct {
mock *MockEventParsing
methodInvocations []pegomock.MethodInvocation
}
func (c *EventParsing_ExtractRepoData_OngoingVerification) GetCapturedArguments() *github.Repository {
func (c *EventParsing_ParseGithubRepo_OngoingVerification) GetCapturedArguments() *github.Repository {
ghRepo := c.GetAllCapturedArguments()
return ghRepo[len(ghRepo)-1]
}
func (c *EventParsing_ExtractRepoData_OngoingVerification) GetAllCapturedArguments() (_param0 []*github.Repository) {
func (c *EventParsing_ParseGithubRepo_OngoingVerification) GetAllCapturedArguments() (_param0 []*github.Repository) {
params := pegomock.GetGenericMockFrom(c.mock).GetInvocationParams(c.methodInvocations)
if len(params) > 0 {
_param0 = make([]*github.Repository, len(params[0]))
@@ -220,3 +275,84 @@ func (c *EventParsing_ExtractRepoData_OngoingVerification) GetAllCapturedArgumen
}
return
}
func (verifier *VerifierEventParsing) ParseGitlabMergeEvent(event go_gitlab.MergeEvent) *EventParsing_ParseGitlabMergeEvent_OngoingVerification {
params := []pegomock.Param{event}
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "ParseGitlabMergeEvent", params)
return &EventParsing_ParseGitlabMergeEvent_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations}
}
type EventParsing_ParseGitlabMergeEvent_OngoingVerification struct {
mock *MockEventParsing
methodInvocations []pegomock.MethodInvocation
}
func (c *EventParsing_ParseGitlabMergeEvent_OngoingVerification) GetCapturedArguments() go_gitlab.MergeEvent {
event := c.GetAllCapturedArguments()
return event[len(event)-1]
}
func (c *EventParsing_ParseGitlabMergeEvent_OngoingVerification) GetAllCapturedArguments() (_param0 []go_gitlab.MergeEvent) {
params := pegomock.GetGenericMockFrom(c.mock).GetInvocationParams(c.methodInvocations)
if len(params) > 0 {
_param0 = make([]go_gitlab.MergeEvent, len(params[0]))
for u, param := range params[0] {
_param0[u] = param.(go_gitlab.MergeEvent)
}
}
return
}
func (verifier *VerifierEventParsing) ParseGitlabMergeCommentEvent(event go_gitlab.MergeCommentEvent) *EventParsing_ParseGitlabMergeCommentEvent_OngoingVerification {
params := []pegomock.Param{event}
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "ParseGitlabMergeCommentEvent", params)
return &EventParsing_ParseGitlabMergeCommentEvent_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations}
}
type EventParsing_ParseGitlabMergeCommentEvent_OngoingVerification struct {
mock *MockEventParsing
methodInvocations []pegomock.MethodInvocation
}
func (c *EventParsing_ParseGitlabMergeCommentEvent_OngoingVerification) GetCapturedArguments() go_gitlab.MergeCommentEvent {
event := c.GetAllCapturedArguments()
return event[len(event)-1]
}
func (c *EventParsing_ParseGitlabMergeCommentEvent_OngoingVerification) GetAllCapturedArguments() (_param0 []go_gitlab.MergeCommentEvent) {
params := pegomock.GetGenericMockFrom(c.mock).GetInvocationParams(c.methodInvocations)
if len(params) > 0 {
_param0 = make([]go_gitlab.MergeCommentEvent, len(params[0]))
for u, param := range params[0] {
_param0[u] = param.(go_gitlab.MergeCommentEvent)
}
}
return
}
func (verifier *VerifierEventParsing) ParseGitlabMergeRequest(mr *go_gitlab.MergeRequest) *EventParsing_ParseGitlabMergeRequest_OngoingVerification {
params := []pegomock.Param{mr}
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "ParseGitlabMergeRequest", params)
return &EventParsing_ParseGitlabMergeRequest_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations}
}
type EventParsing_ParseGitlabMergeRequest_OngoingVerification struct {
mock *MockEventParsing
methodInvocations []pegomock.MethodInvocation
}
func (c *EventParsing_ParseGitlabMergeRequest_OngoingVerification) GetCapturedArguments() *go_gitlab.MergeRequest {
mr := c.GetAllCapturedArguments()
return mr[len(mr)-1]
}
func (c *EventParsing_ParseGitlabMergeRequest_OngoingVerification) GetAllCapturedArguments() (_param0 []*go_gitlab.MergeRequest) {
params := pegomock.GetGenericMockFrom(c.mock).GetInvocationParams(c.methodInvocations)
if len(params) > 0 {
_param0 = make([]*go_gitlab.MergeRequest, len(params[0]))
for u, param := range params[0] {
_param0[u] = param.(*go_gitlab.MergeRequest)
}
}
return
}

View File

@@ -4,9 +4,10 @@
package mocks
import (
"reflect"
events "github.com/hootsuite/atlantis/server/events"
pegomock "github.com/petergtz/pegomock"
"reflect"
)
type MockExecutor struct {

View File

@@ -0,0 +1,85 @@
// Automatically generated by pegomock. DO NOT EDIT!
// Source: github.com/hootsuite/atlantis/server/events (interfaces: GithubPullGetter)
package mocks
import (
"reflect"
github "github.com/google/go-github/github"
models "github.com/hootsuite/atlantis/server/events/models"
pegomock "github.com/petergtz/pegomock"
)
type MockGithubPullGetter struct {
fail func(message string, callerSkip ...int)
}
func NewMockGithubPullGetter() *MockGithubPullGetter {
return &MockGithubPullGetter{fail: pegomock.GlobalFailHandler}
}
func (mock *MockGithubPullGetter) GetPullRequest(repo models.Repo, pullNum int) (*github.PullRequest, error) {
params := []pegomock.Param{repo, pullNum}
result := pegomock.GetGenericMockFrom(mock).Invoke("GetPullRequest", params, []reflect.Type{reflect.TypeOf((**github.PullRequest)(nil)).Elem(), reflect.TypeOf((*error)(nil)).Elem()})
var ret0 *github.PullRequest
var ret1 error
if len(result) != 0 {
if result[0] != nil {
ret0 = result[0].(*github.PullRequest)
}
if result[1] != nil {
ret1 = result[1].(error)
}
}
return ret0, ret1
}
func (mock *MockGithubPullGetter) VerifyWasCalledOnce() *VerifierGithubPullGetter {
return &VerifierGithubPullGetter{mock, pegomock.Times(1), nil}
}
func (mock *MockGithubPullGetter) VerifyWasCalled(invocationCountMatcher pegomock.Matcher) *VerifierGithubPullGetter {
return &VerifierGithubPullGetter{mock, invocationCountMatcher, nil}
}
func (mock *MockGithubPullGetter) VerifyWasCalledInOrder(invocationCountMatcher pegomock.Matcher, inOrderContext *pegomock.InOrderContext) *VerifierGithubPullGetter {
return &VerifierGithubPullGetter{mock, invocationCountMatcher, inOrderContext}
}
type VerifierGithubPullGetter struct {
mock *MockGithubPullGetter
invocationCountMatcher pegomock.Matcher
inOrderContext *pegomock.InOrderContext
}
func (verifier *VerifierGithubPullGetter) GetPullRequest(repo models.Repo, pullNum int) *GithubPullGetter_GetPullRequest_OngoingVerification {
params := []pegomock.Param{repo, pullNum}
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "GetPullRequest", params)
return &GithubPullGetter_GetPullRequest_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations}
}
type GithubPullGetter_GetPullRequest_OngoingVerification struct {
mock *MockGithubPullGetter
methodInvocations []pegomock.MethodInvocation
}
func (c *GithubPullGetter_GetPullRequest_OngoingVerification) GetCapturedArguments() (models.Repo, int) {
repo, pullNum := c.GetAllCapturedArguments()
return repo[len(repo)-1], pullNum[len(pullNum)-1]
}
func (c *GithubPullGetter_GetPullRequest_OngoingVerification) GetAllCapturedArguments() (_param0 []models.Repo, _param1 []int) {
params := pegomock.GetGenericMockFrom(c.mock).GetInvocationParams(c.methodInvocations)
if len(params) > 0 {
_param0 = make([]models.Repo, len(params[0]))
for u, param := range params[0] {
_param0[u] = param.(models.Repo)
}
_param1 = make([]int, len(params[1]))
for u, param := range params[1] {
_param1[u] = param.(int)
}
}
return
}

View File

@@ -1,131 +0,0 @@
// Automatically generated by pegomock. DO NOT EDIT!
// Source: github.com/hootsuite/atlantis/server/events (interfaces: GHStatusUpdater)
package mocks
import (
events "github.com/hootsuite/atlantis/server/events"
models "github.com/hootsuite/atlantis/server/events/models"
pegomock "github.com/petergtz/pegomock"
"reflect"
)
type MockGHStatusUpdater struct {
fail func(message string, callerSkip ...int)
}
func NewMockGHStatusUpdater() *MockGHStatusUpdater {
return &MockGHStatusUpdater{fail: pegomock.GlobalFailHandler}
}
func (mock *MockGHStatusUpdater) Update(repo models.Repo, pull models.PullRequest, status events.Status, cmd *events.Command) error {
params := []pegomock.Param{repo, pull, status, cmd}
result := pegomock.GetGenericMockFrom(mock).Invoke("Update", params, []reflect.Type{reflect.TypeOf((*error)(nil)).Elem()})
var ret0 error
if len(result) != 0 {
if result[0] != nil {
ret0 = result[0].(error)
}
}
return ret0
}
func (mock *MockGHStatusUpdater) UpdateProjectResult(ctx *events.CommandContext, res events.CommandResponse) error {
params := []pegomock.Param{ctx, res}
result := pegomock.GetGenericMockFrom(mock).Invoke("UpdateProjectResult", params, []reflect.Type{reflect.TypeOf((*error)(nil)).Elem()})
var ret0 error
if len(result) != 0 {
if result[0] != nil {
ret0 = result[0].(error)
}
}
return ret0
}
func (mock *MockGHStatusUpdater) VerifyWasCalledOnce() *VerifierGHStatusUpdater {
return &VerifierGHStatusUpdater{mock, pegomock.Times(1), nil}
}
func (mock *MockGHStatusUpdater) VerifyWasCalled(invocationCountMatcher pegomock.Matcher) *VerifierGHStatusUpdater {
return &VerifierGHStatusUpdater{mock, invocationCountMatcher, nil}
}
func (mock *MockGHStatusUpdater) VerifyWasCalledInOrder(invocationCountMatcher pegomock.Matcher, inOrderContext *pegomock.InOrderContext) *VerifierGHStatusUpdater {
return &VerifierGHStatusUpdater{mock, invocationCountMatcher, inOrderContext}
}
type VerifierGHStatusUpdater struct {
mock *MockGHStatusUpdater
invocationCountMatcher pegomock.Matcher
inOrderContext *pegomock.InOrderContext
}
func (verifier *VerifierGHStatusUpdater) Update(repo models.Repo, pull models.PullRequest, status events.Status, cmd *events.Command) *GHStatusUpdater_Update_OngoingVerification {
params := []pegomock.Param{repo, pull, status, cmd}
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "Update", params)
return &GHStatusUpdater_Update_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations}
}
type GHStatusUpdater_Update_OngoingVerification struct {
mock *MockGHStatusUpdater
methodInvocations []pegomock.MethodInvocation
}
func (c *GHStatusUpdater_Update_OngoingVerification) GetCapturedArguments() (models.Repo, models.PullRequest, events.Status, *events.Command) {
repo, pull, status, cmd := c.GetAllCapturedArguments()
return repo[len(repo)-1], pull[len(pull)-1], status[len(status)-1], cmd[len(cmd)-1]
}
func (c *GHStatusUpdater_Update_OngoingVerification) GetAllCapturedArguments() (_param0 []models.Repo, _param1 []models.PullRequest, _param2 []events.Status, _param3 []*events.Command) {
params := pegomock.GetGenericMockFrom(c.mock).GetInvocationParams(c.methodInvocations)
if len(params) > 0 {
_param0 = make([]models.Repo, len(params[0]))
for u, param := range params[0] {
_param0[u] = param.(models.Repo)
}
_param1 = make([]models.PullRequest, len(params[1]))
for u, param := range params[1] {
_param1[u] = param.(models.PullRequest)
}
_param2 = make([]events.Status, len(params[2]))
for u, param := range params[2] {
_param2[u] = param.(events.Status)
}
_param3 = make([]*events.Command, len(params[3]))
for u, param := range params[3] {
_param3[u] = param.(*events.Command)
}
}
return
}
func (verifier *VerifierGHStatusUpdater) UpdateProjectResult(ctx *events.CommandContext, res events.CommandResponse) *GHStatusUpdater_UpdateProjectResult_OngoingVerification {
params := []pegomock.Param{ctx, res}
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "UpdateProjectResult", params)
return &GHStatusUpdater_UpdateProjectResult_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations}
}
type GHStatusUpdater_UpdateProjectResult_OngoingVerification struct {
mock *MockGHStatusUpdater
methodInvocations []pegomock.MethodInvocation
}
func (c *GHStatusUpdater_UpdateProjectResult_OngoingVerification) GetCapturedArguments() (*events.CommandContext, events.CommandResponse) {
ctx, res := c.GetAllCapturedArguments()
return ctx[len(ctx)-1], res[len(res)-1]
}
func (c *GHStatusUpdater_UpdateProjectResult_OngoingVerification) GetAllCapturedArguments() (_param0 []*events.CommandContext, _param1 []events.CommandResponse) {
params := pegomock.GetGenericMockFrom(c.mock).GetInvocationParams(c.methodInvocations)
if len(params) > 0 {
_param0 = make([]*events.CommandContext, len(params[0]))
for u, param := range params[0] {
_param0[u] = param.(*events.CommandContext)
}
_param1 = make([]events.CommandResponse, len(params[1]))
for u, param := range params[1] {
_param1[u] = param.(events.CommandResponse)
}
}
return
}

View File

@@ -0,0 +1,84 @@
// Automatically generated by pegomock. DO NOT EDIT!
// Source: github.com/hootsuite/atlantis/server/events (interfaces: GitlabMergeRequestGetter)
package mocks
import (
"reflect"
go_gitlab "github.com/lkysow/go-gitlab"
pegomock "github.com/petergtz/pegomock"
)
type MockGitlabMergeRequestGetter struct {
fail func(message string, callerSkip ...int)
}
func NewMockGitlabMergeRequestGetter() *MockGitlabMergeRequestGetter {
return &MockGitlabMergeRequestGetter{fail: pegomock.GlobalFailHandler}
}
func (mock *MockGitlabMergeRequestGetter) GetMergeRequest(repoFullName string, pullNum int) (*go_gitlab.MergeRequest, error) {
params := []pegomock.Param{repoFullName, pullNum}
result := pegomock.GetGenericMockFrom(mock).Invoke("GetMergeRequest", params, []reflect.Type{reflect.TypeOf((**go_gitlab.MergeRequest)(nil)).Elem(), reflect.TypeOf((*error)(nil)).Elem()})
var ret0 *go_gitlab.MergeRequest
var ret1 error
if len(result) != 0 {
if result[0] != nil {
ret0 = result[0].(*go_gitlab.MergeRequest)
}
if result[1] != nil {
ret1 = result[1].(error)
}
}
return ret0, ret1
}
func (mock *MockGitlabMergeRequestGetter) VerifyWasCalledOnce() *VerifierGitlabMergeRequestGetter {
return &VerifierGitlabMergeRequestGetter{mock, pegomock.Times(1), nil}
}
func (mock *MockGitlabMergeRequestGetter) VerifyWasCalled(invocationCountMatcher pegomock.Matcher) *VerifierGitlabMergeRequestGetter {
return &VerifierGitlabMergeRequestGetter{mock, invocationCountMatcher, nil}
}
func (mock *MockGitlabMergeRequestGetter) VerifyWasCalledInOrder(invocationCountMatcher pegomock.Matcher, inOrderContext *pegomock.InOrderContext) *VerifierGitlabMergeRequestGetter {
return &VerifierGitlabMergeRequestGetter{mock, invocationCountMatcher, inOrderContext}
}
type VerifierGitlabMergeRequestGetter struct {
mock *MockGitlabMergeRequestGetter
invocationCountMatcher pegomock.Matcher
inOrderContext *pegomock.InOrderContext
}
func (verifier *VerifierGitlabMergeRequestGetter) GetMergeRequest(repoFullName string, pullNum int) *GitlabMergeRequestGetter_GetMergeRequest_OngoingVerification {
params := []pegomock.Param{repoFullName, pullNum}
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "GetMergeRequest", params)
return &GitlabMergeRequestGetter_GetMergeRequest_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations}
}
type GitlabMergeRequestGetter_GetMergeRequest_OngoingVerification struct {
mock *MockGitlabMergeRequestGetter
methodInvocations []pegomock.MethodInvocation
}
func (c *GitlabMergeRequestGetter_GetMergeRequest_OngoingVerification) GetCapturedArguments() (string, int) {
repoFullName, pullNum := c.GetAllCapturedArguments()
return repoFullName[len(repoFullName)-1], pullNum[len(pullNum)-1]
}
func (c *GitlabMergeRequestGetter_GetMergeRequest_OngoingVerification) GetAllCapturedArguments() (_param0 []string, _param1 []int) {
params := pegomock.GetGenericMockFrom(c.mock).GetInvocationParams(c.methodInvocations)
if len(params) > 0 {
_param0 = make([]string, len(params[0]))
for u, param := range params[0] {
_param0[u] = param.(string)
}
_param1 = make([]int, len(params[1]))
for u, param := range params[1] {
_param1[u] = param.(int)
}
}
return
}

View File

@@ -4,8 +4,9 @@
package mocks
import (
pegomock "github.com/petergtz/pegomock"
"reflect"
pegomock "github.com/petergtz/pegomock"
)
type MockLockURLGenerator struct {

View File

@@ -19,7 +19,7 @@ func NewMockModifiedProjectFinder() *MockModifiedProjectFinder {
return &MockModifiedProjectFinder{fail: pegomock.GlobalFailHandler}
}
func (mock *MockModifiedProjectFinder) GetModified(log *logging.SimpleLogger, modifiedFiles []string, repoFullName string) []models.Project {
func (mock *MockModifiedProjectFinder) FindModified(log *logging.SimpleLogger, modifiedFiles []string, repoFullName string) []models.Project {
params := []pegomock.Param{log, modifiedFiles, repoFullName}
result := pegomock.GetGenericMockFrom(mock).Invoke("FindModified", params, []reflect.Type{reflect.TypeOf((*[]models.Project)(nil)).Elem()})
var ret0 []models.Project
@@ -49,23 +49,23 @@ type VerifierModifiedProjectFinder struct {
inOrderContext *pegomock.InOrderContext
}
func (verifier *VerifierModifiedProjectFinder) GetModified(log *logging.SimpleLogger, modifiedFiles []string, repoFullName string) *ModifiedProjectFinder_GetModified_OngoingVerification {
func (verifier *VerifierModifiedProjectFinder) FindModified(log *logging.SimpleLogger, modifiedFiles []string, repoFullName string) *ModifiedProjectFinder_FindModified_OngoingVerification {
params := []pegomock.Param{log, modifiedFiles, repoFullName}
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "FindModified", params)
return &ModifiedProjectFinder_GetModified_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations}
return &ModifiedProjectFinder_FindModified_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations}
}
type ModifiedProjectFinder_GetModified_OngoingVerification struct {
type ModifiedProjectFinder_FindModified_OngoingVerification struct {
mock *MockModifiedProjectFinder
methodInvocations []pegomock.MethodInvocation
}
func (c *ModifiedProjectFinder_GetModified_OngoingVerification) GetCapturedArguments() (*logging.SimpleLogger, []string, string) {
func (c *ModifiedProjectFinder_FindModified_OngoingVerification) GetCapturedArguments() (*logging.SimpleLogger, []string, string) {
log, modifiedFiles, repoFullName := c.GetAllCapturedArguments()
return log[len(log)-1], modifiedFiles[len(modifiedFiles)-1], repoFullName[len(repoFullName)-1]
}
func (c *ModifiedProjectFinder_GetModified_OngoingVerification) GetAllCapturedArguments() (_param0 []*logging.SimpleLogger, _param1 [][]string, _param2 []string) {
func (c *ModifiedProjectFinder_FindModified_OngoingVerification) GetAllCapturedArguments() (_param0 []*logging.SimpleLogger, _param1 [][]string, _param2 []string) {
params := pegomock.GetGenericMockFrom(c.mock).GetInvocationParams(c.methodInvocations)
if len(params) > 0 {
_param0 = make([]*logging.SimpleLogger, len(params[0]))

View File

@@ -1,107 +0,0 @@
// Automatically generated by pegomock. DO NOT EDIT!
// Source: github.com/hootsuite/atlantis/server/events (interfaces: Planner)
package mocks
import (
events "github.com/hootsuite/atlantis/server/events"
pegomock "github.com/petergtz/pegomock"
"reflect"
)
type MockPlanner struct {
fail func(message string, callerSkip ...int)
}
func NewMockPlanner() *MockPlanner {
return &MockPlanner{fail: pegomock.GlobalFailHandler}
}
func (mock *MockPlanner) Execute(ctx *events.CommandContext) events.CommandResponse {
params := []pegomock.Param{ctx}
result := pegomock.GetGenericMockFrom(mock).Invoke("Execute", params, []reflect.Type{reflect.TypeOf((*events.CommandResponse)(nil)).Elem()})
var ret0 events.CommandResponse
if len(result) != 0 {
if result[0] != nil {
ret0 = result[0].(events.CommandResponse)
}
}
return ret0
}
func (mock *MockPlanner) SetLockURL(_param0 func(string) string) {
params := []pegomock.Param{_param0}
pegomock.GetGenericMockFrom(mock).Invoke("SetLockURL", params, []reflect.Type{})
}
func (mock *MockPlanner) VerifyWasCalledOnce() *VerifierPlanner {
return &VerifierPlanner{mock, pegomock.Times(1), nil}
}
func (mock *MockPlanner) VerifyWasCalled(invocationCountMatcher pegomock.Matcher) *VerifierPlanner {
return &VerifierPlanner{mock, invocationCountMatcher, nil}
}
func (mock *MockPlanner) VerifyWasCalledInOrder(invocationCountMatcher pegomock.Matcher, inOrderContext *pegomock.InOrderContext) *VerifierPlanner {
return &VerifierPlanner{mock, invocationCountMatcher, inOrderContext}
}
type VerifierPlanner struct {
mock *MockPlanner
invocationCountMatcher pegomock.Matcher
inOrderContext *pegomock.InOrderContext
}
func (verifier *VerifierPlanner) Execute(ctx *events.CommandContext) *Planner_Execute_OngoingVerification {
params := []pegomock.Param{ctx}
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "Execute", params)
return &Planner_Execute_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations}
}
type Planner_Execute_OngoingVerification struct {
mock *MockPlanner
methodInvocations []pegomock.MethodInvocation
}
func (c *Planner_Execute_OngoingVerification) GetCapturedArguments() *events.CommandContext {
ctx := c.GetAllCapturedArguments()
return ctx[len(ctx)-1]
}
func (c *Planner_Execute_OngoingVerification) GetAllCapturedArguments() (_param0 []*events.CommandContext) {
params := pegomock.GetGenericMockFrom(c.mock).GetInvocationParams(c.methodInvocations)
if len(params) > 0 {
_param0 = make([]*events.CommandContext, len(params[0]))
for u, param := range params[0] {
_param0[u] = param.(*events.CommandContext)
}
}
return
}
func (verifier *VerifierPlanner) SetLockURL(_param0 func(string) string) *Planner_SetLockURL_OngoingVerification {
params := []pegomock.Param{_param0}
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "SetLockURL", params)
return &Planner_SetLockURL_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations}
}
type Planner_SetLockURL_OngoingVerification struct {
mock *MockPlanner
methodInvocations []pegomock.MethodInvocation
}
func (c *Planner_SetLockURL_OngoingVerification) GetCapturedArguments() func(string) string {
_param0 := c.GetAllCapturedArguments()
return _param0[len(_param0)-1]
}
func (c *Planner_SetLockURL_OngoingVerification) GetAllCapturedArguments() (_param0 []func(string) string) {
params := pegomock.GetGenericMockFrom(c.mock).GetInvocationParams(c.methodInvocations)
if len(params) > 0 {
_param0 = make([]func(string) string, len(params[0]))
for u, param := range params[0] {
_param0[u] = param.(func(string) string)
}
}
return
}

View File

@@ -4,9 +4,10 @@
package mocks
import (
"reflect"
events "github.com/hootsuite/atlantis/server/events"
pegomock "github.com/petergtz/pegomock"
"reflect"
)
type MockProjectConfigReader struct {

View File

@@ -4,9 +4,11 @@
package mocks
import (
models "github.com/hootsuite/atlantis/server/events/models"
pegomock "github.com/petergtz/pegomock"
"reflect"
models "github.com/hootsuite/atlantis/server/events/models"
vcs "github.com/hootsuite/atlantis/server/events/vcs"
pegomock "github.com/petergtz/pegomock"
)
type MockPullCleaner struct {
@@ -17,8 +19,8 @@ func NewMockPullCleaner() *MockPullCleaner {
return &MockPullCleaner{fail: pegomock.GlobalFailHandler}
}
func (mock *MockPullCleaner) CleanUpPull(repo models.Repo, pull models.PullRequest) error {
params := []pegomock.Param{repo, pull}
func (mock *MockPullCleaner) CleanUpPull(repo models.Repo, pull models.PullRequest, host vcs.Host) error {
params := []pegomock.Param{repo, pull, host}
result := pegomock.GetGenericMockFrom(mock).Invoke("CleanUpPull", params, []reflect.Type{reflect.TypeOf((*error)(nil)).Elem()})
var ret0 error
if len(result) != 0 {
@@ -47,8 +49,8 @@ type VerifierPullCleaner struct {
inOrderContext *pegomock.InOrderContext
}
func (verifier *VerifierPullCleaner) CleanUpPull(repo models.Repo, pull models.PullRequest) *PullCleaner_CleanUpPull_OngoingVerification {
params := []pegomock.Param{repo, pull}
func (verifier *VerifierPullCleaner) CleanUpPull(repo models.Repo, pull models.PullRequest, host vcs.Host) *PullCleaner_CleanUpPull_OngoingVerification {
params := []pegomock.Param{repo, pull, host}
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "CleanUpPull", params)
return &PullCleaner_CleanUpPull_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations}
}
@@ -58,12 +60,12 @@ type PullCleaner_CleanUpPull_OngoingVerification struct {
methodInvocations []pegomock.MethodInvocation
}
func (c *PullCleaner_CleanUpPull_OngoingVerification) GetCapturedArguments() (models.Repo, models.PullRequest) {
repo, pull := c.GetAllCapturedArguments()
return repo[len(repo)-1], pull[len(pull)-1]
func (c *PullCleaner_CleanUpPull_OngoingVerification) GetCapturedArguments() (models.Repo, models.PullRequest, vcs.Host) {
repo, pull, host := c.GetAllCapturedArguments()
return repo[len(repo)-1], pull[len(pull)-1], host[len(host)-1]
}
func (c *PullCleaner_CleanUpPull_OngoingVerification) GetAllCapturedArguments() (_param0 []models.Repo, _param1 []models.PullRequest) {
func (c *PullCleaner_CleanUpPull_OngoingVerification) GetAllCapturedArguments() (_param0 []models.Repo, _param1 []models.PullRequest, _param2 []vcs.Host) {
params := pegomock.GetGenericMockFrom(c.mock).GetInvocationParams(c.methodInvocations)
if len(params) > 0 {
_param0 = make([]models.Repo, len(params[0]))
@@ -74,6 +76,10 @@ func (c *PullCleaner_CleanUpPull_OngoingVerification) GetAllCapturedArguments()
for u, param := range params[1] {
_param1[u] = param.(models.PullRequest)
}
_param2 = make([]vcs.Host, len(params[2]))
for u, param := range params[2] {
_param2[u] = param.(vcs.Host)
}
}
return
}

View File

@@ -4,10 +4,11 @@
package mocks
import (
"reflect"
models "github.com/hootsuite/atlantis/server/events/models"
logging "github.com/hootsuite/atlantis/server/logging"
pegomock "github.com/petergtz/pegomock"
"reflect"
)
type MockWorkspace struct {

View File

@@ -8,7 +8,6 @@ var Pull = models.PullRequest{
Branch: "branch",
Author: "lkysow",
URL: "url",
BaseCommit: "8ed0280678d49d42cd286610aabcfceb5bb673c6",
}
var Repo = models.Repo{

View File

@@ -8,7 +8,7 @@ import (
"time"
)
// Repo is a GitHub repository.
// Repo is a VCS repository.
type Repo struct {
// FullName is the owner and repo name separated
// by a "/", ex. "hootsuite/atlantis".
@@ -25,26 +25,35 @@ type Repo struct {
SanitizedCloneURL string
}
// PullRequest is a GitHub pull request.
// PullRequest is a VCS pull request.
// GitLab calls these Merge Requests.
type PullRequest struct {
// Num is the pull request number or ID.
Num int
// HeadCommit points to the head of the branch that is being
// pull requested into the base.
HeadCommit string
// BaseCommit points to the head of the branch that this
// pull request will be merged into.
BaseCommit string
// URL is the url of the pull request.
// ex. "https://github.com/hootsuite/atlantis/pull/1"
URL string
// Branch is the name of the head branch (not the base).
Branch string
// Author is the GitHub username of the pull request author.
// Author is the username of the pull request author.
Author string
// State will be one of Open or Closed.
// Gitlab supports an additional "merged" state but Github doesn't so we map
// merged to Closed.
State PullRequestState
}
// User is a GitHub user.
type PullRequestState int
const (
Open PullRequestState = iota
Closed
)
// User is a VCS user.
type User struct {
Username string
}
@@ -56,7 +65,7 @@ type ProjectLock struct {
// Pull is the pull request from which the command was run that
// created this lock.
Pull PullRequest
// User is the GitHub username of the user that ran the command
// User is the username of the user that ran the command
// that created this lock.
User User
// Env is the Terraform environment that this

View File

@@ -5,11 +5,11 @@ import (
"os"
"path/filepath"
"github.com/hootsuite/atlantis/server/events/github"
"github.com/hootsuite/atlantis/server/events/locking"
"github.com/hootsuite/atlantis/server/events/models"
"github.com/hootsuite/atlantis/server/events/run"
"github.com/hootsuite/atlantis/server/events/terraform"
"github.com/hootsuite/atlantis/server/events/vcs"
"github.com/pkg/errors"
)
@@ -22,20 +22,19 @@ type LockURLGenerator interface {
}
// atlantisUserTFVar is the name of the variable we execute terraform
// with containing the github username of who is running the command
// with, containing the vcs username of who is running the command
const atlantisUserTFVar = "atlantis_user"
// PlanExecutor handles everything related to running terraform plan
// including integration with S3, Terraform, and GitHub
// PlanExecutor handles everything related to running terraform plan.
type PlanExecutor struct {
Github github.Client
VCSClient vcs.ClientProxy
Terraform terraform.Runner
Locker locking.Locker
LockURL func(id string) (url string)
Run run.Runner
Workspace Workspace
ProjectPreExecute ProjectPreExecutor
ModifiedProject ModifiedProjectFinder
ProjectFinder ModifiedProjectFinder
}
type PlanSuccess struct {
@@ -49,12 +48,12 @@ func (p *PlanExecutor) SetLockURL(f func(id string) (url string)) {
func (p *PlanExecutor) Execute(ctx *CommandContext) CommandResponse {
// figure out what projects have been modified so we know where to run plan
modifiedFiles, err := p.Github.GetModifiedFiles(ctx.BaseRepo, ctx.Pull)
modifiedFiles, err := p.VCSClient.GetModifiedFiles(ctx.BaseRepo, ctx.Pull, ctx.VCSHost)
if err != nil {
return CommandResponse{Error: errors.Wrap(err, "getting modified files")}
}
ctx.Log.Info("found %d files modified in this pull request", len(modifiedFiles))
projects := p.ModifiedProject.FindModified(ctx.Log, modifiedFiles, ctx.BaseRepo.FullName)
projects := p.ProjectFinder.FindModified(ctx.Log, modifiedFiles, ctx.BaseRepo.FullName)
if len(projects) == 0 {
return CommandResponse{Failure: "No Terraform files were modified."}
}

View File

@@ -1,18 +1,17 @@
package events_test
import (
"errors"
"testing"
"errors"
"github.com/hootsuite/atlantis/server/events"
ghmocks "github.com/hootsuite/atlantis/server/events/github/mocks"
"github.com/hootsuite/atlantis/server/events/locking"
lmocks "github.com/hootsuite/atlantis/server/events/locking/mocks"
"github.com/hootsuite/atlantis/server/events/mocks"
"github.com/hootsuite/atlantis/server/events/models"
rmocks "github.com/hootsuite/atlantis/server/events/run/mocks"
tmocks "github.com/hootsuite/atlantis/server/events/terraform/mocks"
vcsmocks "github.com/hootsuite/atlantis/server/events/vcs/mocks"
"github.com/hootsuite/atlantis/server/logging"
. "github.com/hootsuite/atlantis/testing"
. "github.com/petergtz/pegomock"
@@ -32,10 +31,10 @@ var planCtx = events.CommandContext{
},
}
func TestExecute_GithubErr(t *testing.T) {
func TestExecute_ModifiedFilesErr(t *testing.T) {
t.Log("If GetModifiedFiles returns an error we return an error")
p, _, _ := setupPlanExecutorTest(t)
When(p.Github.GetModifiedFiles(AnyRepo(), AnyPullRequest())).ThenReturn(nil, errors.New("err"))
When(p.VCSClient.GetModifiedFiles(AnyRepo(), AnyPullRequest(), AnyVCSHost())).ThenReturn(nil, errors.New("err"))
r := p.Execute(&planCtx)
Assert(t, r.Error != nil, "exp .Error to be set")
@@ -45,7 +44,7 @@ func TestExecute_GithubErr(t *testing.T) {
func TestExecute_NoModifiedProjects(t *testing.T) {
t.Log("If there are no modified projects we return a failure")
p, _, _ := setupPlanExecutorTest(t)
// We don't need to actually mock Github.GetModifiedFiles because by
// We don't need to actually mock VCSClient.GetModifiedFiles because by
// default it will return an empty slice which is what we want for this test.
r := p.Execute(&planCtx)
@@ -55,7 +54,7 @@ func TestExecute_NoModifiedProjects(t *testing.T) {
func TestExecute_CloneErr(t *testing.T) {
t.Log("If Workspace.Clone returns an error we return an error")
p, _, _ := setupPlanExecutorTest(t)
When(p.Github.GetModifiedFiles(AnyRepo(), AnyPullRequest())).ThenReturn([]string{"file.tf"}, nil)
When(p.VCSClient.GetModifiedFiles(AnyRepo(), AnyPullRequest(), AnyVCSHost())).ThenReturn([]string{"file.tf"}, nil)
When(p.Workspace.Clone(planCtx.Log, planCtx.BaseRepo, planCtx.HeadRepo, planCtx.Pull, "env")).ThenReturn("", errors.New("err"))
r := p.Execute(&planCtx)
@@ -66,7 +65,7 @@ func TestExecute_CloneErr(t *testing.T) {
func TestExecute_Success(t *testing.T) {
t.Log("If there are no errors, the plan should be returned")
p, runner, _ := setupPlanExecutorTest(t)
When(p.Github.GetModifiedFiles(AnyRepo(), AnyPullRequest())).ThenReturn([]string{"file.tf"}, nil)
When(p.VCSClient.GetModifiedFiles(AnyRepo(), AnyPullRequest(), AnyVCSHost())).ThenReturn([]string{"file.tf"}, nil)
When(p.Workspace.Clone(planCtx.Log, planCtx.BaseRepo, planCtx.HeadRepo, planCtx.Pull, "env")).
ThenReturn("/tmp/clone-repo", nil)
When(p.ProjectPreExecute.Execute(&planCtx, "/tmp/clone-repo", models.Project{RepoFullName: "", Path: "."})).
@@ -95,7 +94,7 @@ func TestExecute_Success(t *testing.T) {
func TestExecute_PreExecuteResult(t *testing.T) {
t.Log("If ProjectPreExecute.Execute returns a ProjectResult we should return it")
p, _, _ := setupPlanExecutorTest(t)
When(p.Github.GetModifiedFiles(AnyRepo(), AnyPullRequest())).ThenReturn([]string{"file.tf"}, nil)
When(p.VCSClient.GetModifiedFiles(AnyRepo(), AnyPullRequest(), AnyVCSHost())).ThenReturn([]string{"file.tf"}, nil)
When(p.Workspace.Clone(planCtx.Log, planCtx.BaseRepo, planCtx.HeadRepo, planCtx.Pull, "env")).
ThenReturn("/tmp/clone-repo", nil)
projectResult := events.ProjectResult{
@@ -114,7 +113,7 @@ func TestExecute_MultiProjectFailure(t *testing.T) {
t.Log("If is an error planning in one project it should be returned. It shouldn't affect another project though.")
p, runner, locker := setupPlanExecutorTest(t)
// Two projects have been modified so we should run plan in two paths.
When(p.Github.GetModifiedFiles(AnyRepo(), AnyPullRequest())).ThenReturn([]string{"path1/file.tf", "path2/file.tf"}, nil)
When(p.VCSClient.GetModifiedFiles(AnyRepo(), AnyPullRequest(), AnyVCSHost())).ThenReturn([]string{"path1/file.tf", "path2/file.tf"}, nil)
When(p.Workspace.Clone(planCtx.Log, planCtx.BaseRepo, planCtx.HeadRepo, planCtx.Pull, "env")).
ThenReturn("/tmp/clone-repo", nil)
@@ -154,7 +153,7 @@ func TestExecute_MultiProjectFailure(t *testing.T) {
func TestExecute_PostPlanCommands(t *testing.T) {
t.Log("Should execute post-plan commands and return if there is an error")
p, _, _ := setupPlanExecutorTest(t)
When(p.Github.GetModifiedFiles(AnyRepo(), AnyPullRequest())).ThenReturn([]string{"file.tf"}, nil)
When(p.VCSClient.GetModifiedFiles(AnyRepo(), AnyPullRequest(), AnyVCSHost())).ThenReturn([]string{"file.tf"}, nil)
When(p.Workspace.Clone(planCtx.Log, planCtx.BaseRepo, planCtx.HeadRepo, planCtx.Pull, "env")).
ThenReturn("/tmp/clone-repo", nil)
When(p.ProjectPreExecute.Execute(&planCtx, "/tmp/clone-repo", models.Project{RepoFullName: "", Path: "."})).
@@ -174,15 +173,15 @@ func TestExecute_PostPlanCommands(t *testing.T) {
func setupPlanExecutorTest(t *testing.T) (*events.PlanExecutor, *tmocks.MockRunner, *lmocks.MockLocker) {
RegisterMockTestingT(t)
gh := ghmocks.NewMockClient()
vcsProxy := vcsmocks.NewMockClientProxy()
w := mocks.NewMockWorkspace()
ppe := mocks.NewMockProjectPreExecutor()
runner := tmocks.NewMockRunner()
locker := lmocks.NewMockLocker()
run := rmocks.NewMockRunner()
p := events.PlanExecutor{
Github: gh,
ModifiedProject: &events.ProjectFinder{},
VCSClient: vcsProxy,
ProjectFinder: &events.ProjectFinder{},
Workspace: w,
ProjectPreExecute: ppe,
Terraform: runner,

View File

@@ -8,7 +8,7 @@ import (
. "github.com/hootsuite/atlantis/testing"
)
var log = logging.NewNoopLogger()
var noopLogger = logging.NewNoopLogger()
var modifiedRepo = "owner/repo"
var m = events.ProjectFinder{}
@@ -66,7 +66,7 @@ func TestGetModified_NoFiles(t *testing.T) {
}
for _, c := range cases {
t.Log(c.description)
projects := m.FindModified(log, c.files, modifiedRepo)
projects := m.FindModified(noopLogger, c.files, modifiedRepo)
// Extract the paths from the projects. We use a slice here instead of a
// map so we can test whether there are duplicates returned.

View File

@@ -1,5 +1,7 @@
package events
import "github.com/hootsuite/atlantis/server/events/vcs"
type ProjectResult struct {
Path string
Error error
@@ -8,12 +10,12 @@ type ProjectResult struct {
ApplySuccess string
}
func (p ProjectResult) Status() Status {
func (p ProjectResult) Status() vcs.CommitStatus {
if p.Error != nil {
return Error
return vcs.Failed
}
if p.Failure != "" {
return Failure
return vcs.Failed
}
return Success
return vcs.Success
}

View File

@@ -8,21 +8,21 @@ import (
"sort"
"github.com/hootsuite/atlantis/server/events/github"
"github.com/hootsuite/atlantis/server/events/locking"
"github.com/hootsuite/atlantis/server/events/models"
"github.com/hootsuite/atlantis/server/events/vcs"
"github.com/pkg/errors"
)
//go:generate pegomock generate --use-experimental-model-gen --package mocks -o mocks/mock_pull_cleaner.go PullCleaner
type PullCleaner interface {
CleanUpPull(repo models.Repo, pull models.PullRequest) error
CleanUpPull(repo models.Repo, pull models.PullRequest, host vcs.Host) error
}
type PullClosedExecutor struct {
Locker locking.Locker
Github github.Client
VCSClient vcs.ClientProxy
Workspace Workspace
}
@@ -36,7 +36,7 @@ var pullClosedTemplate = template.Must(template.New("").Parse(
"{{ range . }}\n" +
"- path: `{{ .Path }}` {{ .Envs }}{{ end }}"))
func (p *PullClosedExecutor) CleanUpPull(repo models.Repo, pull models.PullRequest) error {
func (p *PullClosedExecutor) CleanUpPull(repo models.Repo, pull models.PullRequest, host vcs.Host) error {
// delete the workspace
if err := p.Workspace.Delete(repo, pull); err != nil {
return errors.Wrap(err, "cleaning workspace")
@@ -60,11 +60,11 @@ func (p *PullClosedExecutor) CleanUpPull(repo models.Repo, pull models.PullReque
if err = pullClosedTemplate.Execute(&buf, templateData); err != nil {
return errors.Wrap(err, "rendering template for comment")
}
return p.Github.CreateComment(repo, pull, buf.String())
return p.VCSClient.CreateComment(repo, pull, buf.String(), host)
}
// buildTemplateData formats the lock data into a slice that can easily be templated
// for the GitHub comment. We organize all the environments by their respective project paths
// for the VCS comment. We organize all the environments by their respective project paths
// so the comment can look like: path: {path}, environments: {all-envs}
func (p *PullClosedExecutor) buildTemplateData(locks []models.ProjectLock) []templatedProject {
envsByPath := make(map[string][]string)

View File

@@ -1,18 +1,19 @@
package events_test
import (
"errors"
"reflect"
"testing"
"github.com/hootsuite/atlantis/server/events"
ghmocks "github.com/hootsuite/atlantis/server/events/github/mocks"
lockmocks "github.com/hootsuite/atlantis/server/events/locking/mocks"
"github.com/hootsuite/atlantis/server/events/mocks"
"github.com/hootsuite/atlantis/server/events/models"
"github.com/hootsuite/atlantis/server/events/models/fixtures"
. "github.com/hootsuite/atlantis/testing"
. "github.com/petergtz/pegomock"
"testing"
"github.com/hootsuite/atlantis/server/events/mocks"
lockmocks "github.com/hootsuite/atlantis/server/events/locking/mocks"
vcsmocks "github.com/hootsuite/atlantis/server/events/vcs/mocks"
"github.com/hootsuite/atlantis/server/events"
"errors"
"github.com/hootsuite/atlantis/server/events/vcs"
"github.com/hootsuite/atlantis/server/events/models/fixtures"
)
func TestCleanUpPullWorkspaceErr(t *testing.T) {
@@ -24,7 +25,7 @@ func TestCleanUpPullWorkspaceErr(t *testing.T) {
}
err := errors.New("err")
When(w.Delete(fixtures.Repo, fixtures.Pull)).ThenReturn(err)
actualErr := pce.CleanUpPull(fixtures.Repo, fixtures.Pull)
actualErr := pce.CleanUpPull(fixtures.Repo, fixtures.Pull, vcs.Github)
Equals(t, "cleaning workspace: err", actualErr.Error())
}
@@ -39,7 +40,7 @@ func TestCleanUpPullUnlockErr(t *testing.T) {
}
err := errors.New("err")
When(l.UnlockByPull(fixtures.Repo.FullName, fixtures.Pull.Num)).ThenReturn(nil, err)
actualErr := pce.CleanUpPull(fixtures.Repo, fixtures.Pull)
actualErr := pce.CleanUpPull(fixtures.Repo, fixtures.Pull, vcs.Github)
Equals(t, "cleaning up locks: err", actualErr.Error())
}
@@ -48,16 +49,16 @@ func TestCleanUpPullNoLocks(t *testing.T) {
RegisterMockTestingT(t)
w := mocks.NewMockWorkspace()
l := lockmocks.NewMockLocker()
gh := ghmocks.NewMockClient()
cp := vcsmocks.NewMockClientProxy()
pce := events.PullClosedExecutor{
Locker: l,
Github: gh,
VCSClient: cp,
Workspace: w,
}
When(l.UnlockByPull(fixtures.Repo.FullName, fixtures.Pull.Num)).ThenReturn(nil, nil)
err := pce.CleanUpPull(fixtures.Repo, fixtures.Pull)
err := pce.CleanUpPull(fixtures.Repo, fixtures.Pull, vcs.Github)
Ok(t, err)
gh.VerifyWasCalled(Never()).CreateComment(AnyRepo(), AnyPullRequest(), AnyString())
cp.VerifyWasCalled(Never()).CreateComment(AnyRepo(), AnyPullRequest(), AnyString(), AnyVCSHost())
}
func TestCleanUpPullComments(t *testing.T) {
@@ -127,18 +128,18 @@ func TestCleanUpPullComments(t *testing.T) {
}
for _, c := range cases {
w := mocks.NewMockWorkspace()
gh := ghmocks.NewMockClient()
cp := vcsmocks.NewMockClientProxy()
l := lockmocks.NewMockLocker()
pce := events.PullClosedExecutor{
Locker: l,
Github: gh,
VCSClient: cp,
Workspace: w,
}
t.Log("testing: " + c.Description)
When(l.UnlockByPull(fixtures.Repo.FullName, fixtures.Pull.Num)).ThenReturn(c.Locks, nil)
err := pce.CleanUpPull(fixtures.Repo, fixtures.Pull)
err := pce.CleanUpPull(fixtures.Repo, fixtures.Pull, vcs.Github)
Ok(t, err)
_, _, comment := gh.VerifyWasCalledOnce().CreateComment(AnyRepo(), AnyPullRequest(), AnyString()).GetCapturedArguments()
_, _, comment, _ := cp.VerifyWasCalledOnce().CreateComment(AnyRepo(), AnyPullRequest(), AnyString(), AnyVCSHost()).GetCapturedArguments()
expected := "Locks and plans deleted for the projects and environments modified in this pull request:\n\n" + c.Exp
Equals(t, expected, comment)
@@ -154,3 +155,4 @@ func AnyPullRequest() models.PullRequest {
RegisterMatcher(NewAnyMatcher(reflect.TypeOf(models.PullRequest{})))
return models.PullRequest{}
}

View File

@@ -4,10 +4,11 @@
package mocks
import (
"reflect"
go_version "github.com/hashicorp/go-version"
logging "github.com/hootsuite/atlantis/server/logging"
pegomock "github.com/petergtz/pegomock"
"reflect"
)
type MockRunner struct {

View File

@@ -4,10 +4,11 @@
package mocks
import (
"reflect"
go_version "github.com/hashicorp/go-version"
logging "github.com/hootsuite/atlantis/server/logging"
pegomock "github.com/petergtz/pegomock"
"reflect"
)
type MockRunner struct {

View File

@@ -0,0 +1,15 @@
package vcs
import (
"github.com/hootsuite/atlantis/server/events/models"
)
//go:generate pegomock generate --use-experimental-model-gen --package mocks -o mocks/mock_client.go Client
// Client is used to make API calls to a VCS host like GitHub or GitLab.
type Client interface {
GetModifiedFiles(repo models.Repo, pull models.PullRequest) ([]string, error)
CreateComment(repo models.Repo, pull models.PullRequest, comment string) error
PullIsApproved(repo models.Repo, pull models.PullRequest) (bool, error)
UpdateStatus(repo models.Repo, pull models.PullRequest, state CommitStatus, description string) error
}

View File

@@ -1,4 +1,4 @@
package github_test
package vcs
// todo: actually test
// purposefully empty to trigger coverage report

View File

@@ -16,6 +16,7 @@ var Pull = github.PullRequest{
Login: github.String("user"),
},
Number: github.Int(1),
State: github.String("open"),
}
var Repo = github.Repository{

View File

@@ -1,9 +1,7 @@
// Package github provides convenience wrappers around the go-github package.
package github
package vcs
import (
"context"
"fmt"
"net/url"
"strings"
@@ -13,24 +11,14 @@ import (
"github.com/pkg/errors"
)
//go:generate pegomock generate --package mocks -o mocks/mock_client.go client.go
type Client interface {
GetModifiedFiles(repo models.Repo, pull models.PullRequest) ([]string, error)
CreateComment(repo models.Repo, pull models.PullRequest, comment string) error
PullIsApproved(repo models.Repo, pull models.PullRequest) (bool, error)
GetPullRequest(repo models.Repo, num int) (*github.PullRequest, *github.Response, error)
UpdateStatus(repo models.Repo, pull models.PullRequest, state string, description string, context string) error
}
// ConcreteClient is used to perform GitHub actions.
type ConcreteClient struct {
// GithubClient is used to perform GitHub actions.
type GithubClient struct {
client *github.Client
ctx context.Context
}
// NewClient returns a valid GitHub client.
func NewClient(hostname string, user string, pass string) (*ConcreteClient, error) {
// NewGithubClient returns a valid GitHub client.
func NewGithubClient(hostname string, user string, pass string) (*GithubClient, error) {
tp := github.BasicAuthTransport{
Username: strings.TrimSpace(user),
Password: strings.TrimSpace(pass),
@@ -48,7 +36,7 @@ func NewClient(hostname string, user string, pass string) (*ConcreteClient, erro
client.BaseURL = base
}
return &ConcreteClient{
return &GithubClient{
client: client,
ctx: context.Background(),
}, nil
@@ -56,7 +44,7 @@ func NewClient(hostname string, user string, pass string) (*ConcreteClient, erro
// GetModifiedFiles returns the names of files that were modified in the pull request.
// The names include the path to the file from the repo root, ex. parent/child/file.txt.
func (c *ConcreteClient) GetModifiedFiles(repo models.Repo, pull models.PullRequest) ([]string, error) {
func (g *GithubClient) GetModifiedFiles(repo models.Repo, pull models.PullRequest) ([]string, error) {
var files []string
nextPage := 0
for {
@@ -66,7 +54,7 @@ func (c *ConcreteClient) GetModifiedFiles(repo models.Repo, pull models.PullRequ
if nextPage != 0 {
opts.Page = nextPage
}
pageFiles, resp, err := c.client.PullRequests.ListFiles(c.ctx, repo.Owner, repo.Name, pull.Num, &opts)
pageFiles, resp, err := g.client.PullRequests.ListFiles(g.ctx, repo.Owner, repo.Name, pull.Num, &opts)
if err != nil {
return files, err
}
@@ -82,14 +70,14 @@ func (c *ConcreteClient) GetModifiedFiles(repo models.Repo, pull models.PullRequ
}
// CreateComment creates a comment on the pull request.
func (c *ConcreteClient) CreateComment(repo models.Repo, pull models.PullRequest, comment string) error {
_, _, err := c.client.Issues.CreateComment(c.ctx, repo.Owner, repo.Name, pull.Num, &github.IssueComment{Body: &comment})
func (g *GithubClient) CreateComment(repo models.Repo, pull models.PullRequest, comment string) error {
_, _, err := g.client.Issues.CreateComment(g.ctx, repo.Owner, repo.Name, pull.Num, &github.IssueComment{Body: &comment})
return err
}
// PullIsApproved returns true if the pull request was approved.
func (c *ConcreteClient) PullIsApproved(repo models.Repo, pull models.PullRequest) (bool, error) {
reviews, _, err := c.client.PullRequests.ListReviews(c.ctx, repo.Owner, repo.Name, pull.Num, nil)
func (g *GithubClient) PullIsApproved(repo models.Repo, pull models.PullRequest) (bool, error) {
reviews, _, err := g.client.PullRequests.ListReviews(g.ctx, repo.Owner, repo.Name, pull.Num, nil)
if err != nil {
return false, errors.Wrap(err, "getting reviews")
}
@@ -102,17 +90,28 @@ func (c *ConcreteClient) PullIsApproved(repo models.Repo, pull models.PullReques
}
// GetPullRequest returns the pull request.
func (c *ConcreteClient) GetPullRequest(repo models.Repo, num int) (*github.PullRequest, *github.Response, error) {
return c.client.PullRequests.Get(c.ctx, repo.Owner, repo.Name, num)
func (g *GithubClient) GetPullRequest(repo models.Repo, num int) (*github.PullRequest, error) {
pull, _, err := g.client.PullRequests.Get(g.ctx, repo.Owner, repo.Name, num)
return pull, err
}
// UpdateStatus updates the status badge on the pull request.
// See https://github.com/blog/1227-commit-status-api.
func (c *ConcreteClient) UpdateStatus(repo models.Repo, pull models.PullRequest, state string, description string, context string) error {
func (g *GithubClient) UpdateStatus(repo models.Repo, pull models.PullRequest, state CommitStatus, description string) error {
const statusContext = "Atlantis"
ghState := "error"
switch state {
case Pending:
ghState = "pending"
case Success:
ghState = "success"
case Failed:
ghState = "failure"
}
status := &github.RepoStatus{
State: github.String(state),
State: github.String(ghState),
Description: github.String(description),
Context: github.String(context)}
_, _, err := c.client.Repositories.CreateStatus(c.ctx, repo.Owner, repo.Name, pull.HeadCommit, status)
Context: github.String(statusContext)}
_, _, err := g.client.Repositories.CreateStatus(g.ctx, repo.Owner, repo.Name, pull.HeadCommit, status)
return err
}

View File

@@ -0,0 +1,92 @@
package vcs
import (
"fmt"
"net/url"
"github.com/hootsuite/atlantis/server/events/models"
"github.com/lkysow/go-gitlab"
)
type GitlabClient struct {
Client *gitlab.Client
}
// GetModifiedFiles returns the names of files that were modified in the merge request.
// The names include the path to the file from the repo root, ex. parent/child/file.txt.
func (g *GitlabClient) GetModifiedFiles(repo models.Repo, pull models.PullRequest) ([]string, error) {
const maxPerPage = 100
var files []string
nextPage := 1
// Constructing the api url by hand so we can do pagination.
apiURL := fmt.Sprintf("projects/%s/merge_requests/%d/changes", url.QueryEscape(repo.FullName), pull.Num)
for {
opts := gitlab.ListOptions{
Page: nextPage,
PerPage: maxPerPage,
}
req, err := g.Client.NewRequest("GET", apiURL, opts, nil)
if err != nil {
return nil, err
}
mr := new(gitlab.MergeRequest)
resp, err := g.Client.Do(req, mr)
if err != nil {
return nil, err
}
for _, f := range mr.Changes {
files = append(files, f.NewPath)
}
if resp.NextPage == 0 {
break
}
nextPage = resp.NextPage
}
return files, nil
}
// CreateComment creates a comment on the merge request.
func (g *GitlabClient) CreateComment(repo models.Repo, pull models.PullRequest, comment string) error {
_, _, err := g.Client.Notes.CreateMergeRequestNote(repo.FullName, pull.Num, &gitlab.CreateMergeRequestNoteOptions{Body: gitlab.String(comment)})
return err
}
// PullIsApproved returns true if the merge request was approved.
func (g *GitlabClient) PullIsApproved(repo models.Repo, pull models.PullRequest) (bool, error) {
approvals, _, err := g.Client.MergeRequests.GetMergeRequestApprovals(repo.FullName, pull.Num)
if err != nil {
return false, err
}
if approvals.ApprovalsMissing > 0 {
return false, nil
}
return true, nil
}
// UpdateStatus updates the build status of a commit.
func (g *GitlabClient) UpdateStatus(repo models.Repo, pull models.PullRequest, state CommitStatus, description string) error {
const statusContext = "Atlantis"
gitlabState := gitlab.Failed
switch state {
case Pending:
gitlabState = gitlab.Pending
case Failed:
gitlabState = gitlab.Failed
case Success:
gitlabState = gitlab.Success
}
_, _, err := g.Client.Commits.SetCommitStatus(repo.FullName, pull.HeadCommit, &gitlab.SetCommitStatusOptions{
State: gitlabState,
Context: gitlab.String(statusContext),
Description: gitlab.String(description),
})
return err
}
func (g *GitlabClient) GetMergeRequest(repoFullName string, pullNum int) (*gitlab.MergeRequest, error) {
mr, _, err := g.Client.MergeRequests.GetMergeRequest(repoFullName, pullNum)
return mr, err
}

View File

@@ -1,13 +1,14 @@
// Automatically generated by pegomock. DO NOT EDIT!
// Source: client.go
// Source: github.com/hootsuite/atlantis/server/events/vcs (interfaces: Client)
package mocks
import (
github "github.com/google/go-github/github"
models "github.com/hootsuite/atlantis/server/events/models"
pegomock "github.com/petergtz/pegomock"
"reflect"
models "github.com/hootsuite/atlantis/server/events/models"
vcs "github.com/hootsuite/atlantis/server/events/vcs"
pegomock "github.com/petergtz/pegomock"
)
type MockClient struct {
@@ -62,28 +63,8 @@ func (mock *MockClient) PullIsApproved(repo models.Repo, pull models.PullRequest
return ret0, ret1
}
func (mock *MockClient) GetPullRequest(repo models.Repo, num int) (*github.PullRequest, *github.Response, error) {
params := []pegomock.Param{repo, num}
result := pegomock.GetGenericMockFrom(mock).Invoke("GetPullRequest", params, []reflect.Type{reflect.TypeOf((**github.PullRequest)(nil)).Elem(), reflect.TypeOf((**github.Response)(nil)).Elem(), reflect.TypeOf((*error)(nil)).Elem()})
var ret0 *github.PullRequest
var ret1 *github.Response
var ret2 error
if len(result) != 0 {
if result[0] != nil {
ret0 = result[0].(*github.PullRequest)
}
if result[1] != nil {
ret1 = result[1].(*github.Response)
}
if result[2] != nil {
ret2 = result[2].(error)
}
}
return ret0, ret1, ret2
}
func (mock *MockClient) UpdateStatus(repo models.Repo, pull models.PullRequest, state string, description string, context string) error {
params := []pegomock.Param{repo, pull, state, description, context}
func (mock *MockClient) UpdateStatus(repo models.Repo, pull models.PullRequest, state vcs.CommitStatus, description string) error {
params := []pegomock.Param{repo, pull, state, description}
result := pegomock.GetGenericMockFrom(mock).Invoke("UpdateStatus", params, []reflect.Type{reflect.TypeOf((*error)(nil)).Elem()})
var ret0 error
if len(result) != 0 {
@@ -209,39 +190,8 @@ func (c *Client_PullIsApproved_OngoingVerification) GetAllCapturedArguments() (_
return
}
func (verifier *VerifierClient) GetPullRequest(repo models.Repo, num int) *Client_GetPullRequest_OngoingVerification {
params := []pegomock.Param{repo, num}
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "GetPullRequest", params)
return &Client_GetPullRequest_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations}
}
type Client_GetPullRequest_OngoingVerification struct {
mock *MockClient
methodInvocations []pegomock.MethodInvocation
}
func (c *Client_GetPullRequest_OngoingVerification) GetCapturedArguments() (models.Repo, int) {
repo, num := c.GetAllCapturedArguments()
return repo[len(repo)-1], num[len(num)-1]
}
func (c *Client_GetPullRequest_OngoingVerification) GetAllCapturedArguments() (_param0 []models.Repo, _param1 []int) {
params := pegomock.GetGenericMockFrom(c.mock).GetInvocationParams(c.methodInvocations)
if len(params) > 0 {
_param0 = make([]models.Repo, len(params[0]))
for u, param := range params[0] {
_param0[u] = param.(models.Repo)
}
_param1 = make([]int, len(params[1]))
for u, param := range params[1] {
_param1[u] = param.(int)
}
}
return
}
func (verifier *VerifierClient) UpdateStatus(repo models.Repo, pull models.PullRequest, state string, description string, context string) *Client_UpdateStatus_OngoingVerification {
params := []pegomock.Param{repo, pull, state, description, context}
func (verifier *VerifierClient) UpdateStatus(repo models.Repo, pull models.PullRequest, state vcs.CommitStatus, description string) *Client_UpdateStatus_OngoingVerification {
params := []pegomock.Param{repo, pull, state, description}
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "UpdateStatus", params)
return &Client_UpdateStatus_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations}
}
@@ -251,12 +201,12 @@ type Client_UpdateStatus_OngoingVerification struct {
methodInvocations []pegomock.MethodInvocation
}
func (c *Client_UpdateStatus_OngoingVerification) GetCapturedArguments() (models.Repo, models.PullRequest, string, string, string) {
repo, pull, state, description, context := c.GetAllCapturedArguments()
return repo[len(repo)-1], pull[len(pull)-1], state[len(state)-1], description[len(description)-1], context[len(context)-1]
func (c *Client_UpdateStatus_OngoingVerification) GetCapturedArguments() (models.Repo, models.PullRequest, vcs.CommitStatus, string) {
repo, pull, state, description := c.GetAllCapturedArguments()
return repo[len(repo)-1], pull[len(pull)-1], state[len(state)-1], description[len(description)-1]
}
func (c *Client_UpdateStatus_OngoingVerification) GetAllCapturedArguments() (_param0 []models.Repo, _param1 []models.PullRequest, _param2 []string, _param3 []string, _param4 []string) {
func (c *Client_UpdateStatus_OngoingVerification) GetAllCapturedArguments() (_param0 []models.Repo, _param1 []models.PullRequest, _param2 []vcs.CommitStatus, _param3 []string) {
params := pegomock.GetGenericMockFrom(c.mock).GetInvocationParams(c.methodInvocations)
if len(params) > 0 {
_param0 = make([]models.Repo, len(params[0]))
@@ -267,18 +217,14 @@ func (c *Client_UpdateStatus_OngoingVerification) GetAllCapturedArguments() (_pa
for u, param := range params[1] {
_param1[u] = param.(models.PullRequest)
}
_param2 = make([]string, len(params[2]))
_param2 = make([]vcs.CommitStatus, len(params[2]))
for u, param := range params[2] {
_param2[u] = param.(string)
_param2[u] = param.(vcs.CommitStatus)
}
_param3 = make([]string, len(params[3]))
for u, param := range params[3] {
_param3[u] = param.(string)
}
_param4 = make([]string, len(params[4]))
for u, param := range params[4] {
_param4[u] = param.(string)
}
}
return
}

View File

@@ -0,0 +1,246 @@
// Automatically generated by pegomock. DO NOT EDIT!
// Source: github.com/hootsuite/atlantis/server/events/vcs (interfaces: ClientProxy)
package mocks
import (
"reflect"
models "github.com/hootsuite/atlantis/server/events/models"
vcs "github.com/hootsuite/atlantis/server/events/vcs"
pegomock "github.com/petergtz/pegomock"
)
type MockClientProxy struct {
fail func(message string, callerSkip ...int)
}
func NewMockClientProxy() *MockClientProxy {
return &MockClientProxy{fail: pegomock.GlobalFailHandler}
}
func (mock *MockClientProxy) GetModifiedFiles(repo models.Repo, pull models.PullRequest, host vcs.Host) ([]string, error) {
params := []pegomock.Param{repo, pull, host}
result := pegomock.GetGenericMockFrom(mock).Invoke("GetModifiedFiles", params, []reflect.Type{reflect.TypeOf((*[]string)(nil)).Elem(), reflect.TypeOf((*error)(nil)).Elem()})
var ret0 []string
var ret1 error
if len(result) != 0 {
if result[0] != nil {
ret0 = result[0].([]string)
}
if result[1] != nil {
ret1 = result[1].(error)
}
}
return ret0, ret1
}
func (mock *MockClientProxy) CreateComment(repo models.Repo, pull models.PullRequest, comment string, host vcs.Host) error {
params := []pegomock.Param{repo, pull, comment, host}
result := pegomock.GetGenericMockFrom(mock).Invoke("CreateComment", params, []reflect.Type{reflect.TypeOf((*error)(nil)).Elem()})
var ret0 error
if len(result) != 0 {
if result[0] != nil {
ret0 = result[0].(error)
}
}
return ret0
}
func (mock *MockClientProxy) PullIsApproved(repo models.Repo, pull models.PullRequest, host vcs.Host) (bool, error) {
params := []pegomock.Param{repo, pull, host}
result := pegomock.GetGenericMockFrom(mock).Invoke("PullIsApproved", params, []reflect.Type{reflect.TypeOf((*bool)(nil)).Elem(), reflect.TypeOf((*error)(nil)).Elem()})
var ret0 bool
var ret1 error
if len(result) != 0 {
if result[0] != nil {
ret0 = result[0].(bool)
}
if result[1] != nil {
ret1 = result[1].(error)
}
}
return ret0, ret1
}
func (mock *MockClientProxy) UpdateStatus(repo models.Repo, pull models.PullRequest, state vcs.CommitStatus, description string, host vcs.Host) error {
params := []pegomock.Param{repo, pull, state, description, host}
result := pegomock.GetGenericMockFrom(mock).Invoke("UpdateStatus", params, []reflect.Type{reflect.TypeOf((*error)(nil)).Elem()})
var ret0 error
if len(result) != 0 {
if result[0] != nil {
ret0 = result[0].(error)
}
}
return ret0
}
func (mock *MockClientProxy) VerifyWasCalledOnce() *VerifierClientProxy {
return &VerifierClientProxy{mock, pegomock.Times(1), nil}
}
func (mock *MockClientProxy) VerifyWasCalled(invocationCountMatcher pegomock.Matcher) *VerifierClientProxy {
return &VerifierClientProxy{mock, invocationCountMatcher, nil}
}
func (mock *MockClientProxy) VerifyWasCalledInOrder(invocationCountMatcher pegomock.Matcher, inOrderContext *pegomock.InOrderContext) *VerifierClientProxy {
return &VerifierClientProxy{mock, invocationCountMatcher, inOrderContext}
}
type VerifierClientProxy struct {
mock *MockClientProxy
invocationCountMatcher pegomock.Matcher
inOrderContext *pegomock.InOrderContext
}
func (verifier *VerifierClientProxy) GetModifiedFiles(repo models.Repo, pull models.PullRequest, host vcs.Host) *ClientProxy_GetModifiedFiles_OngoingVerification {
params := []pegomock.Param{repo, pull, host}
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "GetModifiedFiles", params)
return &ClientProxy_GetModifiedFiles_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations}
}
type ClientProxy_GetModifiedFiles_OngoingVerification struct {
mock *MockClientProxy
methodInvocations []pegomock.MethodInvocation
}
func (c *ClientProxy_GetModifiedFiles_OngoingVerification) GetCapturedArguments() (models.Repo, models.PullRequest, vcs.Host) {
repo, pull, host := c.GetAllCapturedArguments()
return repo[len(repo)-1], pull[len(pull)-1], host[len(host)-1]
}
func (c *ClientProxy_GetModifiedFiles_OngoingVerification) GetAllCapturedArguments() (_param0 []models.Repo, _param1 []models.PullRequest, _param2 []vcs.Host) {
params := pegomock.GetGenericMockFrom(c.mock).GetInvocationParams(c.methodInvocations)
if len(params) > 0 {
_param0 = make([]models.Repo, len(params[0]))
for u, param := range params[0] {
_param0[u] = param.(models.Repo)
}
_param1 = make([]models.PullRequest, len(params[1]))
for u, param := range params[1] {
_param1[u] = param.(models.PullRequest)
}
_param2 = make([]vcs.Host, len(params[2]))
for u, param := range params[2] {
_param2[u] = param.(vcs.Host)
}
}
return
}
func (verifier *VerifierClientProxy) CreateComment(repo models.Repo, pull models.PullRequest, comment string, host vcs.Host) *ClientProxy_CreateComment_OngoingVerification {
params := []pegomock.Param{repo, pull, comment, host}
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "CreateComment", params)
return &ClientProxy_CreateComment_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations}
}
type ClientProxy_CreateComment_OngoingVerification struct {
mock *MockClientProxy
methodInvocations []pegomock.MethodInvocation
}
func (c *ClientProxy_CreateComment_OngoingVerification) GetCapturedArguments() (models.Repo, models.PullRequest, string, vcs.Host) {
repo, pull, comment, host := c.GetAllCapturedArguments()
return repo[len(repo)-1], pull[len(pull)-1], comment[len(comment)-1], host[len(host)-1]
}
func (c *ClientProxy_CreateComment_OngoingVerification) GetAllCapturedArguments() (_param0 []models.Repo, _param1 []models.PullRequest, _param2 []string, _param3 []vcs.Host) {
params := pegomock.GetGenericMockFrom(c.mock).GetInvocationParams(c.methodInvocations)
if len(params) > 0 {
_param0 = make([]models.Repo, len(params[0]))
for u, param := range params[0] {
_param0[u] = param.(models.Repo)
}
_param1 = make([]models.PullRequest, len(params[1]))
for u, param := range params[1] {
_param1[u] = param.(models.PullRequest)
}
_param2 = make([]string, len(params[2]))
for u, param := range params[2] {
_param2[u] = param.(string)
}
_param3 = make([]vcs.Host, len(params[3]))
for u, param := range params[3] {
_param3[u] = param.(vcs.Host)
}
}
return
}
func (verifier *VerifierClientProxy) PullIsApproved(repo models.Repo, pull models.PullRequest, host vcs.Host) *ClientProxy_PullIsApproved_OngoingVerification {
params := []pegomock.Param{repo, pull, host}
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "PullIsApproved", params)
return &ClientProxy_PullIsApproved_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations}
}
type ClientProxy_PullIsApproved_OngoingVerification struct {
mock *MockClientProxy
methodInvocations []pegomock.MethodInvocation
}
func (c *ClientProxy_PullIsApproved_OngoingVerification) GetCapturedArguments() (models.Repo, models.PullRequest, vcs.Host) {
repo, pull, host := c.GetAllCapturedArguments()
return repo[len(repo)-1], pull[len(pull)-1], host[len(host)-1]
}
func (c *ClientProxy_PullIsApproved_OngoingVerification) GetAllCapturedArguments() (_param0 []models.Repo, _param1 []models.PullRequest, _param2 []vcs.Host) {
params := pegomock.GetGenericMockFrom(c.mock).GetInvocationParams(c.methodInvocations)
if len(params) > 0 {
_param0 = make([]models.Repo, len(params[0]))
for u, param := range params[0] {
_param0[u] = param.(models.Repo)
}
_param1 = make([]models.PullRequest, len(params[1]))
for u, param := range params[1] {
_param1[u] = param.(models.PullRequest)
}
_param2 = make([]vcs.Host, len(params[2]))
for u, param := range params[2] {
_param2[u] = param.(vcs.Host)
}
}
return
}
func (verifier *VerifierClientProxy) UpdateStatus(repo models.Repo, pull models.PullRequest, state vcs.CommitStatus, description string, host vcs.Host) *ClientProxy_UpdateStatus_OngoingVerification {
params := []pegomock.Param{repo, pull, state, description, host}
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "UpdateStatus", params)
return &ClientProxy_UpdateStatus_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations}
}
type ClientProxy_UpdateStatus_OngoingVerification struct {
mock *MockClientProxy
methodInvocations []pegomock.MethodInvocation
}
func (c *ClientProxy_UpdateStatus_OngoingVerification) GetCapturedArguments() (models.Repo, models.PullRequest, vcs.CommitStatus, string, vcs.Host) {
repo, pull, state, description, host := c.GetAllCapturedArguments()
return repo[len(repo)-1], pull[len(pull)-1], state[len(state)-1], description[len(description)-1], host[len(host)-1]
}
func (c *ClientProxy_UpdateStatus_OngoingVerification) GetAllCapturedArguments() (_param0 []models.Repo, _param1 []models.PullRequest, _param2 []vcs.CommitStatus, _param3 []string, _param4 []vcs.Host) {
params := pegomock.GetGenericMockFrom(c.mock).GetInvocationParams(c.methodInvocations)
if len(params) > 0 {
_param0 = make([]models.Repo, len(params[0]))
for u, param := range params[0] {
_param0[u] = param.(models.Repo)
}
_param1 = make([]models.PullRequest, len(params[1]))
for u, param := range params[1] {
_param1[u] = param.(models.PullRequest)
}
_param2 = make([]vcs.CommitStatus, len(params[2]))
for u, param := range params[2] {
_param2[u] = param.(vcs.CommitStatus)
}
_param3 = make([]string, len(params[3]))
for u, param := range params[3] {
_param3[u] = param.(string)
}
_param4 = make([]vcs.Host, len(params[4]))
for u, param := range params[4] {
_param4[u] = param.(vcs.Host)
}
}
return
}

View File

@@ -0,0 +1,31 @@
package vcs
import (
"fmt"
"github.com/hootsuite/atlantis/server/events/models"
)
// NotConfiguredVCSClient is used as a placeholder when Atlantis isn't configured
// on startup to support a certain VCS host. For example, if there is no GitHub
// config then this client will be used which will error if it's ever called.
type NotConfiguredVCSClient struct {
Host Host
}
func (a *NotConfiguredVCSClient) GetModifiedFiles(repo models.Repo, pull models.PullRequest) ([]string, error) {
return nil, a.err()
}
func (a *NotConfiguredVCSClient) CreateComment(repo models.Repo, pull models.PullRequest, comment string) error {
return a.err()
}
func (a *NotConfiguredVCSClient) PullIsApproved(repo models.Repo, pull models.PullRequest) (bool, error) {
return false, a.err()
}
func (a *NotConfiguredVCSClient) UpdateStatus(repo models.Repo, pull models.PullRequest, state CommitStatus, description string) error {
return a.err()
}
func (a *NotConfiguredVCSClient) err() error {
//noinspection GoErrorStringFormat
return fmt.Errorf("Atlantis was not configured to support repos from %s", a.Host.String())
}

View File

@@ -0,0 +1,79 @@
package vcs
import (
"github.com/hootsuite/atlantis/server/events/models"
"github.com/pkg/errors"
)
//go:generate pegomock generate --use-experimental-model-gen --package mocks -o mocks/mock_proxy.go ClientProxy
// ClientProxy proxies calls to the correct VCS client depending on which
// VCS host is required.
type ClientProxy interface {
GetModifiedFiles(repo models.Repo, pull models.PullRequest, host Host) ([]string, error)
CreateComment(repo models.Repo, pull models.PullRequest, comment string, host Host) error
PullIsApproved(repo models.Repo, pull models.PullRequest, host Host) (bool, error)
UpdateStatus(repo models.Repo, pull models.PullRequest, state CommitStatus, description string, host Host) error
}
// DefaultClientProxy proxies calls to the correct VCS client depending on which
// VCS host is required.
type DefaultClientProxy struct {
GithubClient Client
GitlabClient Client
}
func NewDefaultClientProxy(githubClient Client, gitlabClient Client) *DefaultClientProxy {
if githubClient == nil {
githubClient = &NotConfiguredVCSClient{}
}
if gitlabClient == nil {
gitlabClient = &NotConfiguredVCSClient{}
}
return &DefaultClientProxy{
GitlabClient: gitlabClient,
GithubClient: githubClient,
}
}
var invalidVCSErr = errors.New("Invalid VCS Host. This is a bug!")
func (d *DefaultClientProxy) GetModifiedFiles(repo models.Repo, pull models.PullRequest, host Host) ([]string, error) {
switch host {
case Github:
return d.GithubClient.GetModifiedFiles(repo, pull)
case Gitlab:
return d.GitlabClient.GetModifiedFiles(repo, pull)
}
return nil, invalidVCSErr
}
func (d *DefaultClientProxy) CreateComment(repo models.Repo, pull models.PullRequest, comment string, host Host) error {
switch host {
case Github:
return d.GithubClient.CreateComment(repo, pull, comment)
case Gitlab:
return d.GitlabClient.CreateComment(repo, pull, comment)
}
return invalidVCSErr
}
func (d *DefaultClientProxy) PullIsApproved(repo models.Repo, pull models.PullRequest, host Host) (bool, error) {
switch host {
case Github:
return d.GithubClient.PullIsApproved(repo, pull)
case Gitlab:
return d.GitlabClient.PullIsApproved(repo, pull)
}
return false, invalidVCSErr
}
func (d *DefaultClientProxy) UpdateStatus(repo models.Repo, pull models.PullRequest, state CommitStatus, description string, host Host) error {
switch host {
case Github:
return d.GithubClient.UpdateStatus(repo, pull, state, description)
case Gitlab:
return d.GitlabClient.UpdateStatus(repo, pull, state, description)
}
return invalidVCSErr
}

42
server/events/vcs/vcs.go Normal file
View File

@@ -0,0 +1,42 @@
package vcs
type Host int
const (
Github Host = iota
Gitlab
)
func (h Host) String() string {
switch h {
case Github:
return "Github"
case Gitlab:
return "Gitlab"
}
return "<missing String() implementation>"
}
// CommitStatus is the result of executing an Atlantis command for the commit.
// In Github the options are: error, failure, pending, success.
// In Gitlab the options are: failed, canceled, pending, running, success.
// We only support Failed, Pending, Success.
type CommitStatus int
const (
Pending CommitStatus = iota
Success
Failed
)
func (s CommitStatus) String() string {
switch s {
case Pending:
return "pending"
case Success:
return "success"
case Failed:
return "failed"
}
return "failed"
}

View File

@@ -6,9 +6,15 @@ import (
"github.com/google/go-github/github"
"github.com/hootsuite/atlantis/server/events"
"github.com/hootsuite/atlantis/server/events/models"
"github.com/hootsuite/atlantis/server/events/vcs"
"github.com/hootsuite/atlantis/server/logging"
"github.com/lkysow/go-gitlab"
)
const githubHeader = "X-Github-Event"
const gitlabHeader = "X-Gitlab-Event"
type EventsController struct {
CommandRunner events.CommandRunner
PullCleaner events.PullCleaner
@@ -18,12 +24,66 @@ type EventsController struct {
// UI that identifies this call as coming from GitHub. If empty, no
// request validation is done.
GithubWebHookSecret []byte
Validator GHRequestValidator
GithubRequestValidator GithubRequestValidator
GitlabRequestParser GitlabRequestParser
// GitlabWebHookSecret is the secret added to this webhook via the GitLab
// UI that identifies this call as coming from GitLab. If empty, no
// request validation is done.
GitlabWebHookSecret []byte
// SupportedVCSHosts is which VCS hosts Atlantis was configured upon
// startup to support.
SupportedVCSHosts []vcs.Host
}
func (e *EventsController) Post(w http.ResponseWriter, r *http.Request) {
if r.Header.Get(githubHeader) != "" {
if !e.supportsHost(vcs.Github) {
e.respond(w, logging.Debug, http.StatusBadRequest, "Ignoring request since not configured to support GitHub")
return
}
e.handleGithubPost(w, r)
return
} else if r.Header.Get(gitlabHeader) != "" {
if !e.supportsHost(vcs.Gitlab) {
e.respond(w, logging.Debug, http.StatusBadRequest, "Ignoring request since not configured to support GitLab")
return
}
e.handleGitlabPost(w, r)
return
}
e.respond(w, logging.Debug, http.StatusBadRequest, "Ignoring request")
}
// supportsHost returns true if h is in e.SupportedVCSHosts and false otherwise
func (e *EventsController) supportsHost(h vcs.Host) bool {
for _, supported := range e.SupportedVCSHosts {
if h == supported {
return true
}
}
return false
}
func (e *EventsController) handleGitlabPost(w http.ResponseWriter, r *http.Request) {
event, err := e.GitlabRequestParser.Validate(r, e.GitlabWebHookSecret)
if err != nil {
e.respond(w, logging.Warn, http.StatusBadRequest, err.Error())
return
}
switch event := event.(type) {
case gitlab.MergeCommentEvent:
e.HandleGitlabCommentEvent(w, event)
case gitlab.MergeEvent:
e.HandleGitlabMergeRequestEvent(w, event)
default:
e.respond(w, logging.Debug, http.StatusOK, "Ignoring unsupported event")
}
}
func (e *EventsController) handleGithubPost(w http.ResponseWriter, r *http.Request) {
// Validate the request against the optional webhook secret.
payload, err := e.Validator.Validate(r, e.GithubWebHookSecret)
payload, err := e.GithubRequestValidator.Validate(r, e.GithubWebHookSecret)
if err != nil {
e.respond(w, logging.Warn, http.StatusBadRequest, err.Error())
return
@@ -33,62 +93,87 @@ func (e *EventsController) Post(w http.ResponseWriter, r *http.Request) {
event, _ := github.ParseWebHook(github.WebHookType(r), payload)
switch event := event.(type) {
case *github.IssueCommentEvent:
e.HandleCommentEvent(w, event, githubReqID)
e.HandleGithubCommentEvent(w, event, githubReqID)
case *github.PullRequestEvent:
e.HandlePullRequestEvent(w, event, githubReqID)
e.HandleGithubPullRequestEvent(w, event, githubReqID)
default:
e.respond(w, logging.Debug, http.StatusOK, "Ignoring unsupported event %s", githubReqID)
}
}
func (e *EventsController) HandleCommentEvent(w http.ResponseWriter, event *github.IssueCommentEvent, githubReqID string) {
func (e *EventsController) HandleGithubCommentEvent(w http.ResponseWriter, event *github.IssueCommentEvent, githubReqID string) {
if event.GetAction() != "created" {
e.respond(w, logging.Debug, http.StatusOK, "Ignoring comment event since action was not created %s", githubReqID)
return
}
baseRepo, user, pull, err := e.Parser.ExtractCommentData(event)
baseRepo, user, pullNum, err := e.Parser.ParseGithubIssueCommentEvent(event)
if err != nil {
e.respond(w, logging.Error, http.StatusBadRequest, "Failed parsing event: %v %s", err, githubReqID)
return
}
ctx := &events.CommandContext{}
ctx.BaseRepo = baseRepo
ctx.User = user
ctx.Pull = pull
command, err := e.Parser.DetermineCommand(event)
command, err := e.Parser.DetermineCommand(event.Comment.GetBody(), vcs.Github)
if err != nil {
e.respond(w, logging.Debug, http.StatusOK, "Ignoring: %s %s", err, githubReqID)
return
}
ctx.Command = command
// Respond with success and then actually execute the command asynchronously.
// We use a goroutine so that this function returns and the connection is
// closed.
fmt.Fprintln(w, "Processing...")
go e.CommandRunner.ExecuteCommand(ctx)
go e.CommandRunner.ExecuteCommand(baseRepo, models.Repo{}, user, pullNum, command, vcs.Github)
}
// HandlePullRequestEvent will delete any locks associated with the pull request
func (e *EventsController) HandlePullRequestEvent(w http.ResponseWriter, pullEvent *github.PullRequestEvent, githubReqID string) {
func (e *EventsController) HandleGitlabCommentEvent(w http.ResponseWriter, event gitlab.MergeCommentEvent) {
baseRepo, headRepo, user := e.Parser.ParseGitlabMergeCommentEvent(event)
command, err := e.Parser.DetermineCommand(event.ObjectAttributes.Note, vcs.Gitlab)
if err != nil {
e.respond(w, logging.Debug, http.StatusOK, "Ignoring: %s", err)
return
}
// Respond with success and then actually execute the command asynchronously.
// We use a goroutine so that this function returns and the connection is
// closed.
fmt.Fprintln(w, "Processing...")
go e.CommandRunner.ExecuteCommand(baseRepo, headRepo, user, event.MergeRequest.IID, command, vcs.Gitlab)
}
// HandleGitlabMergeRequestEvent will delete any locks associated with the merge request
func (e *EventsController) HandleGitlabMergeRequestEvent(w http.ResponseWriter, event gitlab.MergeEvent) {
pull, repo := e.Parser.ParseGitlabMergeEvent(event)
if pull.State != models.Closed {
e.respond(w, logging.Debug, http.StatusOK, "Ignoring opened merge request event")
return
}
if err := e.PullCleaner.CleanUpPull(repo, pull, vcs.Gitlab); err != nil {
e.respond(w, logging.Error, http.StatusInternalServerError, "Error cleaning pull request: %s", err)
return
}
e.Logger.Info("deleted locks and workspace for repo %s, pull %d", repo.FullName, pull.Num)
fmt.Fprintln(w, "Merge request cleaned successfully")
}
// HandleGithubPullRequestEvent will delete any locks associated with the pull request
func (e *EventsController) HandleGithubPullRequestEvent(w http.ResponseWriter, pullEvent *github.PullRequestEvent, githubReqID string) {
if pullEvent.GetAction() != "closed" {
e.respond(w, logging.Debug, http.StatusOK, "Ignoring pull request event since action was not closed %s", githubReqID)
return
}
pull, _, err := e.Parser.ExtractPullData(pullEvent.PullRequest)
pull, _, err := e.Parser.ParseGithubPull(pullEvent.PullRequest)
if err != nil {
e.respond(w, logging.Error, http.StatusBadRequest, "Error parsing pull data: %s", err)
return
}
repo, err := e.Parser.ExtractRepoData(pullEvent.Repo)
repo, err := e.Parser.ParseGithubRepo(pullEvent.Repo)
if err != nil {
e.respond(w, logging.Error, http.StatusBadRequest, "Error parsing repo data: %s", err)
return
}
if err := e.PullCleaner.CleanUpPull(repo, pull); err != nil {
if err := e.PullCleaner.CleanUpPull(repo, pull, vcs.Github); err != nil {
e.respond(w, logging.Error, http.StatusInternalServerError, "Error cleaning pull request: %s", err)
return
}

View File

@@ -14,185 +14,303 @@ import (
"github.com/hootsuite/atlantis/server/events"
emocks "github.com/hootsuite/atlantis/server/events/mocks"
"github.com/hootsuite/atlantis/server/events/models"
"github.com/hootsuite/atlantis/server/events/vcs"
"github.com/hootsuite/atlantis/server/logging"
"github.com/hootsuite/atlantis/server/mocks"
. "github.com/hootsuite/atlantis/testing"
"github.com/lkysow/go-gitlab"
. "github.com/petergtz/pegomock"
)
const secret = "secret"
const githubHeader = "X-Github-Event"
const gitlabHeader = "X-Gitlab-Event"
var secret = []byte("secret")
var eventsReq *http.Request
func TestPost_InvalidSecret(t *testing.T) {
t.Log("when the payload can't be validated a 400 is returned")
e, v, _, _, _ := setup(t)
func TestPost_NotGithubOrGitlab(t *testing.T) {
t.Log("when the request is not for gitlab or github a 400 is returned")
e, _, _, _, _, _ := setup(t)
w := httptest.NewRecorder()
When(v.Validate(eventsReq, []byte(secret))).ThenReturn(nil, errors.New("err"))
e.Post(w, eventsReq)
responseContains(t, w, http.StatusBadRequest, "Ignoring request")
}
func TestPost_UnsupportedVCSGithub(t *testing.T) {
t.Log("when the request is for an unsupported vcs a 400 is returned")
e, _, _, _, _, _ := setup(t)
e.SupportedVCSHosts = nil
eventsReq.Header.Set(githubHeader, "value")
w := httptest.NewRecorder()
e.Post(w, eventsReq)
responseContains(t, w, http.StatusBadRequest, "Ignoring request since not configured to support GitHub")
}
func TestPost_UnsupportedVCSGitlab(t *testing.T) {
t.Log("when the request is for an unsupported vcs a 400 is returned")
e, _, _, _, _, _ := setup(t)
e.SupportedVCSHosts = nil
eventsReq.Header.Set(gitlabHeader, "value")
w := httptest.NewRecorder()
e.Post(w, eventsReq)
responseContains(t, w, http.StatusBadRequest, "Ignoring request since not configured to support GitLab")
}
func TestPost_InvalidGithubSecret(t *testing.T) {
t.Log("when the github payload can't be validated a 400 is returned")
e, v, _, _, _, _ := setup(t)
w := httptest.NewRecorder()
eventsReq.Header.Set(githubHeader, "value")
When(v.Validate(eventsReq, secret)).ThenReturn(nil, errors.New("err"))
e.Post(w, eventsReq)
responseContains(t, w, http.StatusBadRequest, "err")
}
func TestPost_UnsupportedEvent(t *testing.T) {
t.Log("when the event type is unsupported we ignore it")
e, v, _, _, _ := setup(t)
func TestPost_InvalidGitlabSecret(t *testing.T) {
t.Log("when the gitlab payload can't be validated a 400 is returned")
e, _, gl, _, _, _ := setup(t)
w := httptest.NewRecorder()
eventsReq.Header.Set(gitlabHeader, "value")
When(gl.Validate(eventsReq, secret)).ThenReturn(nil, errors.New("err"))
e.Post(w, eventsReq)
responseContains(t, w, http.StatusBadRequest, "err")
}
func TestPost_UnsupportedGithubEvent(t *testing.T) {
t.Log("when the event type is an unsupported github event we ignore it")
e, v, _, _, _, _ := setup(t)
w := httptest.NewRecorder()
eventsReq.Header.Set(githubHeader, "value")
When(v.Validate(eventsReq, nil)).ThenReturn([]byte(`{"not an event": ""}`), nil)
e.Post(w, eventsReq)
responseContains(t, w, http.StatusOK, "Ignoring unsupported event")
}
func TestPost_CommentNotCreated(t *testing.T) {
t.Log("when the event is a comment but it's not a created event we ignore it")
e, v, _, _, _ := setup(t)
eventsReq.Header.Set("X-Github-Event", "issue_comment")
func TestPost_UnsupportedGitlabEvent(t *testing.T) {
t.Log("when the event type is an unsupported gitlab event we ignore it")
e, _, gl, _, _, _ := setup(t)
w := httptest.NewRecorder()
eventsReq.Header.Set(gitlabHeader, "value")
When(gl.Validate(eventsReq, secret)).ThenReturn([]byte(`{"not an event": ""}`), nil)
e.Post(w, eventsReq)
responseContains(t, w, http.StatusOK, "Ignoring unsupported event")
}
func TestPost_GithubCommentNotCreated(t *testing.T) {
t.Log("when the event is a github comment but it's not a created event we ignore it")
e, v, _, _, _, _ := setup(t)
eventsReq.Header.Set(githubHeader, "issue_comment")
// comment action is deleted, not created
event := `{"action": "deleted"}`
When(v.Validate(eventsReq, []byte(secret))).ThenReturn([]byte(event), nil)
When(v.Validate(eventsReq, secret)).ThenReturn([]byte(event), nil)
w := httptest.NewRecorder()
e.Post(w, eventsReq)
responseContains(t, w, http.StatusOK, "Ignoring comment event since action was not created")
}
func TestPost_CommentInvalidComment(t *testing.T) {
t.Log("when the event is a comment without all expected data we return a 400")
e, v, p, _, _ := setup(t)
eventsReq.Header.Set("X-Github-Event", "issue_comment")
func TestPost_GithubInvalidComment(t *testing.T) {
t.Log("when the event is a github comment without all expected data we return a 400")
e, v, _, p, _, _ := setup(t)
eventsReq.Header.Set(githubHeader, "issue_comment")
event := `{"action": "created"}`
When(v.Validate(eventsReq, []byte(secret))).ThenReturn([]byte(event), nil)
When(p.ExtractCommentData(AnyComment())).ThenReturn(models.Repo{}, models.User{}, models.PullRequest{}, errors.New("err"))
When(v.Validate(eventsReq, secret)).ThenReturn([]byte(event), nil)
When(p.ParseGithubIssueCommentEvent(AnyComment())).ThenReturn(models.Repo{}, models.User{}, 1, errors.New("err"))
w := httptest.NewRecorder()
e.Post(w, eventsReq)
responseContains(t, w, http.StatusBadRequest, "Failed parsing event")
}
func TestPost_CommentInvalidCommand(t *testing.T) {
t.Log("when the event is a comment with an invalid command we ignore it")
e, v, p, _, _ := setup(t)
eventsReq.Header.Set("X-Github-Event", "issue_comment")
event := `{"action": "created"}`
When(v.Validate(eventsReq, []byte(secret))).ThenReturn([]byte(event), nil)
When(p.ExtractCommentData(AnyComment())).ThenReturn(models.Repo{}, models.User{}, models.PullRequest{}, nil)
When(p.DetermineCommand(AnyComment())).ThenReturn(nil, errors.New("err"))
func TestPost_GitlabCommentInvalidCommand(t *testing.T) {
t.Log("when the event is a gitlab comment with an invalid command we ignore it")
e, _, gl, p, _, _ := setup(t)
eventsReq.Header.Set(gitlabHeader, "value")
When(gl.Validate(eventsReq, secret)).ThenReturn(gitlab.MergeCommentEvent{}, nil)
When(p.DetermineCommand("", vcs.Gitlab)).ThenReturn(nil, errors.New("err"))
w := httptest.NewRecorder()
e.Post(w, eventsReq)
responseContains(t, w, http.StatusOK, "Ignoring: err")
}
func TestPost_CommentSuccess(t *testing.T) {
t.Log("when the event is comment with a valid command we call the command handler")
e, v, p, cr, _ := setup(t)
eventsReq.Header.Set("X-Github-Event", "issue_comment")
func TestPost_GithubCommentInvalidCommand(t *testing.T) {
t.Log("when the event is a github comment with an invalid command we ignore it")
e, v, _, p, _, _ := setup(t)
eventsReq.Header.Set(githubHeader, "issue_comment")
event := `{"action": "created"}`
When(v.Validate(eventsReq, []byte(secret))).ThenReturn([]byte(event), nil)
baseRepo := models.Repo{}
user := models.User{}
pull := models.PullRequest{}
cmd := events.Command{}
When(p.ExtractCommentData(AnyComment())).ThenReturn(baseRepo, user, pull, nil)
When(p.DetermineCommand(AnyComment())).ThenReturn(&cmd, nil)
When(v.Validate(eventsReq, secret)).ThenReturn([]byte(event), nil)
When(p.ParseGithubIssueCommentEvent(AnyComment())).ThenReturn(models.Repo{}, models.User{}, 1, nil)
When(p.DetermineCommand("", vcs.Github)).ThenReturn(nil, errors.New("err"))
w := httptest.NewRecorder()
e.Post(w, eventsReq)
responseContains(t, w, http.StatusOK, "Ignoring: err")
}
func TestPost_GitlabCommentSuccess(t *testing.T) {
t.Log("when the event is a gitlab comment with a valid command we call the command handler")
e, _, gl, _, cr, _ := setup(t)
eventsReq.Header.Set(gitlabHeader, "value")
When(gl.Validate(eventsReq, secret)).ThenReturn(gitlab.MergeCommentEvent{}, nil)
w := httptest.NewRecorder()
e.Post(w, eventsReq)
responseContains(t, w, http.StatusOK, "Processing...")
// wait for 200ms so goroutine is called
time.Sleep(200 * time.Millisecond)
ctx := cr.VerifyWasCalledOnce().ExecuteCommand(AnyCommandContext()).GetCapturedArguments()
Equals(t, baseRepo, ctx.BaseRepo)
Equals(t, user, ctx.User)
Equals(t, pull, ctx.Pull)
Equals(t, cmd, *ctx.Command)
cr.VerifyWasCalledOnce().ExecuteCommand(models.Repo{}, models.Repo{}, models.User{}, 0, nil, vcs.Gitlab)
}
func TestPost_PullRequestNotClosed(t *testing.T) {
t.Log("when the event is pull reuqest but it's not a closed event we ignore it")
e, v, _, _, _ := setup(t)
eventsReq.Header.Set("X-Github-Event", "pull_request")
func TestPost_GithubCommentSuccess(t *testing.T) {
t.Log("when the event is a github comment with a valid command we call the command handler")
e, v, _, p, cr, _ := setup(t)
eventsReq.Header.Set(githubHeader, "issue_comment")
event := `{"action": "created"}`
When(v.Validate(eventsReq, secret)).ThenReturn([]byte(event), nil)
baseRepo := models.Repo{}
user := models.User{}
cmd := events.Command{}
When(p.ParseGithubIssueCommentEvent(AnyComment())).ThenReturn(baseRepo, user, 1, nil)
When(p.DetermineCommand("", vcs.Github)).ThenReturn(&cmd, nil)
w := httptest.NewRecorder()
e.Post(w, eventsReq)
responseContains(t, w, http.StatusOK, "Processing...")
// wait for 200ms so goroutine is called
time.Sleep(200 * time.Millisecond)
cr.VerifyWasCalledOnce().ExecuteCommand(baseRepo, baseRepo, user, 1, &cmd, vcs.Github)
}
func TestPost_GithubPullRequestNotClosed(t *testing.T) {
t.Log("when the event is a github pull reuqest but it's not a closed event we ignore it")
e, v, _, _, _, _ := setup(t)
eventsReq.Header.Set(githubHeader, "pull_request")
event := `{"action": "opened"}`
When(v.Validate(eventsReq, []byte(secret))).ThenReturn([]byte(event), nil)
When(v.Validate(eventsReq, secret)).ThenReturn([]byte(event), nil)
w := httptest.NewRecorder()
e.Post(w, eventsReq)
responseContains(t, w, http.StatusOK, "Ignoring pull request event since action was not closed")
}
func TestPost_PullRequestInvalid(t *testing.T) {
t.Log("when the event is pull request with invalid data we return a 400")
e, v, p, _, _ := setup(t)
eventsReq.Header.Set("X-Github-Event", "pull_request")
func TestPost_GitlabMergeRequestNotClosed(t *testing.T) {
t.Log("when the event is a gitlab merge request but it's not a closed event we ignore it")
e, _, gl, p, _, _ := setup(t)
eventsReq.Header.Set(gitlabHeader, "value")
event := gitlab.MergeEvent{}
When(gl.Validate(eventsReq, secret)).ThenReturn(event, nil)
When(p.ParseGitlabMergeEvent(event)).ThenReturn(models.PullRequest{State: models.Open}, models.Repo{})
w := httptest.NewRecorder()
e.Post(w, eventsReq)
responseContains(t, w, http.StatusOK, "Ignoring opened merge request event")
}
func TestPost_GithubPullRequestInvalid(t *testing.T) {
t.Log("when the event is a github pull request with invalid data we return a 400")
e, v, _, p, _, _ := setup(t)
eventsReq.Header.Set(githubHeader, "pull_request")
event := `{"action": "closed"}`
When(v.Validate(eventsReq, []byte(secret))).ThenReturn([]byte(event), nil)
When(p.ExtractPullData(AnyPull())).ThenReturn(models.PullRequest{}, models.Repo{}, errors.New("err"))
When(v.Validate(eventsReq, secret)).ThenReturn([]byte(event), nil)
When(p.ParseGithubPull(AnyPull())).ThenReturn(models.PullRequest{}, models.Repo{}, errors.New("err"))
w := httptest.NewRecorder()
e.Post(w, eventsReq)
responseContains(t, w, http.StatusBadRequest, "Error parsing pull data: err")
}
func TestPost_PullRequestInvalidRepo(t *testing.T) {
t.Log("when the event is pull reuqest with invalid repo data we return a 400")
e, v, p, _, _ := setup(t)
eventsReq.Header.Set("X-Github-Event", "pull_request")
func TestPost_GithubPullRequestInvalidRepo(t *testing.T) {
t.Log("when the event is a github pull request with invalid repo data we return a 400")
e, v, _, p, _, _ := setup(t)
eventsReq.Header.Set(githubHeader, "pull_request")
event := `{"action": "closed"}`
When(v.Validate(eventsReq, []byte(secret))).ThenReturn([]byte(event), nil)
When(p.ExtractPullData(AnyPull())).ThenReturn(models.PullRequest{}, models.Repo{}, nil)
When(p.ExtractRepoData(AnyRepo())).ThenReturn(models.Repo{}, errors.New("err"))
When(v.Validate(eventsReq, secret)).ThenReturn([]byte(event), nil)
When(p.ParseGithubPull(AnyPull())).ThenReturn(models.PullRequest{}, models.Repo{}, nil)
When(p.ParseGithubRepo(AnyRepo())).ThenReturn(models.Repo{}, errors.New("err"))
w := httptest.NewRecorder()
e.Post(w, eventsReq)
responseContains(t, w, http.StatusBadRequest, "Error parsing repo data: err")
}
func TestPost_PullRequestErrCleaningPull(t *testing.T) {
func TestPost_GithubPullRequestErrCleaningPull(t *testing.T) {
t.Log("when the event is a pull request and we have an error calling CleanUpPull we return a 503")
RegisterMockTestingT(t)
e, v, p, _, c := setup(t)
eventsReq.Header.Set("X-Github-Event", "pull_request")
e, v, _, p, _, c := setup(t)
eventsReq.Header.Set(githubHeader, "pull_request")
event := `{"action": "closed"}`
When(v.Validate(eventsReq, []byte(secret))).ThenReturn([]byte(event), nil)
When(v.Validate(eventsReq, secret)).ThenReturn([]byte(event), nil)
repo := models.Repo{}
pull := models.PullRequest{}
When(p.ExtractPullData(AnyPull())).ThenReturn(pull, repo, nil)
When(p.ExtractRepoData(AnyRepo())).ThenReturn(repo, nil)
When(c.CleanUpPull(repo, pull)).ThenReturn(errors.New("cleanup err"))
When(p.ParseGithubPull(AnyPull())).ThenReturn(pull, repo, nil)
When(p.ParseGithubRepo(AnyRepo())).ThenReturn(repo, nil)
When(c.CleanUpPull(repo, pull, vcs.Github)).ThenReturn(errors.New("cleanup err"))
w := httptest.NewRecorder()
e.Post(w, eventsReq)
responseContains(t, w, http.StatusInternalServerError, "Error cleaning pull request: cleanup err")
}
func TestPost_PullRequestSuccess(t *testing.T) {
func TestPost_GitlabMergeRequestErrCleaningPull(t *testing.T) {
t.Log("when the event is a gitlab merge request and an error occurs calling CleanUpPull we return a 503")
e, _, gl, p, _, c := setup(t)
eventsReq.Header.Set(gitlabHeader, "value")
event := gitlab.MergeEvent{}
When(gl.Validate(eventsReq, secret)).ThenReturn(event, nil)
repo := models.Repo{}
pullRequest := models.PullRequest{State: models.Closed}
When(p.ParseGitlabMergeEvent(event)).ThenReturn(pullRequest, repo)
When(c.CleanUpPull(repo, pullRequest, vcs.Gitlab)).ThenReturn(errors.New("err"))
w := httptest.NewRecorder()
e.Post(w, eventsReq)
responseContains(t, w, http.StatusInternalServerError, "Error cleaning pull request: err")
}
func TestPost_GithubPullRequestSuccess(t *testing.T) {
t.Log("when the event is a pull request and everything works we return a 200")
e, v, p, _, c := setup(t)
eventsReq.Header.Set("X-Github-Event", "pull_request")
e, v, _, p, _, c := setup(t)
eventsReq.Header.Set(githubHeader, "pull_request")
event := `{"action": "closed"}`
When(v.Validate(eventsReq, []byte(secret))).ThenReturn([]byte(event), nil)
When(v.Validate(eventsReq, secret)).ThenReturn([]byte(event), nil)
repo := models.Repo{}
pull := models.PullRequest{}
When(p.ExtractPullData(AnyPull())).ThenReturn(pull, repo, nil)
When(p.ExtractRepoData(AnyRepo())).ThenReturn(repo, nil)
When(c.CleanUpPull(repo, pull)).ThenReturn(nil)
When(p.ParseGithubPull(AnyPull())).ThenReturn(pull, repo, nil)
When(p.ParseGithubRepo(AnyRepo())).ThenReturn(repo, nil)
When(c.CleanUpPull(repo, pull, vcs.Github)).ThenReturn(nil)
w := httptest.NewRecorder()
e.Post(w, eventsReq)
responseContains(t, w, http.StatusOK, "Pull request cleaned successfully")
}
func setup(t *testing.T) (server.EventsController, *mocks.MockGHRequestValidator, *emocks.MockEventParsing, *emocks.MockCommandRunner, *emocks.MockPullCleaner) {
func TestPost_GitlabMergeRequestSuccess(t *testing.T) {
t.Log("when the event is a gitlab merge request and the cleanup works we return a 200")
e, _, gl, p, _, _ := setup(t)
eventsReq.Header.Set(gitlabHeader, "value")
event := gitlab.MergeEvent{}
When(gl.Validate(eventsReq, secret)).ThenReturn(event, nil)
repo := models.Repo{}
pullRequest := models.PullRequest{State: models.Closed}
When(p.ParseGitlabMergeEvent(event)).ThenReturn(pullRequest, repo)
w := httptest.NewRecorder()
e.Post(w, eventsReq)
responseContains(t, w, http.StatusOK, "Merge request cleaned successfully")
}
func setup(t *testing.T) (server.EventsController, *mocks.MockGithubRequestValidator, *mocks.MockGitlabRequestParser, *emocks.MockEventParsing, *emocks.MockCommandRunner, *emocks.MockPullCleaner) {
RegisterMockTestingT(t)
eventsReq, _ = http.NewRequest("GET", "", bytes.NewBuffer(nil))
v := mocks.NewMockGHRequestValidator()
v := mocks.NewMockGithubRequestValidator()
gl := mocks.NewMockGitlabRequestParser()
p := emocks.NewMockEventParsing()
cr := emocks.NewMockCommandRunner()
c := emocks.NewMockPullCleaner()
e := server.EventsController{
Logger: logging.NewNoopLogger(),
Validator: v,
GithubRequestValidator: v,
Parser: p,
CommandRunner: cr,
PullCleaner: c,
GithubWebHookSecret: []byte(secret),
GithubWebHookSecret: secret,
SupportedVCSHosts: []vcs.Host{vcs.Github, vcs.Gitlab},
GitlabWebHookSecret: secret,
GitlabRequestParser: gl,
}
return e, v, p, cr, c
return e, v, gl, p, cr, c
}
func AnyComment() *github.IssueCommentEvent {
@@ -209,8 +327,3 @@ func AnyRepo() *github.Repository {
RegisterMatcher(NewAnyMatcher(reflect.TypeOf(&github.Repository{})))
return &github.Repository{}
}
func AnyCommandContext() *events.CommandContext {
RegisterMatcher(NewAnyMatcher(reflect.TypeOf(&events.CommandContext{})))
return &events.CommandContext{}
}

View File

@@ -9,10 +9,10 @@ import (
"github.com/google/go-github/github"
)
//go:generate pegomock generate --use-experimental-model-gen --package mocks -o mocks/mock_gh_request_validation.go GHRequestValidator
//go:generate pegomock generate --use-experimental-model-gen --package mocks -o mocks/mock_gh_request_validation.go GithubRequestValidator
// GHRequestValidator validates GitHub requests.
type GHRequestValidator interface {
// GithubRequestValidator validates GitHub requests.
type GithubRequestValidator interface {
// Validate returns the JSON payload of the request.
// If secret is not empty, it checks that the request was signed
// by secret and returns an error if it was not.
@@ -20,21 +20,21 @@ type GHRequestValidator interface {
Validate(r *http.Request, secret []byte) ([]byte, error)
}
// GHRequestValidation implements the GHRequestValidator interface.
type GHRequestValidation struct{}
// DefaultGithubRequestValidator validates GitHub requests.
type DefaultGithubRequestValidator struct{}
// Validate returns the JSON payload of the request.
// If secret is not empty, it checks that the request was signed
// by secret and returns an error if it was not.
// If secret is empty, it does not check if the request was signed.
func (g *GHRequestValidation) Validate(r *http.Request, secret []byte) ([]byte, error) {
func (d *DefaultGithubRequestValidator) Validate(r *http.Request, secret []byte) ([]byte, error) {
if len(secret) != 0 {
return g.validateAgainstSecret(r, secret)
return d.validateAgainstSecret(r, secret)
}
return g.validateWithoutSecret(r)
return d.validateWithoutSecret(r)
}
func (g *GHRequestValidation) validateAgainstSecret(r *http.Request, secret []byte) ([]byte, error) {
func (d *DefaultGithubRequestValidator) validateAgainstSecret(r *http.Request, secret []byte) ([]byte, error) {
payload, err := github.ValidatePayload(r, secret)
if err != nil {
return nil, err
@@ -42,7 +42,7 @@ func (g *GHRequestValidation) validateAgainstSecret(r *http.Request, secret []by
return payload, nil
}
func (g *GHRequestValidation) validateWithoutSecret(r *http.Request) ([]byte, error) {
func (d *DefaultGithubRequestValidator) validateWithoutSecret(r *http.Request) ([]byte, error) {
switch ct := r.Header.Get("Content-Type"); ct {
case "application/json":
payload, err := ioutil.ReadAll(r.Body)

View File

@@ -14,7 +14,7 @@ import (
func TestValidate_WithSecretErr(t *testing.T) {
t.Log("if the request is not valid against the secret there is an error")
RegisterMockTestingT(t)
g := server.GHRequestValidation{}
g := server.DefaultGithubRequestValidator{}
buf := bytes.NewBufferString("")
req, err := http.NewRequest("POST", "http://localhost/event", buf)
Ok(t, err)
@@ -29,7 +29,7 @@ func TestValidate_WithSecretErr(t *testing.T) {
func TestValidate_WithSecret(t *testing.T) {
t.Log("if the request is valid against the secret the payload is returned")
RegisterMockTestingT(t)
g := server.GHRequestValidation{}
g := server.DefaultGithubRequestValidator{}
buf := bytes.NewBufferString(`{"yo":true}`)
req, err := http.NewRequest("POST", "http://localhost/event", buf)
Ok(t, err)
@@ -44,7 +44,7 @@ func TestValidate_WithSecret(t *testing.T) {
func TestValidate_WithoutSecretInvalidContentType(t *testing.T) {
t.Log("if the request has an invalid content type an error is returned")
RegisterMockTestingT(t)
g := server.GHRequestValidation{}
g := server.DefaultGithubRequestValidator{}
buf := bytes.NewBufferString("")
req, err := http.NewRequest("POST", "http://localhost/event", buf)
Ok(t, err)
@@ -58,7 +58,7 @@ func TestValidate_WithoutSecretInvalidContentType(t *testing.T) {
func TestValidate_WithoutSecretJSON(t *testing.T) {
t.Log("if the request is JSON the body is returned")
RegisterMockTestingT(t)
g := server.GHRequestValidation{}
g := server.DefaultGithubRequestValidator{}
buf := bytes.NewBufferString(`{"yo":true}`)
req, err := http.NewRequest("POST", "http://localhost/event", buf)
Ok(t, err)
@@ -72,7 +72,7 @@ func TestValidate_WithoutSecretJSON(t *testing.T) {
func TestValidate_WithoutSecretFormNoPayload(t *testing.T) {
t.Log("if the request is form encoded and does not contain a payload param an error is returned")
RegisterMockTestingT(t)
g := server.GHRequestValidation{}
g := server.DefaultGithubRequestValidator{}
buf := bytes.NewBufferString("")
req, err := http.NewRequest("POST", "http://localhost/event", buf)
Ok(t, err)
@@ -86,7 +86,7 @@ func TestValidate_WithoutSecretFormNoPayload(t *testing.T) {
func TestValidate_WithoutSecretForm(t *testing.T) {
t.Log("if the request is form encoded and does not contain a payload param an error is returned")
RegisterMockTestingT(t)
g := server.GHRequestValidation{}
g := server.DefaultGithubRequestValidator{}
form := url.Values{}
form.Set("payload", `{"yo":true}`)
buf := bytes.NewBufferString(form.Encode())

View File

@@ -0,0 +1,79 @@
package server
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"github.com/lkysow/go-gitlab"
)
const secretHeader = "X-Gitlab-Token"
//go:generate pegomock generate --use-experimental-model-gen --package mocks -o mocks/mock_gitlab_request_parser.go GitlabRequestParser
// GitlabRequestParser parses and validates GitLab requests.
type GitlabRequestParser interface {
// Validate validates that the request has a token header matching secret.
// If the secret does not match it returns an error.
// If secret is empty it does not check the token header.
// It then parses the request as a gitlab object depending on the header
// provided by GitLab identifying the webhook type. If the webhook type
// is not recognized it will return nil but will not return an error.
// Usage:
// event, err := GitlabRequestParser.Validate(r, secret)
// if err != nil {
// return
// }
// switch event := event.(type) {
// case gitlab.MergeCommentEvent:
// // handle
// case gitlab.MergeEvent:
// // handle
// default:
// // unsupported event
// }
Validate(r *http.Request, secret []byte) (interface{}, error)
}
// DefaultGitlabRequestParser parses GitLab requests.
type DefaultGitlabRequestParser struct{}
// Validate returns the JSON payload of the request.
// If secret is not empty, it checks that the request was signed
// by secret and returns an error if it was not.
// If secret is empty, it does not check if the request was signed.
func (d *DefaultGitlabRequestParser) Validate(r *http.Request, secret []byte) (interface{}, error) {
const mergeEventHeader = "Merge Request Hook"
const noteEventHeader = "Note Hook"
// Validate secret if specified.
headerSecret := r.Header.Get(secretHeader)
secretStr := string(secret)
if len(secret) != 0 && headerSecret != secretStr {
return nil, fmt.Errorf("header %s=%s did not match expected secret", secretHeader, headerSecret)
}
// Parse request into a gitlab object based on the object type specified
// in the gitlabHeader.
bytes, err := ioutil.ReadAll(r.Body)
if err != nil {
return nil, err
}
switch r.Header.Get(gitlabHeader) {
case mergeEventHeader:
var m gitlab.MergeEvent
if err := json.Unmarshal(bytes, &m); err != nil {
return nil, err
}
return m, nil
case noteEventHeader:
var m gitlab.MergeCommentEvent
if err := json.Unmarshal(bytes, &m); err != nil {
return nil, err
}
return m, nil
}
return nil, nil
}

View File

@@ -0,0 +1,372 @@
package server_test
import (
"testing"
"github.com/hootsuite/atlantis/server"
"bytes"
. "github.com/hootsuite/atlantis/testing"
. "github.com/petergtz/pegomock"
"net/http"
"github.com/lkysow/go-gitlab"
)
var parser = server.DefaultGitlabRequestParser{}
func TestValidate_InvalidSecret(t *testing.T) {
t.Log("If the secret header is set and doesn't match expected an error is returned")
RegisterMockTestingT(t)
buf := bytes.NewBufferString("")
req, err := http.NewRequest("POST", "http://localhost/event", buf)
Ok(t, err)
req.Header.Set("X-Gitlab-Token", "does-not-match")
_, err = parser.Validate(req, []byte("secret"))
Assert(t, err != nil , "should be an error")
Equals(t, "header X-Gitlab-Token=does-not-match did not match expected secret", err.Error())
}
func TestValidate_ValidSecret(t *testing.T) {
t.Log("If the secret header matches then the event is returned")
RegisterMockTestingT(t)
buf := bytes.NewBufferString(mergeEventJSON)
req, err := http.NewRequest("POST", "http://localhost/event", buf)
Ok(t, err)
req.Header.Set("X-Gitlab-Token", "secret")
req.Header.Set("X-Gitlab-Event", "Merge Request Hook")
b, err := parser.Validate(req, []byte("secret"))
Ok(t, err)
Equals(t, "Gitlab Test", b.(gitlab.MergeEvent).Project.Name)
}
func TestValidate_NoSecret(t *testing.T) {
t.Log("If there is no secret then we ignore the secret header and return the event")
RegisterMockTestingT(t)
buf := bytes.NewBufferString(mergeEventJSON)
req, err := http.NewRequest("POST", "http://localhost/event", buf)
Ok(t, err)
req.Header.Set("X-Gitlab-Token", "random secret")
req.Header.Set("X-Gitlab-Event", "Merge Request Hook")
b, err := parser.Validate(req, nil)
Ok(t, err)
Equals(t, "Gitlab Test", b.(gitlab.MergeEvent).Project.Name)
}
func TestValidate_InvalidMergeEvent(t *testing.T) {
t.Log("If the merge event is malformed there should be an error")
RegisterMockTestingT(t)
buf := bytes.NewBufferString("{")
req, err := http.NewRequest("POST", "http://localhost/event", buf)
Ok(t, err)
req.Header.Set("X-Gitlab-Event", "Merge Request Hook")
_, err = parser.Validate(req, nil)
Assert(t, err != nil , "should be an error")
Equals(t, "unexpected end of JSON input", err.Error())
}
func TestValidate_InvalidMergeCommentEvent(t *testing.T) {
t.Log("If the merge comment event is malformed there should be an error")
RegisterMockTestingT(t)
buf := bytes.NewBufferString("{")
req, err := http.NewRequest("POST", "http://localhost/event", buf)
Ok(t, err)
req.Header.Set("X-Gitlab-Event", "Note Hook")
_, err = parser.Validate(req, nil)
Assert(t, err != nil , "should be an error")
Equals(t, "unexpected end of JSON input", err.Error())
}
func TestValidate_UnrecognizedEvent(t *testing.T) {
t.Log("If the event is not one we care about we return nil")
RegisterMockTestingT(t)
buf := bytes.NewBufferString("")
req, err := http.NewRequest("POST", "http://localhost/event", buf)
Ok(t, err)
req.Header.Set("X-Gitlab-Event", "Random Event")
event, err := parser.Validate(req, nil)
Ok(t, err)
Equals(t, nil, event)
}
func TestValidate_ValidMergeEvent(t *testing.T) {
t.Log("If the merge event is valid it should be returned")
RegisterMockTestingT(t)
buf := bytes.NewBufferString(mergeEventJSON)
req, err := http.NewRequest("POST", "http://localhost/event", buf)
Ok(t, err)
req.Header.Set("X-Gitlab-Event", "Merge Request Hook")
b, err := parser.Validate(req, nil)
Ok(t, err)
Equals(t, "Gitlab Test", b.(gitlab.MergeEvent).Project.Name)
RegisterMockTestingT(t)
}
func TestValidate_ValidMergeCommentEvent(t *testing.T) {
t.Log("If the merge comment event is valid it should be returned")
RegisterMockTestingT(t)
buf := bytes.NewBufferString(mergeCommentEventJSON)
req, err := http.NewRequest("POST", "http://localhost/event", buf)
Ok(t, err)
req.Header.Set("X-Gitlab-Event", "Note Hook")
b, err := parser.Validate(req, nil)
Ok(t, err)
Equals(t, "Gitlab Test", b.(gitlab.MergeCommentEvent).Project.Name)
RegisterMockTestingT(t)
}
var mergeEventJSON = `{
"object_kind": "merge_request",
"user": {
"name": "Administrator",
"username": "root",
"avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=40\u0026d=identicon"
},
"project": {
"id": 1,
"name":"Gitlab Test",
"description":"Aut reprehenderit ut est.",
"web_url":"http://example.com/gitlabhq/gitlab-test",
"avatar_url":null,
"git_ssh_url":"git@example.com:gitlabhq/gitlab-test.git",
"git_http_url":"https://example.com/gitlabhq/gitlab-test.git",
"namespace":"GitlabHQ",
"visibility_level":20,
"path_with_namespace":"gitlabhq/gitlab-test",
"default_branch":"master",
"homepage":"http://example.com/gitlabhq/gitlab-test",
"url":"https://example.com/gitlabhq/gitlab-test.git",
"ssh_url":"git@example.com:gitlabhq/gitlab-test.git",
"http_url":"https://example.com/gitlabhq/gitlab-test.git"
},
"repository": {
"name": "Gitlab Test",
"url": "https://example.com/gitlabhq/gitlab-test.git",
"description": "Aut reprehenderit ut est.",
"homepage": "http://example.com/gitlabhq/gitlab-test"
},
"object_attributes": {
"id": 99,
"target_branch": "master",
"source_branch": "ms-viewport",
"source_project_id": 14,
"author_id": 51,
"assignee_id": 6,
"title": "MS-Viewport",
"created_at": "2013-12-03T17:23:34Z",
"updated_at": "2013-12-03T17:23:34Z",
"st_commits": null,
"st_diffs": null,
"milestone_id": null,
"state": "opened",
"merge_status": "unchecked",
"target_project_id": 14,
"iid": 1,
"description": "",
"source": {
"name":"Awesome Project",
"description":"Aut reprehenderit ut est.",
"web_url":"http://example.com/awesome_space/awesome_project",
"avatar_url":null,
"git_ssh_url":"git@example.com:awesome_space/awesome_project.git",
"git_http_url":"http://example.com/awesome_space/awesome_project.git",
"namespace":"Awesome Space",
"visibility_level":20,
"path_with_namespace":"awesome_space/awesome_project",
"default_branch":"master",
"homepage":"http://example.com/awesome_space/awesome_project",
"url":"http://example.com/awesome_space/awesome_project.git",
"ssh_url":"git@example.com:awesome_space/awesome_project.git",
"http_url":"http://example.com/awesome_space/awesome_project.git"
},
"target": {
"name":"Awesome Project",
"description":"Aut reprehenderit ut est.",
"web_url":"http://example.com/awesome_space/awesome_project",
"avatar_url":null,
"git_ssh_url":"git@example.com:awesome_space/awesome_project.git",
"git_http_url":"http://example.com/awesome_space/awesome_project.git",
"namespace":"Awesome Space",
"visibility_level":20,
"path_with_namespace":"awesome_space/awesome_project",
"default_branch":"master",
"homepage":"http://example.com/awesome_space/awesome_project",
"url":"http://example.com/awesome_space/awesome_project.git",
"ssh_url":"git@example.com:awesome_space/awesome_project.git",
"http_url":"http://example.com/awesome_space/awesome_project.git"
},
"last_commit": {
"id": "da1560886d4f094c3e6c9ef40349f7d38b5d27d7",
"message": "fixed readme",
"timestamp": "2012-01-03T23:36:29+02:00",
"url": "http://example.com/awesome_space/awesome_project/commits/da1560886d4f094c3e6c9ef40349f7d38b5d27d7",
"author": {
"name": "GitLab dev user",
"email": "gitlabdev@dv6700.(none)"
}
},
"work_in_progress": false,
"url": "http://example.com/diaspora/merge_requests/1",
"action": "open",
"assignee": {
"name": "User1",
"username": "user1",
"avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=40\u0026d=identicon"
}
},
"labels": [{
"id": 206,
"title": "API",
"color": "#ffffff",
"project_id": 14,
"created_at": "2013-12-03T17:15:43Z",
"updated_at": "2013-12-03T17:15:43Z",
"template": false,
"description": "API related issues",
"type": "ProjectLabel",
"group_id": 41
}],
"changes": {
"updated_by_id": [null, 1],
"updated_at": ["2017-09-15 16:50:55 UTC", "2017-09-15 16:52:00 UTC"],
"labels": {
"previous": [{
"id": 206,
"title": "API",
"color": "#ffffff",
"project_id": 14,
"created_at": "2013-12-03T17:15:43Z",
"updated_at": "2013-12-03T17:15:43Z",
"template": false,
"description": "API related issues",
"type": "ProjectLabel",
"group_id": 41
}],
"current": [{
"id": 205,
"title": "Platform",
"color": "#123123",
"project_id": 14,
"created_at": "2013-12-03T17:15:43Z",
"updated_at": "2013-12-03T17:15:43Z",
"template": false,
"description": "Platform related issues",
"type": "ProjectLabel",
"group_id": 41
}]
}
}
}`
var mergeCommentEventJSON = `{
"object_kind": "note",
"user": {
"name": "Administrator",
"username": "root",
"avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=40\u0026d=identicon"
},
"project_id": 5,
"project":{
"id": 5,
"name":"Gitlab Test",
"description":"Aut reprehenderit ut est.",
"web_url":"http://example.com/gitlabhq/gitlab-test",
"avatar_url":null,
"git_ssh_url":"git@example.com:gitlabhq/gitlab-test.git",
"git_http_url":"https://example.com/gitlabhq/gitlab-test.git",
"namespace":"Gitlab Org",
"visibility_level":10,
"path_with_namespace":"gitlabhq/gitlab-test",
"default_branch":"master",
"homepage":"http://example.com/gitlabhq/gitlab-test",
"url":"https://example.com/gitlabhq/gitlab-test.git",
"ssh_url":"git@example.com:gitlabhq/gitlab-test.git",
"http_url":"https://example.com/gitlabhq/gitlab-test.git"
},
"repository":{
"name": "Gitlab Test",
"url": "http://localhost/gitlab-org/gitlab-test.git",
"description": "Aut reprehenderit ut est.",
"homepage": "http://example.com/gitlab-org/gitlab-test"
},
"object_attributes": {
"id": 1244,
"note": "This MR needs work.",
"noteable_type": "MergeRequest",
"author_id": 1,
"created_at": "2015-05-17",
"updated_at": "2015-05-17",
"project_id": 5,
"attachment": null,
"line_code": null,
"commit_id": "",
"noteable_id": 7,
"system": false,
"st_diff": null,
"url": "http://example.com/gitlab-org/gitlab-test/merge_requests/1#note_1244"
},
"merge_request": {
"id": 7,
"target_branch": "markdown",
"source_branch": "master",
"source_project_id": 5,
"author_id": 8,
"assignee_id": 28,
"title": "Tempora et eos debitis quae laborum et.",
"created_at": "2015-03-01 20:12:53 UTC",
"updated_at": "2015-03-21 18:27:27 UTC",
"milestone_id": 11,
"state": "opened",
"merge_status": "cannot_be_merged",
"target_project_id": 5,
"iid": 1,
"description": "Et voluptas corrupti assumenda temporibus. Architecto cum animi eveniet amet asperiores. Vitae numquam voluptate est natus sit et ad id.",
"position": 0,
"source":{
"name":"Gitlab Test",
"description":"Aut reprehenderit ut est.",
"web_url":"http://example.com/gitlab-org/gitlab-test",
"avatar_url":null,
"git_ssh_url":"git@example.com:gitlab-org/gitlab-test.git",
"git_http_url":"https://example.com/gitlab-org/gitlab-test.git",
"namespace":"Gitlab Org",
"visibility_level":10,
"path_with_namespace":"gitlab-org/gitlab-test",
"default_branch":"master",
"homepage":"http://example.com/gitlab-org/gitlab-test",
"url":"https://example.com/gitlab-org/gitlab-test.git",
"ssh_url":"git@example.com:gitlab-org/gitlab-test.git",
"http_url":"https://example.com/gitlab-org/gitlab-test.git",
"git_http_url":"https://example.com/gitlab-org/gitlab-test.git"
},
"target": {
"name":"Gitlab Test",
"description":"Aut reprehenderit ut est.",
"web_url":"http://example.com/gitlabhq/gitlab-test",
"avatar_url":null,
"git_ssh_url":"git@example.com:gitlabhq/gitlab-test.git",
"git_http_url":"https://example.com/gitlabhq/gitlab-test.git",
"namespace":"Gitlab Org",
"visibility_level":10,
"path_with_namespace":"gitlabhq/gitlab-test",
"default_branch":"master",
"homepage":"http://example.com/gitlabhq/gitlab-test",
"url":"https://example.com/gitlabhq/gitlab-test.git",
"ssh_url":"git@example.com:gitlabhq/gitlab-test.git",
"http_url":"https://example.com/gitlabhq/gitlab-test.git"
},
"last_commit": {
"id": "562e173be03b8ff2efb05345d12df18815438a4b",
"message": "Merge branch 'another-branch' into 'master'\n\nCheck in this test\n",
"timestamp": "2002-10-02T10:00:00-05:00",
"url": "http://example.com/gitlab-org/gitlab-test/commit/562e173be03b8ff2efb05345d12df18815438a4b",
"author": {
"name": "John Smith",
"email": "john@example.com"
}
},
"work_in_progress": false,
"assignee": {
"name": "User1",
"username": "user1",
"avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=40\u0026d=identicon"
}
}
}`

View File

@@ -0,0 +1,335 @@
// Automatically generated by pegomock. DO NOT EDIT!
// Source: github.com/hootsuite/atlantis/server/logging (interfaces: SimpleLogging)
package mocks
import (
log "log"
"reflect"
logging "github.com/hootsuite/atlantis/server/logging"
pegomock "github.com/petergtz/pegomock"
)
type MockSimpleLogging struct {
fail func(message string, callerSkip ...int)
}
func NewMockSimpleLogging() *MockSimpleLogging {
return &MockSimpleLogging{fail: pegomock.GlobalFailHandler}
}
func (mock *MockSimpleLogging) Debug(format string, a ...interface{}) {
params := []pegomock.Param{format}
for _, param := range a {
params = append(params, param)
}
pegomock.GetGenericMockFrom(mock).Invoke("Debug", params, []reflect.Type{})
}
func (mock *MockSimpleLogging) Info(format string, a ...interface{}) {
params := []pegomock.Param{format}
for _, param := range a {
params = append(params, param)
}
pegomock.GetGenericMockFrom(mock).Invoke("Info", params, []reflect.Type{})
}
func (mock *MockSimpleLogging) Warn(format string, a ...interface{}) {
params := []pegomock.Param{format}
for _, param := range a {
params = append(params, param)
}
pegomock.GetGenericMockFrom(mock).Invoke("Warn", params, []reflect.Type{})
}
func (mock *MockSimpleLogging) Err(format string, a ...interface{}) {
params := []pegomock.Param{format}
for _, param := range a {
params = append(params, param)
}
pegomock.GetGenericMockFrom(mock).Invoke("Err", params, []reflect.Type{})
}
func (mock *MockSimpleLogging) Log(level logging.LogLevel, format string, a ...interface{}) {
params := []pegomock.Param{level, format}
for _, param := range a {
params = append(params, param)
}
pegomock.GetGenericMockFrom(mock).Invoke("Log", params, []reflect.Type{})
}
func (mock *MockSimpleLogging) Underlying() *log.Logger {
params := []pegomock.Param{}
result := pegomock.GetGenericMockFrom(mock).Invoke("Underlying", params, []reflect.Type{reflect.TypeOf((**log.Logger)(nil)).Elem()})
var ret0 *log.Logger
if len(result) != 0 {
if result[0] != nil {
ret0 = result[0].(*log.Logger)
}
}
return ret0
}
func (mock *MockSimpleLogging) GetLevel() logging.LogLevel {
params := []pegomock.Param{}
result := pegomock.GetGenericMockFrom(mock).Invoke("GetLevel", params, []reflect.Type{reflect.TypeOf((*logging.LogLevel)(nil)).Elem()})
var ret0 logging.LogLevel
if len(result) != 0 {
if result[0] != nil {
ret0 = result[0].(logging.LogLevel)
}
}
return ret0
}
func (mock *MockSimpleLogging) VerifyWasCalledOnce() *VerifierSimpleLogging {
return &VerifierSimpleLogging{mock, pegomock.Times(1), nil}
}
func (mock *MockSimpleLogging) VerifyWasCalled(invocationCountMatcher pegomock.Matcher) *VerifierSimpleLogging {
return &VerifierSimpleLogging{mock, invocationCountMatcher, nil}
}
func (mock *MockSimpleLogging) VerifyWasCalledInOrder(invocationCountMatcher pegomock.Matcher, inOrderContext *pegomock.InOrderContext) *VerifierSimpleLogging {
return &VerifierSimpleLogging{mock, invocationCountMatcher, inOrderContext}
}
type VerifierSimpleLogging struct {
mock *MockSimpleLogging
invocationCountMatcher pegomock.Matcher
inOrderContext *pegomock.InOrderContext
}
func (verifier *VerifierSimpleLogging) Debug(format string, a ...interface{}) *SimpleLogging_Debug_OngoingVerification {
params := []pegomock.Param{format}
for _, param := range a {
params = append(params, param)
}
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "Debug", params)
return &SimpleLogging_Debug_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations}
}
type SimpleLogging_Debug_OngoingVerification struct {
mock *MockSimpleLogging
methodInvocations []pegomock.MethodInvocation
}
func (c *SimpleLogging_Debug_OngoingVerification) GetCapturedArguments() (string, []interface{}) {
format, a := c.GetAllCapturedArguments()
return format[len(format)-1], a[len(a)-1]
}
func (c *SimpleLogging_Debug_OngoingVerification) GetAllCapturedArguments() (_param0 []string, _param1 [][]interface{}) {
params := pegomock.GetGenericMockFrom(c.mock).GetInvocationParams(c.methodInvocations)
if len(params) > 0 {
_param0 = make([]string, len(params[0]))
for u, param := range params[0] {
_param0[u] = param.(string)
}
_param1 = make([][]interface{}, len(params[1]))
for u := range params[0] {
_param1[u] = make([]interface{}, len(params)-1)
for x := 1; x < len(params); x++ {
if params[x][u] != nil {
_param1[u][x-1] = params[x][u].(interface{})
}
}
}
}
return
}
func (verifier *VerifierSimpleLogging) Info(format string, a ...interface{}) *SimpleLogging_Info_OngoingVerification {
params := []pegomock.Param{format}
for _, param := range a {
params = append(params, param)
}
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "Info", params)
return &SimpleLogging_Info_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations}
}
type SimpleLogging_Info_OngoingVerification struct {
mock *MockSimpleLogging
methodInvocations []pegomock.MethodInvocation
}
func (c *SimpleLogging_Info_OngoingVerification) GetCapturedArguments() (string, []interface{}) {
format, a := c.GetAllCapturedArguments()
return format[len(format)-1], a[len(a)-1]
}
func (c *SimpleLogging_Info_OngoingVerification) GetAllCapturedArguments() (_param0 []string, _param1 [][]interface{}) {
params := pegomock.GetGenericMockFrom(c.mock).GetInvocationParams(c.methodInvocations)
if len(params) > 0 {
_param0 = make([]string, len(params[0]))
for u, param := range params[0] {
_param0[u] = param.(string)
}
_param1 = make([][]interface{}, len(params[1]))
for u := range params[0] {
_param1[u] = make([]interface{}, len(params)-1)
for x := 1; x < len(params); x++ {
if params[x][u] != nil {
_param1[u][x-1] = params[x][u].(interface{})
}
}
}
}
return
}
func (verifier *VerifierSimpleLogging) Warn(format string, a ...interface{}) *SimpleLogging_Warn_OngoingVerification {
params := []pegomock.Param{format}
for _, param := range a {
params = append(params, param)
}
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "Warn", params)
return &SimpleLogging_Warn_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations}
}
type SimpleLogging_Warn_OngoingVerification struct {
mock *MockSimpleLogging
methodInvocations []pegomock.MethodInvocation
}
func (c *SimpleLogging_Warn_OngoingVerification) GetCapturedArguments() (string, []interface{}) {
format, a := c.GetAllCapturedArguments()
return format[len(format)-1], a[len(a)-1]
}
func (c *SimpleLogging_Warn_OngoingVerification) GetAllCapturedArguments() (_param0 []string, _param1 [][]interface{}) {
params := pegomock.GetGenericMockFrom(c.mock).GetInvocationParams(c.methodInvocations)
if len(params) > 0 {
_param0 = make([]string, len(params[0]))
for u, param := range params[0] {
_param0[u] = param.(string)
}
_param1 = make([][]interface{}, len(params[1]))
for u := range params[0] {
_param1[u] = make([]interface{}, len(params)-1)
for x := 1; x < len(params); x++ {
if params[x][u] != nil {
_param1[u][x-1] = params[x][u].(interface{})
}
}
}
}
return
}
func (verifier *VerifierSimpleLogging) Err(format string, a ...interface{}) *SimpleLogging_Err_OngoingVerification {
params := []pegomock.Param{format}
for _, param := range a {
params = append(params, param)
}
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "Err", params)
return &SimpleLogging_Err_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations}
}
type SimpleLogging_Err_OngoingVerification struct {
mock *MockSimpleLogging
methodInvocations []pegomock.MethodInvocation
}
func (c *SimpleLogging_Err_OngoingVerification) GetCapturedArguments() (string, []interface{}) {
format, a := c.GetAllCapturedArguments()
return format[len(format)-1], a[len(a)-1]
}
func (c *SimpleLogging_Err_OngoingVerification) GetAllCapturedArguments() (_param0 []string, _param1 [][]interface{}) {
params := pegomock.GetGenericMockFrom(c.mock).GetInvocationParams(c.methodInvocations)
if len(params) > 0 {
_param0 = make([]string, len(params[0]))
for u, param := range params[0] {
_param0[u] = param.(string)
}
_param1 = make([][]interface{}, len(params[1]))
for u := range params[0] {
_param1[u] = make([]interface{}, len(params)-1)
for x := 1; x < len(params); x++ {
if params[x][u] != nil {
_param1[u][x-1] = params[x][u].(interface{})
}
}
}
}
return
}
func (verifier *VerifierSimpleLogging) Log(level logging.LogLevel, format string, a ...interface{}) *SimpleLogging_Log_OngoingVerification {
params := []pegomock.Param{level, format}
for _, param := range a {
params = append(params, param)
}
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "Log", params)
return &SimpleLogging_Log_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations}
}
type SimpleLogging_Log_OngoingVerification struct {
mock *MockSimpleLogging
methodInvocations []pegomock.MethodInvocation
}
func (c *SimpleLogging_Log_OngoingVerification) GetCapturedArguments() (logging.LogLevel, string, []interface{}) {
level, format, a := c.GetAllCapturedArguments()
return level[len(level)-1], format[len(format)-1], a[len(a)-1]
}
func (c *SimpleLogging_Log_OngoingVerification) GetAllCapturedArguments() (_param0 []logging.LogLevel, _param1 []string, _param2 [][]interface{}) {
params := pegomock.GetGenericMockFrom(c.mock).GetInvocationParams(c.methodInvocations)
if len(params) > 0 {
_param0 = make([]logging.LogLevel, len(params[0]))
for u, param := range params[0] {
_param0[u] = param.(logging.LogLevel)
}
_param1 = make([]string, len(params[1]))
for u, param := range params[1] {
_param1[u] = param.(string)
}
_param2 = make([][]interface{}, len(params[2]))
for u := range params[0] {
_param2[u] = make([]interface{}, len(params)-2)
for x := 2; x < len(params); x++ {
if params[x][u] != nil {
_param2[u][x-2] = params[x][u].(interface{})
}
}
}
}
return
}
func (verifier *VerifierSimpleLogging) Underlying() *SimpleLogging_Underlying_OngoingVerification {
params := []pegomock.Param{}
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "Underlying", params)
return &SimpleLogging_Underlying_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations}
}
type SimpleLogging_Underlying_OngoingVerification struct {
mock *MockSimpleLogging
methodInvocations []pegomock.MethodInvocation
}
func (c *SimpleLogging_Underlying_OngoingVerification) GetCapturedArguments() {
}
func (c *SimpleLogging_Underlying_OngoingVerification) GetAllCapturedArguments() {
}
func (verifier *VerifierSimpleLogging) GetLevel() *SimpleLogging_GetLevel_OngoingVerification {
params := []pegomock.Param{}
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "GetLevel", params)
return &SimpleLogging_GetLevel_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations}
}
type SimpleLogging_GetLevel_OngoingVerification struct {
mock *MockSimpleLogging
methodInvocations []pegomock.MethodInvocation
}
func (c *SimpleLogging_GetLevel_OngoingVerification) GetCapturedArguments() {
}
func (c *SimpleLogging_GetLevel_OngoingVerification) GetAllCapturedArguments() {
}

View File

@@ -10,9 +10,23 @@ import (
"unicode"
)
//go:generate pegomock generate --use-experimental-model-gen --package mocks -o mocks/mock_simple_logging.go SimpleLogging
type SimpleLogging interface {
Debug(format string, a ...interface{})
Info(format string, a ...interface{})
Warn(format string, a ...interface{})
Err(format string, a ...interface{})
Log(level LogLevel, format string, a ...interface{})
// Underlying returns the underlying logger.
Underlying() *log.Logger
// GetLevel returns the current log level.
GetLevel() LogLevel
}
// SimpleLogger wraps the standard logger with leveled logging
// and the ability to store log history for later adding it
// to a GitHub comment.
// to a VCS comment.
type SimpleLogger struct {
// Source is added as a prefix to each log entry.
// It's useful if you want to trace a log entry back to a
@@ -126,6 +140,14 @@ func (l *SimpleLogger) Log(level LogLevel, format string, a ...interface{}) {
}
}
func (l *SimpleLogger) Underlying() *log.Logger {
return l.Logger
}
func (l *SimpleLogger) GetLevel() LogLevel {
return l.Level
}
func (l *SimpleLogger) saveToHistory(level string, msg string) {
l.History.WriteString(fmt.Sprintf("[%s] %s\n", level, msg))
}

View File

@@ -1,23 +1,24 @@
// Automatically generated by pegomock. DO NOT EDIT!
// Source: github.com/hootsuite/atlantis/server (interfaces: GHRequestValidator)
// Source: github.com/hootsuite/atlantis/server (interfaces: GithubRequestValidator)
package mocks
import (
pegomock "github.com/petergtz/pegomock"
http "net/http"
"reflect"
pegomock "github.com/petergtz/pegomock"
)
type MockGHRequestValidator struct {
type MockGithubRequestValidator struct {
fail func(message string, callerSkip ...int)
}
func NewMockGHRequestValidator() *MockGHRequestValidator {
return &MockGHRequestValidator{fail: pegomock.GlobalFailHandler}
func NewMockGithubRequestValidator() *MockGithubRequestValidator {
return &MockGithubRequestValidator{fail: pegomock.GlobalFailHandler}
}
func (mock *MockGHRequestValidator) Validate(r *http.Request, secret []byte) ([]byte, error) {
func (mock *MockGithubRequestValidator) Validate(r *http.Request, secret []byte) ([]byte, error) {
params := []pegomock.Param{r, secret}
result := pegomock.GetGenericMockFrom(mock).Invoke("Validate", params, []reflect.Type{reflect.TypeOf((*[]byte)(nil)).Elem(), reflect.TypeOf((*error)(nil)).Elem()})
var ret0 []byte
@@ -33,41 +34,41 @@ func (mock *MockGHRequestValidator) Validate(r *http.Request, secret []byte) ([]
return ret0, ret1
}
func (mock *MockGHRequestValidator) VerifyWasCalledOnce() *VerifierGHRequestValidator {
return &VerifierGHRequestValidator{mock, pegomock.Times(1), nil}
func (mock *MockGithubRequestValidator) VerifyWasCalledOnce() *VerifierGithubRequestValidator {
return &VerifierGithubRequestValidator{mock, pegomock.Times(1), nil}
}
func (mock *MockGHRequestValidator) VerifyWasCalled(invocationCountMatcher pegomock.Matcher) *VerifierGHRequestValidator {
return &VerifierGHRequestValidator{mock, invocationCountMatcher, nil}
func (mock *MockGithubRequestValidator) VerifyWasCalled(invocationCountMatcher pegomock.Matcher) *VerifierGithubRequestValidator {
return &VerifierGithubRequestValidator{mock, invocationCountMatcher, nil}
}
func (mock *MockGHRequestValidator) VerifyWasCalledInOrder(invocationCountMatcher pegomock.Matcher, inOrderContext *pegomock.InOrderContext) *VerifierGHRequestValidator {
return &VerifierGHRequestValidator{mock, invocationCountMatcher, inOrderContext}
func (mock *MockGithubRequestValidator) VerifyWasCalledInOrder(invocationCountMatcher pegomock.Matcher, inOrderContext *pegomock.InOrderContext) *VerifierGithubRequestValidator {
return &VerifierGithubRequestValidator{mock, invocationCountMatcher, inOrderContext}
}
type VerifierGHRequestValidator struct {
mock *MockGHRequestValidator
type VerifierGithubRequestValidator struct {
mock *MockGithubRequestValidator
invocationCountMatcher pegomock.Matcher
inOrderContext *pegomock.InOrderContext
}
func (verifier *VerifierGHRequestValidator) Validate(r *http.Request, secret []byte) *GHRequestValidator_Validate_OngoingVerification {
func (verifier *VerifierGithubRequestValidator) Validate(r *http.Request, secret []byte) *GithubRequestValidator_Validate_OngoingVerification {
params := []pegomock.Param{r, secret}
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "Validate", params)
return &GHRequestValidator_Validate_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations}
return &GithubRequestValidator_Validate_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations}
}
type GHRequestValidator_Validate_OngoingVerification struct {
mock *MockGHRequestValidator
type GithubRequestValidator_Validate_OngoingVerification struct {
mock *MockGithubRequestValidator
methodInvocations []pegomock.MethodInvocation
}
func (c *GHRequestValidator_Validate_OngoingVerification) GetCapturedArguments() (*http.Request, []byte) {
func (c *GithubRequestValidator_Validate_OngoingVerification) GetCapturedArguments() (*http.Request, []byte) {
r, secret := c.GetAllCapturedArguments()
return r[len(r)-1], secret[len(secret)-1]
}
func (c *GHRequestValidator_Validate_OngoingVerification) GetAllCapturedArguments() (_param0 []*http.Request, _param1 [][]byte) {
func (c *GithubRequestValidator_Validate_OngoingVerification) GetAllCapturedArguments() (_param0 []*http.Request, _param1 [][]byte) {
params := pegomock.GetGenericMockFrom(c.mock).GetInvocationParams(c.methodInvocations)
if len(params) > 0 {
_param0 = make([]*http.Request, len(params[0]))

View File

@@ -0,0 +1,83 @@
// Automatically generated by pegomock. DO NOT EDIT!
// Source: github.com/hootsuite/atlantis/server (interfaces: GitlabRequestParser)
package mocks
import (
pegomock "github.com/petergtz/pegomock"
http "net/http"
"reflect"
)
type MockGitlabRequestParser struct {
fail func(message string, callerSkip ...int)
}
func NewMockGitlabRequestParser() *MockGitlabRequestParser {
return &MockGitlabRequestParser{fail: pegomock.GlobalFailHandler}
}
func (mock *MockGitlabRequestParser) Validate(r *http.Request, secret []byte) (interface{}, error) {
params := []pegomock.Param{r, secret}
result := pegomock.GetGenericMockFrom(mock).Invoke("Validate", params, []reflect.Type{reflect.TypeOf((*interface{})(nil)).Elem(), reflect.TypeOf((*error)(nil)).Elem()})
var ret0 interface{}
var ret1 error
if len(result) != 0 {
if result[0] != nil {
ret0 = result[0].(interface{})
}
if result[1] != nil {
ret1 = result[1].(error)
}
}
return ret0, ret1
}
func (mock *MockGitlabRequestParser) VerifyWasCalledOnce() *VerifierGitlabRequestParser {
return &VerifierGitlabRequestParser{mock, pegomock.Times(1), nil}
}
func (mock *MockGitlabRequestParser) VerifyWasCalled(invocationCountMatcher pegomock.Matcher) *VerifierGitlabRequestParser {
return &VerifierGitlabRequestParser{mock, invocationCountMatcher, nil}
}
func (mock *MockGitlabRequestParser) VerifyWasCalledInOrder(invocationCountMatcher pegomock.Matcher, inOrderContext *pegomock.InOrderContext) *VerifierGitlabRequestParser {
return &VerifierGitlabRequestParser{mock, invocationCountMatcher, inOrderContext}
}
type VerifierGitlabRequestParser struct {
mock *MockGitlabRequestParser
invocationCountMatcher pegomock.Matcher
inOrderContext *pegomock.InOrderContext
}
func (verifier *VerifierGitlabRequestParser) Validate(r *http.Request, secret []byte) *GitlabRequestParser_Validate_OngoingVerification {
params := []pegomock.Param{r, secret}
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "Validate", params)
return &GitlabRequestParser_Validate_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations}
}
type GitlabRequestParser_Validate_OngoingVerification struct {
mock *MockGitlabRequestParser
methodInvocations []pegomock.MethodInvocation
}
func (c *GitlabRequestParser_Validate_OngoingVerification) GetCapturedArguments() (*http.Request, []byte) {
r, secret := c.GetAllCapturedArguments()
return r[len(r)-1], secret[len(secret)-1]
}
func (c *GitlabRequestParser_Validate_OngoingVerification) GetAllCapturedArguments() (_param0 []*http.Request, _param1 [][]byte) {
params := pegomock.GetGenericMockFrom(c.mock).GetInvocationParams(c.methodInvocations)
if len(params) > 0 {
_param0 = make([]*http.Request, len(params[0]))
for u, param := range params[0] {
_param0[u] = param.(*http.Request)
}
_param1 = make([][]byte, len(params[1]))
for u, param := range params[1] {
_param1[u] = param.([]byte)
}
}
return
}

View File

@@ -13,21 +13,24 @@ import (
"github.com/elazarl/go-bindata-assetfs"
"github.com/gorilla/mux"
"github.com/hootsuite/atlantis/server/events"
"github.com/hootsuite/atlantis/server/events/github"
"github.com/hootsuite/atlantis/server/events/locking"
"github.com/hootsuite/atlantis/server/events/locking/boltdb"
"github.com/hootsuite/atlantis/server/events/run"
"github.com/hootsuite/atlantis/server/events/terraform"
"github.com/hootsuite/atlantis/server/events/vcs"
"github.com/hootsuite/atlantis/server/logging"
"github.com/hootsuite/atlantis/server/static"
"github.com/lkysow/go-gitlab"
"github.com/pkg/errors"
"github.com/urfave/cli"
"github.com/urfave/negroni"
"flag"
)
const LockRouteName = "lock-detail"
// Server listens for GitHub events and runs the necessary Atlantis command
// Server runs the Atlantis web server. It's used for webhook requests and the
// Atlantis UI.
type Server struct {
Router *mux.Router
Port int
@@ -50,22 +53,43 @@ type Config struct {
GithubToken string `mapstructure:"gh-token"`
GithubUser string `mapstructure:"gh-user"`
GithubWebHookSecret string `mapstructure:"gh-webhook-secret"`
GitlabHostname string `mapstructure:"gitlab-hostname"`
GitlabToken string `mapstructure:"gitlab-token"`
GitlabUser string `mapstructure:"gitlab-user"`
GitlabWebHookSecret string `mapstructure:"gitlab-webhook-secret"`
LogLevel string `mapstructure:"log-level"`
Port int `mapstructure:"port"`
RequireApproval bool `mapstructure:"require-approval"`
}
func NewServer(config Config) (*Server, error) {
githubClient, err := github.NewClient(config.GithubHostname, config.GithubUser, config.GithubToken)
var supportedVCSHosts []vcs.Host
var githubClient *vcs.GithubClient
var gitlabClient *vcs.GitlabClient
if config.GithubUser != "" {
supportedVCSHosts = append(supportedVCSHosts, vcs.Github)
var err error
githubClient, err = vcs.NewGithubClient(config.GithubHostname, config.GithubUser, config.GithubToken)
if err != nil {
return nil, err
}
githubStatus := &events.GithubStatus{Client: githubClient}
}
if config.GitlabUser != "" {
supportedVCSHosts = append(supportedVCSHosts, vcs.Gitlab)
gitlabClient = &vcs.GitlabClient{
Client: gitlab.NewClient(nil, config.GitlabToken),
}
}
vcsClient := vcs.NewDefaultClientProxy(githubClient, gitlabClient)
commitStatusUpdater := &events.DefaultCommitStatusUpdater{Client: vcsClient}
terraformClient, err := terraform.NewClient()
if err != nil {
// The flag.Lookup call is to detect if we're running in a unit test. If we
// are, then we don't error out because we don't have/want terraform
// installed on our CI system where the unit tests run.
if err != nil && flag.Lookup("test.v") == nil {
return nil, errors.Wrap(err, "initializing terraform")
}
githubComments := &events.GithubCommentRenderer{}
markdownRenderer := &events.MarkdownRenderer{}
boltdb, err := boltdb.New(config.DataDir)
if err != nil {
return nil, err
@@ -84,7 +108,7 @@ func NewServer(config Config) (*Server, error) {
Terraform: terraformClient,
}
applyExecutor := &events.ApplyExecutor{
Github: githubClient,
VCSClient: vcsClient,
Terraform: terraformClient,
RequireApproval: config.RequireApproval,
Run: run,
@@ -92,17 +116,17 @@ func NewServer(config Config) (*Server, error) {
ProjectPreExecute: projectPreExecute,
}
planExecutor := &events.PlanExecutor{
Github: githubClient,
VCSClient: vcsClient,
Terraform: terraformClient,
Run: run,
Workspace: workspace,
ProjectPreExecute: projectPreExecute,
Locker: lockingClient,
ModifiedProject: &events.ProjectFinder{},
ProjectFinder: &events.ProjectFinder{},
}
helpExecutor := &events.HelpExecutor{}
pullClosedExecutor := &events.PullClosedExecutor{
Github: githubClient,
VCSClient: vcsClient,
Locker: lockingClient,
Workspace: workspace,
}
@@ -110,6 +134,8 @@ func NewServer(config Config) (*Server, error) {
eventParser := &events.EventParser{
GithubUser: config.GithubUser,
GithubToken: config.GithubToken,
GitlabUser: config.GitlabUser,
GitlabToken: config.GitlabToken,
}
commandHandler := &events.CommandHandler{
ApplyExecutor: applyExecutor,
@@ -117,10 +143,12 @@ func NewServer(config Config) (*Server, error) {
HelpExecutor: helpExecutor,
LockURLGenerator: planExecutor,
EventParser: eventParser,
GHClient: githubClient,
GHStatus: githubStatus,
VCSClient: vcsClient,
GithubPullGetter: githubClient,
GitlabMergeRequestGetter: gitlabClient,
CommitStatusUpdater: commitStatusUpdater,
EnvLocker: concurrentRunLocker,
GHCommentRenderer: githubComments,
MarkdownRenderer: markdownRenderer,
Logger: logger,
}
eventsController := &EventsController{
@@ -129,7 +157,10 @@ func NewServer(config Config) (*Server, error) {
Parser: eventParser,
Logger: logger,
GithubWebHookSecret: []byte(config.GithubWebHookSecret),
Validator: &GHRequestValidation{},
GithubRequestValidator: &DefaultGithubRequestValidator{},
GitlabRequestParser: &DefaultGitlabRequestParser{},
GitlabWebHookSecret: []byte(config.GitlabWebHookSecret),
SupportedVCSHosts: supportedVCSHosts,
}
router := mux.NewRouter()
return &Server{
@@ -270,7 +301,7 @@ func (s *Server) DeleteLock(w http.ResponseWriter, _ *http.Request, id string) {
}
// postEvents handles POST requests to our /events endpoint. These should be
// GitHub webhook requests.
// VCS webhook requests.
func (s *Server) postEvents(w http.ResponseWriter, r *http.Request) {
s.EventsController.Post(w, r)
}

View File

@@ -20,6 +20,16 @@ import (
. "github.com/petergtz/pegomock"
)
func TestNewServer(t *testing.T) {
t.Log("Run through NewServer constructor")
tmpDir, err := ioutil.TempDir("", "")
Ok(t, err)
_, err = server.NewServer(server.Config{
DataDir: tmpDir,
})
Ok(t, err)
}
func TestIndex_LockErr(t *testing.T) {
t.Log("index should return a 503 if unable to list locks")
RegisterMockTestingT(t)

24
vendor/github.com/lkysow/go-gitlab/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,24 @@
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so
# Folders
_obj
_test
# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
_testmain.go
*.exe
*.test
*.prof

5
vendor/github.com/lkysow/go-gitlab/.travis.yml generated vendored Normal file
View File

@@ -0,0 +1,5 @@
language: go
go:
- 1.7.x
- 1.8.x

27
vendor/github.com/lkysow/go-gitlab/CHANGELOG.md generated vendored Normal file
View File

@@ -0,0 +1,27 @@
go-github CHANGELOG
===================
0.6.0
-----
- Add support for the V4 Gitlab API. This means the older V3 API is no longer fully supported
with this version. If you still need that version, please use the `f-api-v3` branch.
0.4.0
-----
- Add support to use [`sudo`](https://docs.gitlab.com/ce/api/README.html#sudo) for all API calls.
- Add support for the Notification Settings API.
- Add support for the Time Tracking API.
- Make sure that the error response correctly outputs any returned errors.
- And a reasonable number of smaller enhanchements and bugfixes.
0.3.0
-----
- Moved the tags related API calls to their own service, following the Gitlab API structure.
0.2.0
-----
- Convert all Option structs to use pointers for their fields.
0.1.0
-----
- Initial release.

202
vendor/github.com/lkysow/go-gitlab/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

129
vendor/github.com/lkysow/go-gitlab/README.md generated vendored Normal file
View File

@@ -0,0 +1,129 @@
# go-gitlab
A GitLab API client enabling Go programs to interact with GitLab in a simple and uniform way
**Documentation:** [![GoDoc](https://godoc.org/github.com/xanzy/go-gitlab?status.svg)](https://godoc.org/github.com/xanzy/go-gitlab)
**Build Status:** [![Build Status](https://travis-ci.org/xanzy/go-gitlab.svg?branch=master)](https://travis-ci.org/xanzy/go-gitlab)
## NOTE
Release v0.6.0 (released on 25-08-2017) no longer supports the older V3 Gitlab API. If
you need V3 support, please use the `f-api-v3` branch. This release contains some backwards
incompatible changes that were needed to fully support the V4 Gitlab API.
## Coverage
This API client package covers **100%** of the existing GitLab API calls! So this
includes all calls to the following services:
- [x] Branches
- [x] Commits
- [x] Deploy Keys
- [x] Environments
- [x] Groups
- [x] Issues
- [x] Labels
- [x] Merge Requests
- [x] Milestones
- [x] Namespaces
- [x] Notes (comments)
- [x] Pipelines
- [x] Project Snippets
- [x] Projects (including setting Webhooks)
- [x] Repositories
- [x] Repository Files
- [x] Services
- [x] Session
- [x] Settings
- [x] System Hooks
- [x] Users
- [x] Version
- [x] Wikis
## Usage
```go
import "github.com/xanzy/go-gitlab"
```
Construct a new GitLab client, then use the various services on the client to
access different parts of the GitLab API. For example, to list all
users:
```go
git := gitlab.NewClient(nil, "yourtokengoeshere")
//git.SetBaseURL("https://git.mydomain.com/api/v3")
users, _, err := git.Users.ListUsers()
```
Some API methods have optional parameters that can be passed. For example,
to list all projects for user "svanharmelen":
```go
git := gitlab.NewClient(nil)
opt := &ListProjectsOptions{Search: gitlab.String("svanharmelen")}
projects, _, err := git.Projects.ListProjects(opt)
```
### Examples
The [examples](https://github.com/xanzy/go-gitlab/tree/master/examples) directory
contains a couple for clear examples, of which one is partially listed here as well:
```go
package main
import (
"log"
"github.com/xanzy/go-gitlab"
)
func main() {
git := gitlab.NewClient(nil, "yourtokengoeshere")
// Create new project
p := &gitlab.CreateProjectOptions{
Name: gitlab.String("My Project"),
Description: gitlab.String("Just a test project to play with"),
MergeRequestsEnabled: gitlab.Bool(true),
SnippetsEnabled: gitlab.Bool(true),
Visibility: gitlab.VisibilityLevel(gitlab.PublicVisibility),
}
project, _, err := git.Projects.CreateProject(p)
if err != nil {
log.Fatal(err)
}
// Add a new snippet
s := &gitlab.CreateSnippetOptions{
Title: gitlab.String("Dummy Snippet"),
FileName: gitlab.String("snippet.go"),
Code: gitlab.String("package main...."),
Visibility: gitlab.VisibilityLevel(gitlab.PublicVisibility),
}
_, _, err = git.ProjectSnippets.CreateSnippet(project.ID, s)
if err != nil {
log.Fatal(err)
}
}
```
For complete usage of go-gitlab, see the full [package docs](https://godoc.org/github.com/xanzy/go-gitlab).
## ToDo
- The biggest thing this package still needs is tests :disappointed:
## Issues
- If you have an issue: report it on the [issue tracker](https://github.com/xanzy/go-gitlab/issues)
## Author
Sander van Harmelen (<sander@xanzy.io>)
## License
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at <http://www.apache.org/licenses/LICENSE-2.0>

240
vendor/github.com/lkysow/go-gitlab/branches.go generated vendored Normal file
View File

@@ -0,0 +1,240 @@
//
// Copyright 2017, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
package gitlab
import (
"fmt"
"net/url"
)
// BranchesService handles communication with the branch related methods
// of the GitLab API.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/branches.html
type BranchesService struct {
client *Client
}
// Branch represents a GitLab branch.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/branches.html
type Branch struct {
Commit *Commit `json:"commit"`
Name string `json:"name"`
Protected bool `json:"protected"`
Merged bool `json:"merged"`
DevelopersCanPush bool `json:"developers_can_push"`
DevelopersCanMerge bool `json:"developers_can_merge"`
}
func (b Branch) String() string {
return Stringify(b)
}
// ListBranchesOptions represents the available ListBranches() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/branches.html#list-repository-branches
type ListBranchesOptions struct {
ListOptions
}
// ListBranches gets a list of repository branches from a project, sorted by
// name alphabetically.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/branches.html#list-repository-branches
func (s *BranchesService) ListBranches(pid interface{}, opts *ListBranchesOptions, options ...OptionFunc) ([]*Branch, *Response, error) {
project, err := parseID(pid)
if err != nil {
return nil, nil, err
}
u := fmt.Sprintf("projects/%s/repository/branches", url.QueryEscape(project))
req, err := s.client.NewRequest("GET", u, opts, options)
if err != nil {
return nil, nil, err
}
var b []*Branch
resp, err := s.client.Do(req, &b)
if err != nil {
return nil, resp, err
}
return b, resp, err
}
// GetBranch gets a single project repository branch.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/branches.html#get-single-repository-branch
func (s *BranchesService) GetBranch(pid interface{}, branch string, options ...OptionFunc) (*Branch, *Response, error) {
project, err := parseID(pid)
if err != nil {
return nil, nil, err
}
u := fmt.Sprintf("projects/%s/repository/branches/%s", url.QueryEscape(project), branch)
req, err := s.client.NewRequest("GET", u, nil, options)
if err != nil {
return nil, nil, err
}
b := new(Branch)
resp, err := s.client.Do(req, b)
if err != nil {
return nil, resp, err
}
return b, resp, err
}
// ProtectBranchOptions represents the available ProtectBranch() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/branches.html#protect-repository-branch
type ProtectBranchOptions struct {
DevelopersCanPush *bool `url:"developers_can_push,omitempty" json:"developers_can_push,omitempty"`
DevelopersCanMerge *bool `url:"developers_can_merge,omitempty" json:"developers_can_merge,omitempty"`
}
// ProtectBranch protects a single project repository branch. This is an
// idempotent function, protecting an already protected repository branch
// still returns a 200 OK status code.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/branches.html#protect-repository-branch
func (s *BranchesService) ProtectBranch(pid interface{}, branch string, opts *ProtectBranchOptions, options ...OptionFunc) (*Branch, *Response, error) {
project, err := parseID(pid)
if err != nil {
return nil, nil, err
}
u := fmt.Sprintf("projects/%s/repository/branches/%s/protect", url.QueryEscape(project), branch)
req, err := s.client.NewRequest("PUT", u, opts, options)
if err != nil {
return nil, nil, err
}
b := new(Branch)
resp, err := s.client.Do(req, b)
if err != nil {
return nil, resp, err
}
return b, resp, err
}
// UnprotectBranch unprotects a single project repository branch. This is an
// idempotent function, unprotecting an already unprotected repository branch
// still returns a 200 OK status code.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/branches.html#unprotect-repository-branch
func (s *BranchesService) UnprotectBranch(pid interface{}, branch string, options ...OptionFunc) (*Branch, *Response, error) {
project, err := parseID(pid)
if err != nil {
return nil, nil, err
}
u := fmt.Sprintf("projects/%s/repository/branches/%s/unprotect", url.QueryEscape(project), branch)
req, err := s.client.NewRequest("PUT", u, nil, options)
if err != nil {
return nil, nil, err
}
b := new(Branch)
resp, err := s.client.Do(req, b)
if err != nil {
return nil, resp, err
}
return b, resp, err
}
// CreateBranchOptions represents the available CreateBranch() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/branches.html#create-repository-branch
type CreateBranchOptions struct {
Branch *string `url:"branch,omitempty" json:"branch,omitempty"`
Ref *string `url:"ref,omitempty" json:"ref,omitempty"`
}
// CreateBranch creates branch from commit SHA or existing branch.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/branches.html#create-repository-branch
func (s *BranchesService) CreateBranch(pid interface{}, opt *CreateBranchOptions, options ...OptionFunc) (*Branch, *Response, error) {
project, err := parseID(pid)
if err != nil {
return nil, nil, err
}
u := fmt.Sprintf("projects/%s/repository/branches", url.QueryEscape(project))
req, err := s.client.NewRequest("POST", u, opt, options)
if err != nil {
return nil, nil, err
}
b := new(Branch)
resp, err := s.client.Do(req, b)
if err != nil {
return nil, resp, err
}
return b, resp, err
}
// DeleteBranch deletes an existing branch.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/branches.html#delete-repository-branch
func (s *BranchesService) DeleteBranch(pid interface{}, branch string, options ...OptionFunc) (*Response, error) {
project, err := parseID(pid)
if err != nil {
return nil, err
}
u := fmt.Sprintf("projects/%s/repository/branches/%s", url.QueryEscape(project), branch)
req, err := s.client.NewRequest("DELETE", u, nil, options)
if err != nil {
return nil, err
}
return s.client.Do(req, nil)
}
// DeleteMergedBranches deletes all branches that are merged into the project's default branch.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/branches.html#delete-merged-branches
func (s *BranchesService) DeleteMergedBranches(pid interface{}, options ...OptionFunc) (*Response, error) {
project, err := parseID(pid)
if err != nil {
return nil, err
}
u := fmt.Sprintf("projects/%s/repository/merged_branches", url.QueryEscape(project))
req, err := s.client.NewRequest("DELETE", u, nil, options)
if err != nil {
return nil, err
}
return s.client.Do(req, nil)
}

175
vendor/github.com/lkysow/go-gitlab/build_variables.go generated vendored Normal file
View File

@@ -0,0 +1,175 @@
package gitlab
import (
"fmt"
"net/url"
)
// BuildVariablesService handles communication with the project variables related methods
// of the Gitlab API
//
// Gitlab API Docs : https://docs.gitlab.com/ce/api/build_variables.html
type BuildVariablesService struct {
client *Client
}
// BuildVariable represents a variable available for each build of the given project
//
// Gitlab API Docs : https://docs.gitlab.com/ce/api/build_variables.html
type BuildVariable struct {
Key string `json:"key"`
Value string `json:"value"`
Protected bool `json:"protected"`
}
func (v BuildVariable) String() string {
return Stringify(v)
}
// ListBuildVariablesOptions are the parameters to ListBuildVariables()
//
// Gitlab API Docs:
// https://docs.gitlab.com/ce/api/build_variables.html#list-project-variables
type ListBuildVariablesOptions struct {
ListOptions
}
// ListBuildVariables gets the a list of project variables in a project
//
// Gitlab API Docs:
// https://docs.gitlab.com/ce/api/build_variables.html#list-project-variables
func (s *BuildVariablesService) ListBuildVariables(pid interface{}, opts *ListBuildVariablesOptions, options ...OptionFunc) ([]*BuildVariable, *Response, error) {
project, err := parseID(pid)
if err != nil {
return nil, nil, err
}
u := fmt.Sprintf("projects/%s/variables", url.QueryEscape(project))
req, err := s.client.NewRequest("GET", u, opts, options)
if err != nil {
return nil, nil, err
}
var v []*BuildVariable
resp, err := s.client.Do(req, &v)
if err != nil {
return nil, resp, err
}
return v, resp, err
}
// GetBuildVariable gets a single project variable of a project
//
// Gitlab API Docs:
// https://docs.gitlab.com/ce/api/build_variables.html#show-variable-details
func (s *BuildVariablesService) GetBuildVariable(pid interface{}, key string, options ...OptionFunc) (*BuildVariable, *Response, error) {
project, err := parseID(pid)
if err != nil {
return nil, nil, err
}
u := fmt.Sprintf("projects/%s/variables/%s", url.QueryEscape(project), key)
req, err := s.client.NewRequest("GET", u, nil, options)
if err != nil {
return nil, nil, err
}
v := new(BuildVariable)
resp, err := s.client.Do(req, v)
if err != nil {
return nil, resp, err
}
return v, resp, err
}
// CreateBuildVariableOptions are the parameters to CreateBuildVariable()
//
// Gitlab API Docs:
// https://docs.gitlab.com/ce/api/build_variables.html#create-variable
type CreateBuildVariableOptions struct {
Key *string `url:"key" json:"key"`
Value *string `url:"value" json:"value"`
Protected *bool `url:"protected,omitempty" json:"protected,omitempty"`
}
// CreateBuildVariable creates a variable for a given project
//
// Gitlab API Docs:
// https://docs.gitlab.com/ce/api/build_variables.html#create-variable
func (s *BuildVariablesService) CreateBuildVariable(pid interface{}, opt *CreateBuildVariableOptions, options ...OptionFunc) (*BuildVariable, *Response, error) {
project, err := parseID(pid)
if err != nil {
return nil, nil, err
}
u := fmt.Sprintf("projects/%s/variables", url.QueryEscape(project))
req, err := s.client.NewRequest("POST", u, opt, options)
if err != nil {
return nil, nil, err
}
v := new(BuildVariable)
resp, err := s.client.Do(req, v)
if err != nil {
return nil, resp, err
}
return v, resp, err
}
// UpdateBuildVariableOptions are the parameters to UpdateBuildVariable()
//
// Gitlab API Docs:
// https://docs.gitlab.com/ce/api/build_variables.html#update-variable
type UpdateBuildVariableOptions struct {
Key *string `url:"key" json:"key"`
Value *string `url:"value" json:"value"`
Protected *bool `url:"protected,omitempty" json:"protected,omitempty"`
}
// UpdateBuildVariable updates an existing project variable
// The variable key must exist
//
// Gitlab API Docs:
// https://docs.gitlab.com/ce/api/build_variables.html#update-variable
func (s *BuildVariablesService) UpdateBuildVariable(pid interface{}, key string, opt *UpdateBuildVariableOptions, options ...OptionFunc) (*BuildVariable, *Response, error) {
project, err := parseID(pid)
if err != nil {
return nil, nil, err
}
u := fmt.Sprintf("projects/%s/variables/%s", url.QueryEscape(project), key)
req, err := s.client.NewRequest("PUT", u, opt, options)
if err != nil {
return nil, nil, err
}
v := new(BuildVariable)
resp, err := s.client.Do(req, v)
if err != nil {
return nil, resp, err
}
return v, resp, err
}
// RemoveBuildVariable removes a project variable of a given project identified by its key
//
// Gitlab API Docs:
// https://docs.gitlab.com/ce/api/build_variables.html#remove-variable
func (s *BuildVariablesService) RemoveBuildVariable(pid interface{}, key string, options ...OptionFunc) (*Response, error) {
project, err := parseID(pid)
if err != nil {
return nil, err
}
u := fmt.Sprintf("projects/%s/variables/%s", url.QueryEscape(project), key)
req, err := s.client.NewRequest("DELETE", u, nil, options)
if err != nil {
return nil, err
}
return s.client.Do(req, nil)
}

View File

@@ -0,0 +1,113 @@
package gitlab
import (
"fmt"
"net/http"
"reflect"
"testing"
)
const (
myKey = "MY_KEY"
myValue = "MY_VALUE"
myKey2 = "MY_KEY2"
myValue2 = "MY_VALUE2"
myNewValue = "MY_NEW_VALUE"
)
func TestListBuildVariables(t *testing.T) {
mux, server, client := setup()
defer teardown(server)
mux.HandleFunc("/projects/1/variables", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
fmt.Fprintf(w,
`[{"key":"%s","value":"%s"},{"key":"%s","value":"%s"}]`, myKey, myValue, myKey2, myValue2)
})
variables, _, err := client.BuildVariables.ListBuildVariables(1, nil)
if err != nil {
t.Errorf("ListBuildVariables returned error: %v", err)
}
want := []*BuildVariable{{Key: myKey, Value: myValue}, {Key: myKey2, Value: myValue2}}
if !reflect.DeepEqual(want, variables) {
t.Errorf("ListBuildVariables returned %+v, want %+v", variables, want)
}
}
func TestGetBuildVariable(t *testing.T) {
mux, server, client := setup()
defer teardown(server)
mux.HandleFunc("/projects/1/variables/"+myKey, func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
fmt.Fprintf(w, `{"key":"%s","value":"%s"}`, myKey, myValue)
})
variable, _, err := client.BuildVariables.GetBuildVariable(1, myKey)
if err != nil {
t.Errorf("GetBuildVariable returned error: %v", err)
}
want := &BuildVariable{Key: myKey, Value: myValue}
if !reflect.DeepEqual(want, variable) {
t.Errorf("GetBuildVariable returned %+v, want %+v", variable, want)
}
}
func TestCreateBuildVariable(t *testing.T) {
mux, server, client := setup()
defer teardown(server)
mux.HandleFunc("/projects/1/variables", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "POST")
fmt.Fprintf(w, `{"key":"%s","value":"%s", "protected": false}`, myKey, myValue)
})
opt := &CreateBuildVariableOptions{String(myKey), String(myValue), Bool(false)}
variable, _, err := client.BuildVariables.CreateBuildVariable(1, opt)
if err != nil {
t.Errorf("CreateBuildVariable returned error: %v", err)
}
want := &BuildVariable{Key: myKey, Value: myValue, Protected: false}
if !reflect.DeepEqual(want, variable) {
t.Errorf("CreateBuildVariable returned %+v, want %+v", variable, want)
}
}
func TestUpdateBuildVariable(t *testing.T) {
mux, server, client := setup()
defer teardown(server)
mux.HandleFunc("/projects/1/variables/"+myKey, func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "PUT")
fmt.Fprintf(w, `{"key":"%s","value":"%s", "protected": false}`, myKey, myNewValue)
})
opt := &UpdateBuildVariableOptions{String(myKey), String(myNewValue), Bool(false)}
variable, _, err := client.BuildVariables.UpdateBuildVariable(1, myKey, opt)
if err != nil {
t.Errorf("UpdateBuildVariable returned error: %v", err)
}
want := &BuildVariable{Key: myKey, Value: myNewValue, Protected: false}
if !reflect.DeepEqual(want, variable) {
t.Errorf("UpdateBuildVariable returned %+v, want %+v", variable, want)
}
}
func TestRemoveBuildVariable(t *testing.T) {
mux, server, client := setup()
defer teardown(server)
mux.HandleFunc("/projects/1/variables/"+myKey, func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "DELETE")
})
_, err := client.BuildVariables.RemoveBuildVariable(1, myKey)
if err != nil {
t.Errorf("RemoveBuildVariable returned error: %v", err)
}
}

444
vendor/github.com/lkysow/go-gitlab/commits.go generated vendored Normal file
View File

@@ -0,0 +1,444 @@
//
// Copyright 2017, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
package gitlab
import (
"fmt"
"net/url"
"time"
)
// CommitsService handles communication with the commit related methods
// of the GitLab API.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html
type CommitsService struct {
client *Client
}
// Commit represents a GitLab commit.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html
type Commit struct {
ID string `json:"id"`
ShortID string `json:"short_id"`
Title string `json:"title"`
AuthorName string `json:"author_name"`
AuthorEmail string `json:"author_email"`
AuthoredDate *time.Time `json:"authored_date"`
CommitterName string `json:"committer_name"`
CommitterEmail string `json:"committer_email"`
CommittedDate *time.Time `json:"committed_date"`
CreatedAt *time.Time `json:"created_at"`
Message string `json:"message"`
ParentIDs []string `json:"parent_ids"`
Stats *CommitStats `json:"stats"`
Status *BuildState `json:"status"`
}
// CommitStats represents the number of added and deleted files in a commit.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html
type CommitStats struct {
Additions int `json:"additions"`
Deletions int `json:"deletions"`
Total int `json:"total"`
}
func (c Commit) String() string {
return Stringify(c)
}
// ListCommitsOptions represents the available ListCommits() options.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#list-repository-commits
type ListCommitsOptions struct {
ListOptions
RefName *string `url:"ref_name,omitempty" json:"ref_name,omitempty"`
Since time.Time `url:"since,omitempty" json:"since,omitempty"`
Until time.Time `url:"until,omitempty" json:"until,omitempty"`
}
// ListCommits gets a list of repository commits in a project.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#list-commits
func (s *CommitsService) ListCommits(pid interface{}, opt *ListCommitsOptions, options ...OptionFunc) ([]*Commit, *Response, error) {
project, err := parseID(pid)
if err != nil {
return nil, nil, err
}
u := fmt.Sprintf("projects/%s/repository/commits", url.QueryEscape(project))
req, err := s.client.NewRequest("GET", u, opt, options)
if err != nil {
return nil, nil, err
}
var c []*Commit
resp, err := s.client.Do(req, &c)
if err != nil {
return nil, resp, err
}
return c, resp, err
}
// FileAction represents the available actions that can be performed on a file.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#create-a-commit-with-multiple-files-and-actions
type FileAction string
// The available file actions.
const (
FileCreate FileAction = "create"
FileDelete FileAction = "delete"
FileMove FileAction = "move"
FileUpdate FileAction = "update"
)
// CommitAction represents a single file action within a commit.
type CommitAction struct {
Action FileAction `url:"action" json:"action"`
FilePath string `url:"file_path" json:"file_path"`
PreviousPath string `url:"previous_path,omitempty" json:"previous_path,omitempty"`
Content string `url:"content,omitempty" json:"content,omitempty"`
Encoding string `url:"encoding,omitempty" json:"encoding,omitempty"`
}
// GetCommit gets a specific commit identified by the commit hash or name of a
// branch or tag.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#get-a-single-commit
func (s *CommitsService) GetCommit(pid interface{}, sha string, options ...OptionFunc) (*Commit, *Response, error) {
project, err := parseID(pid)
if err != nil {
return nil, nil, err
}
u := fmt.Sprintf("projects/%s/repository/commits/%s", url.QueryEscape(project), sha)
req, err := s.client.NewRequest("GET", u, nil, options)
if err != nil {
return nil, nil, err
}
c := new(Commit)
resp, err := s.client.Do(req, c)
if err != nil {
return nil, resp, err
}
return c, resp, err
}
// CreateCommitOptions represents the available options for a new commit.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#create-a-commit-with-multiple-files-and-actions
type CreateCommitOptions struct {
Branch *string `url:"branch" json:"branch"`
CommitMessage *string `url:"commit_message" json:"commit_message"`
Actions []*CommitAction `url:"actions" json:"actions"`
AuthorEmail *string `url:"author_email,omitempty" json:"author_email,omitempty"`
AuthorName *string `url:"author_name,omitempty" json:"author_name,omitempty"`
}
// CreateCommit creates a commit with multiple files and actions.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#create-a-commit-with-multiple-files-and-actions
func (s *CommitsService) CreateCommit(pid interface{}, opt *CreateCommitOptions, options ...OptionFunc) (*Commit, *Response, error) {
project, err := parseID(pid)
if err != nil {
return nil, nil, err
}
u := fmt.Sprintf("projects/%s/repository/commits", url.QueryEscape(project))
req, err := s.client.NewRequest("POST", u, opt, options)
if err != nil {
return nil, nil, err
}
var c *Commit
resp, err := s.client.Do(req, &c)
if err != nil {
return nil, resp, err
}
return c, resp, err
}
// Diff represents a GitLab diff.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html
type Diff struct {
Diff string `json:"diff"`
NewPath string `json:"new_path"`
OldPath string `json:"old_path"`
AMode string `json:"a_mode"`
BMode string `json:"b_mode"`
NewFile bool `json:"new_file"`
RenamedFile bool `json:"renamed_file"`
DeletedFile bool `json:"deleted_file"`
}
func (d Diff) String() string {
return Stringify(d)
}
// GetCommitDiff gets the diff of a commit in a project..
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/commits.html#get-the-diff-of-a-commit
func (s *CommitsService) GetCommitDiff(pid interface{}, sha string, options ...OptionFunc) ([]*Diff, *Response, error) {
project, err := parseID(pid)
if err != nil {
return nil, nil, err
}
u := fmt.Sprintf("projects/%s/repository/commits/%s/diff", url.QueryEscape(project), sha)
req, err := s.client.NewRequest("GET", u, nil, options)
if err != nil {
return nil, nil, err
}
var d []*Diff
resp, err := s.client.Do(req, &d)
if err != nil {
return nil, resp, err
}
return d, resp, err
}
// CommitComment represents a GitLab commit comment.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html
type CommitComment struct {
Note string `json:"note"`
Path string `json:"path"`
Line int `json:"line"`
LineType string `json:"line_type"`
Author Author `json:"author"`
}
// Author represents a GitLab commit author
type Author struct {
ID int `json:"id"`
Username string `json:"username"`
Email string `json:"email"`
Name string `json:"name"`
State string `json:"state"`
Blocked bool `json:"blocked"`
CreatedAt *time.Time `json:"created_at"`
}
func (c CommitComment) String() string {
return Stringify(c)
}
// GetCommitComments gets the comments of a commit in a project.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/commits.html#get-the-comments-of-a-commit
func (s *CommitsService) GetCommitComments(pid interface{}, sha string, options ...OptionFunc) ([]*CommitComment, *Response, error) {
project, err := parseID(pid)
if err != nil {
return nil, nil, err
}
u := fmt.Sprintf("projects/%s/repository/commits/%s/comments", url.QueryEscape(project), sha)
req, err := s.client.NewRequest("GET", u, nil, options)
if err != nil {
return nil, nil, err
}
var c []*CommitComment
resp, err := s.client.Do(req, &c)
if err != nil {
return nil, resp, err
}
return c, resp, err
}
// PostCommitCommentOptions represents the available PostCommitComment()
// options.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/commits.html#post-comment-to-commit
type PostCommitCommentOptions struct {
Note *string `url:"note,omitempty" json:"note,omitempty"`
Path *string `url:"path" json:"path"`
Line *int `url:"line" json:"line"`
LineType *string `url:"line_type" json:"line_type"`
}
// PostCommitComment adds a comment to a commit. Optionally you can post
// comments on a specific line of a commit. Therefor both path, line_new and
// line_old are required.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/commits.html#post-comment-to-commit
func (s *CommitsService) PostCommitComment(pid interface{}, sha string, opt *PostCommitCommentOptions, options ...OptionFunc) (*CommitComment, *Response, error) {
project, err := parseID(pid)
if err != nil {
return nil, nil, err
}
u := fmt.Sprintf("projects/%s/repository/commits/%s/comments", url.QueryEscape(project), sha)
req, err := s.client.NewRequest("POST", u, opt, options)
if err != nil {
return nil, nil, err
}
c := new(CommitComment)
resp, err := s.client.Do(req, c)
if err != nil {
return nil, resp, err
}
return c, resp, err
}
// GetCommitStatusesOptions represents the available GetCommitStatuses() options.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#get-the-status-of-a-commit
type GetCommitStatusesOptions struct {
Ref *string `url:"ref,omitempty" json:"ref,omitempty"`
Stage *string `url:"stage,omitempty" json:"stage,omitempty"`
Name *string `url:"name,omitempty" json:"name,omitempty"`
All *bool `url:"all,omitempty" json:"all,omitempty"`
}
// CommitStatus represents a GitLab commit status.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#get-the-status-of-a-commit
type CommitStatus struct {
ID int `json:"id"`
SHA string `json:"sha"`
Ref string `json:"ref"`
Status string `json:"status"`
Name string `json:"name"`
TargetURL string `json:"target_url"`
Description string `json:"description"`
CreatedAt *time.Time `json:"created_at"`
StartedAt *time.Time `json:"started_at"`
FinishedAt *time.Time `json:"finished_at"`
Author Author `json:"author"`
}
// GetCommitStatuses gets the statuses of a commit in a project.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#get-the-status-of-a-commit
func (s *CommitsService) GetCommitStatuses(pid interface{}, sha string, opt *GetCommitStatusesOptions, options ...OptionFunc) ([]*CommitStatus, *Response, error) {
project, err := parseID(pid)
if err != nil {
return nil, nil, err
}
u := fmt.Sprintf("projects/%s/repository/commits/%s/statuses", url.QueryEscape(project), sha)
req, err := s.client.NewRequest("GET", u, opt, options)
if err != nil {
return nil, nil, err
}
var cs []*CommitStatus
resp, err := s.client.Do(req, &cs)
if err != nil {
return nil, resp, err
}
return cs, resp, err
}
// SetCommitStatusOptions represents the available SetCommitStatus() options.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#post-the-status-to-commit
type SetCommitStatusOptions struct {
State BuildState `url:"state" json:"state"`
Ref *string `url:"ref,omitempty" json:"ref,omitempty"`
Name *string `url:"name,omitempty" json:"name,omitempty"`
Context *string `url:"context,omitempty" json:"context,omitempty"`
TargetURL *string `url:"target_url,omitempty" json:"target_url,omitempty"`
Description *string `url:"description,omitempty" json:"description,omitempty"`
}
// BuildState represents a GitLab build state.
type BuildState string
// These constants represent all valid build states.
const (
Pending BuildState = "pending"
Running BuildState = "running"
Success BuildState = "success"
Failed BuildState = "failed"
Canceled BuildState = "canceled"
)
// SetCommitStatus sets the status of a commit in a project.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#post-the-status-to-commit
func (s *CommitsService) SetCommitStatus(pid interface{}, sha string, opt *SetCommitStatusOptions, options ...OptionFunc) (*CommitStatus, *Response, error) {
project, err := parseID(pid)
if err != nil {
return nil, nil, err
}
u := fmt.Sprintf("projects/%s/statuses/%s", url.QueryEscape(project), sha)
req, err := s.client.NewRequest("POST", u, opt, options)
if err != nil {
return nil, nil, err
}
var cs *CommitStatus
resp, err := s.client.Do(req, &cs)
if err != nil {
return nil, resp, err
}
return cs, resp, err
}
// CherryPickCommitOptions represents the available options for cherry-picking a commit.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#cherry-pick-a-commit
type CherryPickCommitOptions struct {
TargetBranch *string `url:"branch" json:"branch,omitempty"`
}
// CherryPickCommit sherry picks a commit to a given branch.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#cherry-pick-a-commit
func (s *CommitsService) CherryPickCommit(pid interface{}, sha string, opt *CherryPickCommitOptions, options ...OptionFunc) (*Commit, *Response, error) {
project, err := parseID(pid)
if err != nil {
return nil, nil, err
}
u := fmt.Sprintf("projects/%s/repository/commits/%s/cherry_pick",
url.QueryEscape(project), url.QueryEscape(sha))
req, err := s.client.NewRequest("POST", u, opt, options)
if err != nil {
return nil, nil, err
}
var c *Commit
resp, err := s.client.Do(req, &c)
if err != nil {
return nil, resp, err
}
return c, resp, err
}

52
vendor/github.com/lkysow/go-gitlab/commits_test.go generated vendored Normal file
View File

@@ -0,0 +1,52 @@
package gitlab
import (
"fmt"
"net/http"
"reflect"
"testing"
)
func TestGetCommitStatuses(t *testing.T) {
mux, server, client := setup()
defer teardown(server)
mux.HandleFunc("/projects/1/repository/commits/b0b3a907f41409829b307a28b82fdbd552ee5a27/statuses", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
fmt.Fprint(w, `[{"id":1}]`)
})
opt := &GetCommitStatusesOptions{String("master"), String("test"), String("ci/jenkins"), Bool(true)}
statuses, _, err := client.Commits.GetCommitStatuses("1", "b0b3a907f41409829b307a28b82fdbd552ee5a27", opt)
if err != nil {
t.Errorf("Commits.GetCommitStatuses returned error: %v", err)
}
want := []*CommitStatus{{ID: 1}}
if !reflect.DeepEqual(want, statuses) {
t.Errorf("Commits.GetCommitStatuses returned %+v, want %+v", statuses, want)
}
}
func TestSetCommitStatus(t *testing.T) {
mux, server, client := setup()
defer teardown(server)
mux.HandleFunc("/projects/1/statuses/b0b3a907f41409829b307a28b82fdbd552ee5a27", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "POST")
fmt.Fprint(w, `{"id":1}`)
})
opt := &SetCommitStatusOptions{Running, String("master"), String("ci/jenkins"), String(""), String("http://abc"), String("build")}
status, _, err := client.Commits.SetCommitStatus("1", "b0b3a907f41409829b307a28b82fdbd552ee5a27", opt)
if err != nil {
t.Errorf("Commits.SetCommitStatus returned error: %v", err)
}
want := &CommitStatus{ID: 1}
if !reflect.DeepEqual(want, status) {
t.Errorf("Commits.SetCommitStatus returned %+v, want %+v", status, want)
}
}

194
vendor/github.com/lkysow/go-gitlab/deploy_keys.go generated vendored Normal file
View File

@@ -0,0 +1,194 @@
//
// Copyright 2017, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
package gitlab
import (
"fmt"
"net/url"
"time"
)
// DeployKeysService handles communication with the keys related methods
// of the GitLab API.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/deploy_keys.html
type DeployKeysService struct {
client *Client
}
// DeployKey represents a GitLab deploy key.
type DeployKey struct {
ID int `json:"id"`
Title string `json:"title"`
Key string `json:"key"`
CanPush *bool `json:"can_push"`
CreatedAt *time.Time `json:"created_at"`
}
func (k DeployKey) String() string {
return Stringify(k)
}
// ListAllDeployKeys gets a list of all deploy keys
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/deploy_keys.html#list-all-deploy-keys
func (s *DeployKeysService) ListAllDeployKeys(options ...OptionFunc) ([]*DeployKey, *Response, error) {
req, err := s.client.NewRequest("GET", "deploy_keys", nil, options)
if err != nil {
return nil, nil, err
}
var ks []*DeployKey
resp, err := s.client.Do(req, &ks)
if err != nil {
return nil, resp, err
}
return ks, resp, err
}
// ListProjectDeployKeys gets a list of a project's deploy keys
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/deploy_keys.html#list-project-deploy-keys
func (s *DeployKeysService) ListProjectDeployKeys(pid interface{}, options ...OptionFunc) ([]*DeployKey, *Response, error) {
project, err := parseID(pid)
if err != nil {
return nil, nil, err
}
u := fmt.Sprintf("projects/%s/deploy_keys", url.QueryEscape(project))
req, err := s.client.NewRequest("GET", u, nil, options)
if err != nil {
return nil, nil, err
}
var ks []*DeployKey
resp, err := s.client.Do(req, &ks)
if err != nil {
return nil, resp, err
}
return ks, resp, err
}
// GetDeployKey gets a single deploy key.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/deploy_keys.html#single-deploy-key
func (s *DeployKeysService) GetDeployKey(pid interface{}, deployKey int, options ...OptionFunc) (*DeployKey, *Response, error) {
project, err := parseID(pid)
if err != nil {
return nil, nil, err
}
u := fmt.Sprintf("projects/%s/deploy_keys/%d", url.QueryEscape(project), deployKey)
req, err := s.client.NewRequest("GET", u, nil, options)
if err != nil {
return nil, nil, err
}
k := new(DeployKey)
resp, err := s.client.Do(req, k)
if err != nil {
return nil, resp, err
}
return k, resp, err
}
// AddDeployKeyOptions represents the available ADDDeployKey() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/deploy_keys.html#add-deploy-key
type AddDeployKeyOptions struct {
Title *string `url:"title,omitempty" json:"title,omitempty"`
Key *string `url:"key,omitempty" json:"key,omitempty"`
CanPush *bool `url:"can_push,omitempty" json:"can_push,omitempty"`
}
// AddDeployKey creates a new deploy key for a project. If deploy key already
// exists in another project - it will be joined to project but only if
// original one was is accessible by same user.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/deploy_keys.html#add-deploy-key
func (s *DeployKeysService) AddDeployKey(pid interface{}, opt *AddDeployKeyOptions, options ...OptionFunc) (*DeployKey, *Response, error) {
project, err := parseID(pid)
if err != nil {
return nil, nil, err
}
u := fmt.Sprintf("projects/%s/deploy_keys", url.QueryEscape(project))
req, err := s.client.NewRequest("POST", u, opt, options)
if err != nil {
return nil, nil, err
}
k := new(DeployKey)
resp, err := s.client.Do(req, k)
if err != nil {
return nil, resp, err
}
return k, resp, err
}
// DeleteDeployKey deletes a deploy key from a project.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/deploy_keys.html#delete-deploy-key
func (s *DeployKeysService) DeleteDeployKey(pid interface{}, deployKey int, options ...OptionFunc) (*Response, error) {
project, err := parseID(pid)
if err != nil {
return nil, err
}
u := fmt.Sprintf("projects/%s/deploy_keys/%d", url.QueryEscape(project), deployKey)
req, err := s.client.NewRequest("DELETE", u, nil, options)
if err != nil {
return nil, err
}
return s.client.Do(req, nil)
}
// EnableDeployKey enables a deploy key.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/deploy_keys.html#enable-deploy-key
func (s *DeployKeysService) EnableDeployKey(pid interface{}, deployKey int, options ...OptionFunc) (*DeployKey, *Response, error) {
project, err := parseID(pid)
if err != nil {
return nil, nil, err
}
u := fmt.Sprintf("projects/%s/deploy_keys/%d/enable", url.QueryEscape(project), deployKey)
req, err := s.client.NewRequest("POST", u, nil, options)
if err != nil {
return nil, nil, err
}
k := new(DeployKey)
resp, err := s.client.Do(req, k)
if err != nil {
return nil, resp, err
}
return k, resp, err
}

168
vendor/github.com/lkysow/go-gitlab/environments.go generated vendored Normal file
View File

@@ -0,0 +1,168 @@
//
// Copyright 2017, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
package gitlab
import (
"fmt"
"net/url"
)
// EnvironmentsService handles communication with the environment related methods
// of the GitLab API.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/environments.html
type EnvironmentsService struct {
client *Client
}
// Environment represents a GitLab environment.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/environments.html
type Environment struct {
ID int `json:"id"`
Name string `json:"name"`
Slug string `json:"slug"`
ExternalURL string `json:"external_url"`
}
func (env Environment) String() string {
return Stringify(env)
}
// ListEnvironmentsOptions represents the available ListEnvironments() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/environments.html#list-environments
type ListEnvironmentsOptions struct {
ListOptions
}
// ListEnvironments gets a list of environments from a project, sorted by name
// alphabetically.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/environments.html#list-environments
func (s *EnvironmentsService) ListEnvironments(pid interface{}, opts *ListEnvironmentsOptions, options ...OptionFunc) ([]*Environment, *Response, error) {
project, err := parseID(pid)
if err != nil {
return nil, nil, err
}
u := fmt.Sprintf("projects/%s/environments", url.QueryEscape(project))
req, err := s.client.NewRequest("GET", u, opts, options)
if err != nil {
return nil, nil, err
}
var envs []*Environment
resp, err := s.client.Do(req, &envs)
if err != nil {
return nil, resp, err
}
return envs, resp, err
}
// CreateEnvironmentOptions represents the available CreateEnvironment() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/environments.html#create-a-new-environment
type CreateEnvironmentOptions struct {
Name *string `url:"name,omitempty" json:"name,omitempty"`
ExternalURL *string `url:"external_url,omitempty" json:"external_url,omitempty"`
}
// CreateEnvironment adds a environment to a project. This is an idempotent
// method and can be called multiple times with the same parameters. Createing
// an environment that is already a environment does not affect the
// existing environmentship.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/environments.html#create-a-new-environment
func (s *EnvironmentsService) CreateEnvironment(pid interface{}, opt *CreateEnvironmentOptions, options ...OptionFunc) (*Environment, *Response, error) {
project, err := parseID(pid)
if err != nil {
return nil, nil, err
}
u := fmt.Sprintf("projects/%s/environments", url.QueryEscape(project))
req, err := s.client.NewRequest("POST", u, opt, options)
if err != nil {
return nil, nil, err
}
env := new(Environment)
resp, err := s.client.Do(req, env)
if err != nil {
return nil, resp, err
}
return env, resp, err
}
// EditEnvironmentOptions represents the available EditEnvironment() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/environments.html#edit-an-existing-environment
type EditEnvironmentOptions struct {
Name *string `url:"name,omitempty" json:"name,omitempty"`
ExternalURL *string `url:"external_url,omitempty" json:"external_url,omitempty"`
}
// EditEnvironment updates a project team environment to a specified access level..
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/environments.html#edit-an-existing-environment
func (s *EnvironmentsService) EditEnvironment(pid interface{}, environment int, opt *EditEnvironmentOptions, options ...OptionFunc) (*Environment, *Response, error) {
project, err := parseID(pid)
if err != nil {
return nil, nil, err
}
u := fmt.Sprintf("projects/%s/environments/%d", url.QueryEscape(project), environment)
req, err := s.client.NewRequest("PUT", u, opt, options)
if err != nil {
return nil, nil, err
}
env := new(Environment)
resp, err := s.client.Do(req, env)
if err != nil {
return nil, resp, err
}
return env, resp, err
}
// DeleteEnvironment removes a environment from a project team.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/environments.html#remove-a-environment-from-a-group-or-project
func (s *EnvironmentsService) DeleteEnvironment(pid interface{}, environment int, options ...OptionFunc) (*Response, error) {
project, err := parseID(pid)
if err != nil {
return nil, err
}
u := fmt.Sprintf("projects/%s/environments/%d", url.QueryEscape(project), environment)
req, err := s.client.NewRequest("DELETE", u, nil, options)
if err != nil {
return nil, err
}
return s.client.Do(req, nil)
}

613
vendor/github.com/lkysow/go-gitlab/events.go generated vendored Normal file
View File

@@ -0,0 +1,613 @@
//
// Copyright 2017, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
package gitlab
import "time"
// PushEvent represents a push event.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/web_hooks/web_hooks.html#push-events
type PushEvent struct {
ObjectKind string `json:"object_kind"`
Before string `json:"before"`
After string `json:"after"`
Ref string `json:"ref"`
CheckoutSha string `json:"checkout_sha"`
UserID int `json:"user_id"`
UserName string `json:"user_name"`
UserEmail string `json:"user_email"`
UserAvatar string `json:"user_avatar"`
ProjectID int `json:"project_id"`
Project struct {
Name string `json:"name"`
Description string `json:"description"`
AvatarURL string `json:"avatar_url"`
GitSSHURL string `json:"git_ssh_url"`
GitHTTPURL string `json:"git_http_url"`
Namespace string `json:"namespace"`
PathWithNamespace string `json:"path_with_namespace"`
DefaultBranch string `json:"default_branch"`
Homepage string `json:"homepage"`
URL string `json:"url"`
SSHURL string `json:"ssh_url"`
HTTPURL string `json:"http_url"`
WebURL string `json:"web_url"`
Visibility VisibilityValue `json:"visibility"`
} `json:"project"`
Repository *Repository `json:"repository"`
Commits []*Commit `json:"commits"`
TotalCommitsCount int `json:"total_commits_count"`
}
// TagEvent represents a tag event.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/web_hooks/web_hooks.html#tag-events
type TagEvent struct {
ObjectKind string `json:"object_kind"`
Before string `json:"before"`
After string `json:"after"`
Ref string `json:"ref"`
CheckoutSha string `json:"checkout_sha"`
UserID int `json:"user_id"`
UserName string `json:"user_name"`
UserAvatar string `json:"user_avatar"`
ProjectID int `json:"project_id"`
Project struct {
Name string `json:"name"`
Description string `json:"description"`
AvatarURL string `json:"avatar_url"`
GitSSHURL string `json:"git_ssh_url"`
GitHTTPURL string `json:"git_http_url"`
Namespace string `json:"namespace"`
PathWithNamespace string `json:"path_with_namespace"`
DefaultBranch string `json:"default_branch"`
Homepage string `json:"homepage"`
URL string `json:"url"`
SSHURL string `json:"ssh_url"`
HTTPURL string `json:"http_url"`
WebURL string `json:"web_url"`
Visibility VisibilityValue `json:"visibility"`
} `json:"project"`
Repository *Repository `json:"repository"`
Commits []*Commit `json:"commits"`
TotalCommitsCount int `json:"total_commits_count"`
}
// IssueEvent represents a issue event.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/web_hooks/web_hooks.html#issues-events
type IssueEvent struct {
ObjectKind string `json:"object_kind"`
User *User `json:"user"`
Project struct {
Name string `json:"name"`
Description string `json:"description"`
AvatarURL string `json:"avatar_url"`
GitSSHURL string `json:"git_ssh_url"`
GitHTTPURL string `json:"git_http_url"`
Namespace string `json:"namespace"`
PathWithNamespace string `json:"path_with_namespace"`
DefaultBranch string `json:"default_branch"`
Homepage string `json:"homepage"`
URL string `json:"url"`
SSHURL string `json:"ssh_url"`
HTTPURL string `json:"http_url"`
WebURL string `json:"web_url"`
Visibility VisibilityValue `json:"visibility"`
} `json:"project"`
Repository *Repository `json:"repository"`
ObjectAttributes struct {
ID int `json:"id"`
Title string `json:"title"`
AssigneeID int `json:"assignee_id"`
AuthorID int `json:"author_id"`
ProjectID int `json:"project_id"`
CreatedAt string `json:"created_at"` // Should be *time.Time (see Gitlab issue #21468)
UpdatedAt string `json:"updated_at"` // Should be *time.Time (see Gitlab issue #21468)
Position int `json:"position"`
BranchName string `json:"branch_name"`
Description string `json:"description"`
MilestoneID int `json:"milestone_id"`
State string `json:"state"`
IID int `json:"iid"`
URL string `json:"url"`
Action string `json:"action"`
} `json:"object_attributes"`
Assignee struct {
Name string `json:"name"`
Username string `json:"username"`
AvatarURL string `json:"avatar_url"`
} `json:"assignee"`
}
// CommitCommentEvent represents a comment on a commit event.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/web_hooks/web_hooks.html#comment-on-commit
type CommitCommentEvent struct {
ObjectKind string `json:"object_kind"`
User *User `json:"user"`
ProjectID int `json:"project_id"`
Project struct {
Name string `json:"name"`
Description string `json:"description"`
AvatarURL string `json:"avatar_url"`
GitSSHURL string `json:"git_ssh_url"`
GitHTTPURL string `json:"git_http_url"`
Namespace string `json:"namespace"`
PathWithNamespace string `json:"path_with_namespace"`
DefaultBranch string `json:"default_branch"`
Homepage string `json:"homepage"`
URL string `json:"url"`
SSHURL string `json:"ssh_url"`
HTTPURL string `json:"http_url"`
WebURL string `json:"web_url"`
Visibility VisibilityValue `json:"visibility"`
} `json:"project"`
Repository *Repository `json:"repository"`
ObjectAttributes struct {
ID int `json:"id"`
Note string `json:"note"`
NoteableType string `json:"noteable_type"`
AuthorID int `json:"author_id"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
ProjectID int `json:"project_id"`
Attachment string `json:"attachment"`
LineCode string `json:"line_code"`
CommitID string `json:"commit_id"`
NoteableID int `json:"noteable_id"`
System bool `json:"system"`
StDiff struct {
Diff string `json:"diff"`
NewPath string `json:"new_path"`
OldPath string `json:"old_path"`
AMode string `json:"a_mode"`
BMode string `json:"b_mode"`
NewFile bool `json:"new_file"`
RenamedFile bool `json:"renamed_file"`
DeletedFile bool `json:"deleted_file"`
} `json:"st_diff"`
} `json:"object_attributes"`
Commit *Commit `json:"commit"`
}
// MergeCommentEvent represents a comment on a merge event.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/web_hooks/web_hooks.html#comment-on-merge-request
type MergeCommentEvent struct {
ObjectKind string `json:"object_kind"`
User *User `json:"user"`
ProjectID int `json:"project_id"`
Project struct {
Name string `json:"name"`
Description string `json:"description"`
AvatarURL string `json:"avatar_url"`
GitSSHURL string `json:"git_ssh_url"`
GitHTTPURL string `json:"git_http_url"`
Namespace string `json:"namespace"`
PathWithNamespace string `json:"path_with_namespace"`
DefaultBranch string `json:"default_branch"`
Homepage string `json:"homepage"`
URL string `json:"url"`
SSHURL string `json:"ssh_url"`
HTTPURL string `json:"http_url"`
WebURL string `json:"web_url"`
Visibility VisibilityValue `json:"visibility"`
} `json:"project"`
Repository *Repository `json:"repository"`
ObjectAttributes struct {
ID int `json:"id"`
Note string `json:"note"`
NoteableType string `json:"noteable_type"`
AuthorID int `json:"author_id"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
ProjectID int `json:"project_id"`
Attachment string `json:"attachment"`
LineCode string `json:"line_code"`
CommitID string `json:"commit_id"`
NoteableID int `json:"noteable_id"`
System bool `json:"system"`
StDiff *Diff `json:"st_diff"`
URL string `json:"url"`
} `json:"object_attributes"`
MergeRequest struct {
ID int `json:"id"`
TargetBranch string `json:"target_branch"`
SourceBranch string `json:"source_branch"`
SourceProjectID int `json:"source_project_id"`
AuthorID int `json:"author_id"`
AssigneeID int `json:"assignee_id"`
Title string `json:"title"`
CreatedAt string `json:"created_at"` // Should be *time.Time (see Gitlab issue #21468)
UpdatedAt string `json:"updated_at"` // Should be *time.Time (see Gitlab issue #21468)
StCommits []*Commit `json:"st_commits"`
StDiffs []*Diff `json:"st_diffs"`
MilestoneID int `json:"milestone_id"`
State string `json:"state"`
MergeStatus string `json:"merge_status"`
TargetProjectID int `json:"target_project_id"`
IID int `json:"iid"`
Description string `json:"description"`
Position int `json:"position"`
LockedAt string `json:"locked_at"`
UpdatedByID int `json:"updated_by_id"`
MergeError string `json:"merge_error"`
MergeParams struct {
ForceRemoveSourceBranch string `json:"force_remove_source_branch"`
} `json:"merge_params"`
MergeWhenBuildSucceeds bool `json:"merge_when_build_succeeds"`
MergeUserID int `json:"merge_user_id"`
MergeCommitSha string `json:"merge_commit_sha"`
DeletedAt string `json:"deleted_at"`
ApprovalsBeforeMerge string `json:"approvals_before_merge"`
RebaseCommitSha string `json:"rebase_commit_sha"`
InProgressMergeCommitSha string `json:"in_progress_merge_commit_sha"`
LockVersion int `json:"lock_version"`
TimeEstimate int `json:"time_estimate"`
Source *Repository `json:"source"`
Target *Repository `json:"target"`
LastCommit struct {
ID string `json:"id"`
Message string `json:"message"`
Timestamp *time.Time `json:"timestamp"`
URL string `json:"url"`
Author *Author `json:"author"`
} `json:"last_commit"`
WorkInProgress bool `json:"work_in_progress"`
URL string `json:"url"`
Action string `json:"action"`
Assignee struct {
Name string `json:"name"`
Username string `json:"username"`
AvatarURL string `json:"avatar_url"`
} `json:"assignee"`
} `json:"merge_request"`
}
// IssueCommentEvent represents a comment on an issue event.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/web_hooks/web_hooks.html#comment-on-issue
type IssueCommentEvent struct {
ObjectKind string `json:"object_kind"`
User *User `json:"user"`
ProjectID int `json:"project_id"`
Project struct {
Name string `json:"name"`
Description string `json:"description"`
AvatarURL string `json:"avatar_url"`
GitSSHURL string `json:"git_ssh_url"`
GitHTTPURL string `json:"git_http_url"`
Namespace string `json:"namespace"`
PathWithNamespace string `json:"path_with_namespace"`
DefaultBranch string `json:"default_branch"`
Homepage string `json:"homepage"`
URL string `json:"url"`
SSHURL string `json:"ssh_url"`
HTTPURL string `json:"http_url"`
WebURL string `json:"web_url"`
Visibility VisibilityValue `json:"visibility"`
} `json:"project"`
Repository *Repository `json:"repository"`
ObjectAttributes struct {
ID int `json:"id"`
Note string `json:"note"`
NoteableType string `json:"noteable_type"`
AuthorID int `json:"author_id"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
ProjectID int `json:"project_id"`
Attachment string `json:"attachment"`
LineCode string `json:"line_code"`
CommitID string `json:"commit_id"`
NoteableID int `json:"noteable_id"`
System bool `json:"system"`
StDiff []*Diff `json:"st_diff"`
URL string `json:"url"`
} `json:"object_attributes"`
Issue *Issue `json:"issue"`
}
// SnippetCommentEvent represents a comment on a snippet event.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/web_hooks/web_hooks.html#comment-on-code-snippet
type SnippetCommentEvent struct {
ObjectKind string `json:"object_kind"`
User *User `json:"user"`
ProjectID int `json:"project_id"`
Project struct {
Name string `json:"name"`
Description string `json:"description"`
AvatarURL string `json:"avatar_url"`
GitSSHURL string `json:"git_ssh_url"`
GitHTTPURL string `json:"git_http_url"`
Namespace string `json:"namespace"`
PathWithNamespace string `json:"path_with_namespace"`
DefaultBranch string `json:"default_branch"`
Homepage string `json:"homepage"`
URL string `json:"url"`
SSHURL string `json:"ssh_url"`
HTTPURL string `json:"http_url"`
WebURL string `json:"web_url"`
Visibility VisibilityValue `json:"visibility"`
} `json:"project"`
Repository *Repository `json:"repository"`
ObjectAttributes struct {
ID int `json:"id"`
Note string `json:"note"`
NoteableType string `json:"noteable_type"`
AuthorID int `json:"author_id"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
ProjectID int `json:"project_id"`
Attachment string `json:"attachment"`
LineCode string `json:"line_code"`
CommitID string `json:"commit_id"`
NoteableID int `json:"noteable_id"`
System bool `json:"system"`
StDiff *Diff `json:"st_diff"`
URL string `json:"url"`
} `json:"object_attributes"`
Snippet *Snippet `json:"snippet"`
}
// MergeEvent represents a merge event.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/web_hooks/web_hooks.html#merge-request-events
type MergeEvent struct {
ObjectKind string `json:"object_kind"`
User *User `json:"user"`
Project struct {
Name string `json:"name"`
Description string `json:"description"`
AvatarURL string `json:"avatar_url"`
GitSSHURL string `json:"git_ssh_url"`
GitHTTPURL string `json:"git_http_url"`
Namespace string `json:"namespace"`
PathWithNamespace string `json:"path_with_namespace"`
DefaultBranch string `json:"default_branch"`
Homepage string `json:"homepage"`
URL string `json:"url"`
SSHURL string `json:"ssh_url"`
HTTPURL string `json:"http_url"`
WebURL string `json:"web_url"`
Visibility VisibilityValue `json:"visibility"`
} `json:"project"`
ObjectAttributes struct {
ID int `json:"id"`
TargetBranch string `json:"target_branch"`
SourceBranch string `json:"source_branch"`
SourceProjectID int `json:"source_project_id"`
AuthorID int `json:"author_id"`
AssigneeID int `json:"assignee_id"`
Title string `json:"title"`
CreatedAt string `json:"created_at"` // Should be *time.Time (see Gitlab issue #21468)
UpdatedAt string `json:"updated_at"` // Should be *time.Time (see Gitlab issue #21468)
StCommits []*Commit `json:"st_commits"`
StDiffs []*Diff `json:"st_diffs"`
MilestoneID int `json:"milestone_id"`
State string `json:"state"`
MergeStatus string `json:"merge_status"`
TargetProjectID int `json:"target_project_id"`
IID int `json:"iid"`
Description string `json:"description"`
Position int `json:"position"`
LockedAt string `json:"locked_at"`
UpdatedByID int `json:"updated_by_id"`
MergeError string `json:"merge_error"`
MergeParams struct {
ForceRemoveSourceBranch string `json:"force_remove_source_branch"`
} `json:"merge_params"`
MergeWhenBuildSucceeds bool `json:"merge_when_build_succeeds"`
MergeUserID int `json:"merge_user_id"`
MergeCommitSha string `json:"merge_commit_sha"`
DeletedAt string `json:"deleted_at"`
ApprovalsBeforeMerge string `json:"approvals_before_merge"`
RebaseCommitSha string `json:"rebase_commit_sha"`
InProgressMergeCommitSha string `json:"in_progress_merge_commit_sha"`
LockVersion int `json:"lock_version"`
TimeEstimate int `json:"time_estimate"`
Source *Repository `json:"source"`
Target *Repository `json:"target"`
LastCommit struct {
ID string `json:"id"`
Message string `json:"message"`
Timestamp *time.Time `json:"timestamp"`
URL string `json:"url"`
Author *Author `json:"author"`
} `json:"last_commit"`
WorkInProgress bool `json:"work_in_progress"`
URL string `json:"url"`
Action string `json:"action"`
Assignee struct {
Name string `json:"name"`
Username string `json:"username"`
AvatarURL string `json:"avatar_url"`
} `json:"assignee"`
} `json:"object_attributes"`
Repository *Repository `json:"repository"`
Assignee struct {
Name string `json:"name"`
Username string `json:"username"`
AvatarURL string `json:"avatar_url"`
} `json:"assignee"`
}
// WikiPageEvent represents a wiki page event.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/web_hooks/web_hooks.html#wiki-page-events
type WikiPageEvent struct {
ObjectKind string `json:"object_kind"`
User *User `json:"user"`
Project struct {
Name string `json:"name"`
Description string `json:"description"`
AvatarURL string `json:"avatar_url"`
GitSSHURL string `json:"git_ssh_url"`
GitHTTPURL string `json:"git_http_url"`
Namespace string `json:"namespace"`
PathWithNamespace string `json:"path_with_namespace"`
DefaultBranch string `json:"default_branch"`
Homepage string `json:"homepage"`
URL string `json:"url"`
SSHURL string `json:"ssh_url"`
HTTPURL string `json:"http_url"`
WebURL string `json:"web_url"`
Visibility VisibilityValue `json:"visibility"`
} `json:"project"`
Wiki struct {
WebURL string `json:"web_url"`
GitSSHURL string `json:"git_ssh_url"`
GitHTTPURL string `json:"git_http_url"`
PathWithNamespace string `json:"path_with_namespace"`
DefaultBranch string `json:"default_branch"`
} `json:"wiki"`
ObjectAttributes struct {
Title string `json:"title"`
Content string `json:"content"`
Format string `json:"format"`
Message string `json:"message"`
Slug string `json:"slug"`
URL string `json:"url"`
Action string `json:"action"`
} `json:"object_attributes"`
}
// PipelineEvent represents a pipeline event.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/web_hooks/web_hooks.html#pipeline-events
type PipelineEvent struct {
ObjectKind string `json:"object_kind"`
ObjectAttributes struct {
ID int `json:"id"`
Ref string `json:"ref"`
Tag bool `json:"tag"`
Sha string `json:"sha"`
BeforeSha string `json:"before_sha"`
Status string `json:"status"`
Stages []string `json:"stages"`
CreatedAt string `json:"created_at"`
FinishedAt string `json:"finished_at"`
Duration int `json:"duration"`
} `json:"object_attributes"`
User struct {
Name string `json:"name"`
Username string `json:"username"`
AvatarURL string `json:"avatar_url"`
} `json:"user"`
Project struct {
Name string `json:"name"`
Description string `json:"description"`
AvatarURL string `json:"avatar_url"`
GitSSHURL string `json:"git_ssh_url"`
GitHTTPURL string `json:"git_http_url"`
Namespace string `json:"namespace"`
PathWithNamespace string `json:"path_with_namespace"`
DefaultBranch string `json:"default_branch"`
Homepage string `json:"homepage"`
URL string `json:"url"`
SSHURL string `json:"ssh_url"`
HTTPURL string `json:"http_url"`
WebURL string `json:"web_url"`
Visibility VisibilityValue `json:"visibility"`
} `json:"project"`
Commit struct {
ID string `json:"id"`
Message string `json:"message"`
Timestamp time.Time `json:"timestamp"`
URL string `json:"url"`
Author struct {
Name string `json:"name"`
Email string `json:"email"`
} `json:"author"`
} `json:"commit"`
Builds []struct {
ID int `json:"id"`
Stage string `json:"stage"`
Name string `json:"name"`
Status string `json:"status"`
CreatedAt string `json:"created_at"`
StartedAt string `json:"started_at"`
FinishedAt string `json:"finished_at"`
When string `json:"when"`
Manual bool `json:"manual"`
User struct {
Name string `json:"name"`
Username string `json:"username"`
AvatarURL string `json:"avatar_url"`
} `json:"user"`
Runner struct {
ID int `json:"id"`
Description string `json:"description"`
Active bool `json:"active"`
IsShared bool `json:"is_shared"`
} `json:"runner"`
ArtifactsFile struct {
Filename string `json:"filename"`
Size int `json:"size"`
} `json:"artifacts_file"`
} `json:"builds"`
}
//BuildEvent represents a build event
//
// GitLab API docs:
// https://docs.gitlab.com/ce/web_hooks/web_hooks.html#build-events
type BuildEvent struct {
ObjectKind string `json:"object_kind"`
Ref string `json:"ref"`
Tag bool `json:"tag"`
BeforeSha string `json:"before_sha"`
Sha string `json:"sha"`
BuildID int `json:"build_id"`
BuildName string `json:"build_name"`
BuildStage string `json:"build_stage"`
BuildStatus string `json:"build_status"`
BuildStartedAt string `json:"build_started_at"`
BuildFinishedAt string `json:"build_finished_at"`
BuildDuration float64 `json:"build_duration"`
BuildAllowFailure bool `json:"build_allow_failure"`
ProjectID int `json:"project_id"`
ProjectName string `json:"project_name"`
User struct {
ID int `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
} `json:"user"`
Commit struct {
ID int `json:"id"`
Sha string `json:"sha"`
Message string `json:"message"`
AuthorName string `json:"author_name"`
AuthorEmail string `json:"author_email"`
Status string `json:"status"`
Duration string `json:"duration"`
StartedAt string `json:"started_at"`
FinishedAt string `json:"finished_at"`
} `json:"commit"`
Repository *Repository `json:"repository"`
}

598
vendor/github.com/lkysow/go-gitlab/events_test.go generated vendored Normal file
View File

@@ -0,0 +1,598 @@
package gitlab
import (
"encoding/json"
"testing"
)
func TestPushEventUnmarshal(t *testing.T) {
jsonObject := `{
"object_kind": "push",
"before": "95790bf891e76fee5e1747ab589903a6a1f80f22",
"after": "da1560886d4f094c3e6c9ef40349f7d38b5d27d7",
"ref": "refs/heads/master",
"checkout_sha": "da1560886d4f094c3e6c9ef40349f7d38b5d27d7",
"user_id": 4,
"user_name": "John Smith",
"user_email": "john@example.com",
"user_avatar": "https://s.gravatar.com/avatar/d4c74594d841139328695756648b6bd6?s=8://s.gravatar.com/avatar/d4c74594d841139328695756648b6bd6?s=80",
"project_id": 15,
"project":{
"name":"Diaspora",
"description":"",
"web_url":"http://example.com/mike/diaspora",
"avatar_url":null,
"git_ssh_url":"git@example.com:mike/diaspora.git",
"git_http_url":"http://example.com/mike/diaspora.git",
"namespace":"Mike",
"visibility":"public",
"path_with_namespace":"mike/diaspora",
"default_branch":"master",
"homepage":"http://example.com/mike/diaspora",
"url":"git@example.com:mike/diaspora.git",
"ssh_url":"git@example.com:mike/diaspora.git",
"http_url":"http://example.com/mike/diaspora.git"
},
"repository":{
"name": "Diaspora",
"url": "git@example.com:mike/diaspora.git",
"description": "",
"homepage": "http://example.com/mike/diaspora",
"git_http_url":"http://example.com/mike/diaspora.git",
"git_ssh_url":"git@example.com:mike/diaspora.git",
"visibility":"public"
},
"commits": [
{
"id": "b6568db1bc1dcd7f8b4d5a946b0b91f9dacd7327",
"message": "Update Catalan translation to e38cb41.",
"timestamp": "2011-12-12T14:27:31+02:00",
"url": "http://example.com/mike/diaspora/commit/b6568db1bc1dcd7f8b4d5a946b0b91f9dacd7327",
"author": {
"name": "Jordi Mallach",
"email": "jordi@softcatala.org"
},
"added": ["CHANGELOG"],
"modified": ["app/controller/application.rb"],
"removed": []
},
{
"id": "da1560886d4f094c3e6c9ef40349f7d38b5d27d7",
"message": "fixed readme",
"timestamp": "2012-01-03T23:36:29+02:00",
"url": "http://example.com/mike/diaspora/commit/da1560886d4f094c3e6c9ef40349f7d38b5d27d7",
"author": {
"name": "GitLab dev user",
"email": "gitlabdev@dv6700.(none)"
},
"added": ["CHANGELOG"],
"modified": ["app/controller/application.rb"],
"removed": []
}
],
"total_commits_count": 4
}`
var event *PushEvent
err := json.Unmarshal([]byte(jsonObject), &event)
if err != nil {
t.Errorf("Push Event can not unmarshaled: %v\n ", err.Error())
}
if event == nil {
t.Errorf("Push Event is null")
}
if event.ProjectID != 15 {
t.Errorf("ProjectID is %v, want %v", event.ProjectID, 15)
}
}
func TestMergeEventUnmarshal(t *testing.T) {
jsonObject := `{
"object_kind": "merge_request",
"user": {
"name": "Administrator",
"username": "root",
"avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=40\u0026d=identicon"
},
"object_attributes": {
"id": 99,
"target_branch": "master",
"source_branch": "ms-viewport",
"source_project_id": 14,
"author_id": 51,
"assignee_id": 6,
"title": "MS-Viewport",
"created_at": "2013-12-03T17:23:34Z",
"updated_at": "2013-12-03T17:23:34Z",
"st_commits": null,
"st_diffs": null,
"milestone_id": null,
"state": "opened",
"merge_status": "unchecked",
"target_project_id": 14,
"iid": 1,
"description": "",
"source":{
"name":"Awesome Project",
"description":"Aut reprehenderit ut est.",
"web_url":"http://example.com/awesome_space/awesome_project",
"avatar_url":null,
"git_ssh_url":"git@example.com:awesome_space/awesome_project.git",
"git_http_url":"http://example.com/awesome_space/awesome_project.git",
"namespace":"Awesome Space",
"visibility":"private",
"path_with_namespace":"awesome_space/awesome_project",
"default_branch":"master",
"homepage":"http://example.com/awesome_space/awesome_project",
"url":"http://example.com/awesome_space/awesome_project.git",
"ssh_url":"git@example.com:awesome_space/awesome_project.git",
"http_url":"http://example.com/awesome_space/awesome_project.git"
},
"target": {
"name":"Awesome Project",
"description":"Aut reprehenderit ut est.",
"web_url":"http://example.com/awesome_space/awesome_project",
"avatar_url":null,
"git_ssh_url":"git@example.com:awesome_space/awesome_project.git",
"git_http_url":"http://example.com/awesome_space/awesome_project.git",
"namespace":"Awesome Space",
"visibility":"private",
"path_with_namespace":"awesome_space/awesome_project",
"default_branch":"master",
"homepage":"http://example.com/awesome_space/awesome_project",
"url":"http://example.com/awesome_space/awesome_project.git",
"ssh_url":"git@example.com:awesome_space/awesome_project.git",
"http_url":"http://example.com/awesome_space/awesome_project.git"
},
"last_commit": {
"id": "da1560886d4f094c3e6c9ef40349f7d38b5d27d7",
"message": "fixed readme",
"timestamp": "2012-01-03T23:36:29+02:00",
"url": "http://example.com/awesome_space/awesome_project/commits/da1560886d4f094c3e6c9ef40349f7d38b5d27d7",
"author": {
"name": "GitLab dev user",
"email": "gitlabdev@dv6700.(none)"
}
},
"work_in_progress": false,
"url": "http://example.com/diaspora/merge_requests/1",
"action": "open",
"assignee": {
"name": "User1",
"username": "user1",
"avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=40\u0026d=identicon"
}
}
}`
var event *MergeEvent
err := json.Unmarshal([]byte(jsonObject), &event)
if err != nil {
t.Errorf("Merge Event can not unmarshaled: %v\n ", err.Error())
}
if event == nil {
t.Errorf("Merge Event is null")
}
if event.ObjectAttributes.ID != 99 {
t.Errorf("ObjectAttributes.ID is %v, want %v", event.ObjectAttributes.ID, 99)
}
if event.ObjectAttributes.Source.Homepage != "http://example.com/awesome_space/awesome_project" {
t.Errorf("ObjectAttributes.Source.Homepage is %v, want %v", event.ObjectAttributes.Source.Homepage, "http://example.com/awesome_space/awesome_project")
}
if event.ObjectAttributes.LastCommit.ID != "da1560886d4f094c3e6c9ef40349f7d38b5d27d7" {
t.Errorf("ObjectAttributes.LastCommit.ID is %v, want %s", event.ObjectAttributes.LastCommit.ID, "da1560886d4f094c3e6c9ef40349f7d38b5d27d7")
}
if event.ObjectAttributes.Assignee.Name != "User1" {
t.Errorf("Assignee.Name is %v, want %v", event.ObjectAttributes.ID, "User1")
}
if event.ObjectAttributes.Assignee.Username != "user1" {
t.Errorf("ObjectAttributes is %v, want %v", event.ObjectAttributes.Assignee.Username, "user1")
}
}
func TestPipelineEventUnmarshal(t *testing.T) {
jsonObject := `{
"object_kind": "pipeline",
"object_attributes":{
"id": 31,
"ref": "master",
"tag": false,
"sha": "bcbb5ec396a2c0f828686f14fac9b80b780504f2",
"before_sha": "bcbb5ec396a2c0f828686f14fac9b80b780504f2",
"status": "success",
"stages":[
"build",
"test",
"deploy"
],
"created_at": "2016-08-12 15:23:28 UTC",
"finished_at": "2016-08-12 15:26:29 UTC",
"duration": 63
},
"user":{
"name": "Administrator",
"username": "root",
"avatar_url": "http://www.gravatar.com/avatar/e32bd13e2add097461cb96824b7a829c?s=80\u0026d=identicon"
},
"project":{
"name": "Gitlab Test",
"description": "Atque in sunt eos similique dolores voluptatem.",
"web_url": "http://192.168.64.1:3005/gitlab-org/gitlab-test",
"avatar_url": null,
"git_ssh_url": "git@192.168.64.1:gitlab-org/gitlab-test.git",
"git_http_url": "http://192.168.64.1:3005/gitlab-org/gitlab-test.git",
"namespace": "Gitlab Org",
"visibility": "private",
"path_with_namespace": "gitlab-org/gitlab-test",
"default_branch": "master"
},
"commit":{
"id": "bcbb5ec396a2c0f828686f14fac9b80b780504f2",
"message": "test\n",
"timestamp": "2016-08-12T17:23:21+02:00",
"url": "http://example.com/gitlab-org/gitlab-test/commit/bcbb5ec396a2c0f828686f14fac9b80b780504f2",
"author":{
"name": "User",
"email": "user@gitlab.com"
}
},
"builds":[
{
"id": 380,
"stage": "deploy",
"name": "production",
"status": "skipped",
"created_at": "2016-08-12 15:23:28 UTC",
"started_at": null,
"finished_at": null,
"when": "manual",
"manual": true,
"user":{
"name": "Administrator",
"username": "root",
"avatar_url": "http://www.gravatar.com/avatar/e32bd13e2add097461cb96824b7a829c?s=80\u0026d=identicon"
},
"runner": null,
"artifacts_file":{
"filename": null,
"size": null
}
},
{
"id": 377,
"stage": "test",
"name": "test-image",
"status": "success",
"created_at": "2016-08-12 15:23:28 UTC",
"started_at": "2016-08-12 15:26:12 UTC",
"finished_at": null,
"when": "on_success",
"manual": false,
"user":{
"name": "Administrator",
"username": "root",
"avatar_url": "http://www.gravatar.com/avatar/e32bd13e2add097461cb96824b7a829c?s=80\u0026d=identicon"
},
"runner": {
"id": 6,
"description": "Kubernetes Runner",
"active": true,
"is_shared": true
},
"artifacts_file":{
"filename": "artifacts.zip",
"size": 1319148
}
},
{
"id": 378,
"stage": "test",
"name": "test-build",
"status": "success",
"created_at": "2016-08-12 15:23:28 UTC",
"started_at": "2016-08-12 15:26:12 UTC",
"finished_at": "2016-08-12 15:26:29 UTC",
"when": "on_success",
"manual": false,
"user":{
"name": "Administrator",
"username": "root",
"avatar_url": "http://www.gravatar.com/avatar/e32bd13e2add097461cb96824b7a829c?s=80\u0026d=identicon"
},
"runner": null,
"artifacts_file":{
"filename": null,
"size": null
}
},
{
"id": 376,
"stage": "build",
"name": "build-image",
"status": "success",
"created_at": "2016-08-12 15:23:28 UTC",
"started_at": "2016-08-12 15:24:56 UTC",
"finished_at": "2016-08-12 15:25:26 UTC",
"when": "on_success",
"manual": false,
"user":{
"name": "Administrator",
"username": "root",
"avatar_url": "http://www.gravatar.com/avatar/e32bd13e2add097461cb96824b7a829c?s=80\u0026d=identicon"
},
"runner": null,
"artifacts_file":{
"filename": null,
"size": null
}
},
{
"id": 379,
"stage": "deploy",
"name": "staging",
"status": "created",
"created_at": "2016-08-12 15:23:28 UTC",
"started_at": null,
"finished_at": null,
"when": "on_success",
"manual": false,
"user":{
"name": "Administrator",
"username": "root",
"avatar_url": "http://www.gravatar.com/avatar/e32bd13e2add097461cb96824b7a829c?s=80\u0026d=identicon"
},
"runner": null,
"artifacts_file":{
"filename": null,
"size": null
}
}
]
}`
var event *PipelineEvent
err := json.Unmarshal([]byte(jsonObject), &event)
if err != nil {
t.Errorf("Pipeline Event can not unmarshaled: %v\n ", err.Error())
}
if event == nil {
t.Errorf("Pipeline Event is null")
}
if event.ObjectAttributes.ID != 31 {
t.Errorf("ObjectAttributes is %v, want %v", event.ObjectAttributes.ID, 1977)
}
}
func TestBuildEventUnmarshal(t *testing.T) {
jsonObject := `{
"object_kind": "build",
"ref": "gitlab-script-trigger",
"tag": false,
"before_sha": "2293ada6b400935a1378653304eaf6221e0fdb8f",
"sha": "2293ada6b400935a1378653304eaf6221e0fdb8f",
"build_id": 1977,
"build_name": "test",
"build_stage": "test",
"build_status": "created",
"build_started_at": null,
"build_finished_at": null,
"build_duration": 23.265997,
"build_allow_failure": false,
"project_id": 380,
"project_name": "gitlab-org/gitlab-test",
"user": {
"id": 3,
"name": "User",
"email": "user@gitlab.com"
},
"commit": {
"id": 2366,
"sha": "2293ada6b400935a1378653304eaf6221e0fdb8f",
"message": "test\n",
"author_name": "User",
"author_email": "user@gitlab.com",
"status": "created",
"duration": null,
"started_at": null,
"finished_at": null
},
"repository": {
"name": "gitlab_test",
"git_ssh_url": "git@192.168.64.1:gitlab-org/gitlab-test.git",
"description": "Atque in sunt eos similique dolores voluptatem.",
"homepage": "http://192.168.64.1:3005/gitlab-org/gitlab-test",
"git_ssh_url": "git@192.168.64.1:gitlab-org/gitlab-test.git",
"git_http_url": "http://192.168.64.1:3005/gitlab-org/gitlab-test.git",
"visibility": "private"
}
}`
var event *BuildEvent
err := json.Unmarshal([]byte(jsonObject), &event)
if err != nil {
t.Errorf("Build Event can not unmarshaled: %v\n ", err.Error())
}
if event == nil {
t.Errorf("Build Event is null")
}
if event.BuildID != 1977 {
t.Errorf("BuildID is %v, want %v", event.BuildID, 1977)
}
}
func TestMergeEventUnmarshalFromGroup(t *testing.T) {
jsonObject := `{
"object_kind": "merge_request",
"user": {
"name": "Administrator",
"username": "root",
"avatar_url": "http://www.gravatar.com/avatar/d22738dc40839e3d95fca77ca3eac067?s=80\u0026d=identicon"
},
"project": {
"name": "example-project",
"description": "",
"web_url": "http://example.com/exm-namespace/example-project",
"avatar_url": null,
"git_ssh_url": "git@example.com:exm-namespace/example-project.git",
"git_http_url": "http://example.com/exm-namespace/example-project.git",
"namespace": "exm-namespace",
"visibility": "public",
"path_with_namespace": "exm-namespace/example-project",
"default_branch": "master",
"homepage": "http://example.com/exm-namespace/example-project",
"url": "git@example.com:exm-namespace/example-project.git",
"ssh_url": "git@example.com:exm-namespace/example-project.git",
"http_url": "http://example.com/exm-namespace/example-project.git"
},
"object_attributes": {
"id": 15917,
"target_branch ": "master ",
"source_branch ": "source-branch-test ",
"source_project_id ": 87,
"author_id ": 15,
"assignee_id ": 29,
"title ": "source-branch-test ",
"created_at ": "2016 - 12 - 01 13: 11: 10 UTC ",
"updated_at ": "2016 - 12 - 01 13: 21: 20 UTC ",
"milestone_id ": null,
"state ": "merged ",
"merge_status ": "can_be_merged ",
"target_project_id ": 87,
"iid ": 1402,
"description ": "word doc support for e - ticket ",
"position ": 0,
"locked_at ": null,
"updated_by_id ": null,
"merge_error ": null,
"merge_params": {
"force_remove_source_branch": "0"
},
"merge_when_build_succeeds": false,
"merge_user_id": null,
"merge_commit_sha": "ac3ca1559bc39abf963586372eff7f8fdded646e",
"deleted_at": null,
"approvals_before_merge": null,
"rebase_commit_sha": null,
"in_progress_merge_commit_sha": null,
"lock_version": 0,
"time_estimate": 0,
"source": {
"name": "example-project",
"description": "",
"web_url": "http://example.com/exm-namespace/example-project",
"avatar_url": null,
"git_ssh_url": "git@example.com:exm-namespace/example-project.git",
"git_http_url": "http://example.com/exm-namespace/example-project.git",
"namespace": "exm-namespace",
"visibility": "public",
"path_with_namespace": "exm-namespace/example-project",
"default_branch": "master",
"homepage": "http://example.com/exm-namespace/example-project",
"url": "git@example.com:exm-namespace/example-project.git",
"ssh_url": "git@example.com:exm-namespace/example-project.git",
"http_url": "http://example.com/exm-namespace/example-project.git"
},
"target": {
"name": "example-project",
"description": "",
"web_url": "http://example.com/exm-namespace/example-project",
"avatar_url": null,
"git_ssh_url": "git@example.com:exm-namespace/example-project.git",
"git_http_url": "http://example.com/exm-namespace/example-project.git",
"namespace": "exm-namespace",
"visibility": "public",
"path_with_namespace": "exm-namespace/example-project",
"default_branch": "master",
"homepage": "http://example.com/exm-namespace/example-project",
"url": "git@example.com:exm-namespace/example-project.git",
"ssh_url": "git@example.com:exm-namespace/example-project.git",
"http_url": "http://example.com/exm-namespace/example-project.git"
},
"last_commit": {
"id": "61b6a0d35dbaf915760233b637622e383d3cc9ec",
"message": "commit message",
"timestamp": "2016-12-01T15:07:53+02:00",
"url": "http://example.com/exm-namespace/example-project/commit/61b6a0d35dbaf915760233b637622e383d3cc9ec",
"author": {
"name": "Test User",
"email": "test.user@mail.com"
}
},
"work_in_progress": false,
"url": "http://example.com/exm-namespace/example-project/merge_requests/1402",
"action": "merge"
},
"repository": {
"name": "example-project",
"url": "git@example.com:exm-namespace/example-project.git",
"description": "",
"homepage": "http://example.com/exm-namespace/example-project"
},
"assignee": {
"name": "Administrator",
"username": "root",
"avatar_url": "http://www.gravatar.com/avatar/d22738dc40839e3d95fca77ca3eac067?s=80\u0026d=identicon"
}
}`
var event *MergeEvent
err := json.Unmarshal([]byte(jsonObject), &event)
if err != nil {
t.Errorf("Group Merge Event can not unmarshaled: %v\n ", err.Error())
}
if event == nil {
t.Errorf("Group Merge Event is null")
}
if event.ObjectKind != "merge_request" {
t.Errorf("ObjectKind is %v, want %v", event.ObjectKind, "merge_request")
}
if event.User.Username != "root" {
t.Errorf("User.Username is %v, want %v", event.User.Username, "root")
}
if event.Project.Name != "example-project" {
t.Errorf("Project.Name is %v, want %v", event.Project.Name, "example-project")
}
if event.ObjectAttributes.ID != 15917 {
t.Errorf("ObjectAttributes.ID is %v, want %v", event.ObjectAttributes.ID, 15917)
}
if event.ObjectAttributes.Source.Name != "example-project" {
t.Errorf("ObjectAttributes.Source.Name is %v, want %v", event.ObjectAttributes.Source.Name, "example-project")
}
if event.ObjectAttributes.LastCommit.Author.Email != "test.user@mail.com" {
t.Errorf("ObjectAttributes.LastCommit.Author.Email is %v, want %v", event.ObjectAttributes.LastCommit.Author.Email, "test.user@mail.com")
}
if event.Repository.Name != "example-project" {
t.Errorf("Repository.Name is %v, want %v", event.Repository.Name, "example-project")
}
if event.Assignee.Username != "root" {
t.Errorf("Assignee.Username is %v, want %v", event.Assignee, "root")
}
}

View File

@@ -0,0 +1,37 @@
package main
import (
"log"
"github.com/xanzy/go-gitlab"
)
func impersonationExample() {
git := gitlab.NewClient(nil, "yourtokengoeshere")
uid := 1
//list impersonation token from an user
tokens, _, err := git.Users.GetAllImpersonationTokens(
uid,
&gitlab.GetAllImpersonationTokensOptions{State: gitlab.String("active")},
)
if err != nil {
panic(err)
}
for _, token := range tokens {
log.Println(token.Token)
}
//create an impersonation token of an user
token, _, err := git.Users.CreateImpersonationToken(
uid,
&gitlab.CreateImpersonationTokenOptions{Scopes: &[]string{"api"}},
)
if err != nil {
panic(err)
}
log.Println(token.Token)
}

32
vendor/github.com/lkysow/go-gitlab/examples/labels.go generated vendored Normal file
View File

@@ -0,0 +1,32 @@
package main
import (
"log"
"github.com/xanzy/go-gitlab"
)
func labelExample() {
git := gitlab.NewClient(nil, "yourtokengoeshere")
// Create new label
l := &gitlab.CreateLabelOptions{
Name: gitlab.String("My Label"),
Color: gitlab.String("#11FF22"),
}
label, _, err := git.Labels.CreateLabel("myname/myproject", l)
if err != nil {
log.Fatal(err)
}
log.Printf("Created label: %s\nWith color: %s\n", label.Name, label.Color)
// List all labels
labels, _, err := git.Labels.ListLabels("myname/myproject")
if err != nil {
log.Fatal(err)
}
for _, label := range labels {
log.Printf("Found label: %s", label.Name)
}
}

6
vendor/github.com/lkysow/go-gitlab/examples/main.go generated vendored Normal file
View File

@@ -0,0 +1,6 @@
package main
func main() {
// See the separate files in this directory for the examples. This file is only
// here to provide a main() function for the `example` package, keeping Travis happy.
}

View File

@@ -0,0 +1,21 @@
package main
import (
"log"
"github.com/xanzy/go-gitlab"
)
func pipelineExample() {
git := gitlab.NewClient(nil, "yourtokengoeshere")
git.SetBaseURL("https://gitlab.com/api/v4")
pipelines, _, err := git.Pipelines.ListProjectPipelines(2743054)
if err != nil {
log.Fatal(err)
}
for _, pipeline := range pipelines {
log.Printf("Found pipeline: %v", pipeline)
}
}

View File

@@ -0,0 +1,46 @@
package main
import (
"log"
"github.com/xanzy/go-gitlab"
)
func projectExample() {
git := gitlab.NewClient(nil, "yourtokengoeshere")
// Create new project
p := &gitlab.CreateProjectOptions{
Name: gitlab.String("My Project"),
Description: gitlab.String("Just a test project to play with"),
MergeRequestsEnabled: gitlab.Bool(true),
SnippetsEnabled: gitlab.Bool(true),
Visibility: gitlab.Visibility(gitlab.PublicVisibility),
}
project, _, err := git.Projects.CreateProject(p)
if err != nil {
log.Fatal(err)
}
// Add a new snippet
s := &gitlab.CreateSnippetOptions{
Title: gitlab.String("Dummy Snippet"),
FileName: gitlab.String("snippet.go"),
Code: gitlab.String("package main...."),
Visibility: gitlab.Visibility(gitlab.PublicVisibility),
}
_, _, err = git.ProjectSnippets.CreateSnippet(project.ID, s)
if err != nil {
log.Fatal(err)
}
// List all project snippets
snippets, _, err := git.ProjectSnippets.ListSnippets(project.PathWithNamespace, &gitlab.ListSnippetsOptions{})
if err != nil {
log.Fatal(err)
}
for _, snippet := range snippets {
log.Printf("Found snippet: %s", snippet.Title)
}
}

View File

@@ -0,0 +1,43 @@
package main
import (
"log"
"github.com/xanzy/go-gitlab"
)
func repositoryFileExample() {
git := gitlab.NewClient(nil, "yourtokengoeshere")
// Create a new repository file
cf := &gitlab.CreateFileOptions{
Branch: gitlab.String("master"),
Content: gitlab.String("My file contents"),
CommitMessage: gitlab.String("Adding a test file"),
}
file, _, err := git.RepositoryFiles.CreateFile("myname/myproject", "file.go", cf)
if err != nil {
log.Fatal(err)
}
// Update a repository file
uf := &gitlab.UpdateFileOptions{
Branch: gitlab.String("master"),
Content: gitlab.String("My file content"),
CommitMessage: gitlab.String("Fixing typo"),
}
_, _, err = git.RepositoryFiles.UpdateFile("myname/myproject", file.FilePath, uf)
if err != nil {
log.Fatal(err)
}
gf := &gitlab.GetFileOptions{
Ref: gitlab.String("master"),
}
f, _, err := git.RepositoryFiles.GetFile("myname/myproject", file.FilePath, gf)
if err != nil {
log.Fatal(err)
}
log.Printf("File contains: %s", f.Content)
}

79
vendor/github.com/lkysow/go-gitlab/feature_flags.go generated vendored Normal file
View File

@@ -0,0 +1,79 @@
package gitlab
import (
"fmt"
"net/url"
)
// FeaturesService handles the communication with the application FeaturesService
// related methods of the GitLab API.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/features.html
type FeaturesService struct {
client *Client
}
// Feature represents a GitLab feature flag.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/features.html
type Feature struct {
Name string `json:"name"`
State string `json:"state"`
Gates []Gate
}
// Gate represents a gate of a GitLab feature flag.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/features.html
type Gate struct {
Key string `json:"key"`
Value interface{} `json:"value"`
}
func (f Feature) String() string {
return Stringify(f)
}
// ListFeatures gets a list of feature flags
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/features.html#list-all-features
func (s *FeaturesService) ListFeatures(options ...OptionFunc) ([]*Feature, *Response, error) {
req, err := s.client.NewRequest("GET", "features", nil, options)
if err != nil {
return nil, nil, err
}
var f []*Feature
resp, err := s.client.Do(req, &f)
if err != nil {
return nil, resp, err
}
return f, resp, err
}
// SetFeatureFlag sets or creates a feature flag gate
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/features.html#set-or-create-a-feature
func (s *FeaturesService) SetFeatureFlag(name string, value interface{}, options ...OptionFunc) (*Feature, *Response, error) {
u := fmt.Sprintf("features/%s", url.QueryEscape(name))
opt := struct {
Value interface{} `url:"value" json:"value"`
}{
value,
}
req, err := s.client.NewRequest("POST", u, opt, options)
if err != nil {
return nil, nil, err
}
f := &Feature{}
resp, err := s.client.Do(req, f)
if err != nil {
return nil, resp, err
}
return f, resp, err
}

View File

@@ -0,0 +1,92 @@
package gitlab
import (
"fmt"
"net/http"
"reflect"
"testing"
)
func TestListFeatureFlags(t *testing.T) {
mux, server, client := setup()
defer teardown(server)
mux.HandleFunc("/features", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
fmt.Fprint(w, `
[
{
"name": "experimental_feature",
"state": "off",
"gates": [
{
"key": "boolean",
"value": false
}
]
},
{
"name": "new_library",
"state": "on"
}
]
`)
})
features, _, err := client.Features.ListFeatures()
if err != nil {
t.Errorf("Features.ListFeatures returned error: %v", err)
}
want := []*Feature{
{Name: "experimental_feature", State: "off", Gates: []Gate{
{Key: "boolean", Value: false},
}},
{Name: "new_library", State: "on"},
}
if !reflect.DeepEqual(want, features) {
t.Errorf("Features.ListFeatures returned %+v, want %+v", features, want)
}
}
func TestSetFeatureFlag(t *testing.T) {
mux, server, client := setup()
defer teardown(server)
mux.HandleFunc("/features/new_library", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "POST")
fmt.Fprint(w, `
{
"name": "new_library",
"state": "conditional",
"gates": [
{
"key": "boolean",
"value": false
},
{
"key": "percentage_of_time",
"value": 30
}
]
}
`)
})
feature, _, err := client.Features.SetFeatureFlag("new_library", "30")
if err != nil {
t.Errorf("Features.SetFeatureFlag returned error: %v", err)
}
want := &Feature{
Name: "new_library",
State: "conditional",
Gates: []Gate{
{Key: "boolean", Value: false},
{Key: "percentage_of_time", Value: 30.0},
},
}
if !reflect.DeepEqual(want, feature) {
t.Errorf("Features.SetFeatureFlag returned %+v, want %+v", feature, want)
}
}

620
vendor/github.com/lkysow/go-gitlab/gitlab.go generated vendored Normal file
View File

@@ -0,0 +1,620 @@
//
// Copyright 2017, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
package gitlab
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net/http"
"net/url"
"sort"
"strconv"
"strings"
"github.com/google/go-querystring/query"
)
const (
libraryVersion = "0.2.0"
defaultBaseURL = "https://gitlab.com/api/v4/"
userAgent = "go-gitlab/" + libraryVersion
)
// tokenType represents a token type within GitLab.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/
type tokenType int
// List of available token type
//
// GitLab API docs: https://docs.gitlab.com/ce/api/
const (
privateToken tokenType = iota
oAuthToken
)
// AccessLevelValue represents a permission level within GitLab.
//
// GitLab API docs: https://docs.gitlab.com/ce/permissions/permissions.html
type AccessLevelValue int
// List of available access levels
//
// GitLab API docs: https://docs.gitlab.com/ce/permissions/permissions.html
const (
GuestPermissions AccessLevelValue = 10
ReporterPermissions AccessLevelValue = 20
DeveloperPermissions AccessLevelValue = 30
MasterPermissions AccessLevelValue = 40
OwnerPermission AccessLevelValue = 50
)
// NotificationLevelValue represents a notification level.
type NotificationLevelValue int
// String implements the fmt.Stringer interface.
func (l NotificationLevelValue) String() string {
return notificationLevelNames[l]
}
// MarshalJSON implements the json.Marshaler interface.
func (l NotificationLevelValue) MarshalJSON() ([]byte, error) {
return json.Marshal(l.String())
}
// UnmarshalJSON implements the json.Unmarshaler interface.
func (l *NotificationLevelValue) UnmarshalJSON(data []byte) error {
var raw interface{}
if err := json.Unmarshal(data, &raw); err != nil {
return err
}
switch raw := raw.(type) {
case float64:
*l = NotificationLevelValue(raw)
case string:
*l = notificationLevelTypes[raw]
default:
return fmt.Errorf("json: cannot unmarshal %T into Go value of type %T", raw, *l)
}
return nil
}
// List of valid notification levels.
const (
DisabledNotificationLevel NotificationLevelValue = iota
ParticipatingNotificationLevel
WatchNotificationLevel
GlobalNotificationLevel
MentionNotificationLevel
CustomNotificationLevel
)
var notificationLevelNames = [...]string{
"disabled",
"participating",
"watch",
"global",
"mention",
"custom",
}
var notificationLevelTypes = map[string]NotificationLevelValue{
"disabled": DisabledNotificationLevel,
"participating": ParticipatingNotificationLevel,
"watch": WatchNotificationLevel,
"global": GlobalNotificationLevel,
"mention": MentionNotificationLevel,
"custom": CustomNotificationLevel,
}
// VisibilityValue represents a visibility level within GitLab.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/
type VisibilityValue string
// List of available visibility levels
//
// GitLab API docs: https://docs.gitlab.com/ce/api/
const (
PrivateVisibility VisibilityValue = "private"
InternalVisibility VisibilityValue = "internal"
PublicVisibility VisibilityValue = "public"
)
// A Client manages communication with the GitLab API.
type Client struct {
// HTTP client used to communicate with the API.
client *http.Client
// Base URL for API requests. Defaults to the public GitLab API, but can be
// set to a domain endpoint to use with aself hosted GitLab server. baseURL
// should always be specified with a trailing slash.
baseURL *url.URL
// token type used to make authenticated API calls.
tokenType tokenType
// token used to make authenticated API calls.
token string
// User agent used when communicating with the GitLab API.
UserAgent string
// Services used for talking to different parts of the GitLab API.
Branches *BranchesService
BuildVariables *BuildVariablesService
Commits *CommitsService
DeployKeys *DeployKeysService
Environments *EnvironmentsService
Features *FeaturesService
Groups *GroupsService
GroupMembers *GroupMembersService
Issues *IssuesService
Jobs *JobsService
Labels *LabelsService
MergeRequests *MergeRequestsService
Milestones *MilestonesService
Namespaces *NamespacesService
Notes *NotesService
NotificationSettings *NotificationSettingsService
Projects *ProjectsService
ProjectMembers *ProjectMembersService
ProjectSnippets *ProjectSnippetsService
Pipelines *PipelinesService
PipelineTriggers *PipelineTriggersService
Repositories *RepositoriesService
RepositoryFiles *RepositoryFilesService
Services *ServicesService
Session *SessionService
Settings *SettingsService
SystemHooks *SystemHooksService
Tags *TagsService
Todos *TodosService
Users *UsersService
Version *VersionService
Wikis *WikisService
}
// ListOptions specifies the optional parameters to various List methods that
// support pagination.
type ListOptions struct {
// For paginated result sets, page of results to retrieve.
Page int `url:"page,omitempty" json:"page,omitempty"`
// For paginated result sets, the number of results to include per page.
PerPage int `url:"per_page,omitempty" json:"per_page,omitempty"`
}
// NewClient returns a new GitLab API client. If a nil httpClient is
// provided, http.DefaultClient will be used. To use API methods which require
// authentication, provide a valid private token.
func NewClient(httpClient *http.Client, token string) *Client {
return newClient(httpClient, privateToken, token)
}
// NewOAuthClient returns a new GitLab API client. If a nil httpClient is
// provided, http.DefaultClient will be used. To use API methods which require
// authentication, provide a valid oauth token.
func NewOAuthClient(httpClient *http.Client, token string) *Client {
return newClient(httpClient, oAuthToken, token)
}
func newClient(httpClient *http.Client, tokenType tokenType, token string) *Client {
if httpClient == nil {
httpClient = http.DefaultClient
}
c := &Client{client: httpClient, tokenType: tokenType, token: token, UserAgent: userAgent}
if err := c.SetBaseURL(defaultBaseURL); err != nil {
// Should never happen since defaultBaseURL is our constant.
panic(err)
}
// Create the internal timeStats service.
timeStats := &timeStatsService{client: c}
// Create all the public services.
c.Branches = &BranchesService{client: c}
c.BuildVariables = &BuildVariablesService{client: c}
c.Commits = &CommitsService{client: c}
c.DeployKeys = &DeployKeysService{client: c}
c.Environments = &EnvironmentsService{client: c}
c.Features = &FeaturesService{client: c}
c.Groups = &GroupsService{client: c}
c.GroupMembers = &GroupMembersService{client: c}
c.Issues = &IssuesService{client: c, timeStats: timeStats}
c.Jobs = &JobsService{client: c}
c.Labels = &LabelsService{client: c}
c.MergeRequests = &MergeRequestsService{client: c, timeStats: timeStats}
c.Milestones = &MilestonesService{client: c}
c.Namespaces = &NamespacesService{client: c}
c.Notes = &NotesService{client: c}
c.NotificationSettings = &NotificationSettingsService{client: c}
c.Projects = &ProjectsService{client: c}
c.ProjectMembers = &ProjectMembersService{client: c}
c.ProjectSnippets = &ProjectSnippetsService{client: c}
c.Pipelines = &PipelinesService{client: c}
c.PipelineTriggers = &PipelineTriggersService{client: c}
c.Repositories = &RepositoriesService{client: c}
c.RepositoryFiles = &RepositoryFilesService{client: c}
c.Services = &ServicesService{client: c}
c.Session = &SessionService{client: c}
c.Settings = &SettingsService{client: c}
c.SystemHooks = &SystemHooksService{client: c}
c.Tags = &TagsService{client: c}
c.Todos = &TodosService{client: c}
c.Users = &UsersService{client: c}
c.Version = &VersionService{client: c}
c.Wikis = &WikisService{client: c}
return c
}
// BaseURL return a copy of the baseURL.
func (c *Client) BaseURL() *url.URL {
u := *c.baseURL
return &u
}
// SetBaseURL sets the base URL for API requests to a custom endpoint. urlStr
// should always be specified with a trailing slash.
func (c *Client) SetBaseURL(urlStr string) error {
// Make sure the given URL end with a slash
if !strings.HasSuffix(urlStr, "/") {
urlStr += "/"
}
var err error
c.baseURL, err = url.Parse(urlStr)
return err
}
// NewRequest creates an API request. A relative URL path can be provided in
// urlStr, in which case it is resolved relative to the base URL of the Client.
// Relative URL paths should always be specified without a preceding slash. If
// specified, the value pointed to by body is JSON encoded and included as the
// request body.
func (c *Client) NewRequest(method, path string, opt interface{}, options []OptionFunc) (*http.Request, error) {
u := *c.baseURL
// Set the encoded opaque data
u.Opaque = c.baseURL.Path + path
if opt != nil {
q, err := query.Values(opt)
if err != nil {
return nil, err
}
u.RawQuery = q.Encode()
}
req := &http.Request{
Method: method,
URL: &u,
Proto: "HTTP/1.1",
ProtoMajor: 1,
ProtoMinor: 1,
Header: make(http.Header),
Host: u.Host,
}
for _, fn := range options {
if err := fn(req); err != nil {
return nil, err
}
}
if method == "POST" || method == "PUT" {
bodyBytes, err := json.Marshal(opt)
if err != nil {
return nil, err
}
bodyReader := bytes.NewReader(bodyBytes)
u.RawQuery = ""
req.Body = ioutil.NopCloser(bodyReader)
req.ContentLength = int64(bodyReader.Len())
req.Header.Set("Content-Type", "application/json")
}
req.Header.Set("Accept", "application/json")
switch c.tokenType {
case privateToken:
req.Header.Set("PRIVATE-TOKEN", c.token)
case oAuthToken:
req.Header.Set("Authorization", "Bearer "+c.token)
}
if c.UserAgent != "" {
req.Header.Set("User-Agent", c.UserAgent)
}
return req, nil
}
// Response is a GitLab API response. This wraps the standard http.Response
// returned from GitLab and provides convenient access to things like
// pagination links.
type Response struct {
*http.Response
// These fields provide the page values for paginating through a set of
// results. Any or all of these may be set to the zero value for
// responses that are not part of a paginated set, or for which there
// are no additional pages.
NextPage int
PrevPage int
FirstPage int
LastPage int
}
// newResponse creates a new Response for the provided http.Response.
func newResponse(r *http.Response) *Response {
response := &Response{Response: r}
response.populatePageValues()
return response
}
// populatePageValues parses the HTTP Link response headers and populates the
// various pagination link values in the Response.
func (r *Response) populatePageValues() {
if links, ok := r.Response.Header["Link"]; ok && len(links) > 0 {
for _, link := range strings.Split(links[0], ",") {
segments := strings.Split(strings.TrimSpace(link), ";")
// link must at least have href and rel
if len(segments) < 2 {
continue
}
// ensure href is properly formatted
if !strings.HasPrefix(segments[0], "<") || !strings.HasSuffix(segments[0], ">") {
continue
}
// try to pull out page parameter
url, err := url.Parse(segments[0][1 : len(segments[0])-1])
if err != nil {
continue
}
page := url.Query().Get("page")
if page == "" {
continue
}
for _, segment := range segments[1:] {
switch strings.TrimSpace(segment) {
case `rel="next"`:
r.NextPage, _ = strconv.Atoi(page)
case `rel="prev"`:
r.PrevPage, _ = strconv.Atoi(page)
case `rel="first"`:
r.FirstPage, _ = strconv.Atoi(page)
case `rel="last"`:
r.LastPage, _ = strconv.Atoi(page)
}
}
}
}
}
// Do sends an API request and returns the API response. The API response is
// JSON decoded and stored in the value pointed to by v, or returned as an
// error if an API error has occurred. If v implements the io.Writer
// interface, the raw response body will be written to v, without attempting to
// first decode it.
func (c *Client) Do(req *http.Request, v interface{}) (*Response, error) {
resp, err := c.client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
response := newResponse(resp)
err = CheckResponse(resp)
if err != nil {
// even though there was an error, we still return the response
// in case the caller wants to inspect it further
return response, err
}
if v != nil {
if w, ok := v.(io.Writer); ok {
_, err = io.Copy(w, resp.Body)
} else {
err = json.NewDecoder(resp.Body).Decode(v)
}
}
return response, err
}
// Helper function to accept and format both the project ID or name as project
// identifier for all API calls.
func parseID(id interface{}) (string, error) {
switch v := id.(type) {
case int:
return strconv.Itoa(v), nil
case string:
return v, nil
default:
return "", fmt.Errorf("invalid ID type %#v, the ID must be an int or a string", id)
}
}
// An ErrorResponse reports one or more errors caused by an API request.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/README.html#data-validation-and-error-reporting
type ErrorResponse struct {
Response *http.Response
Message string
}
func (e *ErrorResponse) Error() string {
path, _ := url.QueryUnescape(e.Response.Request.URL.Opaque)
u := fmt.Sprintf("%s://%s%s", e.Response.Request.URL.Scheme, e.Response.Request.URL.Host, path)
return fmt.Sprintf("%s %s: %d %s", e.Response.Request.Method, u, e.Response.StatusCode, e.Message)
}
// CheckResponse checks the API response for errors, and returns them if present.
func CheckResponse(r *http.Response) error {
switch r.StatusCode {
case 200, 201, 202, 204, 304:
return nil
}
errorResponse := &ErrorResponse{Response: r}
data, err := ioutil.ReadAll(r.Body)
if err == nil && data != nil {
var raw interface{}
if err := json.Unmarshal(data, &raw); err != nil {
errorResponse.Message = "failed to parse unknown error format"
}
errorResponse.Message = parseError(raw)
}
return errorResponse
}
// Format:
// {
// "message": {
// "<property-name>": [
// "<error-message>",
// "<error-message>",
// ...
// ],
// "<embed-entity>": {
// "<property-name>": [
// "<error-message>",
// "<error-message>",
// ...
// ],
// }
// },
// "error": "<error-message>"
// }
func parseError(raw interface{}) string {
switch raw := raw.(type) {
case string:
return raw
case []interface{}:
var errs []string
for _, v := range raw {
errs = append(errs, parseError(v))
}
return fmt.Sprintf("[%s]", strings.Join(errs, ", "))
case map[string]interface{}:
var errs []string
for k, v := range raw {
errs = append(errs, fmt.Sprintf("{%s: %s}", k, parseError(v)))
}
sort.Strings(errs)
return strings.Join(errs, ", ")
default:
return fmt.Sprintf("failed to parse unexpected error type: %T", raw)
}
}
// OptionFunc can be passed to all API requests to make the API call as if you were
// another user, provided your private token is from an administrator account.
//
// GitLab docs: https://docs.gitlab.com/ce/api/README.html#sudo
type OptionFunc func(*http.Request) error
// WithSudo takes either a username or user ID and sets the SUDO request header
func WithSudo(uid interface{}) OptionFunc {
return func(req *http.Request) error {
user, err := parseID(uid)
if err != nil {
return err
}
req.Header.Set("SUDO", user)
return nil
}
}
// WithContext runs the request with the provided context
func WithContext(ctx context.Context) OptionFunc {
return func(req *http.Request) error {
*req = *req.WithContext(ctx)
return nil
}
}
// Bool is a helper routine that allocates a new bool value
// to store v and returns a pointer to it.
func Bool(v bool) *bool {
p := new(bool)
*p = v
return p
}
// Int is a helper routine that allocates a new int32 value
// to store v and returns a pointer to it, but unlike Int32
// its argument value is an int.
func Int(v int) *int {
p := new(int)
*p = v
return p
}
// String is a helper routine that allocates a new string value
// to store v and returns a pointer to it.
func String(v string) *string {
p := new(string)
*p = v
return p
}
// AccessLevel is a helper routine that allocates a new AccessLevelValue
// to store v and returns a pointer to it.
func AccessLevel(v AccessLevelValue) *AccessLevelValue {
p := new(AccessLevelValue)
*p = v
return p
}
// NotificationLevel is a helper routine that allocates a new NotificationLevelValue
// to store v and returns a pointer to it.
func NotificationLevel(v NotificationLevelValue) *NotificationLevelValue {
p := new(NotificationLevelValue)
*p = v
return p
}
// Visibility is a helper routine that allocates a new VisibilityValue
// to store v and returns a pointer to it.
func Visibility(v VisibilityValue) *VisibilityValue {
p := new(VisibilityValue)
*p = v
return p
}

114
vendor/github.com/lkysow/go-gitlab/gitlab_test.go generated vendored Normal file
View File

@@ -0,0 +1,114 @@
package gitlab
import (
"context"
"io/ioutil"
"net/http"
"net/http/httptest"
"strings"
"testing"
)
// setup sets up a test HTTP server along with a gitlab.Client that is
// configured to talk to that test server. Tests should register handlers on
// mux which provide mock responses for the API method being tested.
func setup() (*http.ServeMux, *httptest.Server, *Client) {
// mux is the HTTP request multiplexer used with the test server.
mux := http.NewServeMux()
// server is a test HTTP server used to provide mock API responses.
server := httptest.NewServer(mux)
// client is the Gitlab client being tested.
client := NewClient(nil, "")
client.SetBaseURL(server.URL)
return mux, server, client
}
// teardown closes the test HTTP server.
func teardown(server *httptest.Server) {
server.Close()
}
func testURL(t *testing.T, r *http.Request, want string) {
if got := r.RequestURI; got != want {
t.Errorf("Request url: %+v, want %s", got, want)
}
}
func testMethod(t *testing.T, r *http.Request, want string) {
if got := r.Method; got != want {
t.Errorf("Request method: %s, want %s", got, want)
}
}
func TestNewClient(t *testing.T) {
c := NewClient(nil, "")
if c.BaseURL().String() != defaultBaseURL {
t.Errorf("NewClient BaseURL is %s, want %s", c.BaseURL().String(), defaultBaseURL)
}
if c.UserAgent != userAgent {
t.Errorf("NewClient UserAgent is %s, want %s", c.UserAgent, userAgent)
}
}
func TestCheckResponse(t *testing.T) {
req, err := NewClient(nil, "").NewRequest("GET", "test", nil, nil)
if err != nil {
t.Fatalf("Failed to create request: %v", err)
}
resp := &http.Response{
Request: req,
StatusCode: http.StatusBadRequest,
Body: ioutil.NopCloser(strings.NewReader(`
{
"message": {
"prop1": [
"message 1",
"message 2"
],
"prop2":[
"message 3"
],
"embed1": {
"prop3": [
"msg 1",
"msg2"
]
},
"embed2": {
"prop4": [
"some msg"
]
}
},
"error": "message 1"
}`)),
}
errResp := CheckResponse(resp)
if errResp == nil {
t.Fatal("Expected error response.")
}
want := "GET https://gitlab.com/api/v4/test: 400 {error: message 1}, {message: {embed1: {prop3: [msg 1, msg2]}}, {embed2: {prop4: [some msg]}}, {prop1: [message 1, message 2]}, {prop2: [message 3]}}"
if errResp.Error() != want {
t.Errorf("Expected error: %s, got %s", want, errResp.Error())
}
}
func TestRequestWithContext(t *testing.T) {
ctx := context.WithValue(context.Background(), interface{}("myKey"), interface{}("myValue"))
req, err := NewClient(nil, "").NewRequest("GET", "test", nil, []OptionFunc{WithContext(ctx)})
if err != nil {
t.Fatalf("Failed to create request: %v", err)
}
if req.Context() != ctx {
t.Fatal("Context was not set correctly")
}
}

193
vendor/github.com/lkysow/go-gitlab/group_members.go generated vendored Normal file
View File

@@ -0,0 +1,193 @@
//
// Copyright 2017, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
package gitlab
import (
"fmt"
"net/url"
"time"
)
// GroupMembersService handles communication with the group members
// related methods of the GitLab API.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/members.html
type GroupMembersService struct {
client *Client
}
// GroupMember represents a GitLab group member.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/members.html
type GroupMember struct {
ID int `json:"id"`
Username string `json:"username"`
Email string `json:"email"`
Name string `json:"name"`
State string `json:"state"`
CreatedAt *time.Time `json:"created_at"`
AccessLevel AccessLevelValue `json:"access_level"`
}
// ListGroupMembersOptions represents the available ListGroupMembers()
// options.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/members.html#list-all-members-of-a-group-or-project
type ListGroupMembersOptions struct {
ListOptions
}
// ListGroupMembers get a list of group members viewable by the authenticated
// user.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/members.html#list-all-members-of-a-group-or-project
func (s *GroupsService) ListGroupMembers(gid interface{}, opt *ListGroupMembersOptions, options ...OptionFunc) ([]*GroupMember, *Response, error) {
group, err := parseID(gid)
if err != nil {
return nil, nil, err
}
u := fmt.Sprintf("groups/%s/members", url.QueryEscape(group))
req, err := s.client.NewRequest("GET", u, opt, options)
if err != nil {
return nil, nil, err
}
var gm []*GroupMember
resp, err := s.client.Do(req, &gm)
if err != nil {
return nil, resp, err
}
return gm, resp, err
}
// AddGroupMemberOptions represents the available AddGroupMember() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/members.html#add-a-member-to-a-group-or-project
type AddGroupMemberOptions struct {
UserID *int `url:"user_id,omitempty" json:"user_id,omitempty"`
AccessLevel *AccessLevelValue `url:"access_level,omitempty" json:"access_level,omitempty"`
ExpiresAt *string `url:"expires_at,omitempty" json:"expires_at"`
}
// GetGroupMember gets a member of a group.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/members.html#get-a-member-of-a-group-or-project
func (s *GroupMembersService) GetGroupMember(gid interface{}, user int, options ...OptionFunc) (*GroupMember, *Response, error) {
group, err := parseID(gid)
if err != nil {
return nil, nil, err
}
u := fmt.Sprintf("groups/%s/members/%d", url.QueryEscape(group), user)
req, err := s.client.NewRequest("GET", u, nil, options)
if err != nil {
return nil, nil, err
}
gm := new(GroupMember)
resp, err := s.client.Do(req, gm)
if err != nil {
return nil, resp, err
}
return gm, resp, err
}
// AddGroupMember adds a user to the list of group members.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/members.html#add-a-member-to-a-group-or-project
func (s *GroupMembersService) AddGroupMember(gid interface{}, opt *AddGroupMemberOptions, options ...OptionFunc) (*GroupMember, *Response, error) {
group, err := parseID(gid)
if err != nil {
return nil, nil, err
}
u := fmt.Sprintf("groups/%s/members", url.QueryEscape(group))
req, err := s.client.NewRequest("POST", u, opt, options)
if err != nil {
return nil, nil, err
}
gm := new(GroupMember)
resp, err := s.client.Do(req, gm)
if err != nil {
return nil, resp, err
}
return gm, resp, err
}
// EditGroupMemberOptions represents the available EditGroupMember()
// options.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/members.html#edit-a-member-of-a-group-or-project
type EditGroupMemberOptions struct {
AccessLevel *AccessLevelValue `url:"access_level,omitempty" json:"access_level,omitempty"`
ExpiresAt *string `url:"expires_at,omitempty" json:"expires_at"`
}
// EditGroupMember updates a member of a group.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/members.html#edit-a-member-of-a-group-or-project
func (s *GroupMembersService) EditGroupMember(gid interface{}, user int, opt *EditGroupMemberOptions, options ...OptionFunc) (*GroupMember, *Response, error) {
group, err := parseID(gid)
if err != nil {
return nil, nil, err
}
u := fmt.Sprintf("groups/%s/members/%d", url.QueryEscape(group), user)
req, err := s.client.NewRequest("PUT", u, opt, options)
if err != nil {
return nil, nil, err
}
gm := new(GroupMember)
resp, err := s.client.Do(req, gm)
if err != nil {
return nil, resp, err
}
return gm, resp, err
}
// RemoveGroupMember removes user from user team.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/members.html#remove-a-member-from-a-group-or-project
func (s *GroupMembersService) RemoveGroupMember(gid interface{}, user int, options ...OptionFunc) (*Response, error) {
group, err := parseID(gid)
if err != nil {
return nil, err
}
u := fmt.Sprintf("groups/%s/members/%d", url.QueryEscape(group), user)
req, err := s.client.NewRequest("DELETE", u, nil, options)
if err != nil {
return nil, err
}
return s.client.Do(req, nil)
}

270
vendor/github.com/lkysow/go-gitlab/groups.go generated vendored Normal file
View File

@@ -0,0 +1,270 @@
//
// Copyright 2017, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
package gitlab
import (
"fmt"
"net/url"
)
// GroupsService handles communication with the group related methods of
// the GitLab API.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/groups.html
type GroupsService struct {
client *Client
}
// Group represents a GitLab group.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/groups.html
type Group struct {
ID int `json:"id"`
Name string `json:"name"`
Path string `json:"path"`
Description string `json:"description"`
Visibility *VisibilityValue `json:"visibility"`
LFSEnabled bool `json:"lfs_enabled"`
AvatarURL string `json:"avatar_url"`
WebURL string `json:"web_url"`
RequestAccessEnabled bool `json:"request_access_enabled"`
FullName string `json:"full_name"`
FullPath string `json:"full_path"`
ParentID int `json:"parent_id"`
Projects []*Project `json:"projects"`
Statistics *StorageStatistics `json:"statistics"`
}
// ListGroupsOptions represents the available ListGroups() options.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/groups.html#list-project-groups
type ListGroupsOptions struct {
ListOptions
AllAvailable *bool `url:"all_available,omitempty" json:"all_available,omitempty"`
OrderBy *string `url:"order_by,omitempty" json:"order_by,omitempty"`
Owned *bool `url:"owned,omitempty" json:"owned,omitempty"`
Search *string `url:"search,omitempty" json:"search,omitempty"`
Sort *string `url:"sort,omitempty" json:"sort,omitempty"`
Statistics *bool `url:"statistics,omitempty" json:"statistics,omitempty"`
}
// ListGroups gets a list of groups (as user: my groups, as admin: all groups).
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/groups.html#list-project-groups
func (s *GroupsService) ListGroups(opt *ListGroupsOptions, options ...OptionFunc) ([]*Group, *Response, error) {
req, err := s.client.NewRequest("GET", "groups", opt, options)
if err != nil {
return nil, nil, err
}
var g []*Group
resp, err := s.client.Do(req, &g)
if err != nil {
return nil, resp, err
}
return g, resp, err
}
// GetGroup gets all details of a group.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/groups.html#details-of-a-group
func (s *GroupsService) GetGroup(gid interface{}, options ...OptionFunc) (*Group, *Response, error) {
group, err := parseID(gid)
if err != nil {
return nil, nil, err
}
u := fmt.Sprintf("groups/%s", url.QueryEscape(group))
req, err := s.client.NewRequest("GET", u, nil, options)
if err != nil {
return nil, nil, err
}
g := new(Group)
resp, err := s.client.Do(req, g)
if err != nil {
return nil, resp, err
}
return g, resp, err
}
// CreateGroupOptions represents the available CreateGroup() options.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/groups.html#new-group
type CreateGroupOptions struct {
Name *string `url:"name,omitempty" json:"name,omitempty"`
Path *string `url:"path,omitempty" json:"path,omitempty"`
Description *string `url:"description,omitempty" json:"description,omitempty"`
Visibility *VisibilityValue `url:"visibility,omitempty" json:"visibility,omitempty"`
LFSEnabled *bool `url:"lfs_enabled,omitempty" json:"lfs_enabled,omitempty"`
RequestAccessEnabled *bool `url:"request_access_enabled,omitempty" json:"request_access_enabled,omitempty"`
ParentID *int `url:"parent_id,omitempty" json:"parent_id,omitempty"`
}
// CreateGroup creates a new project group. Available only for users who can
// create groups.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/groups.html#new-group
func (s *GroupsService) CreateGroup(opt *CreateGroupOptions, options ...OptionFunc) (*Group, *Response, error) {
req, err := s.client.NewRequest("POST", "groups", opt, options)
if err != nil {
return nil, nil, err
}
g := new(Group)
resp, err := s.client.Do(req, g)
if err != nil {
return nil, resp, err
}
return g, resp, err
}
// TransferGroup transfers a project to the Group namespace. Available only
// for admin.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/groups.html#transfer-project-to-group
func (s *GroupsService) TransferGroup(gid interface{}, project int, options ...OptionFunc) (*Group, *Response, error) {
group, err := parseID(gid)
if err != nil {
return nil, nil, err
}
u := fmt.Sprintf("groups/%s/projects/%d", url.QueryEscape(group), project)
req, err := s.client.NewRequest("POST", u, nil, options)
if err != nil {
return nil, nil, err
}
g := new(Group)
resp, err := s.client.Do(req, g)
if err != nil {
return nil, resp, err
}
return g, resp, err
}
// UpdateGroupOptions represents the set of available options to update a Group;
// as of today these are exactly the same available when creating a new Group.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/groups.html#update-group
type UpdateGroupOptions CreateGroupOptions
// UpdateGroup updates an existing group; only available to group owners and
// administrators.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/groups.html#update-group
func (s *GroupsService) UpdateGroup(gid interface{}, opt *UpdateGroupOptions, options ...OptionFunc) (*Group, *Response, error) {
group, err := parseID(gid)
if err != nil {
return nil, nil, err
}
u := fmt.Sprintf("groups/%s", url.QueryEscape(group))
req, err := s.client.NewRequest("PUT", u, opt, options)
if err != nil {
return nil, nil, err
}
g := new(Group)
resp, err := s.client.Do(req, g)
if err != nil {
return nil, resp, err
}
return g, resp, err
}
// DeleteGroup removes group with all projects inside.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/groups.html#remove-group
func (s *GroupsService) DeleteGroup(gid interface{}, options ...OptionFunc) (*Response, error) {
group, err := parseID(gid)
if err != nil {
return nil, err
}
u := fmt.Sprintf("groups/%s", url.QueryEscape(group))
req, err := s.client.NewRequest("DELETE", u, nil, options)
if err != nil {
return nil, err
}
return s.client.Do(req, nil)
}
// SearchGroup get all groups that match your string in their name or path.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/groups.html#search-for-group
func (s *GroupsService) SearchGroup(query string, options ...OptionFunc) ([]*Group, *Response, error) {
var q struct {
Search string `url:"search,omitempty" json:"search,omitempty"`
}
q.Search = query
req, err := s.client.NewRequest("GET", "groups", &q, options)
if err != nil {
return nil, nil, err
}
var g []*Group
resp, err := s.client.Do(req, &g)
if err != nil {
return nil, resp, err
}
return g, resp, err
}
// ListGroupProjectsOptions represents the available ListGroupProjects()
// options.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/groups.html#list-a-group-s-projects
type ListGroupProjectsOptions struct {
ListOptions
}
// ListGroupProjects get a list of group projects
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/groups.html#list-a-group-s-projects
func (s *GroupsService) ListGroupProjects(gid interface{}, opt *ListGroupProjectsOptions, options ...OptionFunc) ([]*Project, *Response, error) {
group, err := parseID(gid)
if err != nil {
return nil, nil, err
}
u := fmt.Sprintf("groups/%s/projects", url.QueryEscape(group))
req, err := s.client.NewRequest("GET", u, opt, options)
if err != nil {
return nil, nil, err
}
var p []*Project
resp, err := s.client.Do(req, &p)
if err != nil {
return nil, resp, err
}
return p, resp, err
}

362
vendor/github.com/lkysow/go-gitlab/issues.go generated vendored Normal file
View File

@@ -0,0 +1,362 @@
//
// Copyright 2017, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
package gitlab
import (
"encoding/json"
"fmt"
"net/url"
"strings"
"time"
)
// IssuesService handles communication with the issue related methods
// of the GitLab API.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/issues.html
type IssuesService struct {
client *Client
timeStats *timeStatsService
}
// Issue represents a GitLab issue.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/issues.html
type Issue struct {
ID int `json:"id"`
IID int `json:"iid"`
ProjectID int `json:"project_id"`
Title string `json:"title"`
Description string `json:"description"`
Labels []string `json:"labels"`
Milestone *Milestone `json:"milestone"`
Assignee struct {
ID int `json:"id"`
Username string `json:"username"`
Email string `json:"email"`
Name string `json:"name"`
State string `json:"state"`
CreatedAt *time.Time `json:"created_at"`
} `json:"assignee"`
Author struct {
ID int `json:"id"`
Username string `json:"username"`
Email string `json:"email"`
Name string `json:"name"`
State string `json:"state"`
CreatedAt *time.Time `json:"created_at"`
} `json:"author"`
State string `json:"state"`
UpdatedAt *time.Time `json:"updated_at"`
CreatedAt *time.Time `json:"created_at"`
Subscribed bool `json:"subscribed"`
UserNotesCount int `json:"user_notes_count"`
Confidential bool `json:"confidential"`
DueDate string `json:"due_date"`
WebURL string `json:"web_url"`
}
func (i Issue) String() string {
return Stringify(i)
}
// Labels is a custom type with specific marshaling characteristics.
type Labels []string
// MarshalJSON implements the json.Marshaler interface.
func (l *Labels) MarshalJSON() ([]byte, error) {
return json.Marshal(strings.Join(*l, ","))
}
// ListIssuesOptions represents the available ListIssues() options.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/issues.html#list-issues
type ListIssuesOptions struct {
ListOptions
State *string `url:"state,omitempty" json:"state,omitempty"`
Labels Labels `url:"labels,comma,omitempty" json:"labels,omitempty"`
Milestone *string `url:"milestone,omitempty" json:"milestone,omitempty"`
Scope *string `url:"scope,omitempty" json:"scope,omitempty"`
AuthorID *int `url:"author_id,omitempty" json:"author_id,omitempty"`
AssigneeID *int `url:"assignee_id,omitempty" json:"assignee_id,omitempty"`
MyReactionEmoji *string `url:"my_reaction_emoji,omitempty" json:"my_reaction_emoji,omitempty"`
IIDs []int `url:"iids[],omitempty" json:"iids,omitempty"`
OrderBy *string `url:"order_by,omitempty" json:"order_by,omitempty"`
Sort *string `url:"sort,omitempty" json:"sort,omitempty"`
Search *string `url:"search,omitempty" json:"search,omitempty"`
}
// ListIssues gets all issues created by authenticated user. This function
// takes pagination parameters page and per_page to restrict the list of issues.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/issues.html#list-issues
func (s *IssuesService) ListIssues(opt *ListIssuesOptions, options ...OptionFunc) ([]*Issue, *Response, error) {
req, err := s.client.NewRequest("GET", "issues", opt, options)
if err != nil {
return nil, nil, err
}
var i []*Issue
resp, err := s.client.Do(req, &i)
if err != nil {
return nil, resp, err
}
return i, resp, err
}
// ListGroupIssuesOptions represents the available ListGroupIssues() options.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/issues.html#list-group-issues
type ListGroupIssuesOptions struct {
ListOptions
State *string `url:"state,omitempty" json:"state,omitempty"`
Labels Labels `url:"labels,comma,omitempty" json:"labels,omitempty"`
IIDs []int `url:"iids[],omitempty" json:"iids,omitempty"`
Milestone *string `url:"milestone,omitempty" json:"milestone,omitempty"`
Scope *string `url:"scope,omitempty" json:"scope,omitempty"`
AuthorID *int `url:"author_id,omitempty" json:"author_id,omitempty"`
AssigneeID *int `url:"assignee_id,omitempty" json:"assignee_id,omitempty"`
MyReactionEmoji *string `url:"my_reaction_emoji,omitempty" json:"my_reaction_emoji,omitempty"`
OrderBy *string `url:"order_by,omitempty" json:"order_by,omitempty"`
Sort *string `url:"sort,omitempty" json:"sort,omitempty"`
Search *string `url:"search,omitempty" json:"search,omitempty"`
}
// ListGroupIssues gets a list of group issues. This function accepts
// pagination parameters page and per_page to return the list of group issues.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/issues.html#list-group-issues
func (s *IssuesService) ListGroupIssues(pid interface{}, opt *ListGroupIssuesOptions, options ...OptionFunc) ([]*Issue, *Response, error) {
group, err := parseID(pid)
if err != nil {
return nil, nil, err
}
u := fmt.Sprintf("groups/%s/issues", url.QueryEscape(group))
req, err := s.client.NewRequest("GET", u, opt, options)
if err != nil {
return nil, nil, err
}
var i []*Issue
resp, err := s.client.Do(req, &i)
if err != nil {
return nil, resp, err
}
return i, resp, err
}
// ListProjectIssuesOptions represents the available ListProjectIssues() options.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/issues.html#list-project-issues
type ListProjectIssuesOptions struct {
ListOptions
IIDs []int `url:"iids[],omitempty" json:"iids,omitempty"`
State *string `url:"state,omitempty" json:"state,omitempty"`
Labels Labels `url:"labels,comma,omitempty" json:"labels,omitempty"`
Milestone *string `url:"milestone,omitempty" json:"milestone,omitempty"`
Scope *string `url:"scope,omitempty" json:"scope,omitempty"`
AuthorID *int `url:"author_id,omitempty" json:"author_id,omitempty"`
AssigneeID *int `url:"assignee_id,omitempty" json:"assignee_id,omitempty"`
MyReactionEmoji *string `url:"my_reaction_emoji,omitempty" json:"my_reaction_emoji,omitempty"`
OrderBy *string `url:"order_by,omitempty" json:"order_by,omitempty"`
Sort *string `url:"sort,omitempty" json:"sort,omitempty"`
Search *string `url:"search,omitempty" json:"search,omitempty"`
CreatedAfter *time.Time `url:"created_after,omitempty" json:"created_after,omitempty"`
CreatedBefore *time.Time `url:"created_before,omitempty" json:"created_before,omitempty"`
}
// ListProjectIssues gets a list of project issues. This function accepts
// pagination parameters page and per_page to return the list of project issues.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/issues.html#list-project-issues
func (s *IssuesService) ListProjectIssues(pid interface{}, opt *ListProjectIssuesOptions, options ...OptionFunc) ([]*Issue, *Response, error) {
project, err := parseID(pid)
if err != nil {
return nil, nil, err
}
u := fmt.Sprintf("projects/%s/issues", url.QueryEscape(project))
req, err := s.client.NewRequest("GET", u, opt, options)
if err != nil {
return nil, nil, err
}
var i []*Issue
resp, err := s.client.Do(req, &i)
if err != nil {
return nil, resp, err
}
return i, resp, err
}
// GetIssue gets a single project issue.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/issues.html#single-issues
func (s *IssuesService) GetIssue(pid interface{}, issue int, options ...OptionFunc) (*Issue, *Response, error) {
project, err := parseID(pid)
if err != nil {
return nil, nil, err
}
u := fmt.Sprintf("projects/%s/issues/%d", url.QueryEscape(project), issue)
req, err := s.client.NewRequest("GET", u, nil, options)
if err != nil {
return nil, nil, err
}
i := new(Issue)
resp, err := s.client.Do(req, i)
if err != nil {
return nil, resp, err
}
return i, resp, err
}
// CreateIssueOptions represents the available CreateIssue() options.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/issues.html#new-issues
type CreateIssueOptions struct {
Title *string `url:"title,omitempty" json:"title,omitempty"`
Description *string `url:"description,omitempty" json:"description,omitempty"`
AssigneeID *int `url:"assignee_id,omitempty" json:"assignee_id,omitempty"`
MilestoneID *int `url:"milestone_id,omitempty" json:"milestone_id,omitempty"`
Labels Labels `url:"labels,comma,omitempty" json:"labels,omitempty"`
}
// CreateIssue creates a new project issue.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/issues.html#new-issues
func (s *IssuesService) CreateIssue(pid interface{}, opt *CreateIssueOptions, options ...OptionFunc) (*Issue, *Response, error) {
project, err := parseID(pid)
if err != nil {
return nil, nil, err
}
u := fmt.Sprintf("projects/%s/issues", url.QueryEscape(project))
req, err := s.client.NewRequest("POST", u, opt, options)
if err != nil {
return nil, nil, err
}
i := new(Issue)
resp, err := s.client.Do(req, i)
if err != nil {
return nil, resp, err
}
return i, resp, err
}
// UpdateIssueOptions represents the available UpdateIssue() options.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/issues.html#edit-issues
type UpdateIssueOptions struct {
Title *string `url:"title,omitempty" json:"title,omitempty"`
Description *string `url:"description,omitempty" json:"description,omitempty"`
AssigneeID *int `url:"assignee_id,omitempty" json:"assignee_id,omitempty"`
MilestoneID *int `url:"milestone_id,omitempty" json:"milestone_id,omitempty"`
Labels Labels `url:"labels,comma,omitempty" json:"labels,omitempty"`
StateEvent *string `url:"state_event,omitempty" json:"state_event,omitempty"`
}
// UpdateIssue updates an existing project issue. This function is also used
// to mark an issue as closed.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/issues.html#edit-issues
func (s *IssuesService) UpdateIssue(pid interface{}, issue int, opt *UpdateIssueOptions, options ...OptionFunc) (*Issue, *Response, error) {
project, err := parseID(pid)
if err != nil {
return nil, nil, err
}
u := fmt.Sprintf("projects/%s/issues/%d", url.QueryEscape(project), issue)
req, err := s.client.NewRequest("PUT", u, opt, options)
if err != nil {
return nil, nil, err
}
i := new(Issue)
resp, err := s.client.Do(req, i)
if err != nil {
return nil, resp, err
}
return i, resp, err
}
// DeleteIssue deletes a single project issue.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/issues.html#delete-an-issue
func (s *IssuesService) DeleteIssue(pid interface{}, issue int, options ...OptionFunc) (*Response, error) {
project, err := parseID(pid)
if err != nil {
return nil, err
}
u := fmt.Sprintf("projects/%s/issues/%d", url.QueryEscape(project), issue)
req, err := s.client.NewRequest("DELETE", u, nil, options)
if err != nil {
return nil, err
}
return s.client.Do(req, nil)
}
// SetTimeEstimate sets the time estimate for a single project issue.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/issues.html#set-a-time-estimate-for-an-issue
func (s *IssuesService) SetTimeEstimate(pid interface{}, issue int, opt *SetTimeEstimateOptions, options ...OptionFunc) (*TimeStats, *Response, error) {
return s.timeStats.setTimeEstimate(pid, "issues", issue, opt, options...)
}
// ResetTimeEstimate resets the time estimate for a single project issue.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/issues.html#reset-the-time-estimate-for-an-issue
func (s *IssuesService) ResetTimeEstimate(pid interface{}, issue int, options ...OptionFunc) (*TimeStats, *Response, error) {
return s.timeStats.resetTimeEstimate(pid, "issues", issue, options...)
}
// AddSpentTime adds spent time for a single project issue.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/issues.html#add-spent-time-for-an-issue
func (s *IssuesService) AddSpentTime(pid interface{}, issue int, opt *AddSpentTimeOptions, options ...OptionFunc) (*TimeStats, *Response, error) {
return s.timeStats.addSpentTime(pid, "issues", issue, opt, options...)
}
// ResetSpentTime resets the spent time for a single project issue.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/issues.html#reset-spent-time-for-an-issue
func (s *IssuesService) ResetSpentTime(pid interface{}, issue int, options ...OptionFunc) (*TimeStats, *Response, error) {
return s.timeStats.resetSpentTime(pid, "issues", issue, options...)
}
// GetTimeSpent gets the spent time for a single project issue.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/issues.html#get-time-tracking-stats
func (s *IssuesService) GetTimeSpent(pid interface{}, issue int, options ...OptionFunc) (*TimeStats, *Response, error) {
return s.timeStats.getTimeSpent(pid, "issues", issue, options...)
}

349
vendor/github.com/lkysow/go-gitlab/jobs.go generated vendored Normal file
View File

@@ -0,0 +1,349 @@
//
// Copyright 2017, Arkbriar
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
package gitlab
import (
"bytes"
"fmt"
"io"
"net/url"
"time"
)
// ListJobsOptions are options for two list apis
type ListJobsOptions struct {
ListOptions
Scope []BuildState `url:"scope,omitempty" json:"scope,omitempty"`
}
// JobsService handles communication with the ci builds related methods
// of the GitLab API.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/jobs.html
type JobsService struct {
client *Client
}
// Job represents a ci build.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/jobs.html
type Job struct {
Commit *Commit `json:"commit"`
CreatedAt *time.Time `json:"created_at"`
ArtifactsFile struct {
Filename string `json:"filename"`
Size int `json:"size"`
} `json:"artifacts_file"`
FinishedAt *time.Time `json:"finished_at"`
ID int `json:"id"`
Name string `json:"name"`
Ref string `json:"ref"`
Runner struct {
ID int `json:"id"`
Description string `json:"description"`
Active bool `json:"active"`
IsShared bool `json:"is_shared"`
Name string `json:"name"`
} `json:"runner"`
Stage string `json:"stage"`
StartedAt *time.Time `json:"started_at"`
Status string `json:"status"`
Tag bool `json:"tag"`
User *User `json:"user"`
}
// ListProjectJobs gets a list of jobs in a project.
//
// The scope of jobs to show, one or array of: created, pending, running,
// failed, success, canceled, skipped; showing all jobs if none provided
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/jobs.html#list-project-jobs
func (s *JobsService) ListProjectJobs(pid interface{}, opts *ListJobsOptions, options ...OptionFunc) ([]Job, *Response, error) {
project, err := parseID(pid)
if err != nil {
return nil, nil, err
}
u := fmt.Sprintf("projects/%s/jobs", url.QueryEscape(project))
req, err := s.client.NewRequest("GET", u, opts, options)
if err != nil {
return nil, nil, err
}
var jobs []Job
resp, err := s.client.Do(req, &jobs)
if err != nil {
return nil, resp, err
}
return jobs, resp, err
}
// ListPipelineJobs gets a list of jobs for specific pipeline in a
// project. If the pipeline ID is not found, it will respond with 404.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/jobs.html#list-pipeline-jobs
func (s *JobsService) ListPipelineJobs(pid interface{}, pipelineID int, opts *ListJobsOptions, options ...OptionFunc) ([]Job, *Response, error) {
project, err := parseID(pid)
if err != nil {
return nil, nil, err
}
u := fmt.Sprintf("projects/%s/pipelines/%d/jobs", project, pipelineID)
req, err := s.client.NewRequest("GET", u, opts, options)
if err != nil {
return nil, nil, err
}
var jobs []Job
resp, err := s.client.Do(req, &jobs)
if err != nil {
return nil, resp, err
}
return jobs, resp, err
}
// GetJob gets a single job of a project.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/jobs.html#get-a-single-job
func (s *JobsService) GetJob(pid interface{}, jobID int, options ...OptionFunc) (*Job, *Response, error) {
project, err := parseID(pid)
if err != nil {
return nil, nil, err
}
u := fmt.Sprintf("projects/%s/jobs/%d", project, jobID)
req, err := s.client.NewRequest("GET", u, nil, options)
if err != nil {
return nil, nil, err
}
job := new(Job)
resp, err := s.client.Do(req, job)
if err != nil {
return nil, resp, err
}
return job, resp, err
}
// GetJobArtifacts get jobs artifacts of a project
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/jobs.html#get-job-artifacts
func (s *JobsService) GetJobArtifacts(pid interface{}, jobID int, options ...OptionFunc) (io.Reader, *Response, error) {
project, err := parseID(pid)
if err != nil {
return nil, nil, err
}
u := fmt.Sprintf("projects/%s/jobs/%d/artifacts", project, jobID)
req, err := s.client.NewRequest("GET", u, nil, options)
if err != nil {
return nil, nil, err
}
artifactsBuf := new(bytes.Buffer)
resp, err := s.client.Do(req, artifactsBuf)
if err != nil {
return nil, resp, err
}
return artifactsBuf, resp, err
}
// DownloadArtifactsFile download the artifacts file from the given
// reference name and job provided the job finished successfully.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/jobs.html#download-the-artifacts-file
func (s *JobsService) DownloadArtifactsFile(pid interface{}, refName string, job string, options ...OptionFunc) (io.Reader, *Response, error) {
project, err := parseID(pid)
if err != nil {
return nil, nil, err
}
u := fmt.Sprintf("projects/%s/jobs/artifacts/%s/download?job=%s", project, refName, job)
req, err := s.client.NewRequest("GET", u, nil, options)
if err != nil {
return nil, nil, err
}
artifactsBuf := new(bytes.Buffer)
resp, err := s.client.Do(req, artifactsBuf)
if err != nil {
return nil, resp, err
}
return artifactsBuf, resp, err
}
// GetTraceFile gets a trace of a specific job of a project
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/jobs.html#get-a-trace-file
func (s *JobsService) GetTraceFile(pid interface{}, jobID int, options ...OptionFunc) (io.Reader, *Response, error) {
project, err := parseID(pid)
if err != nil {
return nil, nil, err
}
u := fmt.Sprintf("projects/%s/jobs/%d/trace", project, jobID)
req, err := s.client.NewRequest("GET", u, nil, options)
if err != nil {
return nil, nil, err
}
traceBuf := new(bytes.Buffer)
resp, err := s.client.Do(req, traceBuf)
if err != nil {
return nil, resp, err
}
return traceBuf, resp, err
}
// CancelJob cancels a single job of a project.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/jobs.html#cancel-a-job
func (s *JobsService) CancelJob(pid interface{}, jobID int, options ...OptionFunc) (*Job, *Response, error) {
project, err := parseID(pid)
if err != nil {
return nil, nil, err
}
u := fmt.Sprintf("projects/%s/jobs/%d/cancel", project, jobID)
req, err := s.client.NewRequest("POST", u, nil, options)
if err != nil {
return nil, nil, err
}
job := new(Job)
resp, err := s.client.Do(req, job)
if err != nil {
return nil, resp, err
}
return job, resp, err
}
// RetryJob retries a single job of a project
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/jobs.html#retry-a-job
func (s *JobsService) RetryJob(pid interface{}, jobID int, options ...OptionFunc) (*Job, *Response, error) {
project, err := parseID(pid)
if err != nil {
return nil, nil, err
}
u := fmt.Sprintf("projects/%s/jobs/%d/retry", project, jobID)
req, err := s.client.NewRequest("POST", u, nil, options)
if err != nil {
return nil, nil, err
}
job := new(Job)
resp, err := s.client.Do(req, job)
if err != nil {
return nil, resp, err
}
return job, resp, err
}
// EraseJob erases a single job of a project, removes a job
// artifacts and a job trace.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/jobs.html#erase-a-job
func (s *JobsService) EraseJob(pid interface{}, jobID int, options ...OptionFunc) (*Job, *Response, error) {
project, err := parseID(pid)
if err != nil {
return nil, nil, err
}
u := fmt.Sprintf("projects/%s/jobs/%d/erase", project, jobID)
req, err := s.client.NewRequest("POST", u, nil, options)
if err != nil {
return nil, nil, err
}
job := new(Job)
resp, err := s.client.Do(req, job)
if err != nil {
return nil, resp, err
}
return job, resp, err
}
// KeepArtifacts prevents artifacts from being deleted when
// expiration is set.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/jobs.html#keep-artifacts
func (s *JobsService) KeepArtifacts(pid interface{}, jobID int, options ...OptionFunc) (*Job, *Response, error) {
project, err := parseID(pid)
if err != nil {
return nil, nil, err
}
u := fmt.Sprintf("projects/%s/jobs/%d/artifacts/keep", project, jobID)
req, err := s.client.NewRequest("POST", u, nil, options)
if err != nil {
return nil, nil, err
}
job := new(Job)
resp, err := s.client.Do(req, job)
if err != nil {
return nil, resp, err
}
return job, resp, err
}
// PlayJob triggers a nanual action to start a job.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/jobs.html#play-a-job
func (s *JobsService) PlayJob(pid interface{}, jobID int, options ...OptionFunc) (*Job, *Response, error) {
project, err := parseID(pid)
if err != nil {
return nil, nil, err
}
u := fmt.Sprintf("projects/%s/jobs/%d/play", project, jobID)
req, err := s.client.NewRequest("POST", u, nil, options)
if err != nil {
return nil, nil, err
}
job := new(Job)
resp, err := s.client.Do(req, job)
if err != nil {
return nil, resp, err
}
return job, resp, err
}

164
vendor/github.com/lkysow/go-gitlab/labels.go generated vendored Normal file
View File

@@ -0,0 +1,164 @@
//
// Copyright 2017, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
package gitlab
import (
"fmt"
"net/url"
)
// LabelsService handles communication with the label related methods
// of the GitLab API.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/labels.html
type LabelsService struct {
client *Client
}
// Label represents a GitLab label.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/labels.html
type Label struct {
Name string `json:"name"`
Color string `json:"color"`
Description string `json:"description"`
OpenIssuesCount int `json:"open_issues_count"`
ClosedIssuesCount int `json:"closed_issues_count"`
OpenMergeRequestsCount int `json:"open_merge_requests_count"`
}
func (l Label) String() string {
return Stringify(l)
}
// ListLabels gets all labels for given project.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/labels.html#list-labels
func (s *LabelsService) ListLabels(pid interface{}, options ...OptionFunc) ([]*Label, *Response, error) {
project, err := parseID(pid)
if err != nil {
return nil, nil, err
}
u := fmt.Sprintf("projects/%s/labels", url.QueryEscape(project))
req, err := s.client.NewRequest("GET", u, nil, options)
if err != nil {
return nil, nil, err
}
var l []*Label
resp, err := s.client.Do(req, &l)
if err != nil {
return nil, resp, err
}
return l, resp, err
}
// CreateLabelOptions represents the available CreateLabel() options.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/labels.html#create-a-new-label
type CreateLabelOptions struct {
Name *string `url:"name,omitempty" json:"name,omitempty"`
Color *string `url:"color,omitempty" json:"color,omitempty"`
Description *string `url:"description,omitempty" json:"description,omitempty"`
}
// CreateLabel creates a new label for given repository with given name and
// color.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/labels.html#create-a-new-label
func (s *LabelsService) CreateLabel(pid interface{}, opt *CreateLabelOptions, options ...OptionFunc) (*Label, *Response, error) {
project, err := parseID(pid)
if err != nil {
return nil, nil, err
}
u := fmt.Sprintf("projects/%s/labels", url.QueryEscape(project))
req, err := s.client.NewRequest("POST", u, opt, options)
if err != nil {
return nil, nil, err
}
l := new(Label)
resp, err := s.client.Do(req, l)
if err != nil {
return nil, resp, err
}
return l, resp, err
}
// DeleteLabelOptions represents the available DeleteLabel() options.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/labels.html#delete-a-label
type DeleteLabelOptions struct {
Name *string `url:"name,omitempty" json:"name,omitempty"`
}
// DeleteLabel deletes a label given by its name.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/labels.html#delete-a-label
func (s *LabelsService) DeleteLabel(pid interface{}, opt *DeleteLabelOptions, options ...OptionFunc) (*Response, error) {
project, err := parseID(pid)
if err != nil {
return nil, err
}
u := fmt.Sprintf("projects/%s/labels", url.QueryEscape(project))
req, err := s.client.NewRequest("DELETE", u, opt, options)
if err != nil {
return nil, err
}
return s.client.Do(req, nil)
}
// UpdateLabelOptions represents the available UpdateLabel() options.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/labels.html#delete-a-label
type UpdateLabelOptions struct {
Name *string `url:"name,omitempty" json:"name,omitempty"`
NewName *string `url:"new_name,omitempty" json:"new_name,omitempty"`
Color *string `url:"color,omitempty" json:"color,omitempty"`
Description *string `url:"description,omitempty" json:"description,omitempty"`
}
// UpdateLabel updates an existing label with new name or now color. At least
// one parameter is required, to update the label.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/labels.html#edit-an-existing-label
func (s *LabelsService) UpdateLabel(pid interface{}, opt *UpdateLabelOptions, options ...OptionFunc) (*Label, *Response, error) {
project, err := parseID(pid)
if err != nil {
return nil, nil, err
}
u := fmt.Sprintf("projects/%s/labels", url.QueryEscape(project))
req, err := s.client.NewRequest("PUT", u, opt, options)
if err != nil {
return nil, nil, err
}
l := new(Label)
resp, err := s.client.Do(req, l)
if err != nil {
return nil, resp, err
}
return l, resp, err
}

485
vendor/github.com/lkysow/go-gitlab/merge_requests.go generated vendored Normal file
View File

@@ -0,0 +1,485 @@
//
// Copyright 2017, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
package gitlab
import (
"fmt"
"net/url"
"time"
)
// MergeRequestsService handles communication with the merge requests related
// methods of the GitLab API.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/merge_requests.html
type MergeRequestsService struct {
client *Client
timeStats *timeStatsService
}
// MergeRequest represents a GitLab merge request.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/merge_requests.html
type MergeRequest struct {
ID int `json:"id"`
IID int `json:"iid"`
ProjectID int `json:"project_id"`
Title string `json:"title"`
Description string `json:"description"`
WorkInProgress bool `json:"work_in_progress"`
State string `json:"state"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
TargetBranch string `json:"target_branch"`
SourceBranch string `json:"source_branch"`
Upvotes int `json:"upvotes"`
Downvotes int `json:"downvotes"`
Author struct {
Name string `json:"name"`
Username string `json:"username"`
ID int `json:"id"`
State string `json:"state"`
AvatarURL string `json:"avatar_url"`
} `json:"author"`
Assignee struct {
Name string `json:"name"`
Username string `json:"username"`
ID int `json:"id"`
State string `json:"state"`
AvatarURL string `json:"avatar_url"`
} `json:"assignee"`
SourceProjectID int `json:"source_project_id"`
TargetProjectID int `json:"target_project_id"`
Labels []string `json:"labels"`
Milestone struct {
ID int `json:"id"`
IID int `json:"iid"`
ProjectID int `json:"project_id"`
Title string `json:"title"`
Description string `json:"description"`
State string `json:"state"`
CreatedAt *time.Time `json:"created_at"`
UpdatedAt *time.Time `json:"updated_at"`
DueDate string `json:"due_date"`
} `json:"milestone"`
MergeWhenPipelineSucceeds bool `json:"merge_when_pipeline_succeeds"`
MergeStatus string `json:"merge_status"`
SHA string `json:"sha"`
Subscribed bool `json:"subscribed"`
UserNotesCount int `json:"user_notes_count"`
SouldRemoveSourceBranch bool `json:"should_remove_source_branch"`
ForceRemoveSourceBranch bool `json:"force_remove_source_branch"`
Changes []struct {
OldPath string `json:"old_path"`
NewPath string `json:"new_path"`
AMode string `json:"a_mode"`
BMode string `json:"b_mode"`
Diff string `json:"diff"`
NewFile bool `json:"new_file"`
RenamedFile bool `json:"renamed_file"`
DeletedFile bool `json:"deleted_file"`
} `json:"changes"`
WebURL string `json:"web_url"`
}
func (m MergeRequest) String() string {
return Stringify(m)
}
// MergeRequestApprovals represents GitLab merge request approvals.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/merge_requests.html#merge-request-approvals
type MergeRequestApprovals struct {
ID int `json:"id"`
ProjectID int `json:"project_id"`
Title string `json:"title"`
Description string `json:"description"`
State string `json:"state"`
CreatedAt *time.Time `json:"created_at"`
UpdatedAt *time.Time `json:"updated_at"`
MergeStatus string `json:"merge_status"`
ApprovalsRequired int `json:"approvals_required"`
ApprovalsMissing int `json:"approvals_missing"`
ApprovedBy []struct {
User struct {
Name string `json:"name"`
Username string `json:"username"`
ID int `json:"id"`
State string `json:"state"`
AvatarURL string `json:"avatar_url"`
WebURL string `json:"web_url"`
} `json:"user"`
} `json:"approved_by"`
}
func (m MergeRequestApprovals) String() string {
return Stringify(m)
}
// ListMergeRequestsOptions represents the available ListMergeRequests()
// options.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/merge_requests.html#list-merge-requests
type ListMergeRequestsOptions struct {
ListOptions
State *string `url:"state,omitempty" json:"state,omitempty"`
OrderBy *string `url:"order_by,omitempty" json:"order_by,omitempty"`
Sort *string `url:"sort,omitempty" json:"sort,omitempty"`
Milestone *string `url:"milestone,omitempty" json:"milestone,omitempty"`
View *string `url:"view,omitempty" json:"view,omitempty"`
Labels Labels `url:"labels,omitempty" json:"labels,omitempty"`
CreatedAfter *time.Time `url:"created_after,omitempty" json:"created_after,omitempty"`
CreatedBefore *time.Time `url:"created_before,omitempty" json:"created_before,omitempty"`
Scope *string `url:"scope,omitempty" json:"scope,omitempty"`
AuthorID *int `url:"author_id,omitempty" json:"author_id,omitempty"`
AssigneeID *int `url:"assignee_id,omitempty" json:"assignee_id,omitempty"`
MyReactionEmoji *string `url:"my_reaction_emoji,omitempty" json:"my_reaction_emoji,omitempty"`
}
// ListMergeRequests gets all merge requests. The state
// parameter can be used to get only merge requests with a given state (opened,
// closed, or merged) or all of them (all). The pagination parameters page and
// per_page can be used to restrict the list of merge requests.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/merge_requests.html#list-merge-requests
func (s *MergeRequestsService) ListMergeRequests(opt *ListMergeRequestsOptions, options ...OptionFunc) ([]*MergeRequest, *Response, error) {
req, err := s.client.NewRequest("GET", "merge_requests", opt, options)
if err != nil {
return nil, nil, err
}
var m []*MergeRequest
resp, err := s.client.Do(req, &m)
if err != nil {
return nil, resp, err
}
return m, resp, err
}
// ListProjectMergeRequestsOptions represents the available ListMergeRequests()
// options.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/merge_requests.html#list-project-merge-requests
type ListProjectMergeRequestsOptions struct {
ListOptions
IIDs []int `url:"iids[],omitempty" json:"iids,omitempty"`
State *string `url:"state,omitempty" json:"state,omitempty"`
OrderBy *string `url:"order_by,omitempty" json:"order_by,omitempty"`
Sort *string `url:"sort,omitempty" json:"sort,omitempty"`
Milestone *string `url:"milestone,omitempty" json:"milestone,omitempty"`
View *string `url:"view,omitempty" json:"view,omitempty"`
Labels Labels `url:"labels,omitempty" json:"labels,omitempty"`
CreatedAfter *time.Time `url:"created_after,omitempty" json:"created_after,omitempty"`
CreatedBefore *time.Time `url:"created_before,omitempty" json:"created_before,omitempty"`
Scope *string `url:"scope,omitempty" json:"scope,omitempty"`
AuthorID *int `url:"author_id,omitempty" json:"author_id,omitempty"`
AssigneeID *int `url:"assignee_id,omitempty" json:"assignee_id,omitempty"`
MyReactionEmoji *string `url:"my_reaction_emoji,omitempty" json:"my_reaction_emoji,omitempty"`
}
// ListProjectMergeRequests gets all merge requests for this project. The state
// parameter can be used to get only merge requests with a given state (opened,
// closed, or merged) or all of them (all). The pagination parameters page and
// per_page can be used to restrict the list of merge requests.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/merge_requests.html#list-merge-requests
func (s *MergeRequestsService) ListProjectMergeRequests(pid interface{}, opt *ListProjectMergeRequestsOptions, options ...OptionFunc) ([]*MergeRequest, *Response, error) {
project, err := parseID(pid)
if err != nil {
return nil, nil, err
}
u := fmt.Sprintf("projects/%s/merge_requests", url.QueryEscape(project))
req, err := s.client.NewRequest("GET", u, opt, options)
if err != nil {
return nil, nil, err
}
var m []*MergeRequest
resp, err := s.client.Do(req, &m)
if err != nil {
return nil, resp, err
}
return m, resp, err
}
// GetMergeRequest shows information about a single merge request.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/merge_requests.html#get-single-mr
func (s *MergeRequestsService) GetMergeRequest(pid interface{}, mergeRequest int, options ...OptionFunc) (*MergeRequest, *Response, error) {
project, err := parseID(pid)
if err != nil {
return nil, nil, err
}
u := fmt.Sprintf("projects/%s/merge_requests/%d", url.QueryEscape(project), mergeRequest)
req, err := s.client.NewRequest("GET", u, nil, options)
if err != nil {
return nil, nil, err
}
m := new(MergeRequest)
resp, err := s.client.Do(req, m)
if err != nil {
return nil, resp, err
}
return m, resp, err
}
// GetMergeRequestApprovals gets information about a merge requests approvals
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/merge_requests.html#merge-request-approvals
func (s *MergeRequestsService) GetMergeRequestApprovals(pid interface{}, mergeRequest int, options ...OptionFunc) (*MergeRequestApprovals, *Response, error) {
project, err := parseID(pid)
if err != nil {
return nil, nil, err
}
u := fmt.Sprintf("projects/%s/merge_requests/%d/approvals", url.QueryEscape(project), mergeRequest)
req, err := s.client.NewRequest("GET", u, nil, options)
if err != nil {
return nil, nil, err
}
a := new(MergeRequestApprovals)
resp, err := s.client.Do(req, a)
if err != nil {
return nil, resp, err
}
return a, resp, err
}
// GetMergeRequestCommits gets a list of merge request commits.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/merge_requests.html#get-single-mr-commits
func (s *MergeRequestsService) GetMergeRequestCommits(pid interface{}, mergeRequest int, options ...OptionFunc) ([]*Commit, *Response, error) {
project, err := parseID(pid)
if err != nil {
return nil, nil, err
}
u := fmt.Sprintf("projects/%s/merge_requests/%d/commits", url.QueryEscape(project), mergeRequest)
req, err := s.client.NewRequest("GET", u, nil, options)
if err != nil {
return nil, nil, err
}
var c []*Commit
resp, err := s.client.Do(req, &c)
if err != nil {
return nil, resp, err
}
return c, resp, err
}
// GetMergeRequestChanges shows information about the merge request including
// its files and changes.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/merge_requests.html#get-single-mr-changes
func (s *MergeRequestsService) GetMergeRequestChanges(pid interface{}, mergeRequest int, options ...OptionFunc) (*MergeRequest, *Response, error) {
project, err := parseID(pid)
if err != nil {
return nil, nil, err
}
u := fmt.Sprintf("projects/%s/merge_requests/%d/changes", url.QueryEscape(project), mergeRequest)
req, err := s.client.NewRequest("GET", u, nil, options)
if err != nil {
return nil, nil, err
}
m := new(MergeRequest)
resp, err := s.client.Do(req, m)
if err != nil {
return nil, resp, err
}
return m, resp, err
}
// CreateMergeRequestOptions represents the available CreateMergeRequest()
// options.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/merge_requests.html#create-mr
type CreateMergeRequestOptions struct {
Title *string `url:"title,omitempty" json:"title,omitempty"`
Description *string `url:"description,omitempty" json:"description,omitempty"`
SourceBranch *string `url:"source_branch,omitempty" json:"source_branch,omitempty"`
TargetBranch *string `url:"target_branch,omitempty" json:"target_branch,omitempty"`
AssigneeID *int `url:"assignee_id,omitempty" json:"assignee_id,omitempty"`
TargetProjectID *int `url:"target_project_id,omitempty" json:"target_project_id,omitempty"`
}
// CreateMergeRequest creates a new merge request.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/merge_requests.html#create-mr
func (s *MergeRequestsService) CreateMergeRequest(pid interface{}, opt *CreateMergeRequestOptions, options ...OptionFunc) (*MergeRequest, *Response, error) {
project, err := parseID(pid)
if err != nil {
return nil, nil, err
}
u := fmt.Sprintf("projects/%s/merge_requests", url.QueryEscape(project))
req, err := s.client.NewRequest("POST", u, opt, options)
if err != nil {
return nil, nil, err
}
m := new(MergeRequest)
resp, err := s.client.Do(req, m)
if err != nil {
return nil, resp, err
}
return m, resp, err
}
// UpdateMergeRequestOptions represents the available UpdateMergeRequest()
// options.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/merge_requests.html#update-mr
type UpdateMergeRequestOptions struct {
Title *string `url:"title,omitempty" json:"title,omitempty"`
Description *string `url:"description,omitempty" json:"description,omitempty"`
TargetBranch *string `url:"target_branch,omitempty" json:"target_branch,omitempty"`
AssigneeID *int `url:"assignee_id,omitempty" json:"assignee_id,omitempty"`
Labels Labels `url:"labels,comma,omitempty" json:"labels,omitempty"`
MilestoneID *int `url:"milestone_id,omitempty" json:"milestone_id,omitempty"`
StateEvent *string `url:"state_event,omitempty" json:"state_event,omitempty"`
}
// UpdateMergeRequest updates an existing project milestone.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/merge_requests.html#update-mr
func (s *MergeRequestsService) UpdateMergeRequest(pid interface{}, mergeRequest int, opt *UpdateMergeRequestOptions, options ...OptionFunc) (*MergeRequest, *Response, error) {
project, err := parseID(pid)
if err != nil {
return nil, nil, err
}
u := fmt.Sprintf("projects/%s/merge_requests/%d", url.QueryEscape(project), mergeRequest)
req, err := s.client.NewRequest("PUT", u, opt, options)
if err != nil {
return nil, nil, err
}
m := new(MergeRequest)
resp, err := s.client.Do(req, m)
if err != nil {
return nil, resp, err
}
return m, resp, err
}
// AcceptMergeRequestOptions represents the available AcceptMergeRequest()
// options.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/merge_requests.html#accept-mr
type AcceptMergeRequestOptions struct {
MergeCommitMessage *string `url:"merge_commit_message,omitempty" json:"merge_commit_message,omitempty"`
ShouldRemoveSourceBranch *bool `url:"should_remove_source_branch,omitempty" json:"should_remove_source_branch,omitempty"`
MergeWhenPipelineSucceeds *bool `url:"merge_when_pipeline_succeeds,omitempty" json:"merge_when_pipeline_succeeds,omitempty"`
Sha *string `url:"sha,omitempty" json:"sha,omitempty"`
}
// AcceptMergeRequest merges changes submitted with MR using this API. If merge
// success you get 200 OK. If it has some conflicts and can not be merged - you
// get 405 and error message 'Branch cannot be merged'. If merge request is
// already merged or closed - you get 405 and error message 'Method Not Allowed'
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/merge_requests.html#accept-mr
func (s *MergeRequestsService) AcceptMergeRequest(pid interface{}, mergeRequest int, opt *AcceptMergeRequestOptions, options ...OptionFunc) (*MergeRequest, *Response, error) {
project, err := parseID(pid)
if err != nil {
return nil, nil, err
}
u := fmt.Sprintf("projects/%s/merge_requests/%d/merge", url.QueryEscape(project), mergeRequest)
req, err := s.client.NewRequest("PUT", u, opt, options)
if err != nil {
return nil, nil, err
}
m := new(MergeRequest)
resp, err := s.client.Do(req, m)
if err != nil {
return nil, resp, err
}
return m, resp, err
}
// SetTimeEstimate sets the time estimate for a single project merge request.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/merge_requests.html#set-a-time-estimate-for-a-merge-request
func (s *MergeRequestsService) SetTimeEstimate(pid interface{}, mergeRequest int, opt *SetTimeEstimateOptions, options ...OptionFunc) (*TimeStats, *Response, error) {
return s.timeStats.setTimeEstimate(pid, "merge_requests", mergeRequest, opt, options...)
}
// ResetTimeEstimate resets the time estimate for a single project merge request.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/merge_requests.html#reset-the-time-estimate-for-a-merge-request
func (s *MergeRequestsService) ResetTimeEstimate(pid interface{}, mergeRequest int, options ...OptionFunc) (*TimeStats, *Response, error) {
return s.timeStats.resetTimeEstimate(pid, "merge_requests", mergeRequest, options...)
}
// AddSpentTime adds spent time for a single project merge request.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/merge_requests.html#add-spent-time-for-a-merge-request
func (s *MergeRequestsService) AddSpentTime(pid interface{}, mergeRequest int, opt *AddSpentTimeOptions, options ...OptionFunc) (*TimeStats, *Response, error) {
return s.timeStats.addSpentTime(pid, "merge_requests", mergeRequest, opt, options...)
}
// ResetSpentTime resets the spent time for a single project merge request.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/merge_requests.html#reset-spent-time-for-a-merge-request
func (s *MergeRequestsService) ResetSpentTime(pid interface{}, mergeRequest int, options ...OptionFunc) (*TimeStats, *Response, error) {
return s.timeStats.resetSpentTime(pid, "merge_requests", mergeRequest, options...)
}
// GetTimeSpent gets the spent time for a single project merge request.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/merge_requests.html#get-time-tracking-stats
func (s *MergeRequestsService) GetTimeSpent(pid interface{}, mergeRequest int, options ...OptionFunc) (*TimeStats, *Response, error) {
return s.timeStats.getTimeSpent(pid, "merge_requests", mergeRequest, options...)
}

216
vendor/github.com/lkysow/go-gitlab/milestones.go generated vendored Normal file
View File

@@ -0,0 +1,216 @@
//
// Copyright 2017, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
package gitlab
import (
"fmt"
"net/url"
"time"
)
// MilestonesService handles communication with the milestone related methods
// of the GitLab API.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/milestones.html
type MilestonesService struct {
client *Client
}
// Milestone represents a GitLab milestone.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/milestones.html
type Milestone struct {
ID int `json:"id"`
IID int `json:"iid"`
ProjectID int `json:"project_id"`
Title string `json:"title"`
Description string `json:"description"`
StartDate string `json:"start_date"`
DueDate string `json:"due_date"`
State string `json:"state"`
UpdatedAt *time.Time `json:"updated_at"`
CreatedAt *time.Time `json:"created_at"`
}
func (m Milestone) String() string {
return Stringify(m)
}
// ListMilestonesOptions represents the available ListMilestones() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/milestones.html#list-project-milestones
type ListMilestonesOptions struct {
ListOptions
IIDs []int `url:"iids,omitempty" json:"iids,omitempty"`
}
// ListMilestones returns a list of project milestones.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/milestones.html#list-project-milestones
func (s *MilestonesService) ListMilestones(pid interface{}, opt *ListMilestonesOptions, options ...OptionFunc) ([]*Milestone, *Response, error) {
project, err := parseID(pid)
if err != nil {
return nil, nil, err
}
u := fmt.Sprintf("projects/%s/milestones", url.QueryEscape(project))
req, err := s.client.NewRequest("GET", u, opt, options)
if err != nil {
return nil, nil, err
}
var m []*Milestone
resp, err := s.client.Do(req, &m)
if err != nil {
return nil, resp, err
}
return m, resp, err
}
// GetMilestone gets a single project milestone.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/milestones.html#get-single-milestone
func (s *MilestonesService) GetMilestone(pid interface{}, milestone int, options ...OptionFunc) (*Milestone, *Response, error) {
project, err := parseID(pid)
if err != nil {
return nil, nil, err
}
u := fmt.Sprintf("projects/%s/milestones/%d", url.QueryEscape(project), milestone)
req, err := s.client.NewRequest("GET", u, nil, options)
if err != nil {
return nil, nil, err
}
m := new(Milestone)
resp, err := s.client.Do(req, m)
if err != nil {
return nil, resp, err
}
return m, resp, err
}
// CreateMilestoneOptions represents the available CreateMilestone() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/milestones.html#create-new-milestone
type CreateMilestoneOptions struct {
Title *string `url:"title,omitempty" json:"title,omitempty"`
Description *string `url:"description,omitempty" json:"description,omitempty"`
StartDate *string `url:"start_date,omitempty" json:"start_date,omitempty"`
DueDate *string `url:"due_date,omitempty" json:"due_date,omitempty"`
}
// CreateMilestone creates a new project milestone.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/milestones.html#create-new-milestone
func (s *MilestonesService) CreateMilestone(pid interface{}, opt *CreateMilestoneOptions, options ...OptionFunc) (*Milestone, *Response, error) {
project, err := parseID(pid)
if err != nil {
return nil, nil, err
}
u := fmt.Sprintf("projects/%s/milestones", url.QueryEscape(project))
req, err := s.client.NewRequest("POST", u, opt, options)
if err != nil {
return nil, nil, err
}
m := new(Milestone)
resp, err := s.client.Do(req, m)
if err != nil {
return nil, resp, err
}
return m, resp, err
}
// UpdateMilestoneOptions represents the available UpdateMilestone() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/milestones.html#edit-milestone
type UpdateMilestoneOptions struct {
Title *string `url:"title,omitempty" json:"title,omitempty"`
Description *string `url:"description,omitempty" json:"description,omitempty"`
StartDate *string `url:"start_date,omitempty" json:"start_date,omitempty"`
DueDate *string `url:"due_date,omitempty" json:"due_date,omitempty"`
StateEvent *string `url:"state_event,omitempty" json:"state_event,omitempty"`
}
// UpdateMilestone updates an existing project milestone.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/milestones.html#edit-milestone
func (s *MilestonesService) UpdateMilestone(pid interface{}, milestone int, opt *UpdateMilestoneOptions, options ...OptionFunc) (*Milestone, *Response, error) {
project, err := parseID(pid)
if err != nil {
return nil, nil, err
}
u := fmt.Sprintf("projects/%s/milestones/%d", url.QueryEscape(project), milestone)
req, err := s.client.NewRequest("PUT", u, opt, options)
if err != nil {
return nil, nil, err
}
m := new(Milestone)
resp, err := s.client.Do(req, m)
if err != nil {
return nil, resp, err
}
return m, resp, err
}
// GetMilestoneIssuesOptions represents the available GetMilestoneIssues() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/milestones.html#get-all-issues-assigned-to-a-single-milestone
type GetMilestoneIssuesOptions struct {
ListOptions
}
// GetMilestoneIssues gets all issues assigned to a single project milestone.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/milestones.html#get-all-issues-assigned-to-a-single-milestone
func (s *MilestonesService) GetMilestoneIssues(pid interface{}, milestone int, opt *GetMilestoneIssuesOptions, options ...OptionFunc) ([]*Issue, *Response, error) {
project, err := parseID(pid)
if err != nil {
return nil, nil, err
}
u := fmt.Sprintf("projects/%s/milestones/%d/issues", url.QueryEscape(project), milestone)
req, err := s.client.NewRequest("GET", u, opt, options)
if err != nil {
return nil, nil, err
}
var i []*Issue
resp, err := s.client.Do(req, &i)
if err != nil {
return nil, resp, err
}
return i, resp, err
}

89
vendor/github.com/lkysow/go-gitlab/namespaces.go generated vendored Normal file
View File

@@ -0,0 +1,89 @@
//
// Copyright 2017, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
package gitlab
// NamespacesService handles communication with the namespace related methods
// of the GitLab API.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/namespaces.html
type NamespacesService struct {
client *Client
}
// Namespace represents a GitLab namespace.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/namespaces.html
type Namespace struct {
ID int `json:"id"`
Path string `json:"path"`
Kind string `json:"kind"`
}
func (n Namespace) String() string {
return Stringify(n)
}
// ListNamespacesOptions represents the available ListNamespaces() options.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/namespaces.html#list-namespaces
type ListNamespacesOptions struct {
ListOptions
Search *string `url:"search,omitempty" json:"search,omitempty"`
}
// ListNamespaces gets a list of projects accessible by the authenticated user.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/namespaces.html#list-namespaces
func (s *NamespacesService) ListNamespaces(opt *ListNamespacesOptions, options ...OptionFunc) ([]*Namespace, *Response, error) {
req, err := s.client.NewRequest("GET", "namespaces", opt, options)
if err != nil {
return nil, nil, err
}
var n []*Namespace
resp, err := s.client.Do(req, &n)
if err != nil {
return nil, resp, err
}
return n, resp, err
}
// SearchNamespace gets all namespaces that match your string in their name
// or path.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/namespaces.html#search-for-namespace
func (s *NamespacesService) SearchNamespace(query string, options ...OptionFunc) ([]*Namespace, *Response, error) {
var q struct {
Search string `url:"search,omitempty" json:"search,omitempty"`
}
q.Search = query
req, err := s.client.NewRequest("GET", "namespaces", &q, options)
if err != nil {
return nil, nil, err
}
var n []*Namespace
resp, err := s.client.Do(req, &n)
if err != nil {
return nil, resp, err
}
return n, resp, err
}

474
vendor/github.com/lkysow/go-gitlab/notes.go generated vendored Normal file
View File

@@ -0,0 +1,474 @@
//
// Copyright 2017, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
package gitlab
import (
"fmt"
"net/url"
"time"
)
// NotesService handles communication with the notes related methods
// of the GitLab API.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/notes.html
type NotesService struct {
client *Client
}
// Note represents a GitLab note.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/notes.html
type Note struct {
ID int `json:"id"`
Body string `json:"body"`
Attachment string `json:"attachment"`
Title string `json:"title"`
FileName string `json:"file_name"`
Author struct {
ID int `json:"id"`
Username string `json:"username"`
Email string `json:"email"`
Name string `json:"name"`
State string `json:"state"`
CreatedAt *time.Time `json:"created_at"`
} `json:"author"`
System bool `json:"system"`
ExpiresAt *time.Time `json:"expires_at"`
UpdatedAt *time.Time `json:"updated_at"`
CreatedAt *time.Time `json:"created_at"`
}
func (n Note) String() string {
return Stringify(n)
}
// ListIssueNotesOptions represents the available ListIssueNotes() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/notes.html#list-project-issue-notes
type ListIssueNotesOptions struct {
ListOptions
}
// ListIssueNotes gets a list of all notes for a single issue.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/notes.html#list-project-issue-notes
func (s *NotesService) ListIssueNotes(pid interface{}, issue int, opt *ListIssueNotesOptions, options ...OptionFunc) ([]*Note, *Response, error) {
project, err := parseID(pid)
if err != nil {
return nil, nil, err
}
u := fmt.Sprintf("projects/%s/issues/%d/notes", url.QueryEscape(project), issue)
req, err := s.client.NewRequest("GET", u, opt, options)
if err != nil {
return nil, nil, err
}
var n []*Note
resp, err := s.client.Do(req, &n)
if err != nil {
return nil, resp, err
}
return n, resp, err
}
// GetIssueNote returns a single note for a specific project issue.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/notes.html#get-single-issue-note
func (s *NotesService) GetIssueNote(pid interface{}, issue, note int, options ...OptionFunc) (*Note, *Response, error) {
project, err := parseID(pid)
if err != nil {
return nil, nil, err
}
u := fmt.Sprintf("projects/%s/issues/%d/notes/%d", url.QueryEscape(project), issue, note)
req, err := s.client.NewRequest("GET", u, nil, options)
if err != nil {
return nil, nil, err
}
n := new(Note)
resp, err := s.client.Do(req, n)
if err != nil {
return nil, resp, err
}
return n, resp, err
}
// CreateIssueNoteOptions represents the available CreateIssueNote()
// options.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/notes.html#create-new-issue-note
type CreateIssueNoteOptions struct {
Body *string `url:"body,omitempty" json:"body,omitempty"`
}
// CreateIssueNote creates a new note to a single project issue.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/notes.html#create-new-issue-note
func (s *NotesService) CreateIssueNote(pid interface{}, issue int, opt *CreateIssueNoteOptions, options ...OptionFunc) (*Note, *Response, error) {
project, err := parseID(pid)
if err != nil {
return nil, nil, err
}
u := fmt.Sprintf("projects/%s/issues/%d/notes", url.QueryEscape(project), issue)
req, err := s.client.NewRequest("POST", u, opt, options)
if err != nil {
return nil, nil, err
}
n := new(Note)
resp, err := s.client.Do(req, n)
if err != nil {
return nil, resp, err
}
return n, resp, err
}
// UpdateIssueNoteOptions represents the available UpdateIssueNote()
// options.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/notes.html#modify-existing-issue-note
type UpdateIssueNoteOptions struct {
Body *string `url:"body,omitempty" json:"body,omitempty"`
}
// UpdateIssueNote modifies existing note of an issue.
//
// https://docs.gitlab.com/ce/api/notes.html#modify-existing-issue-note
func (s *NotesService) UpdateIssueNote(pid interface{}, issue, note int, opt *UpdateIssueNoteOptions, options ...OptionFunc) (*Note, *Response, error) {
project, err := parseID(pid)
if err != nil {
return nil, nil, err
}
u := fmt.Sprintf("projects/%s/issues/%d/notes/%d", url.QueryEscape(project), issue, note)
req, err := s.client.NewRequest("PUT", u, opt, options)
if err != nil {
return nil, nil, err
}
n := new(Note)
resp, err := s.client.Do(req, n)
if err != nil {
return nil, resp, err
}
return n, resp, err
}
// DeleteIssueNote deletes an existing note of an issue.
//
// https://docs.gitlab.com/ce/api/notes.html#delete-an-issue-note
func (s *NotesService) DeleteIssueNote(pid interface{}, issue, note int, options ...OptionFunc) (*Response, error) {
project, err := parseID(pid)
if err != nil {
return nil, err
}
u := fmt.Sprintf("projects/%s/issues/%d/notes/%d", url.QueryEscape(project), issue, note)
req, err := s.client.NewRequest("DELETE", u, nil, options)
if err != nil {
return nil, err
}
return s.client.Do(req, nil)
}
// ListSnippetNotes gets a list of all notes for a single snippet. Snippet
// notes are comments users can post to a snippet.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/notes.html#list-all-snippet-notes
func (s *NotesService) ListSnippetNotes(pid interface{}, snippet int, options ...OptionFunc) ([]*Note, *Response, error) {
project, err := parseID(pid)
if err != nil {
return nil, nil, err
}
u := fmt.Sprintf("projects/%s/snippets/%d/notes", url.QueryEscape(project), snippet)
req, err := s.client.NewRequest("GET", u, nil, options)
if err != nil {
return nil, nil, err
}
var n []*Note
resp, err := s.client.Do(req, &n)
if err != nil {
return nil, resp, err
}
return n, resp, err
}
// GetSnippetNote returns a single note for a given snippet.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/notes.html#get-single-snippet-note
func (s *NotesService) GetSnippetNote(pid interface{}, snippet, note int, options ...OptionFunc) (*Note, *Response, error) {
project, err := parseID(pid)
if err != nil {
return nil, nil, err
}
u := fmt.Sprintf("projects/%s/snippets/%d/notes/%d", url.QueryEscape(project), snippet, note)
req, err := s.client.NewRequest("GET", u, nil, options)
if err != nil {
return nil, nil, err
}
n := new(Note)
resp, err := s.client.Do(req, n)
if err != nil {
return nil, resp, err
}
return n, resp, err
}
// CreateSnippetNoteOptions represents the available CreateSnippetNote()
// options.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/notes.html#create-new-snippet-note
type CreateSnippetNoteOptions struct {
Body *string `url:"body,omitempty" json:"body,omitempty"`
}
// CreateSnippetNote creates a new note for a single snippet. Snippet notes are
// comments users can post to a snippet.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/notes.html#create-new-snippet-note
func (s *NotesService) CreateSnippetNote(pid interface{}, snippet int, opt *CreateSnippetNoteOptions, options ...OptionFunc) (*Note, *Response, error) {
project, err := parseID(pid)
if err != nil {
return nil, nil, err
}
u := fmt.Sprintf("projects/%s/snippets/%d/notes", url.QueryEscape(project), snippet)
req, err := s.client.NewRequest("POST", u, opt, options)
if err != nil {
return nil, nil, err
}
n := new(Note)
resp, err := s.client.Do(req, n)
if err != nil {
return nil, resp, err
}
return n, resp, err
}
// UpdateSnippetNoteOptions represents the available UpdateSnippetNote()
// options.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/notes.html#modify-existing-snippet-note
type UpdateSnippetNoteOptions struct {
Body *string `url:"body,omitempty" json:"body,omitempty"`
}
// UpdateSnippetNote modifies existing note of a snippet.
//
// https://docs.gitlab.com/ce/api/notes.html#modify-existing-snippet-note
func (s *NotesService) UpdateSnippetNote(pid interface{}, snippet, note int, opt *UpdateSnippetNoteOptions, options ...OptionFunc) (*Note, *Response, error) {
project, err := parseID(pid)
if err != nil {
return nil, nil, err
}
u := fmt.Sprintf("projects/%s/snippets/%d/notes/%d", url.QueryEscape(project), snippet, note)
req, err := s.client.NewRequest("PUT", u, opt, options)
if err != nil {
return nil, nil, err
}
n := new(Note)
resp, err := s.client.Do(req, n)
if err != nil {
return nil, resp, err
}
return n, resp, err
}
// DeleteSnippetNote deletes an existing note of a snippet.
//
// https://docs.gitlab.com/ce/api/notes.html#delete-a-snippet-note
func (s *NotesService) DeleteSnippetNote(pid interface{}, snippet, note int, options ...OptionFunc) (*Response, error) {
project, err := parseID(pid)
if err != nil {
return nil, err
}
u := fmt.Sprintf("projects/%s/snippets/%d/notes/%d", url.QueryEscape(project), snippet, note)
req, err := s.client.NewRequest("DELETE", u, nil, options)
if err != nil {
return nil, err
}
return s.client.Do(req, nil)
}
// ListMergeRequestNotes gets a list of all notes for a single merge request.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/notes.html#list-all-merge-request-notes
func (s *NotesService) ListMergeRequestNotes(pid interface{}, mergeRequest int, options ...OptionFunc) ([]*Note, *Response, error) {
project, err := parseID(pid)
if err != nil {
return nil, nil, err
}
u := fmt.Sprintf("projects/%s/merge_requests/%d/notes", url.QueryEscape(project), mergeRequest)
req, err := s.client.NewRequest("GET", u, nil, options)
if err != nil {
return nil, nil, err
}
var n []*Note
resp, err := s.client.Do(req, &n)
if err != nil {
return nil, resp, err
}
return n, resp, err
}
// GetMergeRequestNote returns a single note for a given merge request.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/notes.html#get-single-merge-request-note
func (s *NotesService) GetMergeRequestNote(pid interface{}, mergeRequest, note int, options ...OptionFunc) (*Note, *Response, error) {
project, err := parseID(pid)
if err != nil {
return nil, nil, err
}
u := fmt.Sprintf("projects/%s/merge_requests/%d/notes/%d", url.QueryEscape(project), mergeRequest, note)
req, err := s.client.NewRequest("GET", u, nil, options)
if err != nil {
return nil, nil, err
}
n := new(Note)
resp, err := s.client.Do(req, n)
if err != nil {
return nil, resp, err
}
return n, resp, err
}
// CreateMergeRequestNoteOptions represents the available
// CreateMergeRequestNote() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/notes.html#create-new-merge-request-note
type CreateMergeRequestNoteOptions struct {
Body *string `url:"body,omitempty" json:"body,omitempty"`
}
// CreateMergeRequestNote creates a new note for a single merge request.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/notes.html#create-new-merge-request-note
func (s *NotesService) CreateMergeRequestNote(pid interface{}, mergeRequest int, opt *CreateMergeRequestNoteOptions, options ...OptionFunc) (*Note, *Response, error) {
project, err := parseID(pid)
if err != nil {
return nil, nil, err
}
u := fmt.Sprintf("projects/%s/merge_requests/%d/notes", url.QueryEscape(project), mergeRequest)
req, err := s.client.NewRequest("POST", u, opt, options)
if err != nil {
return nil, nil, err
}
n := new(Note)
resp, err := s.client.Do(req, n)
if err != nil {
return nil, resp, err
}
return n, resp, err
}
// UpdateMergeRequestNoteOptions represents the available
// UpdateMergeRequestNote() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/notes.html#modify-existing-merge-request-note
type UpdateMergeRequestNoteOptions struct {
Body *string `url:"body,omitempty" json:"body,omitempty"`
}
// UpdateMergeRequestNote modifies existing note of a merge request.
//
// https://docs.gitlab.com/ce/api/notes.html#modify-existing-merge-request-note
func (s *NotesService) UpdateMergeRequestNote(pid interface{}, mergeRequest, note int, opt *UpdateMergeRequestNoteOptions, options ...OptionFunc) (*Note, *Response, error) {
project, err := parseID(pid)
if err != nil {
return nil, nil, err
}
u := fmt.Sprintf(
"projects/%s/merge_requests/%d/notes/%d", url.QueryEscape(project), mergeRequest, note)
req, err := s.client.NewRequest("PUT", u, opt, options)
if err != nil {
return nil, nil, err
}
n := new(Note)
resp, err := s.client.Do(req, n)
if err != nil {
return nil, resp, err
}
return n, resp, err
}
// DeleteMergeRequestNote deletes an existing note of a merge request.
//
// https://docs.gitlab.com/ce/api/notes.html#delete-a-merge-request-note
func (s *NotesService) DeleteMergeRequestNote(pid interface{}, mergeRequest, note int, options ...OptionFunc) (*Response, error) {
project, err := parseID(pid)
if err != nil {
return nil, err
}
u := fmt.Sprintf(
"projects/%s/merge_requests/%d/notes/%d", url.QueryEscape(project), mergeRequest, note)
req, err := s.client.NewRequest("DELETE", u, nil, options)
if err != nil {
return nil, err
}
return s.client.Do(req, nil)
}

214
vendor/github.com/lkysow/go-gitlab/notifications.go generated vendored Normal file
View File

@@ -0,0 +1,214 @@
package gitlab
import (
"errors"
"fmt"
"net/url"
)
// NotificationSettingsService handles communication with the notification settings
// related methods of the GitLab API.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/notification_settings.html
type NotificationSettingsService struct {
client *Client
}
// NotificationSettings represents the Gitlab notification setting.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/notification_settings.html#notification-settings
type NotificationSettings struct {
Level NotificationLevelValue `json:"level"`
NotificationEmail string `json:"notification_email"`
Events *NotificationEvents `json:"events"`
}
// NotificationEvents represents the available notification setting events.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/notification_settings.html#notification-settings
type NotificationEvents struct {
CloseIssue bool `json:"close_issue"`
CloseMergeRequest bool `json:"close_merge_request"`
FailedPipeline bool `json:"failed_pipeline"`
MergeMergeRequest bool `json:"merge_merge_request"`
NewIssue bool `json:"new_issue"`
NewMergeRequest bool `json:"new_merge_request"`
NewNote bool `json:"new_note"`
ReassignIssue bool `json:"reassign_issue"`
ReassignMergeRequest bool `json:"reassign_merge_request"`
ReopenIssue bool `json:"reopen_issue"`
ReopenMergeRequest bool `json:"reopen_merge_request"`
SuccessPipeline bool `json:"success_pipeline"`
}
func (ns NotificationSettings) String() string {
return Stringify(ns)
}
// GetGlobalSettings returns current notification settings and email address.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/notification_settings.html#global-notification-settings
func (s *NotificationSettingsService) GetGlobalSettings(options ...OptionFunc) (*NotificationSettings, *Response, error) {
u := "notification_settings"
req, err := s.client.NewRequest("GET", u, nil, options)
if err != nil {
return nil, nil, err
}
ns := new(NotificationSettings)
resp, err := s.client.Do(req, ns)
if err != nil {
return nil, resp, err
}
return ns, resp, err
}
// NotificationSettingsOptions represents the available options that can be passed
// to the API when updating the notification settings.
type NotificationSettingsOptions struct {
Level *NotificationLevelValue `url:"level,omitempty" json:"level,omitempty"`
NotificationEmail *string `url:"notification_email,omitempty" json:"notification_email,omitempty"`
CloseIssue *bool `url:"close_issue,omitempty" json:"close_issue,omitempty"`
CloseMergeRequest *bool `url:"close_merge_request,omitempty" json:"close_merge_request,omitempty"`
FailedPipeline *bool `url:"failed_pipeline,omitempty" json:"failed_pipeline,omitempty"`
MergeMergeRequest *bool `url:"merge_merge_request,omitempty" json:"merge_merge_request,omitempty"`
NewIssue *bool `url:"new_issue,omitempty" json:"new_issue,omitempty"`
NewMergeRequest *bool `url:"new_merge_request,omitempty" json:"new_merge_request,omitempty"`
NewNote *bool `url:"new_note,omitempty" json:"new_note,omitempty"`
ReassignIssue *bool `url:"reassign_issue,omitempty" json:"reassign_issue,omitempty"`
ReassignMergeRequest *bool `url:"reassign_merge_request,omitempty" json:"reassign_merge_request,omitempty"`
ReopenIssue *bool `url:"reopen_issue,omitempty" json:"reopen_issue,omitempty"`
ReopenMergeRequest *bool `url:"reopen_merge_request,omitempty" json:"reopen_merge_request,omitempty"`
SuccessPipeline *bool `url:"success_pipeline,omitempty" json:"success_pipeline,omitempty"`
}
// UpdateGlobalSettings updates current notification settings and email address.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/notification_settings.html#update-global-notification-settings
func (s *NotificationSettingsService) UpdateGlobalSettings(opt *NotificationSettingsOptions, options ...OptionFunc) (*NotificationSettings, *Response, error) {
if opt.Level != nil && *opt.Level == GlobalNotificationLevel {
return nil, nil, errors.New(
"notification level 'global' is not valid for global notification settings")
}
u := "notification_settings"
req, err := s.client.NewRequest("PUT", u, opt, options)
if err != nil {
return nil, nil, err
}
ns := new(NotificationSettings)
resp, err := s.client.Do(req, ns)
if err != nil {
return nil, resp, err
}
return ns, resp, err
}
// GetSettingsForGroup returns current group notification settings.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/notification_settings.html#group-project-level-notification-settings
func (s *NotificationSettingsService) GetSettingsForGroup(gid interface{}, options ...OptionFunc) (*NotificationSettings, *Response, error) {
group, err := parseID(gid)
if err != nil {
return nil, nil, err
}
u := fmt.Sprintf("groups/%s/notification_settings", url.QueryEscape(group))
req, err := s.client.NewRequest("GET", u, nil, options)
if err != nil {
return nil, nil, err
}
ns := new(NotificationSettings)
resp, err := s.client.Do(req, ns)
if err != nil {
return nil, resp, err
}
return ns, resp, err
}
// GetSettingsForProject returns current project notification settings.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/notification_settings.html#group-project-level-notification-settings
func (s *NotificationSettingsService) GetSettingsForProject(pid interface{}, options ...OptionFunc) (*NotificationSettings, *Response, error) {
project, err := parseID(pid)
if err != nil {
return nil, nil, err
}
u := fmt.Sprintf("projects/%s/notification_settings", url.QueryEscape(project))
req, err := s.client.NewRequest("GET", u, nil, options)
if err != nil {
return nil, nil, err
}
ns := new(NotificationSettings)
resp, err := s.client.Do(req, ns)
if err != nil {
return nil, resp, err
}
return ns, resp, err
}
// UpdateSettingsForGroup updates current group notification settings.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/notification_settings.html#update-group-project-level-notification-settings
func (s *NotificationSettingsService) UpdateSettingsForGroup(gid interface{}, opt *NotificationSettingsOptions, options ...OptionFunc) (*NotificationSettings, *Response, error) {
group, err := parseID(gid)
if err != nil {
return nil, nil, err
}
u := fmt.Sprintf("groups/%s/notification_settings", url.QueryEscape(group))
req, err := s.client.NewRequest("PUT", u, opt, options)
if err != nil {
return nil, nil, err
}
ns := new(NotificationSettings)
resp, err := s.client.Do(req, ns)
if err != nil {
return nil, resp, err
}
return ns, resp, err
}
// UpdateSettingsForProject updates current project notification settings.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/notification_settings.html#update-group-project-level-notification-settings
func (s *NotificationSettingsService) UpdateSettingsForProject(pid interface{}, opt *NotificationSettingsOptions, options ...OptionFunc) (*NotificationSettings, *Response, error) {
project, err := parseID(pid)
if err != nil {
return nil, nil, err
}
u := fmt.Sprintf("projects/%s/notification_settings", url.QueryEscape(project))
req, err := s.client.NewRequest("PUT", u, opt, options)
if err != nil {
return nil, nil, err
}
ns := new(NotificationSettings)
resp, err := s.client.Do(req, ns)
if err != nil {
return nil, resp, err
}
return ns, resp, err
}

234
vendor/github.com/lkysow/go-gitlab/pipeline_triggers.go generated vendored Normal file
View File

@@ -0,0 +1,234 @@
package gitlab
import (
"fmt"
"net/url"
"time"
)
// PipelineTriggersService handles Project pipeline triggers.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/pipeline_triggers.html
type PipelineTriggersService struct {
client *Client
}
// PipelineTrigger represents a project pipeline trigger.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/pipeline_triggers.html#pipeline-triggers
type PipelineTrigger struct {
ID int `json:"id"`
Description string `json:"description"`
CreatedAt *time.Time `json:"created_at"`
DeletedAt *time.Time `json:"deleted_at"`
LastUsed *time.Time `json:"last_used"`
Token string `json:"token"`
UpdatedAt *time.Time `json:"updated_at"`
Owner *User `json:"owner"`
}
// ListPipelineTriggersOptions represents the available ListPipelineTriggers() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/pipeline_triggers.html#list-project-triggers
type ListPipelineTriggersOptions struct {
ListOptions
}
// ListPipelineTriggers gets a list of project triggers.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/pipeline_triggers.html#list-project-triggers
func (s *PipelineTriggersService) ListPipelineTriggers(pid interface{}, opt *ListPipelineTriggersOptions, options ...OptionFunc) ([]*PipelineTrigger, *Response, error) {
project, err := parseID(pid)
if err != nil {
return nil, nil, err
}
u := fmt.Sprintf("projects/%s/triggers", url.QueryEscape(project))
req, err := s.client.NewRequest("GET", u, opt, options)
if err != nil {
return nil, nil, err
}
var pt []*PipelineTrigger
resp, err := s.client.Do(req, &pt)
if err != nil {
return nil, resp, err
}
return pt, resp, err
}
// GetPipelineTrigger gets a specific pipeline trigger for a project.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/pipeline_triggers.html#get-trigger-details
func (s *PipelineTriggersService) GetPipelineTrigger(pid interface{}, trigger int, options ...OptionFunc) (*PipelineTrigger, *Response, error) {
project, err := parseID(pid)
if err != nil {
return nil, nil, err
}
u := fmt.Sprintf("projects/%s/triggers/%d", url.QueryEscape(project), trigger)
req, err := s.client.NewRequest("GET", u, nil, options)
if err != nil {
return nil, nil, err
}
pt := new(PipelineTrigger)
resp, err := s.client.Do(req, pt)
if err != nil {
return nil, resp, err
}
return pt, resp, err
}
// AddPipelineTriggerOptions represents the available AddPipelineTrigger() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/pipeline_triggers.html#create-a-project-trigger
type AddPipelineTriggerOptions struct {
Description *string `url:"description,omitempty" json:"description,omitempty"`
}
// AddPipelineTrigger adds a pipeline trigger to a specified project.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/pipeline_triggers.html#create-a-project-trigger
func (s *PipelineTriggersService) AddPipelineTrigger(pid interface{}, opt *AddPipelineTriggerOptions, options ...OptionFunc) (*PipelineTrigger, *Response, error) {
project, err := parseID(pid)
if err != nil {
return nil, nil, err
}
u := fmt.Sprintf("projects/%s/triggers", url.QueryEscape(project))
req, err := s.client.NewRequest("POST", u, opt, options)
if err != nil {
return nil, nil, err
}
pt := new(PipelineTrigger)
resp, err := s.client.Do(req, pt)
if err != nil {
return nil, resp, err
}
return pt, resp, err
}
// EditPipelineTriggerOptions represents the available EditPipelineTrigger() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/pipeline_triggers.html#update-a-project-trigger
type EditPipelineTriggerOptions struct {
Description *string `url:"description,omitempty" json:"description,omitempty"`
}
// EditPipelineTrigger edits a trigger for a specified project.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/pipeline_triggers.html#update-a-project-trigger
func (s *PipelineTriggersService) EditPipelineTrigger(pid interface{}, trigger int, opt *EditPipelineTriggerOptions, options ...OptionFunc) (*PipelineTrigger, *Response, error) {
project, err := parseID(pid)
if err != nil {
return nil, nil, err
}
u := fmt.Sprintf("projects/%s/triggers/%d", url.QueryEscape(project), trigger)
req, err := s.client.NewRequest("PUT", u, opt, options)
if err != nil {
return nil, nil, err
}
pt := new(PipelineTrigger)
resp, err := s.client.Do(req, pt)
if err != nil {
return nil, resp, err
}
return pt, resp, err
}
// TakeOwnershipOfPipelineTrigger sets the owner of the specified
// pipeline trigger to the user issuing the request.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/pipeline_triggers.html#take-ownership-of-a-project-trigger
func (s *PipelineTriggersService) TakeOwnershipOfPipelineTrigger(pid interface{}, trigger int, options ...OptionFunc) (*PipelineTrigger, *Response, error) {
project, err := parseID(pid)
if err != nil {
return nil, nil, err
}
u := fmt.Sprintf("projects/%s/triggers/%d/take_ownership", url.QueryEscape(project), trigger)
req, err := s.client.NewRequest("POST", u, nil, options)
if err != nil {
return nil, nil, err
}
pt := new(PipelineTrigger)
resp, err := s.client.Do(req, pt)
if err != nil {
return nil, resp, err
}
return pt, resp, err
}
// DeletePipelineTrigger removes a trigger from a project.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/pipeline_triggers.html#remove-a-project-trigger
func (s *PipelineTriggersService) DeletePipelineTrigger(pid interface{}, trigger int, options ...OptionFunc) (*Response, error) {
project, err := parseID(pid)
if err != nil {
return nil, err
}
u := fmt.Sprintf("projects/%s/triggers/%d", url.QueryEscape(project), trigger)
req, err := s.client.NewRequest("DELETE", u, nil, options)
if err != nil {
return nil, err
}
return s.client.Do(req, nil)
}
// RunPipelineTriggerOptions represents the available RunPipelineTrigger() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/ci/triggers/README.html#triggering-a-pipeline
type RunPipelineTriggerOptions struct {
Ref *string `url:"ref" json:"ref"`
Token *string `url:"token" json:"token"`
Variables map[string]string `url:"variables,omitempty" json:"variables,omitempty"`
}
// RunPipelineTrigger starts a trigger from a project.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/ci/triggers/README.html#triggering-a-pipeline
func (s *PipelineTriggersService) RunPipelineTrigger(pid interface{}, opt *RunPipelineTriggerOptions, options ...OptionFunc) (*Pipeline, *Response, error) {
project, err := parseID(pid)
if err != nil {
return nil, nil, err
}
u := fmt.Sprintf("projects/%s/trigger/pipeline", url.QueryEscape(project))
req, err := s.client.NewRequest("POST", u, opt, options)
if err != nil {
return nil, nil, err
}
pt := new(Pipeline)
resp, err := s.client.Do(req, pt)
if err != nil {
return nil, resp, err
}
return pt, resp, err
}

View File

@@ -0,0 +1,30 @@
package gitlab
import (
"fmt"
"net/http"
"reflect"
"testing"
)
func TestRunPipeline(t *testing.T) {
mux, server, client := setup()
defer teardown(server)
mux.HandleFunc("/projects/1/trigger/pipeline", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "POST")
fmt.Fprint(w, `{"id":1, "status":"pending"}`)
})
opt := &RunPipelineTriggerOptions{Ref: String("master")}
pipeline, _, err := client.PipelineTriggers.RunPipelineTrigger(1, opt)
if err != nil {
t.Errorf("PipelineTriggers.RunPipelineTrigger returned error: %v", err)
}
want := &Pipeline{ID: 1, Status: "pending"}
if !reflect.DeepEqual(want, pipeline) {
t.Errorf("PipelineTriggers.RunPipelineTrigger returned %+v, want %+v", pipeline, want)
}
}

Some files were not shown because too many files have changed in this diff Show More