From c4a608b38c4609be62ced84c7691bf4c6da40d73 Mon Sep 17 00:00:00 2001 From: Kirilll Zaycev Date: Mon, 1 Feb 2016 01:55:59 +0300 Subject: [PATCH 01/18] Added gitlab repository avatars --- remote/gitlab/client/types.go | 1 + remote/gitlab/gitlab.go | 11 ++++++++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/remote/gitlab/client/types.go b/remote/gitlab/client/types.go index d89311a2..dbd3db97 100644 --- a/remote/gitlab/client/types.go +++ b/remote/gitlab/client/types.go @@ -48,6 +48,7 @@ type Project struct { SshRepoUrl string `json:"ssh_url_to_repo"` HttpRepoUrl string `json:"http_url_to_repo"` Url string `json:"web_url"` + AvatarUrl string `json:"avatar_url"` Permissions *Permissions `json:"permissions,omitempty"` } diff --git a/remote/gitlab/gitlab.go b/remote/gitlab/gitlab.go index 026a63de..faf473c9 100644 --- a/remote/gitlab/gitlab.go +++ b/remote/gitlab/gitlab.go @@ -173,15 +173,20 @@ func (g *Gitlab) Repos(u *model.User) ([]*model.RepoLite, error) { var parts = strings.Split(repo.PathWithNamespace, "/") var owner = parts[0] var name = parts[1] + var avatar = repo.AvatarUrl + + if len(avatar) != 0 && !strings.HasPrefix(avatar, "http") { + avatar = fmt.Sprintf("%s/%s", g.URL, avatar) + } repos = append(repos, &model.RepoLite{ Owner: owner, Name: name, FullName: repo.PathWithNamespace, + Avatar: avatar, }) - - // TODO: add repo.AvatarUrl } + return repos, err } @@ -201,7 +206,7 @@ func (g *Gitlab) Perm(u *model.User, owner, name string) (*model.Perm, error) { // repo owner is granted full access if repo.Owner != nil && repo.Owner.Username == u.Login { - return &model.Perm{true, true, true}, nil + return &model.Perm{true, true, true}, nil } // check permission for current user From 1dbefc5281a364e3ce1c7af573fa7649fee1357c Mon Sep 17 00:00:00 2001 From: Kirilll Zaycev Date: Mon, 1 Feb 2016 02:00:47 +0300 Subject: [PATCH 02/18] Added dummy image to repos list --- static/images/dummy.png | Bin 0 -> 11083 bytes template/amber/repos.amber | 5 ++++- 2 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 static/images/dummy.png diff --git a/static/images/dummy.png b/static/images/dummy.png new file mode 100644 index 0000000000000000000000000000000000000000..f8b12dcd8df58c014e976d63b280cbf867c2ff3e GIT binary patch literal 11083 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 literal 0 HcmV?d00001 diff --git a/template/amber/repos.amber b/template/amber/repos.amber index 37081942..86af56eb 100644 --- a/template/amber/repos.amber +++ b/template/amber/repos.amber @@ -20,7 +20,10 @@ block content div.col-sm-4 a.card[href="/"+$repo.FullName] div.card-header - img.avatar[src=$repo.Avatar] + if $repo.Avatar != "" + img.avatar[src=$repo.Avatar] + else + img.avatar[src="/static/images/dummy.png"] div.card-block h3.login #{$repo.Name} div.full_name.hidden #{$repo.FullName} From 193d52449391954d95ad2a316296f4972aaa2a0f Mon Sep 17 00:00:00 2001 From: Kirilll Zaycev Date: Mon, 1 Feb 2016 02:22:44 +0300 Subject: [PATCH 03/18] Ensure we add avatar when activate repo --- remote/gitlab/gitlab.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/remote/gitlab/gitlab.go b/remote/gitlab/gitlab.go index faf473c9..79471911 100644 --- a/remote/gitlab/gitlab.go +++ b/remote/gitlab/gitlab.go @@ -145,6 +145,12 @@ func (g *Gitlab) Repo(u *model.User, owner, name string) (*model.Repo, error) { repo.Clone = repo_.HttpRepoUrl repo.Branch = "master" + repo.Avatar = repo_.AvatarUrl + + if len(repo.Avatar) != 0 && !strings.HasPrefix(repo.Avatar, "http") { + repo.Avatar = fmt.Sprintf("%s/%s", g.URL, repo.Avatar) + } + if repo_.DefaultBranch != "" { repo.Branch = repo_.DefaultBranch } From fbf36458f128dee22c6573fedd3c4f329a6ac470 Mon Sep 17 00:00:00 2001 From: Kirilll Zaycev Date: Mon, 1 Feb 2016 02:49:52 +0300 Subject: [PATCH 04/18] Committer avatar --- remote/gitlab/gitlab.go | 7 +++++++ remote/gitlab/helper.go | 18 ++++++++++++++++++ template/amber/repo.amber | 5 ++++- 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/remote/gitlab/gitlab.go b/remote/gitlab/gitlab.go index 79471911..01b86e48 100644 --- a/remote/gitlab/gitlab.go +++ b/remote/gitlab/gitlab.go @@ -372,6 +372,10 @@ func mergeRequest(parsed *client.HookPayload, req *http.Request) (*model.Repo, * build.Author = parsed.ObjectAttributes.LastCommit.Author.Name build.Email = parsed.ObjectAttributes.LastCommit.Author.Email + if len(build.Email) != 0 { + build.Avatar = GetUserAvatar(build.Email) + } + build.Title = parsed.ObjectAttributes.Title build.Link = parsed.ObjectAttributes.Url @@ -417,6 +421,9 @@ func push(parsed *client.HookPayload, req *http.Request) (*model.Repo, *model.Bu case head.Author != nil: build.Email = head.Author.Email build.Author = parsed.UserName + if len(build.Email) != 0 { + build.Avatar = GetUserAvatar(build.Email) + } case head.Author == nil: build.Author = parsed.UserName } diff --git a/remote/gitlab/helper.go b/remote/gitlab/helper.go index 62edc019..616a6a04 100644 --- a/remote/gitlab/helper.go +++ b/remote/gitlab/helper.go @@ -1,6 +1,8 @@ package gitlab import ( + "crypto/md5" + "encoding/hex" "fmt" "net/url" "strconv" @@ -8,6 +10,10 @@ import ( "github.com/drone/drone/remote/gitlab/client" ) +const ( + gravatarBase = "https://www.gravatar.com/avatar" +) + // NewClient is a helper function that returns a new GitHub // client using the provided OAuth token. func NewClient(url, accessToken string, skipVerify bool) *client.Client { @@ -79,6 +85,18 @@ func ns(owner, name string) string { return fmt.Sprintf("%s%%2F%s", owner, name) } +func GetUserAvatar(email string) string { + hasher := md5.New() + hasher.Write([]byte(email)) + + return fmt.Sprintf( + "%s/%v.jpg?s=%s", + gravatarBase, + hex.EncodeToString(hasher.Sum(nil)), + "128", + ) +} + func GetUserEmail(c *client.Client, defaultURL string) (*client.Client, error) { return c, nil } diff --git a/template/amber/repo.amber b/template/amber/repo.amber index 79e7d9dc..d7358c69 100644 --- a/template/amber/repo.amber +++ b/template/amber/repo.amber @@ -32,7 +32,10 @@ block content each $build in $group.Builds a.card[href=$repo.Name+"/"+$build.Number][data-build=$build.Number] div.card-header - img[src=$build.Avatar] + if $build.Avatar != "" + img[src=$build.Avatar] + else + img[src="/static/images/dummy.png"] div.card-block div div.status[class=$build.Status] #{$build.Status} From 1f9516a385e7ece242501b83902b9018a5497d21 Mon Sep 17 00:00:00 2001 From: Kirill Zaitsev Date: Tue, 2 Feb 2016 01:11:27 +0300 Subject: [PATCH 05/18] Dot names fix --- remote/gitlab/client/util.go | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/remote/gitlab/client/util.go b/remote/gitlab/client/util.go index 7cc5c37f..c7418525 100644 --- a/remote/gitlab/client/util.go +++ b/remote/gitlab/client/util.go @@ -5,8 +5,18 @@ import ( "strings" ) +var encodeMap = map[string]string{ + ".": "%252E", +} + func encodeParameter(value string) string { - return strings.Replace(url.QueryEscape(value), "/", "%2F", 0) + value = url.QueryEscape(value) + + for before, after := range encodeMap { + value = strings.Replace(value, before, after, -1) + } + + return value } // Tag returns current tag for push event hook payload From 7c311cde4283a8972741e6402f48f1e8737a1404 Mon Sep 17 00:00:00 2001 From: Michael Steinert Date: Fri, 5 Feb 2016 13:13:34 -0600 Subject: [PATCH 06/18] Add public mode --- router/middleware/session/repo.go | 8 +++++ router/middleware/session/repo_test.go | 44 ++++++++++++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 router/middleware/session/repo_test.go diff --git a/router/middleware/session/repo.go b/router/middleware/session/repo.go index 34dd047c..6962bdb4 100644 --- a/router/middleware/session/repo.go +++ b/router/middleware/session/repo.go @@ -2,6 +2,7 @@ package session import ( "net/http" + "os" "github.com/drone/drone/model" "github.com/drone/drone/remote" @@ -104,6 +105,8 @@ func Perm(c *gin.Context) *model.Perm { } func SetPerm() gin.HandlerFunc { + PUBLIC_MODE := os.Getenv("PUBLIC_MODE") + return func(c *gin.Context) { user := User(c) repo := Repo(c) @@ -164,6 +167,11 @@ func SetPerm() gin.HandlerFunc { } } + // all build logs are visible in public mode + if PUBLIC_MODE != "" { + perm.Pull = true + } + if user != nil { log.Debugf("%s granted %+v permission to %s", user.Login, perm, repo.FullName) diff --git a/router/middleware/session/repo_test.go b/router/middleware/session/repo_test.go new file mode 100644 index 00000000..6d524a9b --- /dev/null +++ b/router/middleware/session/repo_test.go @@ -0,0 +1,44 @@ +package session + +import ( + "os" + "testing" + + "github.com/drone/drone/model" + "github.com/franela/goblin" + "github.com/gin-gonic/gin" +) + +func TestSetPerm(t *testing.T) { + g := goblin.Goblin(t) + g.Describe("SetPerm", func() { + g.BeforeEach(func() { + os.Unsetenv("PUBLIC_MODE") + }) + g.It("Should set pull to false (private repo, user not logged in)", func() { + c := gin.Context{} + c.Set("repo", &model.Repo{ + IsPrivate: true, + }) + SetPerm()(&c) + v, ok := c.Get("perm") + g.Assert(ok).IsTrue("perm was not set") + p, ok := v.(*model.Perm) + g.Assert(ok).IsTrue("perm was the wrong type") + g.Assert(p.Pull).IsFalse("pull should be false") + }) + g.It("Should set pull to true (private repo, user not logged in, public mode)", func() { + os.Setenv("PUBLIC_MODE", "true") + c := gin.Context{} + c.Set("repo", &model.Repo{ + IsPrivate: true, + }) + SetPerm()(&c) + v, ok := c.Get("perm") + g.Assert(ok).IsTrue("perm was not set") + p, ok := v.(*model.Perm) + g.Assert(ok).IsTrue("perm was the wrong type") + g.Assert(p.Pull).IsTrue("pull should be true") + }) + }) +} From e018a347118118f267755b872d671f204ee7170a Mon Sep 17 00:00:00 2001 From: Brad Rydzewski Date: Fri, 5 Feb 2016 13:04:46 -0800 Subject: [PATCH 07/18] Added CORS to header to OPTIONS request --- router/middleware/header/header.go | 1 + 1 file changed, 1 insertion(+) diff --git a/router/middleware/header/header.go b/router/middleware/header/header.go index 0e0eaee7..dcd806cf 100644 --- a/router/middleware/header/header.go +++ b/router/middleware/header/header.go @@ -25,6 +25,7 @@ func Options(c *gin.Context) { if c.Request.Method != "OPTIONS" { c.Next() } else { + c.Header("Access-Control-Allow-Origin", "*") c.Header("Access-Control-Allow-Methods", "GET,POST,PUT,PATCH,DELETE,OPTIONS") c.Header("Access-Control-Allow-Headers", "Authorization") c.Header("Allow", "HEAD,GET,POST,PUT,PATCH,DELETE,OPTIONS") From 398db0bda2a46456994c93af8040fdcd37bee1be Mon Sep 17 00:00:00 2001 From: Brad Rydzewski Date: Fri, 5 Feb 2016 13:39:11 -0800 Subject: [PATCH 08/18] add origin, content-type, accept to CORS conf --- router/middleware/header/header.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/router/middleware/header/header.go b/router/middleware/header/header.go index dcd806cf..577d7a7d 100644 --- a/router/middleware/header/header.go +++ b/router/middleware/header/header.go @@ -27,7 +27,7 @@ func Options(c *gin.Context) { } else { c.Header("Access-Control-Allow-Origin", "*") c.Header("Access-Control-Allow-Methods", "GET,POST,PUT,PATCH,DELETE,OPTIONS") - c.Header("Access-Control-Allow-Headers", "Authorization") + c.Header("Access-Control-Allow-Headers", "authorization, origin, content-type, accept") c.Header("Allow", "HEAD,GET,POST,PUT,PATCH,DELETE,OPTIONS") c.Header("Content-Type", "application/json") c.AbortWithStatus(200) From 110b9c3dd2252ff34118767dc7f25096a7de430b Mon Sep 17 00:00:00 2001 From: Mac Browning Date: Fri, 12 Feb 2016 13:17:30 -0500 Subject: [PATCH 09/18] resume waiting for the container to complete --- shared/docker/docker.go | 56 ++++++++++++++++++++++++++++++----------- 1 file changed, 41 insertions(+), 15 deletions(-) diff --git a/shared/docker/docker.go b/shared/docker/docker.go index 29ed1061..7bc2ed07 100644 --- a/shared/docker/docker.go +++ b/shared/docker/docker.go @@ -1,9 +1,11 @@ package docker import ( + "errors" "io" "io/ioutil" + log "github.com/Sirupsen/logrus" "github.com/samalba/dockerclient" ) @@ -80,24 +82,48 @@ func Wait(client dockerclient.Client, name string) (*dockerclient.ContainerInfo, errc := make(chan error, 1) infoc := make(chan *dockerclient.ContainerInfo, 1) go func() { - - // blocks and waits for the container to finish - // by streaming the logs (to /dev/null). Ideally - // we could use the `wait` function instead - rc, err := client.ContainerLogs(name, LogOptsTail) - if err != nil { - errc <- err - return + // options to fetch the stdout and stderr logs + // by tailing the output. + logOptsTail := &dockerclient.LogOptions{ + Follow: true, + Stdout: true, + Stderr: true, } - io.Copy(ioutil.Discard, rc) - rc.Close() - info, err := client.InspectContainer(name) - if err != nil { - errc <- err - return + for attempts := 0; attempts < 5; attempts++ { + if attempts > 0 { + // When resuming the stream, only grab the last line when starting + // the tailing. + logOptsTail.Tail = 1 + } + + // blocks and waits for the container to finish + // by streaming the logs (to /dev/null). Ideally + // we could use the `wait` function instead + rc, err := client.ContainerLogs(name, logOptsTail) + if err != nil { + errc <- err + return + } + io.Copy(ioutil.Discard, rc) + rc.Close() + + info, err := client.InspectContainer(name) + if err != nil { + errc <- err + return + } + + if !info.State.Running { + // The container is no longer running, there should be no more logs to tail. + infoc <- info + return + } + + log.Debugf("Attempting to resume log tailing after %d attempts.\n", attempts) } - infoc <- info + + errc <- errors.New("Maximum number of attempts made while tailing logs.") }() select { From 8df397db9f507952e5f624180dc831768df20252 Mon Sep 17 00:00:00 2001 From: Mac Browning Date: Fri, 12 Feb 2016 13:22:13 -0500 Subject: [PATCH 10/18] mark incomplete builds as errors --- engine/engine.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/engine/engine.go b/engine/engine.go index 29552f34..f3d0c0c2 100644 --- a/engine/engine.go +++ b/engine/engine.go @@ -329,6 +329,11 @@ func (e *engine) runJob(c context.Context, r *Task, updater *updater, client doc info, builderr := docker.Wait(client, name) switch { + case info.State.Running: + // A build unblocked before actually being completed. + log.Errorf("incomplete build: %s", name) + r.Job.ExitCode = 1 + r.Job.Status = model.StatusError case info.State.ExitCode == 128: r.Job.ExitCode = info.State.ExitCode r.Job.Status = model.StatusKilled From 7a49d0324b1ec5190a5091cdc7947ec42f7f6daa Mon Sep 17 00:00:00 2001 From: Mac Browning Date: Fri, 12 Feb 2016 13:54:16 -0500 Subject: [PATCH 11/18] use dockerclient.Wait to block for in-progress builds --- shared/docker/docker.go | 62 ++++++++--------------------------------- 1 file changed, 12 insertions(+), 50 deletions(-) diff --git a/shared/docker/docker.go b/shared/docker/docker.go index 7bc2ed07..e08ec95e 100644 --- a/shared/docker/docker.go +++ b/shared/docker/docker.go @@ -2,8 +2,6 @@ package docker import ( "errors" - "io" - "io/ioutil" log "github.com/Sirupsen/logrus" "github.com/samalba/dockerclient" @@ -79,57 +77,21 @@ func Wait(client dockerclient.Client, name string) (*dockerclient.ContainerInfo, client.KillContainer(name, "9") }() - errc := make(chan error, 1) - infoc := make(chan *dockerclient.ContainerInfo, 1) - go func() { - // options to fetch the stdout and stderr logs - // by tailing the output. - logOptsTail := &dockerclient.LogOptions{ - Follow: true, - Stdout: true, - Stderr: true, + for attempts := 0; attempts < 5; attempts++ { + done := client.Wait(name) + <-done + + info, err := client.InspectContainer(name) + if err != nil { + return nil, err } - for attempts := 0; attempts < 5; attempts++ { - if attempts > 0 { - // When resuming the stream, only grab the last line when starting - // the tailing. - logOptsTail.Tail = 1 - } - - // blocks and waits for the container to finish - // by streaming the logs (to /dev/null). Ideally - // we could use the `wait` function instead - rc, err := client.ContainerLogs(name, logOptsTail) - if err != nil { - errc <- err - return - } - io.Copy(ioutil.Discard, rc) - rc.Close() - - info, err := client.InspectContainer(name) - if err != nil { - errc <- err - return - } - - if !info.State.Running { - // The container is no longer running, there should be no more logs to tail. - infoc <- info - return - } - - log.Debugf("Attempting to resume log tailing after %d attempts.\n", attempts) + if !info.State.Running { + return info, nil } - errc <- errors.New("Maximum number of attempts made while tailing logs.") - }() - - select { - case info := <-infoc: - return info, nil - case err := <-errc: - return nil, err + log.Debugf("attempting to resume waiting after %d attempts.\n", attempts) } + + return nil, errors.New("reached maximum wait attempts") } From b8b120afcc0fbc5eb32380ecebb62b4968b2cf16 Mon Sep 17 00:00:00 2001 From: Kirill Zaitsev Date: Sun, 14 Feb 2016 03:38:31 +0300 Subject: [PATCH 12/18] GitLab allowed groups for login --- remote/gitlab/client/groups.go | 53 ++++++++++++++++++++++++++++++++++ remote/gitlab/client/types.go | 1 + remote/gitlab/gitlab.go | 43 ++++++++++++++------------- 3 files changed, 77 insertions(+), 20 deletions(-) create mode 100644 remote/gitlab/client/groups.go diff --git a/remote/gitlab/client/groups.go b/remote/gitlab/client/groups.go new file mode 100644 index 00000000..0e8f30d2 --- /dev/null +++ b/remote/gitlab/client/groups.go @@ -0,0 +1,53 @@ +package client + +import ( + "encoding/json" + "strconv" +) + +const ( + groupsUrl = "/groups" +) + +// Get a list of all projects owned by the authenticated user. +func (g *Client) AllGroups() ([]*Namespace, error) { + var perPage = 100 + var groups []*Namespace + + for i := 1; true; i++ { + contents, err := g.Groups(i, perPage) + if err != nil { + return groups, err + } + + for _, value := range contents { + groups = append(groups, value) + } + + if len(groups) == 0 { + break + } + + if len(groups)/i < perPage { + break + } + } + + return groups, nil +} + +func (g *Client) Groups(page, perPage int) ([]*Namespace, error) { + url, opaque := g.ResourceUrl(groupsUrl, nil, QMap{ + "page": strconv.Itoa(page), + "per_page": strconv.Itoa(perPage), + }) + + var groups []*Namespace + + contents, err := g.Do("GET", url, opaque, nil) + if err == nil { + err = json.Unmarshal(contents, &groups) + } + + return groups, err +} diff --git a/remote/gitlab/client/types.go b/remote/gitlab/client/types.go index dbd3db97..4c4e79c3 100644 --- a/remote/gitlab/client/types.go +++ b/remote/gitlab/client/types.go @@ -55,6 +55,7 @@ type Project struct { type Namespace struct { Id int `json:"id,omitempty"` Name string `json:"name,omitempty"` + Path string `json:"path,omitempty"` } type Person struct { diff --git a/remote/gitlab/gitlab.go b/remote/gitlab/gitlab.go index 01b86e48..5087d180 100644 --- a/remote/gitlab/gitlab.go +++ b/remote/gitlab/gitlab.go @@ -101,6 +101,28 @@ func (g *Gitlab) Login(res http.ResponseWriter, req *http.Request) (*model.User, if err != nil { return nil, false, err } + + if len(g.AllowedOrgs) != 0 { + groups, err := client.AllGroups() + if err != nil { + return nil, false, fmt.Errorf("Could not check org membership. %s", err) + } + + var member bool + for _, group := range groups { + for _, allowedOrg := range g.AllowedOrgs { + if group.Path == allowedOrg { + member = true + break + } + } + } + + if !member { + return nil, false, fmt.Errorf("User does not belong to correct group. Must belong to %v", g.AllowedOrgs) + } + } + user := &model.User{} user.Login = login.Username user.Email = login.Email @@ -113,7 +135,7 @@ func (g *Gitlab) Login(res http.ResponseWriter, req *http.Request) (*model.User, user.Avatar = g.URL + "/" + login.AvatarUrl } - return user, true, nil + return user, g.Open, nil } func (g *Gitlab) Auth(token, secret string) (string, error) { @@ -454,25 +476,6 @@ func (g *Gitlab) Oauth2Transport(r *http.Request) *oauth2.Transport { } } -// Accessor method, to allowed remote organizations field. -func (g *Gitlab) GetOrgs() []string { - return g.AllowedOrgs -} - -// Accessor method, to open field. -func (g *Gitlab) GetOpen() bool { - return g.Open -} - -// return default scope for GitHub -func (g *Gitlab) Scope() string { - return DefaultScope -} - -func (g *Gitlab) String() string { - return "gitlab" -} - const ( StatusPending = "pending" StatusRunning = "running" From 05207546c6f68fb28f9a1fc1e06a42af1ae9766d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Thieriot?= Date: Tue, 16 Feb 2016 11:35:20 +0000 Subject: [PATCH 13/18] Add ability to hide Archived projects from Gitlab --- docs/setup/gitlab.md | 1 + remote/gitlab/client/project.go | 17 +++++++----- remote/gitlab/gitlab.go | 22 ++++++++------- remote/gitlab/gitlab_test.go | 19 +++++++++++++ remote/gitlab/testdata/projects.go | 43 +++++++++++++++++++++++++++++- remote/gitlab/testdata/testdata.go | 7 ++++- 6 files changed, 91 insertions(+), 18 deletions(-) diff --git a/docs/setup/gitlab.md b/docs/setup/gitlab.md index 705b9078..a13dcbf2 100644 --- a/docs/setup/gitlab.md +++ b/docs/setup/gitlab.md @@ -33,6 +33,7 @@ This section lists all connection options used in the connection string format. * `open=false` allows users to self-register. Defaults to false for security reasons. * `orgs=drone&orgs=docker` restricts access to these GitLab organizations. **Optional** * `skip_verify=false` skip ca verification if self-signed certificate. Defaults to false for security reasons. +* `hide_archives=false` hide projects archived in GitLab from the listing. * `clone_mode=token` a strategy for clone authorization, by default use repo token, but can be changed to `oauth` ( This is not secure, because your user token, with full access to your gitlab account will be written to .netrc, and it can be read by all who have access to project builds ) ## Gitlab registration diff --git a/remote/gitlab/client/project.go b/remote/gitlab/client/project.go index f6f22765..6140ab32 100644 --- a/remote/gitlab/client/project.go +++ b/remote/gitlab/client/project.go @@ -15,12 +15,12 @@ const ( ) // Get a list of all projects owned by the authenticated user. -func (g *Client) AllProjects() ([]*Project, error) { +func (g *Client) AllProjects(hide_archives bool) ([]*Project, error) { var per_page = 100 var projects []*Project for i := 1; true; i++ { - contents, err := g.Projects(i, per_page) + contents, err := g.Projects(i, per_page, hide_archives) if err != nil { return projects, err } @@ -42,12 +42,17 @@ func (g *Client) AllProjects() ([]*Project, error) { } // Get a list of projects owned by the authenticated user. -func (c *Client) Projects(page int, per_page int) ([]*Project, error) { - - url, opaque := c.ResourceUrl(projectsUrl, nil, QMap{ +func (c *Client) Projects(page int, per_page int, hide_archives bool) ([]*Project, error) { + projectsOptions := QMap{ "page": strconv.Itoa(page), "per_page": strconv.Itoa(per_page), - }) + } + + if hide_archives { + projectsOptions["archived"] = "false" + } + + url, opaque := c.ResourceUrl(projectsUrl, nil, projectsOptions) var projects []*Project diff --git a/remote/gitlab/gitlab.go b/remote/gitlab/gitlab.go index 01b86e48..ff0698de 100644 --- a/remote/gitlab/gitlab.go +++ b/remote/gitlab/gitlab.go @@ -23,15 +23,16 @@ const ( ) type Gitlab struct { - URL string - Client string - Secret string - AllowedOrgs []string - CloneMode string - Open bool - PrivateMode bool - SkipVerify bool - Search bool + URL string + Client string + Secret string + AllowedOrgs []string + CloneMode string + Open bool + PrivateMode bool + SkipVerify bool + HideArchives bool + Search bool } func Load(env envconfig.Env) *Gitlab { @@ -50,6 +51,7 @@ func Load(env envconfig.Env) *Gitlab { gitlab.Secret = params.Get("client_secret") gitlab.AllowedOrgs = params["orgs"] gitlab.SkipVerify, _ = strconv.ParseBool(params.Get("skip_verify")) + gitlab.HideArchives, _ = strconv.ParseBool(params.Get("hide_archives")) gitlab.Open, _ = strconv.ParseBool(params.Get("open")) switch params.Get("clone_mode") { @@ -170,7 +172,7 @@ func (g *Gitlab) Repos(u *model.User) ([]*model.RepoLite, error) { var repos = []*model.RepoLite{} - all, err := client.AllProjects() + all, err := client.AllProjects(g.HideArchives) if err != nil { return repos, err } diff --git a/remote/gitlab/gitlab_test.go b/remote/gitlab/gitlab_test.go index 2e876d88..04f18faf 100644 --- a/remote/gitlab/gitlab_test.go +++ b/remote/gitlab/gitlab_test.go @@ -32,6 +32,25 @@ func Test_Gitlab(t *testing.T) { g := goblin.Goblin(t) g.Describe("Gitlab Plugin", func() { + // Test projects method + g.Describe("AllProjects", func() { + g.It("Should return only non-archived projects is hidden", func() { + gitlab.HideArchives = true + _projects, err := gitlab.Repos(&user) + + g.Assert(err == nil).IsTrue() + g.Assert(len(_projects)).Equal(1) + }) + + g.It("Should return all the projects", func() { + gitlab.HideArchives = false + _projects, err := gitlab.Repos(&user) + + g.Assert(err == nil).IsTrue() + g.Assert(len(_projects)).Equal(2) + }) + }) + // Test repository method g.Describe("Repo", func() { g.It("Should return valid repo", func() { diff --git a/remote/gitlab/testdata/projects.go b/remote/gitlab/testdata/projects.go index ebe9bd39..1dd069e3 100644 --- a/remote/gitlab/testdata/projects.go +++ b/remote/gitlab/testdata/projects.go @@ -1,7 +1,7 @@ package testdata // sample repository list -var projectsPayload = []byte(` +var allProjectsPayload = []byte(` [ { "id": 4, @@ -73,6 +73,47 @@ var projectsPayload = []byte(` "path": "brightbox", "updated_at": "2013-09-30T13:46:02Z" }, + "archived": true + } +] +`) + +var notArchivedProjectsPayload = []byte(` +[ + { + "id": 4, + "description": null, + "default_branch": "master", + "public": false, + "visibility_level": 0, + "ssh_url_to_repo": "git@example.com:diaspora/diaspora-client.git", + "http_url_to_repo": "http://example.com/diaspora/diaspora-client.git", + "web_url": "http://example.com/diaspora/diaspora-client", + "owner": { + "id": 3, + "name": "Diaspora", + "username": "some_user", + "created_at": "2013-09-30T13: 46: 02Z" + }, + "name": "Diaspora Client", + "name_with_namespace": "Diaspora / Diaspora Client", + "path": "diaspora-client", + "path_with_namespace": "diaspora/diaspora-client", + "issues_enabled": true, + "merge_requests_enabled": true, + "wiki_enabled": true, + "snippets_enabled": false, + "created_at": "2013-09-30T13: 46: 02Z", + "last_activity_at": "2013-09-30T13: 46: 02Z", + "namespace": { + "created_at": "2013-09-30T13: 46: 02Z", + "description": "", + "id": 3, + "name": "Diaspora", + "owner_id": 1, + "path": "diaspora", + "updated_at": "2013-09-30T13: 46: 02Z" + }, "archived": false } ] diff --git a/remote/gitlab/testdata/testdata.go b/remote/gitlab/testdata/testdata.go index 3702c13d..2fa2322b 100644 --- a/remote/gitlab/testdata/testdata.go +++ b/remote/gitlab/testdata/testdata.go @@ -16,7 +16,12 @@ func NewServer() *httptest.Server { // evaluate the path to serve a dummy data file switch r.URL.Path { case "/api/v3/projects": - w.Write(projectsPayload) + if r.URL.Query().Get("archived") == "false" { + w.Write(notArchivedProjectsPayload) + } else { + w.Write(allProjectsPayload) + } + return case "/api/v3/projects/diaspora/diaspora-client": w.Write(project4Paylod) From 50ae4ba525676e9c0dd9b42e15c3f97232ec5789 Mon Sep 17 00:00:00 2001 From: Brad Rydzewski Date: Wed, 17 Feb 2016 14:37:52 -0800 Subject: [PATCH 14/18] Create ISSUE_TEMPLATE.md [CI SKIP] --- ISSUE_TEMPLATE.md | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 ISSUE_TEMPLATE.md diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md new file mode 100644 index 00000000..39af037d --- /dev/null +++ b/ISSUE_TEMPLATE.md @@ -0,0 +1,8 @@ +Thank you for taking the time to use Drone and file an issue or feature request. Before filing an issue please ensure the following boxes are checked, if application: + +- [ ] I have searched for existing issues +- [ ] I have discussed the issue with the community at https://gitter.im/drone/drone +- [ ] I have provided a sample `.drone.yml` file to help the team reproduce +- [ ] I have provided details from the build logs +- [ ] I have provided details from the server logs by running `docker logs drone` +- [ ] I am not using the issue tracker to ask why my build failed From 670217ac1b7ab19525cf4b584a982cada4b1ea6f Mon Sep 17 00:00:00 2001 From: Brad Rydzewski Date: Wed, 17 Feb 2016 17:05:53 -0800 Subject: [PATCH 15/18] Update ISSUE_TEMPLATE.md [CI SKIP] --- ISSUE_TEMPLATE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md index 39af037d..31407ab7 100644 --- a/ISSUE_TEMPLATE.md +++ b/ISSUE_TEMPLATE.md @@ -1,4 +1,4 @@ -Thank you for taking the time to use Drone and file an issue or feature request. Before filing an issue please ensure the following boxes are checked, if application: +Thank you for taking the time to use Drone and file an issue or feature request. Before filing an issue please ensure the following boxes are checked, if applicable: - [ ] I have searched for existing issues - [ ] I have discussed the issue with the community at https://gitter.im/drone/drone From 39dc9f7c76ea8bf22823c460ecbf1b5e9f379204 Mon Sep 17 00:00:00 2001 From: Kirill Zaitsev Date: Mon, 22 Feb 2016 22:35:53 +0300 Subject: [PATCH 16/18] Added support for new attributes --- remote/gitlab/client/types.go | 18 ++++++++----- remote/gitlab/gitlab.go | 48 ++++++++++++++++++++++++----------- 2 files changed, 45 insertions(+), 21 deletions(-) diff --git a/remote/gitlab/client/types.go b/remote/gitlab/client/types.go index dbd3db97..3aeb2f3e 100644 --- a/remote/gitlab/client/types.go +++ b/remote/gitlab/client/types.go @@ -63,12 +63,17 @@ type Person struct { } type hProject struct { - Name string `json:"name"` - SshUrl string `json:"ssh_url"` - HttpUrl string `json:"http_url"` - VisibilityLevel int `json:"visibility_level"` - WebUrl string `json:"web_url"` - Namespace string `json:"namespace"` + Name string `json:"name"` + SshUrl string `json:"ssh_url"` + HttpUrl string `json:"http_url"` + GitSshUrl string `json:"git_ssh_url"` + GitHttpUrl string `json:"git_http_url"` + AvatarUrl string `json:"avatar_url"` + VisibilityLevel int `json:"visibility_level"` + WebUrl string `json:"web_url"` + PathWithNamespace string `json:"path_with_namespace"` + DefaultBranch string `json:"default_branch"` + Namespace string `json:"namespace"` } type hRepository struct { @@ -123,6 +128,7 @@ type HookPayload struct { UserId int `json:"user_id,omitempty"` UserName string `json:"user_name,omitempty"` ProjectId int `json:"project_id,omitempty"` + Project *hProject `json:"project,omitempty"` Repository *hRepository `json:"repository,omitempty"` Commits []hCommit `json:"commits,omitempty"` TotalCommitsCount int `json:"total_commits_count,omitempty"` diff --git a/remote/gitlab/gitlab.go b/remote/gitlab/gitlab.go index 01b86e48..4eb3cb14 100644 --- a/remote/gitlab/gitlab.go +++ b/remote/gitlab/gitlab.go @@ -383,27 +383,45 @@ func mergeRequest(parsed *client.HookPayload, req *http.Request) (*model.Repo, * } func push(parsed *client.HookPayload, req *http.Request) (*model.Repo, *model.Build, error) { - var cloneUrl = parsed.Repository.GitHttpUrl - repo := &model.Repo{} repo.Owner = req.FormValue("owner") repo.Name = req.FormValue("name") - repo.FullName = fmt.Sprintf("%s/%s", repo.Owner, repo.Name) - repo.Link = parsed.Repository.URL - repo.Clone = cloneUrl - repo.Branch = "master" - switch parsed.Repository.VisibilityLevel { - case 0: - repo.IsPrivate = true - case 10: - repo.IsPrivate = true - case 20: - repo.IsPrivate = false + // Since gitlab 8.5, used project instead repository key + // see https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/web_hooks/web_hooks.md#web-hooks + if project := parsed.Project; project != nil { + repo.Avatar = project.AvatarUrl + repo.Link = project.WebUrl + repo.Clone = project.GitHttpUrl + repo.FullName = project.PathWithNamespace + repo.Branch = project.DefaultBranch + + switch project.VisibilityLevel { + case 0: + repo.IsPrivate = true + case 10: + repo.IsPrivate = true + case 20: + repo.IsPrivate = false + } + } else if repository := parsed.Repository; repository != nil { + repo.Link = repository.URL + repo.Clone = repository.GitHttpUrl + repo.Branch = "master" + repo.FullName = fmt.Sprintf("%s/%s", req.FormValue("owner"), req.FormValue("name")) + + switch repository.VisibilityLevel { + case 0: + repo.IsPrivate = true + case 10: + repo.IsPrivate = true + case 20: + repo.IsPrivate = false + } + } else { + return nil, nil, fmt.Errorf("No project/repository keys given") } - repo.FullName = fmt.Sprintf("%s/%s", req.FormValue("owner"), req.FormValue("name")) - build := &model.Build{} build.Event = model.EventPush build.Commit = parsed.After From 37e29b930e41b5a4834e9005db193677fa02ed2a Mon Sep 17 00:00:00 2001 From: Kirill Zaitsev Date: Mon, 22 Feb 2016 23:43:54 +0300 Subject: [PATCH 17/18] More coverage for gitlab hooks --- remote/gitlab/gitlab.go | 9 ++- remote/gitlab/gitlab_test.go | 86 ++++++++++++++++++------- remote/gitlab/helper.go | 9 +++ remote/gitlab/testdata/hooks.go | 110 +++++++++++++++++++++++++++++++- 4 files changed, 189 insertions(+), 25 deletions(-) diff --git a/remote/gitlab/gitlab.go b/remote/gitlab/gitlab.go index 4eb3cb14..3915758a 100644 --- a/remote/gitlab/gitlab.go +++ b/remote/gitlab/gitlab.go @@ -384,12 +384,15 @@ func mergeRequest(parsed *client.HookPayload, req *http.Request) (*model.Repo, * func push(parsed *client.HookPayload, req *http.Request) (*model.Repo, *model.Build, error) { repo := &model.Repo{} - repo.Owner = req.FormValue("owner") - repo.Name = req.FormValue("name") // Since gitlab 8.5, used project instead repository key // see https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/web_hooks/web_hooks.md#web-hooks if project := parsed.Project; project != nil { + var err error + if repo.Owner, repo.Name, err = ExtractFromPath(project.PathWithNamespace); err != nil { + return nil, nil, err + } + repo.Avatar = project.AvatarUrl repo.Link = project.WebUrl repo.Clone = project.GitHttpUrl @@ -405,6 +408,8 @@ func push(parsed *client.HookPayload, req *http.Request) (*model.Repo, *model.Bu repo.IsPrivate = false } } else if repository := parsed.Repository; repository != nil { + repo.Owner = req.FormValue("owner") + repo.Name = req.FormValue("name") repo.Link = repository.URL repo.Clone = repository.GitHttpUrl repo.Branch = "master" diff --git a/remote/gitlab/gitlab_test.go b/remote/gitlab/gitlab_test.go index 2e876d88..3b555c12 100644 --- a/remote/gitlab/gitlab_test.go +++ b/remote/gitlab/gitlab_test.go @@ -115,36 +115,78 @@ func Test_Gitlab(t *testing.T) { // Test hook method g.Describe("Hook", func() { - g.It("Should parse push hoook", func() { - req, _ := http.NewRequest( - "POST", - "http://example.com/api/hook?owner=diaspora&name=diaspora-client", - bytes.NewReader(testdata.PushHook), - ) + g.Describe("Push hook", func() { + g.It("Should parse actual push hoook", func() { + req, _ := http.NewRequest( + "POST", + "http://example.com/api/hook?owner=diaspora&name=diaspora-client", + bytes.NewReader(testdata.PushHook), + ) - repo, build, err := gitlab.Hook(req) + repo, build, err := gitlab.Hook(req) - g.Assert(err == nil).IsTrue() - g.Assert(repo.Owner).Equal("diaspora") - g.Assert(repo.Name).Equal("diaspora-client") - g.Assert(build.Ref).Equal("refs/heads/master") + g.Assert(err == nil).IsTrue() + g.Assert(repo.Owner).Equal("mike") + g.Assert(repo.Name).Equal("diaspora") + g.Assert(repo.Avatar).Equal("http://example.com/uploads/project/avatar/555/Outh-20-Logo.jpg") + g.Assert(repo.Branch).Equal("develop") + g.Assert(build.Ref).Equal("refs/heads/master") + }) + + g.It("Should parse legacy push hoook", func() { + req, _ := http.NewRequest( + "POST", + "http://example.com/api/hook?owner=diaspora&name=diaspora-client", + bytes.NewReader(testdata.LegacyPushHook), + ) + + repo, build, err := gitlab.Hook(req) + + g.Assert(err == nil).IsTrue() + g.Assert(repo.Owner).Equal("diaspora") + g.Assert(repo.Name).Equal("diaspora-client") + g.Assert(repo.Avatar).Equal("") + g.Assert(repo.Branch).Equal("master") + g.Assert(build.Ref).Equal("refs/heads/master") + + }) }) - g.It("Should parse tag push hook", func() { - req, _ := http.NewRequest( - "POST", - "http://example.com/api/hook?owner=diaspora&name=diaspora-client", - bytes.NewReader(testdata.TagHook), - ) + g.Describe("Tag push hook", func() { + g.It("Should parse tag push hook", func() { + req, _ := http.NewRequest( + "POST", + "http://example.com/api/hook?owner=diaspora&name=diaspora-client", + bytes.NewReader(testdata.TagHook), + ) - repo, build, err := gitlab.Hook(req) + repo, build, err := gitlab.Hook(req) - g.Assert(err == nil).IsTrue() - g.Assert(repo.Owner).Equal("diaspora") - g.Assert(repo.Name).Equal("diaspora-client") - g.Assert(build.Ref).Equal("refs/tags/v1.0.0") + g.Assert(err == nil).IsTrue() + g.Assert(repo.Owner).Equal("jsmith") + g.Assert(repo.Name).Equal("example") + g.Assert(repo.Avatar).Equal("http://example.com/uploads/project/avatar/555/Outh-20-Logo.jpg") + g.Assert(repo.Branch).Equal("develop") + g.Assert(build.Ref).Equal("refs/tags/v1.0.0") + }) + + g.It("Should parse legacy tag push hook", func() { + req, _ := http.NewRequest( + "POST", + "http://example.com/api/hook?owner=diaspora&name=diaspora-client", + bytes.NewReader(testdata.LegacyTagHook), + ) + + repo, build, err := gitlab.Hook(req) + + g.Assert(err == nil).IsTrue() + g.Assert(repo.Owner).Equal("diaspora") + g.Assert(repo.Name).Equal("diaspora-client") + g.Assert(build.Ref).Equal("refs/tags/v1.0.0") + + }) }) g.It("Should parse merge request hook", func() { diff --git a/remote/gitlab/helper.go b/remote/gitlab/helper.go index 616a6a04..38b3bb88 100644 --- a/remote/gitlab/helper.go +++ b/remote/gitlab/helper.go @@ -6,6 +6,7 @@ import ( "fmt" "net/url" "strconv" + "strings" "github.com/drone/drone/remote/gitlab/client" ) @@ -97,6 +98,14 @@ func GetUserAvatar(email string) string { ) } +func ExtractFromPath(str string) (string, string, error) { + s := strings.Split(str, "/") + if len(s) < 2 { + return "", "", fmt.Errorf("Minimum match not found") + } + return s[0], s[1], nil +} + func GetUserEmail(c *client.Client, defaultURL string) (*client.Client, error) { return c, nil } diff --git a/remote/gitlab/testdata/hooks.go b/remote/gitlab/testdata/hooks.go index 9f4a5cea..f23d06b0 100644 --- a/remote/gitlab/testdata/hooks.go +++ b/remote/gitlab/testdata/hooks.go @@ -1,6 +1,46 @@ package testdata var TagHook = []byte(` +{ + "object_kind": "tag_push", + "ref": "refs/tags/v1.0.0", + "before": "0000000000000000000000000000000000000000", + "after": "82b3d5ae55f7080f1e6022629cdb57bfae7cccc7", + "user_id": 1, + "user_name": "John Smith", + "user_avatar": "https://s.gravatar.com/avatar/d4c74594d841139328695756648b6bd6?s=8://s=80", + "project_id": 1, + "project":{ + "name":"Example", + "description":"", + "web_url":"http://example.com/jsmith/example", + "avatar_url":"http://example.com/uploads/project/avatar/555/Outh-20-Logo.jpg", + "git_ssh_url":"git@example.com:jsmith/example.git", + "git_http_url":"http://example.com/jsmith/example.git", + "namespace":"Jsmith", + "visibility_level":0, + "path_with_namespace":"jsmith/example", + "default_branch":"develop", + "homepage":"http://example.com/jsmith/example", + "url":"git@example.com:jsmith/example.git", + "ssh_url":"git@example.com:jsmith/example.git", + "http_url":"http://example.com/jsmith/example.git" + }, + "repository":{ + "name": "jsmith", + "url": "ssh://git@example.com/jsmith/example.git", + "description": "", + "homepage": "http://example.com/jsmith/example", + "git_http_url":"http://example.com/jsmith/example.git", + "git_ssh_url":"git@example.com:jsmith/example.git", + "visibility_level":0 + }, + "commits": [], + "total_commits_count": 0 +} +`) + +var LegacyTagHook = []byte(` { "object_kind": "tag_push", "ref": "refs/tags/v1.0.0", @@ -101,6 +141,74 @@ var MergeRequestHook = []byte(` `) var PushHook = []byte(` +{ + "object_kind": "push", + "before": "95790bf891e76fee5e1747ab589903a6a1f80f22", + "after": "da1560886d4f094c3e6c9ef40349f7d38b5d27d7", + "ref": "refs/heads/master", + "user_id": 4, + "user_name": "John Smith", + "user_email": "john@example.com", + "user_avatar": "https://s.gravatar.com/avatar/d4c74594d841139328695756648b6bd6?s=80", + "project_id": 15, + "project":{ + "name":"Diaspora", + "description":"", + "web_url":"http://example.com/mike/diaspora", + "avatar_url":"http://example.com/uploads/project/avatar/555/Outh-20-Logo.jpg", + "git_ssh_url":"git@example.com:mike/diaspora.git", + "git_http_url":"http://example.com/mike/diaspora.git", + "namespace":"Mike", + "visibility_level":0, + "path_with_namespace":"mike/diaspora", + "default_branch":"develop", + "homepage":"http://example.com/mike/diaspora", + "url":"git@example.com:mike/diasporadiaspora.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/diasporadiaspora.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_level":0 + }, + "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 LegacyPushHook = []byte(` { "object_kind": "push", "before": "95790bf891e76fee5e1747ab589903a6a1f80f22", @@ -114,7 +222,7 @@ var PushHook = []byte(` "name": "Diaspora", "url": "git@example.com:mike/diasporadiaspora.git", "description": "", - "homepage": "http://example.com/mike/diaspora", + "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_level":0 From f9c299ce57e1a7638c96304865988604706d90ce Mon Sep 17 00:00:00 2001 From: Kirill Zaitsev Date: Sun, 28 Feb 2016 20:11:50 +0300 Subject: [PATCH 18/18] Added support for new hook attributes in merge request hook --- remote/gitlab/gitlab.go | 89 ++++++++++++++++++++++++++------- remote/gitlab/gitlab_test.go | 42 ++++++++++++---- remote/gitlab/testdata/hooks.go | 80 +++++++++++++++++++++++++++++ 3 files changed, 181 insertions(+), 30 deletions(-) diff --git a/remote/gitlab/gitlab.go b/remote/gitlab/gitlab.go index 3915758a..c8c73e95 100644 --- a/remote/gitlab/gitlab.go +++ b/remote/gitlab/gitlab.go @@ -348,36 +348,87 @@ func (g *Gitlab) Hook(req *http.Request) (*model.Repo, *model.Build, error) { func mergeRequest(parsed *client.HookPayload, req *http.Request) (*model.Repo, *model.Build, error) { repo := &model.Repo{} - repo.Owner = req.FormValue("owner") - repo.Name = req.FormValue("name") - repo.FullName = fmt.Sprintf("%s/%s", repo.Owner, repo.Name) - repo.Link = parsed.ObjectAttributes.Target.WebUrl - repo.Clone = parsed.ObjectAttributes.Target.HttpUrl - repo.Branch = "master" + + obj := parsed.ObjectAttributes + if obj == nil { + return nil, nil, fmt.Errorf("object_attributes key expected in merge request hook") + } + + target := obj.Target + source := obj.Source + + if target == nil && source == nil { + return nil, nil, fmt.Errorf("target and source keys expected in merge request hook") + } else if target == nil { + return nil, nil, fmt.Errorf("target key expected in merge request hook") + } else if source == nil { + return nil, nil, fmt.Errorf("source key exptected in merge request hook") + } + + if target.PathWithNamespace != "" { + var err error + if repo.Owner, repo.Name, err = ExtractFromPath(target.PathWithNamespace); err != nil { + return nil, nil, err + } + repo.FullName = target.PathWithNamespace + } else { + repo.Owner = req.FormValue("owner") + repo.Name = req.FormValue("name") + repo.FullName = fmt.Sprintf("%s/%s", repo.Owner, repo.Name) + } + + repo.Link = target.WebUrl + + if target.GitHttpUrl != "" { + repo.Clone = target.GitHttpUrl + } else { + repo.Clone = target.HttpUrl + } + + if target.DefaultBranch != "" { + repo.Branch = target.DefaultBranch + } else { + repo.Branch = "master" + } + + if target.AvatarUrl != "" { + repo.Avatar = target.AvatarUrl + } build := &model.Build{} build.Event = "pull_request" - build.Message = parsed.ObjectAttributes.LastCommit.Message - build.Commit = parsed.ObjectAttributes.LastCommit.Id - //build.Remote = parsed.ObjectAttributes.Source.HttpUrl - if parsed.ObjectAttributes.SourceProjectId == parsed.ObjectAttributes.TargetProjectId { - build.Ref = fmt.Sprintf("refs/heads/%s", parsed.ObjectAttributes.SourceBranch) - } else { - build.Ref = fmt.Sprintf("refs/merge-requests/%d/head", parsed.ObjectAttributes.IId) + lastCommit := obj.LastCommit + if lastCommit == nil { + return nil, nil, fmt.Errorf("last_commit key expected in merge request hook") } - build.Branch = parsed.ObjectAttributes.SourceBranch - // build.Timestamp = parsed.ObjectAttributes.LastCommit.Timestamp + build.Message = lastCommit.Message + build.Commit = lastCommit.Id + //build.Remote = parsed.ObjectAttributes.Source.HttpUrl + + if obj.SourceProjectId == obj.TargetProjectId { + build.Ref = fmt.Sprintf("refs/heads/%s", obj.SourceBranch) + } else { + build.Ref = fmt.Sprintf("refs/merge-requests/%d/head", obj.IId) + } + + build.Branch = obj.SourceBranch + + author := lastCommit.Author + if author == nil { + return nil, nil, fmt.Errorf("author key expected in merge request hook") + } + + build.Author = author.Name + build.Email = author.Email - build.Author = parsed.ObjectAttributes.LastCommit.Author.Name - build.Email = parsed.ObjectAttributes.LastCommit.Author.Email if len(build.Email) != 0 { build.Avatar = GetUserAvatar(build.Email) } - build.Title = parsed.ObjectAttributes.Title - build.Link = parsed.ObjectAttributes.Url + build.Title = obj.Title + build.Link = obj.Url return repo, build, nil } diff --git a/remote/gitlab/gitlab_test.go b/remote/gitlab/gitlab_test.go index 3b555c12..2ff07270 100644 --- a/remote/gitlab/gitlab_test.go +++ b/remote/gitlab/gitlab_test.go @@ -189,20 +189,40 @@ func Test_Gitlab(t *testing.T) { }) }) - g.It("Should parse merge request hook", func() { - req, _ := http.NewRequest( - "POST", - "http://example.com/api/hook?owner=diaspora&name=diaspora-client", - bytes.NewReader(testdata.MergeRequestHook), - ) + g.Describe("Merge request hook", func() { + g.It("Should parse merge request hook", func() { + req, _ := http.NewRequest( + "POST", + "http://example.com/api/hook?owner=diaspora&name=diaspora-client", + bytes.NewReader(testdata.MergeRequestHook), + ) - repo, build, err := gitlab.Hook(req) + repo, build, err := gitlab.Hook(req) - g.Assert(err == nil).IsTrue() - g.Assert(repo.Owner).Equal("diaspora") - g.Assert(repo.Name).Equal("diaspora-client") + g.Assert(err == nil).IsTrue() + g.Assert(repo.Avatar).Equal("http://example.com/uploads/project/avatar/555/Outh-20-Logo.jpg") + g.Assert(repo.Branch).Equal("develop") + g.Assert(repo.Owner).Equal("awesome_space") + g.Assert(repo.Name).Equal("awesome_project") - g.Assert(build.Title).Equal("MS-Viewport") + g.Assert(build.Title).Equal("MS-Viewport") + }) + + g.It("Should parse legacy merge request hook", func() { + req, _ := http.NewRequest( + "POST", + "http://example.com/api/hook?owner=diaspora&name=diaspora-client", + bytes.NewReader(testdata.LegacyMergeRequestHook), + ) + + repo, build, err := gitlab.Hook(req) + + g.Assert(err == nil).IsTrue() + g.Assert(repo.Owner).Equal("diaspora") + g.Assert(repo.Name).Equal("diaspora-client") + + g.Assert(build.Title).Equal("MS-Viewport") + }) }) }) }) diff --git a/remote/gitlab/testdata/hooks.go b/remote/gitlab/testdata/hooks.go index f23d06b0..d7e01450 100644 --- a/remote/gitlab/testdata/hooks.go +++ b/remote/gitlab/testdata/hooks.go @@ -85,6 +85,86 @@ var LegacyTagHook = []byte(` `) var MergeRequestHook = []byte(` +{ + "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":"http://example.com/uploads/project/avatar/555/Outh-20-Logo.jpg", + "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":"http://example.com/uploads/project/avatar/555/Outh-20-Logo.jpg", + "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":"develop", + "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 LegacyMergeRequestHook = []byte(` { "object_kind": "merge_request", "user": {