From 2966d294fe7e30ba505aa36ea0942753269ae951 Mon Sep 17 00:00:00 2001 From: Kirill Zaitsev Date: Thu, 23 Jul 2015 20:36:23 +0300 Subject: [PATCH 01/11] Start working on specs implementation --- cmd/drone-server/drone.go | 7 +++- pkg/config/config.go | 57 ++++++++++++++++++---------- pkg/remote/builtin/github/github.go | 58 +++++++++++++++++++++-------- pkg/remote/builtin/github/helper.go | 4 +- pkg/remote/remote.go | 27 ++++++++++++++ pkg/server/login.go | 35 ++++++----------- 6 files changed, 125 insertions(+), 63 deletions(-) diff --git a/cmd/drone-server/drone.go b/cmd/drone-server/drone.go index 9f34453d5..6377e12a3 100644 --- a/cmd/drone-server/drone.go +++ b/cmd/drone-server/drone.go @@ -9,7 +9,7 @@ import ( "github.com/drone/drone/Godeps/_workspace/src/github.com/elazarl/go-bindata-assetfs" "github.com/drone/drone/pkg/config" - "github.com/drone/drone/pkg/remote/builtin/github" + "github.com/drone/drone/pkg/remote" "github.com/drone/drone/pkg/server" "github.com/drone/drone/pkg/server/session" @@ -49,7 +49,10 @@ func main() { panic(err) } - remote := github.New(settings) + remote, err := remote.New(settings) + if err != nil { + panic(err) + } session := session.New(settings) eventbus_ := eventbus.New() queue_ := queue.New() diff --git a/pkg/config/config.go b/pkg/config/config.go index c0367cc63..f227add10 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -1,7 +1,11 @@ package config import ( + "fmt" "io/ioutil" + "os" + "path" + "strings" "github.com/drone/drone/Godeps/_workspace/src/github.com/naoina/toml" "github.com/drone/drone/Godeps/_workspace/src/github.com/vrischmann/envconfig" @@ -9,12 +13,7 @@ import ( type Config struct { Remote struct { - Kind string `envconfig:"optional"` - Base string `envconfig:"optional"` - Orgs []string `envconfig:"optional"` - Open bool `envconfig:"optional"` - Private bool `envconfig:"optional"` - SkipVerify bool `envconfig:"optional"` + Driver string `envconfig:"optional"` } Auth struct { @@ -67,20 +66,14 @@ type Config struct { Plugins []string `envconfig:"optional"` Github struct { - Client string `envconfig:"optional"` - Secret string `envconfig:"optional"` - Orgs []string `envconfig:"optional"` - Open bool `envconfig:"optional"` - } - - GithubEnterprise struct { - URL string `envconfig:"optional"` - Client string `envconfig:"optional"` - Secret string `envconfig:"optional"` - Private bool `envconfig:"optional"` - SkipVerify bool `envconfig:"optional"` - Open bool `envconfig:"optional"` - Orgs []string `envconfig:"optional"` + API string `envconfig:"optional"` + Host string `envconfig:"optional"` + Client string `envconfig:"optional"` + Secret string `envconfig:"optional"` + PrivateMode bool `envconfig:"optional"` + SkipVerify bool `envconfig:"optional"` + Open bool `envconfig:"optional"` + Orgs []string `envconfig:"optional"` } Bitbucket struct { @@ -132,5 +125,29 @@ func applyDefaults(c *Config) *Config { if len(c.Session.Secret) == 0 { c.Session.Secret = c.Auth.Secret } + + // Prevent crash on start, use sqlite3 + // driver as default if DRONE_DATABASE_DRIVER and + // DRONE_DATABASE_DATASOURCE not specifed + if len(c.Database.Driver) == 0 && len(c.Database.Datasource) == 0 { + c.Database.Driver = "sqlite3" + + pwd, err := os.Getwd() + if err != nil { + panic(err) + } + + c.Database.Datasource = path.Join(pwd, "drone.sqlite3") + } + + // Set default settings for remotes + switch strings.ToLower(c.Remote.Driver) { + case "github": + if len(c.Github.API) == 0 && len(c.Github.Host) == 0 { + c.Github.API = "https://api.github.com/" + c.Github.Host = "https://github.com" + } + } + return c } diff --git a/pkg/remote/builtin/github/github.go b/pkg/remote/builtin/github/github.go index 1f50bf135..1deec0577 100644 --- a/pkg/remote/builtin/github/github.go +++ b/pkg/remote/builtin/github/github.go @@ -1,6 +1,7 @@ package github import ( + "crypto/tls" "encoding/json" "fmt" "net/http" @@ -10,14 +11,14 @@ import ( "github.com/drone/drone/Godeps/_workspace/src/github.com/hashicorp/golang-lru" "github.com/drone/drone/pkg/config" + "github.com/drone/drone/pkg/oauth2" common "github.com/drone/drone/pkg/types" + "github.com/drone/drone/pkg/utils/httputil" "github.com/drone/drone/Godeps/_workspace/src/github.com/google/go-github/github" ) const ( - DefaultAPI = "https://api.github.com/" - DefaultURL = "https://github.com" DefaultScope = "repo,repo:status,user:email" ) @@ -26,6 +27,8 @@ type GitHub struct { API string Client string Secret string + AllowedOrgs []string + Open bool PrivateMode bool SkipVerify bool @@ -34,12 +37,14 @@ type GitHub struct { func New(conf *config.Config) *GitHub { var github = GitHub{ - API: DefaultAPI, - URL: DefaultURL, - Client: conf.Auth.Client, - Secret: conf.Auth.Secret, - PrivateMode: conf.Remote.Private, - SkipVerify: conf.Remote.SkipVerify, + API: conf.Github.API, + URL: conf.Github.Host, + Client: conf.Github.Client, + Secret: conf.Github.Secret, + AllowedOrgs: conf.Github.Orgs, + Open: conf.Github.Open, + PrivateMode: conf.Github.PrivateMode, + SkipVerify: conf.Github.SkipVerify, } var err error github.cache, err = lru.New(1028) @@ -47,12 +52,6 @@ func New(conf *config.Config) *GitHub { panic(err) } - // if GitHub enterprise then ensure we're using the - // appropriate URLs - if !strings.HasPrefix(conf.Remote.Base, DefaultURL) && len(conf.Remote.Base) != 0 { - github.URL = conf.Remote.Base - github.API = conf.Remote.Base + "/api/v3/" - } // the API must have a trailing slash if !strings.HasSuffix(github.API, "/") { github.API += "/" @@ -66,7 +65,7 @@ func New(conf *config.Config) *GitHub { func (g *GitHub) Login(token, secret string) (*common.User, error) { client := NewClient(g.API, token, g.SkipVerify) - login, err := GetUserEmail(client) + login, err := GetUserEmail(client, g.URL) if err != nil { return nil, err } @@ -92,6 +91,16 @@ func (g *GitHub) Orgs(u *common.User) ([]string, error) { return orgs_, nil } +// Accessor method, to allowed remote organizations field. +func (g *GitHub) GetOrgs() []string { + return g.AllowedOrgs +} + +// Accessor method, to open field. +func (g *GitHub) GetOpen() bool { + return g.Open +} + // Repo fetches the named repository from the remote system. func (g *GitHub) Repo(u *common.User, owner, name string) (*common.Repo, error) { client := NewClient(g.API, u.Token, g.SkipVerify) @@ -281,6 +290,25 @@ func (g *GitHub) push(r *http.Request) (*common.Hook, error) { return &common.Hook{Repo: repo, Commit: commit}, nil } +// ¯\_(ツ)_/¯ +func (g *GitHub) Oauth2Transport(r *http.Request) *oauth2.Transport { + return &oauth2.Transport{ + Config: &oauth2.Config{ + ClientId: g.Client, + ClientSecret: g.Secret, + Scope: DefaultScope, + AuthURL: fmt.Sprintf("%s/login/oauth/authorize", g.URL), + TokenURL: fmt.Sprintf("%s/login/oauth/access_token", g.URL), + RedirectURL: fmt.Sprintf("%s/authorize", httputil.GetURL(r)), + //settings.Server.Scheme, settings.Server.Hostname), + }, + Transport: &http.Transport{ + Proxy: http.ProxyFromEnvironment, + TLSClientConfig: &tls.Config{InsecureSkipVerify: g.SkipVerify}, + }, + } +} + // pullRequest parses a hook with event type `pullRequest` // and returns the commit data. func (g *GitHub) pullRequest(r *http.Request) (*common.Hook, error) { diff --git a/pkg/remote/builtin/github/helper.go b/pkg/remote/builtin/github/helper.go index bedd39ceb..f54c19d6b 100644 --- a/pkg/remote/builtin/github/helper.go +++ b/pkg/remote/builtin/github/helper.go @@ -37,7 +37,7 @@ func NewClient(uri, token string, skipVerify bool) *github.Client { // GetUserEmail is a heper function that retrieves the currently // authenticated user from GitHub + Email address. -func GetUserEmail(client *github.Client) (*github.User, error) { +func GetUserEmail(client *github.Client, defaultURL string) (*github.User, error) { user, _, err := client.Users.Get("") if err != nil { return nil, err @@ -58,7 +58,7 @@ func GetUserEmail(client *github.Client) (*github.User, error) { // WARNING, HACK // for out-of-date github enterprise editions the primary // and verified fields won't exist. - if !strings.HasPrefix(*user.HTMLURL, DefaultURL) && len(emails) != 0 { + if !strings.HasPrefix(*user.HTMLURL, defaultURL) && len(emails) != 0 { user.Email = emails[0].Email return user, nil } diff --git a/pkg/remote/remote.go b/pkg/remote/remote.go index 8f95572ae..fe4486aeb 100644 --- a/pkg/remote/remote.go +++ b/pkg/remote/remote.go @@ -1,8 +1,14 @@ package remote import ( + "errors" + "fmt" "net/http" + "strings" + "github.com/drone/drone/pkg/config" + "github.com/drone/drone/pkg/oauth2" + "github.com/drone/drone/pkg/remote/builtin/github" common "github.com/drone/drone/pkg/types" ) @@ -45,6 +51,27 @@ type Remote interface { // and returns the required data in a standard format. Hook(r *http.Request) (*common.Hook, error) + // Oauth2Transport + Oauth2Transport(r *http.Request) *oauth2.Transport + + // GetOrgs returns all allowed organizations for remote. + GetOrgs() []string + + // GetOpen returns boolean field with enabled or disabled + // registration. + GetOpen() bool + // Default scope for remote Scope() string } + +func New(conf *config.Config) (Remote, error) { + switch strings.ToLower(conf.Remote.Driver) { + case "github": + return github.New(conf), nil + case "": + return nil, errors.New("Remote not specifed, please set env variable DRONE_REMOTE_DRIVER") + default: + return nil, errors.New(fmt.Sprintf("Remote driver not supported: DRONE_REMOTE_DRIVER=%s", conf.Remote.Driver)) + } +} diff --git a/pkg/server/login.go b/pkg/server/login.go index 5e73129e4..366ed5605 100644 --- a/pkg/server/login.go +++ b/pkg/server/login.go @@ -1,17 +1,13 @@ package server import ( - "fmt" - "strings" "time" "github.com/drone/drone/Godeps/_workspace/src/github.com/gin-gonic/gin" "github.com/drone/drone/Godeps/_workspace/src/github.com/ungerik/go-gravatar" log "github.com/drone/drone/Godeps/_workspace/src/github.com/Sirupsen/logrus" - "github.com/drone/drone/pkg/oauth2" common "github.com/drone/drone/pkg/types" - "github.com/drone/drone/pkg/utils/httputil" ) // GetLogin accepts a request to authorize the user and to @@ -52,9 +48,9 @@ func GetLogin(c *gin.Context) { login := ToUser(c) // check organization membership, if applicable - if len(settings.Remote.Orgs) != 0 { + if len(remote.GetOrgs()) != 0 { orgs, _ := remote.Orgs(login) - if !checkMembership(orgs, settings.Remote.Orgs) { + if !checkMembership(orgs, remote.GetOrgs()) { c.Redirect(303, "/login#error=access_denied_org") return } @@ -73,7 +69,7 @@ func GetLogin(c *gin.Context) { // if self-registration is disabled we should // return a notAuthorized error. the only exception // is if no users exist yet in the system we'll proceed. - if !settings.Remote.Open && count != 0 { + if !remote.GetOpen() && count != 0 { log.Errorf("cannot register %s. registration closed", login.Login) c.Redirect(303, "/login#error=access_denied") return @@ -131,35 +127,26 @@ func GetLogin(c *gin.Context) { // getLoginOauth2 is the default authorization implementation // using the oauth2 protocol. func getLoginOauth2(c *gin.Context) { - var settings = ToSettings(c) var remote = ToRemote(c) - var scope = strings.Join(settings.Auth.Scope, ",") - if scope == "" { - scope = remote.Scope() - } - var config = &oauth2.Config{ - ClientId: settings.Auth.Client, - ClientSecret: settings.Auth.Secret, - Scope: scope, - AuthURL: settings.Auth.Authorize, - TokenURL: settings.Auth.AccessToken, - RedirectURL: fmt.Sprintf("%s/authorize", httputil.GetURL(c.Request)), - //settings.Server.Scheme, settings.Server.Hostname), - } + // Bugagazavr: I think this must be moved to remote config + //var scope = strings.Join(settings.Auth.Scope, ",") + //if scope == "" { + // scope = remote.Scope() + //} + var transport = remote.Oauth2Transport(c.Request) // get the OAuth code var code = c.Request.FormValue("code") //var state = c.Request.FormValue("state") if len(code) == 0 { // TODO this should be a random number, verified by a cookie - c.Redirect(303, config.AuthCodeURL("random")) + c.Redirect(303, transport.AuthCodeURL("random")) return } // exhange for a token - var trans = &oauth2.Transport{Config: config} - var token, err = trans.Exchange(code) + var token, err = transport.Exchange(code) if err != nil { log.Errorf("cannot get access_token. %s", err) c.Redirect(303, "/login#error=token_exchange") From ae0d9ecc39d174256c11195aab50c1929b7b6873 Mon Sep 17 00:00:00 2001 From: Kirill Zaitsev Date: Fri, 24 Jul 2015 22:44:02 +0300 Subject: [PATCH 02/11] New driver load/register system, fixed config and returned DefaultURL for GetUserEmail method --- cmd/drone-server/drone.go | 3 ++- pkg/config/config.go | 1 - pkg/remote/builtin/github/github.go | 14 +++++++--- pkg/remote/builtin/github/helper.go | 4 +-- pkg/remote/remote.go | 42 +++++++++++++++++++---------- 5 files changed, 42 insertions(+), 22 deletions(-) diff --git a/cmd/drone-server/drone.go b/cmd/drone-server/drone.go index 6377e12a3..8d1717eff 100644 --- a/cmd/drone-server/drone.go +++ b/cmd/drone-server/drone.go @@ -19,6 +19,7 @@ import ( runner "github.com/drone/drone/pkg/runner/builtin" "github.com/drone/drone/pkg/store" + _ "github.com/drone/drone/pkg/remote/builtin/github" _ "github.com/drone/drone/pkg/store/builtin" _ "net/http/pprof" @@ -49,7 +50,7 @@ func main() { panic(err) } - remote, err := remote.New(settings) + remote, err := remote.New(settings.Remote.Driver, settings) if err != nil { panic(err) } diff --git a/pkg/config/config.go b/pkg/config/config.go index f227add10..f7805a6d4 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -1,7 +1,6 @@ package config import ( - "fmt" "io/ioutil" "os" "path" diff --git a/pkg/remote/builtin/github/github.go b/pkg/remote/builtin/github/github.go index 1deec0577..796619ce6 100644 --- a/pkg/remote/builtin/github/github.go +++ b/pkg/remote/builtin/github/github.go @@ -12,6 +12,7 @@ import ( "github.com/drone/drone/Godeps/_workspace/src/github.com/hashicorp/golang-lru" "github.com/drone/drone/pkg/config" "github.com/drone/drone/pkg/oauth2" + "github.com/drone/drone/pkg/remote" common "github.com/drone/drone/pkg/types" "github.com/drone/drone/pkg/utils/httputil" @@ -19,6 +20,7 @@ import ( ) const ( + DefaultURL = "https://github.com" DefaultScope = "repo,repo:status,user:email" ) @@ -35,7 +37,11 @@ type GitHub struct { cache *lru.Cache } -func New(conf *config.Config) *GitHub { +func init() { + remote.Register("github", NewDriver) +} + +func NewDriver(conf *config.Config) (remote.Remote, error) { var github = GitHub{ API: conf.Github.API, URL: conf.Github.Host, @@ -49,7 +55,7 @@ func New(conf *config.Config) *GitHub { var err error github.cache, err = lru.New(1028) if err != nil { - panic(err) + return nil, err } // the API must have a trailing slash @@ -60,12 +66,12 @@ func New(conf *config.Config) *GitHub { if strings.HasSuffix(github.URL, "/") { github.URL = github.URL[:len(github.URL)-1] } - return &github + return &github, nil } func (g *GitHub) Login(token, secret string) (*common.User, error) { client := NewClient(g.API, token, g.SkipVerify) - login, err := GetUserEmail(client, g.URL) + login, err := GetUserEmail(client) if err != nil { return nil, err } diff --git a/pkg/remote/builtin/github/helper.go b/pkg/remote/builtin/github/helper.go index f54c19d6b..bedd39ceb 100644 --- a/pkg/remote/builtin/github/helper.go +++ b/pkg/remote/builtin/github/helper.go @@ -37,7 +37,7 @@ func NewClient(uri, token string, skipVerify bool) *github.Client { // GetUserEmail is a heper function that retrieves the currently // authenticated user from GitHub + Email address. -func GetUserEmail(client *github.Client, defaultURL string) (*github.User, error) { +func GetUserEmail(client *github.Client) (*github.User, error) { user, _, err := client.Users.Get("") if err != nil { return nil, err @@ -58,7 +58,7 @@ func GetUserEmail(client *github.Client, defaultURL string) (*github.User, error // WARNING, HACK // for out-of-date github enterprise editions the primary // and verified fields won't exist. - if !strings.HasPrefix(*user.HTMLURL, defaultURL) && len(emails) != 0 { + if !strings.HasPrefix(*user.HTMLURL, DefaultURL) && len(emails) != 0 { user.Email = emails[0].Email return user, nil } diff --git a/pkg/remote/remote.go b/pkg/remote/remote.go index fe4486aeb..0da719816 100644 --- a/pkg/remote/remote.go +++ b/pkg/remote/remote.go @@ -1,17 +1,42 @@ package remote import ( - "errors" "fmt" "net/http" - "strings" "github.com/drone/drone/pkg/config" "github.com/drone/drone/pkg/oauth2" - "github.com/drone/drone/pkg/remote/builtin/github" common "github.com/drone/drone/pkg/types" ) +var drivers = make(map[string]DriverFunc) + +// Register makes a remote driver available by the provided name. +// If Register is called twice with the same name or if driver is nil, +// it panics. +func Register(name string, driver DriverFunc) { + if driver == nil { + panic("remote: Register driver is nil") + } + if _, dup := drivers[name]; dup { + panic("remote: Register called twice for driver " + name) + } + drivers[name] = driver +} + +// DriverFunc returns a new connection to the remote. +// Config is a struct, with base remote configuration. +type DriverFunc func(conf *config.Config) (Remote, error) + +// New creates a new remote connection. +func New(driver string, conf *config.Config) (Remote, error) { + fn, ok := drivers[driver] + if !ok { + return nil, fmt.Errorf("remote: unknown driver %q", driver) + } + return fn(conf) +} + type Remote interface { // Login authenticates the session and returns the // remote user details. @@ -64,14 +89,3 @@ type Remote interface { // Default scope for remote Scope() string } - -func New(conf *config.Config) (Remote, error) { - switch strings.ToLower(conf.Remote.Driver) { - case "github": - return github.New(conf), nil - case "": - return nil, errors.New("Remote not specifed, please set env variable DRONE_REMOTE_DRIVER") - default: - return nil, errors.New(fmt.Sprintf("Remote driver not supported: DRONE_REMOTE_DRIVER=%s", conf.Remote.Driver)) - } -} From ddd4d88d0e4873c28e564305f420854168213517 Mon Sep 17 00:00:00 2001 From: Kirill Zaitsev Date: Fri, 24 Jul 2015 23:07:01 +0300 Subject: [PATCH 03/11] Github set API host automatically, if base host exists, show warns about default settings --- pkg/config/config.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/pkg/config/config.go b/pkg/config/config.go index f7805a6d4..cf477566f 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -1,11 +1,13 @@ package config import ( + "fmt" "io/ioutil" "os" "path" "strings" + log "github.com/drone/drone/Godeps/_workspace/src/github.com/Sirupsen/logrus" "github.com/drone/drone/Godeps/_workspace/src/github.com/naoina/toml" "github.com/drone/drone/Godeps/_workspace/src/github.com/vrischmann/envconfig" ) @@ -137,6 +139,7 @@ func applyDefaults(c *Config) *Config { } c.Database.Datasource = path.Join(pwd, "drone.sqlite3") + log.Warnf("Use default database settings, driver: %q, config: %q", c.Database.Driver, c.Database.Datasource) } // Set default settings for remotes @@ -145,6 +148,10 @@ func applyDefaults(c *Config) *Config { if len(c.Github.API) == 0 && len(c.Github.Host) == 0 { c.Github.API = "https://api.github.com/" c.Github.Host = "https://github.com" + log.Warnf("Use default github settings, host: %q, api: %q", c.Github.Host, c.Github.API) + } else if len(c.Github.API) == 0 && len(c.Github.Host) != 0 { + c.Github.API = fmt.Sprintf("%s/api/v3/", c.Github.Host) + log.Warnf("Github API not specified, use: %q", c.Github.API) } } From 51351d9a9d02f5fef6ee7944e8b2c092f3dbae62 Mon Sep 17 00:00:00 2001 From: letusfly85 Date: Sat, 25 Jul 2015 17:49:39 +0900 Subject: [PATCH 04/11] add gitlab imp --- cmd/drone-server/drone.go | 1 + pkg/remote/gitlab/gitlab.go | 374 ++++++++++++++++++++++++++++++++++++ pkg/remote/gitlab/helper.go | 92 +++++++++ 3 files changed, 467 insertions(+) create mode 100644 pkg/remote/gitlab/gitlab.go create mode 100644 pkg/remote/gitlab/helper.go diff --git a/cmd/drone-server/drone.go b/cmd/drone-server/drone.go index 8d1717eff..1ce65b988 100644 --- a/cmd/drone-server/drone.go +++ b/cmd/drone-server/drone.go @@ -20,6 +20,7 @@ import ( "github.com/drone/drone/pkg/store" _ "github.com/drone/drone/pkg/remote/builtin/github" + _ "github.com/drone/drone/pkg/remote/gitlab" _ "github.com/drone/drone/pkg/store/builtin" _ "net/http/pprof" diff --git a/pkg/remote/gitlab/gitlab.go b/pkg/remote/gitlab/gitlab.go new file mode 100644 index 000000000..499c9a795 --- /dev/null +++ b/pkg/remote/gitlab/gitlab.go @@ -0,0 +1,374 @@ +package gitlab + +import ( + "crypto/tls" + "fmt" + "io/ioutil" + "net/http" + "net/url" + "strconv" + "strings" + + "github.com/Bugagazavr/go-gitlab-client" + "github.com/drone/drone/Godeps/_workspace/src/github.com/hashicorp/golang-lru" + "github.com/drone/drone/pkg/config" + "github.com/drone/drone/pkg/oauth2" + "github.com/drone/drone/pkg/remote" + common "github.com/drone/drone/pkg/types" + "github.com/drone/drone/pkg/utils/httputil" +) + +const ( + DefaultScope = "repo" +) + +const ( + StatusPending = "pending" + StatusSuccess = "success" + StatusFailure = "failure" + StatusError = "error" +) + +const ( + DescPending = "this build is pending" + DescSuccess = "the build was successful" + DescFailure = "the build failed" + DescError = "oops, something went wrong" +) + +type Gitlab struct { + URL string + Client string + Secret string + AllowedOrgs []string + Open bool + PrivateMode bool + SkipVerify bool + + cache *lru.Cache +} + +func init() { + remote.Register("gitlab", NewDriver) +} + +func NewDriver(conf *config.Config) (remote.Remote, error) { + var gitlab = Gitlab{ + URL: conf.Gitlab.URL, + Client: conf.Gitlab.Client, + Secret: conf.Gitlab.Secret, + AllowedOrgs: conf.Gitlab.Orgs, + Open: conf.Gitlab.Open, + SkipVerify: conf.Gitlab.SkipVerify, + } + var err error + gitlab.cache, err = lru.New(1028) + if err != nil { + return nil, err + } + + // the URL must NOT have a trailing slash + if strings.HasSuffix(gitlab.URL, "/") { + gitlab.URL = gitlab.URL[:len(gitlab.URL)-1] + } + return &gitlab, nil +} + +func (r *Gitlab) Login(token, secret string) (*common.User, error) { + client := NewClient(r.URL, token, r.SkipVerify) + var login, err = client.CurrentUser() + if err != nil { + return nil, err + } + user := common.User{} + user.Login = login.Username + user.Email = login.Email + user.Token = token + user.Secret = secret + return &user, nil +} + +// Orgs fetches the organizations for the given user. +func (r *Gitlab) Orgs(u *common.User) ([]string, error) { + return nil, nil +} + +// Repo fetches the named repository from the remote system. +func (r *Gitlab) Repo(u *common.User, owner, name string) (*common.Repo, error) { + client := NewClient(r.URL, u.Token, r.SkipVerify) + id := ns(owner, name) + repo_, err := client.Project(id) + if err != nil { + return nil, err + } + + repo := &common.Repo{} + repo.Owner = owner + repo.Name = name + repo.FullName = repo_.PathWithNamespace + repo.Link = repo_.Url + repo.Clone = repo_.HttpRepoUrl + repo.Branch = "master" + + if repo_.DefaultBranch != "" { + repo.Branch = repo_.DefaultBranch + } + + if r.PrivateMode { + repo.Private = true + } + return repo, err +} + +// Perm fetches the named repository from the remote system. +func (r *Gitlab) Perm(u *common.User, owner, name string) (*common.Perm, error) { + key := fmt.Sprintf("%s/%s/%s", u.Login, owner, name) + val, ok := r.cache.Get(key) + if ok { + return val.(*common.Perm), nil + } + + client := NewClient(r.URL, u.Token, r.SkipVerify) + id := ns(owner, name) + repo, err := client.Project(id) + if err != nil { + return nil, err + } + m := &common.Perm{} + m.Admin = IsAdmin(repo) + m.Pull = IsRead(repo) + m.Push = IsWrite(repo) + r.cache.Add(key, m) + return m, nil +} + +// GetScript fetches the build script (.drone.yml) from the remote +// repository and returns in string format. +func (r *Gitlab) Script(user *common.User, repo *common.Repo, build *common.Build) ([]byte, error) { + var client = NewClient(r.URL, user.Token, r.SkipVerify) + var path = ns(repo.Owner, repo.Name) + return client.RepoRawFile(path, build.Commit.Sha, ".drone.yml") +} + +func (r *Gitlab) Status(u *common.User, repo *common.Repo, b *common.Build) error { + // how can I get Gitlab status + return nil +} + +// Netrc returns a .netrc file that can be used to clone +// private repositories from a remote system. +func (r *Gitlab) Netrc(u *common.User) (*common.Netrc, error) { + url_, err := url.Parse(r.URL) + if err != nil { + return nil, err + } + netrc := &common.Netrc{} + netrc.Login = u.Token + netrc.Password = "x-oauth-basic" + netrc.Machine = url_.Host + return netrc, nil +} + +// Activate activates a repository by adding a Post-commit hook and +// a Public Deploy key, if applicable. +func (r *Gitlab) Activate(user *common.User, repo *common.Repo, keys *common.Keypair, link string) error { + var client = NewClient(r.URL, user.Token, r.SkipVerify) + var path = ns(repo.Owner, repo.Name) + var title, err = GetKeyTitle(link) + if err != nil { + return err + } + + // if the repository is private we'll need + // to upload a github key to the repository + if repo.Private { + var err = client.AddProjectDeployKey(path, title, repo.Keys.Public) + if err != nil { + return err + } + } + + // append the repo owner / name to the hook url since gitlab + // doesn't send this detail in the post-commit hook + link += "?owner=" + repo.Owner + "&name=" + repo.Name + + // add the hook + return client.AddProjectHook(path, link, true, false, true) +} + +// Deactivate removes a repository by removing all the post-commit hooks +// which are equal to link and removing the SSH deploy key. +func (r *Gitlab) Deactivate(user *common.User, repo *common.Repo, link string) error { + var client = NewClient(r.URL, user.Token, r.SkipVerify) + var path = ns(repo.Owner, repo.Name) + + keys, err := client.ProjectDeployKeys(path) + if err != nil { + return err + } + var pubkey = strings.TrimSpace(repo.Keys.Public) + for _, k := range keys { + if pubkey == strings.TrimSpace(k.Key) { + if err := client.RemoveProjectDeployKey(path, strconv.Itoa(k.Id)); err != nil { + return err + } + break + } + } + hooks, err := client.ProjectHooks(path) + if err != nil { + return err + } + link += "?owner=" + repo.Owner + "&name=" + repo.Name + for _, h := range hooks { + if link == h.Url { + if err := client.RemoveProjectHook(path, strconv.Itoa(h.Id)); err != nil { + return err + } + break + } + } + return nil +} + +// ParseHook parses the post-commit hook from the Request body +// and returns the required data in a standard format. +func (r *Gitlab) Hook(req *http.Request) (*common.Hook, error) { + + defer req.Body.Close() + var payload, _ = ioutil.ReadAll(req.Body) + var parsed, err = gogitlab.ParseHook(payload) + if err != nil { + return nil, err + } + + if len(parsed.After) == 0 || parsed.TotalCommitsCount == 0 { + return nil, nil + } + + if parsed.ObjectKind == "merge_request" { + // TODO (bradrydzewski) figure out how to handle merge requests + return nil, nil + } + + if len(parsed.After) == 0 { + return nil, nil + } + + var hook = new(common.Hook) + hook.Repo.Owner = req.FormValue("owner") + hook.Repo.Name = req.FormValue("name") + hook.Commit.Sha = parsed.After + hook.Commit.Branch = parsed.Branch() + + var head = parsed.Head() + hook.Commit.Message = head.Message + hook.Commit.Timestamp = head.Timestamp + + // extracts the commit author (ideally email) + // from the post-commit hook + switch { + case head.Author != nil: + hook.Commit.Author.Login = head.Author.Email + case head.Author == nil: + hook.Commit.Author.Login = parsed.UserName + } + + return hook, nil +} + +// ¯\_(ツ)_/¯ +func (g *Gitlab) Oauth2Transport(r *http.Request) *oauth2.Transport { + return &oauth2.Transport{ + Config: &oauth2.Config{ + ClientId: g.Client, + ClientSecret: g.Secret, + Scope: DefaultScope, + AuthURL: fmt.Sprintf("%s/oauth/authorize", g.URL), + TokenURL: fmt.Sprintf("%s/oauth/access_token", g.URL), + RedirectURL: fmt.Sprintf("%s/authorize", httputil.GetURL(r)), + //settings.Server.Scheme, settings.Server.Hostname), + }, + Transport: &http.Transport{ + Proxy: http.ProxyFromEnvironment, + TLSClientConfig: &tls.Config{InsecureSkipVerify: g.SkipVerify}, + }, + } +} + +// Accessor method, to allowed remote organizations field. +func (r *Gitlab) GetOrgs() []string { + return r.AllowedOrgs +} + +// Accessor method, to open field. +func (r *Gitlab) GetOpen() bool { + return r.Open +} + +// return default scope for GitHub +func (r *Gitlab) Scope() string { + return DefaultScope +} + +// getStatus is a helper functin that converts a Drone +// status to a GitHub status. +func getStatus(status string) string { + switch status { + case common.StatePending, common.StateRunning: + return StatusPending + case common.StateSuccess: + return StatusSuccess + case common.StateFailure: + return StatusFailure + case common.StateError, common.StateKilled: + return StatusError + default: + return StatusError + } +} + +func New(conf *config.Config) *Gitlab { + var gitlab = Gitlab{ + URL: conf.Gitlab.URL, + Client: conf.Gitlab.Client, + Secret: conf.Gitlab.Secret, + SkipVerify: conf.Gitlab.SkipVerify, + AllowedOrgs: conf.Gitlab.Orgs, + Open: conf.Gitlab.Open, + } + var err error + gitlab.cache, err = lru.New(1028) + if err != nil { + panic(err) + } + + // the URL must NOT have a trailing slash + if strings.HasSuffix(gitlab.URL, "/") { + gitlab.URL = gitlab.URL[:len(gitlab.URL)-1] + } + return &gitlab +} + +// GetHost returns the hostname of this remote GitHub instance. +func (r *Gitlab) GetHost() string { + uri, _ := url.Parse(r.URL) + return uri.Host +} + +// getDesc is a helper function that generates a description +// message for the build based on the status. +func getDesc(status string) string { + switch status { + case common.StatePending, common.StateRunning: + return DescPending + case common.StateSuccess: + return DescSuccess + case common.StateFailure: + return DescFailure + case common.StateError, common.StateKilled: + return DescError + default: + return DescError + } +} diff --git a/pkg/remote/gitlab/helper.go b/pkg/remote/gitlab/helper.go new file mode 100644 index 000000000..4d3fc1b89 --- /dev/null +++ b/pkg/remote/gitlab/helper.go @@ -0,0 +1,92 @@ +package gitlab + +import ( + "encoding/base32" + "fmt" + "net/url" + + "github.com/Bugagazavr/go-gitlab-client" + "github.com/gorilla/securecookie" +) + +// NewClient is a helper function that returns a new GitHub +// client using the provided OAuth token. +func NewClient(url, accessToken string, skipVerify bool) *gogitlab.Gitlab { + client := gogitlab.NewGitlabCert(url, "/api/v3", accessToken, skipVerify) + client.Bearer = true + return client +} + +// IsRead is a helper function that returns true if the +// user has Read-only access to the repository. +func IsRead(proj *gogitlab.Project) bool { + var user = proj.Permissions.ProjectAccess + var group = proj.Permissions.GroupAccess + + switch { + case proj.Public: + return true + case user != nil && user.AccessLevel >= 20: + return true + case group != nil && group.AccessLevel >= 20: + return true + default: + return false + } +} + +// IsWrite is a helper function that returns true if the +// user has Read-Write access to the repository. +func IsWrite(proj *gogitlab.Project) bool { + var user = proj.Permissions.ProjectAccess + var group = proj.Permissions.GroupAccess + + switch { + case user != nil && user.AccessLevel >= 30: + return true + case group != nil && group.AccessLevel >= 30: + return true + default: + return false + } +} + +// IsAdmin is a helper function that returns true if the +// user has Admin access to the repository. +func IsAdmin(proj *gogitlab.Project) bool { + var user = proj.Permissions.ProjectAccess + var group = proj.Permissions.GroupAccess + + switch { + case user != nil && user.AccessLevel >= 40: + return true + case group != nil && group.AccessLevel >= 40: + return true + default: + return false + } +} + +// GetKeyTitle is a helper function that generates a title for the +// RSA public key based on the username and domain name. +func GetKeyTitle(rawurl string) (string, error) { + var uri, err = url.Parse(rawurl) + if err != nil { + return "", err + } + return fmt.Sprintf("drone@%s", uri.Host), nil +} + +func ns(owner, name string) string { + return fmt.Sprintf("%s%%2F%s", owner, name) +} + +func GetUserEmail(client *gogitlab.Gitlab, defaultURL string) (*gogitlab.Gitlab, error) { + return client, nil +} + +// GetRandom is a helper function that generates a 32-bit random +// key, base32 encoded as a string value. +func GetRandom() string { + return base32.StdEncoding.EncodeToString(securecookie.GenerateRandomKey(32)) +} From 7baf72156194a465c19ac674d9995f6a64d6f044 Mon Sep 17 00:00:00 2001 From: letusfly85 Date: Sun, 26 Jul 2015 06:06:06 +0900 Subject: [PATCH 05/11] remove unnecessary functions --- pkg/remote/gitlab/gitlab.go | 153 +++++++++--------------------------- pkg/remote/gitlab/helper.go | 8 -- 2 files changed, 37 insertions(+), 124 deletions(-) diff --git a/pkg/remote/gitlab/gitlab.go b/pkg/remote/gitlab/gitlab.go index 499c9a795..eb566e245 100644 --- a/pkg/remote/gitlab/gitlab.go +++ b/pkg/remote/gitlab/gitlab.go @@ -5,7 +5,6 @@ import ( "fmt" "io/ioutil" "net/http" - "net/url" "strconv" "strings" @@ -22,20 +21,6 @@ const ( DefaultScope = "repo" ) -const ( - StatusPending = "pending" - StatusSuccess = "success" - StatusFailure = "failure" - StatusError = "error" -) - -const ( - DescPending = "this build is pending" - DescSuccess = "the build was successful" - DescFailure = "the build failed" - DescError = "oops, something went wrong" -) - type Gitlab struct { URL string Client string @@ -150,23 +135,18 @@ func (r *Gitlab) Script(user *common.User, repo *common.Repo, build *common.Buil return client.RepoRawFile(path, build.Commit.Sha, ".drone.yml") } +// NOTE Currently gitlab doesn't support status for commits and events, +// also if we want get MR status in gitlab we need implement a special plugin for gitlab, +// gitlab uses API to fetch build status on client side. But for now we skip this. func (r *Gitlab) Status(u *common.User, repo *common.Repo, b *common.Build) error { - // how can I get Gitlab status return nil } // Netrc returns a .netrc file that can be used to clone // private repositories from a remote system. +// NOTE gitlab does not support this, so now we skip this. func (r *Gitlab) Netrc(u *common.User) (*common.Netrc, error) { - url_, err := url.Parse(r.URL) - if err != nil { - return nil, err - } - netrc := &common.Netrc{} - netrc.Login = u.Token - netrc.Password = "x-oauth-basic" - netrc.Machine = url_.Host - return netrc, nil + return nil, nil } // Activate activates a repository by adding a Post-commit hook and @@ -235,45 +215,48 @@ func (r *Gitlab) Deactivate(user *common.User, repo *common.Repo, link string) e // and returns the required data in a standard format. func (r *Gitlab) Hook(req *http.Request) (*common.Hook, error) { - defer req.Body.Close() var payload, _ = ioutil.ReadAll(req.Body) - var parsed, err = gogitlab.ParseHook(payload) - if err != nil { - return nil, err - } + var parsed, _ = gogitlab.ParseHook(payload) + obj := parsed.ObjectAttributes - if len(parsed.After) == 0 || parsed.TotalCommitsCount == 0 { - return nil, nil - } - - if parsed.ObjectKind == "merge_request" { - // TODO (bradrydzewski) figure out how to handle merge requests - return nil, nil - } - - if len(parsed.After) == 0 { + if !(obj.State == "opened" && obj.MergeStatus == "unchecked") { return nil, nil } var hook = new(common.Hook) - hook.Repo.Owner = req.FormValue("owner") - hook.Repo.Name = req.FormValue("name") - hook.Commit.Sha = parsed.After - hook.Commit.Branch = parsed.Branch() - var head = parsed.Head() - hook.Commit.Message = head.Message - hook.Commit.Timestamp = head.Timestamp + hook.Repo.Name = obj.Source.Name + hook.Repo.Owner = obj.Source.Namespace - // extracts the commit author (ideally email) - // from the post-commit hook - switch { - case head.Author != nil: - hook.Commit.Author.Login = head.Author.Email - case head.Author == nil: - hook.Commit.Author.Login = parsed.UserName + // Check pull request comes from public fork + if obj.Source.VisibilityLevel < 20 { + //hook.SourceRemote = obj.Source.SshUrl + // If pull request source repo is not a public + // check for non-internal pull request + if obj.Source.Name != obj.Target.Name || obj.Source.Namespace != obj.Target.Namespace { + return nil, nil + } + } else { + //hook.SourceRemote = obj.Source.HttpUrl } + hook.Commit.Author.Login = req.FormValue("owner") + //hook.Repo = req.FormValue("name") + hook.Commit.Sha = obj.LastCommit.Id + hook.Commit.Branch = obj.TargetBranch + //hook.Commit.SourceBranch = obj.SourceBranch + hook.Commit.Timestamp = obj.LastCommit.Timestamp + hook.Commit.Message = obj.Title + + if obj.LastCommit.Author == nil { + // Waiting for merge https://github.com/gitlabhq/gitlabhq/pull/7967 + hook.Commit.Author.Email = "" + } else { + hook.Commit.Author.Email = obj.LastCommit.Author.Email + } + + hook.PullRequest.Number = obj.IId + return hook, nil } @@ -310,65 +293,3 @@ func (r *Gitlab) GetOpen() bool { func (r *Gitlab) Scope() string { return DefaultScope } - -// getStatus is a helper functin that converts a Drone -// status to a GitHub status. -func getStatus(status string) string { - switch status { - case common.StatePending, common.StateRunning: - return StatusPending - case common.StateSuccess: - return StatusSuccess - case common.StateFailure: - return StatusFailure - case common.StateError, common.StateKilled: - return StatusError - default: - return StatusError - } -} - -func New(conf *config.Config) *Gitlab { - var gitlab = Gitlab{ - URL: conf.Gitlab.URL, - Client: conf.Gitlab.Client, - Secret: conf.Gitlab.Secret, - SkipVerify: conf.Gitlab.SkipVerify, - AllowedOrgs: conf.Gitlab.Orgs, - Open: conf.Gitlab.Open, - } - var err error - gitlab.cache, err = lru.New(1028) - if err != nil { - panic(err) - } - - // the URL must NOT have a trailing slash - if strings.HasSuffix(gitlab.URL, "/") { - gitlab.URL = gitlab.URL[:len(gitlab.URL)-1] - } - return &gitlab -} - -// GetHost returns the hostname of this remote GitHub instance. -func (r *Gitlab) GetHost() string { - uri, _ := url.Parse(r.URL) - return uri.Host -} - -// getDesc is a helper function that generates a description -// message for the build based on the status. -func getDesc(status string) string { - switch status { - case common.StatePending, common.StateRunning: - return DescPending - case common.StateSuccess: - return DescSuccess - case common.StateFailure: - return DescFailure - case common.StateError, common.StateKilled: - return DescError - default: - return DescError - } -} diff --git a/pkg/remote/gitlab/helper.go b/pkg/remote/gitlab/helper.go index 4d3fc1b89..39339196e 100644 --- a/pkg/remote/gitlab/helper.go +++ b/pkg/remote/gitlab/helper.go @@ -1,12 +1,10 @@ package gitlab import ( - "encoding/base32" "fmt" "net/url" "github.com/Bugagazavr/go-gitlab-client" - "github.com/gorilla/securecookie" ) // NewClient is a helper function that returns a new GitHub @@ -84,9 +82,3 @@ func ns(owner, name string) string { func GetUserEmail(client *gogitlab.Gitlab, defaultURL string) (*gogitlab.Gitlab, error) { return client, nil } - -// GetRandom is a helper function that generates a 32-bit random -// key, base32 encoded as a string value. -func GetRandom() string { - return base32.StdEncoding.EncodeToString(securecookie.GenerateRandomKey(32)) -} From b516cb57827112d9c1415f963e52d8ff48777bbe Mon Sep 17 00:00:00 2001 From: letusfly85 Date: Sun, 26 Jul 2015 07:52:57 +0900 Subject: [PATCH 06/11] add note for hook function --- pkg/remote/gitlab/gitlab.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pkg/remote/gitlab/gitlab.go b/pkg/remote/gitlab/gitlab.go index eb566e245..087f31931 100644 --- a/pkg/remote/gitlab/gitlab.go +++ b/pkg/remote/gitlab/gitlab.go @@ -213,6 +213,8 @@ func (r *Gitlab) Deactivate(user *common.User, repo *common.Repo, link string) e // ParseHook parses the post-commit hook from the Request body // and returns the required data in a standard format. +// NOTE: in gitlab 8.0, gitlab will get same MR models as github +// https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/981/diffs func (r *Gitlab) Hook(req *http.Request) (*common.Hook, error) { var payload, _ = ioutil.ReadAll(req.Body) From dc1ee4a5f94e82c83133ae5192751d92a399b24b Mon Sep 17 00:00:00 2001 From: letusfly85 Date: Sun, 26 Jul 2015 08:02:33 +0900 Subject: [PATCH 07/11] return nil when pull request comes from gitlab and add note --- pkg/remote/gitlab/gitlab.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/pkg/remote/gitlab/gitlab.go b/pkg/remote/gitlab/gitlab.go index 087f31931..b1d51b4d6 100644 --- a/pkg/remote/gitlab/gitlab.go +++ b/pkg/remote/gitlab/gitlab.go @@ -213,14 +213,18 @@ func (r *Gitlab) Deactivate(user *common.User, repo *common.Repo, link string) e // ParseHook parses the post-commit hook from the Request body // and returns the required data in a standard format. -// NOTE: in gitlab 8.0, gitlab will get same MR models as github -// https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/981/diffs func (r *Gitlab) Hook(req *http.Request) (*common.Hook, error) { var payload, _ = ioutil.ReadAll(req.Body) var parsed, _ = gogitlab.ParseHook(payload) - obj := parsed.ObjectAttributes + if parsed.ObjectKind == "merge_request" { + // NOTE: in gitlab 8.0, gitlab will get same MR models as github + // https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/981/diffs + return nil, nil + } + + obj := parsed.ObjectAttributes if !(obj.State == "opened" && obj.MergeStatus == "unchecked") { return nil, nil } @@ -238,15 +242,11 @@ func (r *Gitlab) Hook(req *http.Request) (*common.Hook, error) { if obj.Source.Name != obj.Target.Name || obj.Source.Namespace != obj.Target.Namespace { return nil, nil } - } else { - //hook.SourceRemote = obj.Source.HttpUrl } hook.Commit.Author.Login = req.FormValue("owner") - //hook.Repo = req.FormValue("name") hook.Commit.Sha = obj.LastCommit.Id hook.Commit.Branch = obj.TargetBranch - //hook.Commit.SourceBranch = obj.SourceBranch hook.Commit.Timestamp = obj.LastCommit.Timestamp hook.Commit.Message = obj.Title From a3d227fb1220ea6daf1c177c4e6a82741ce477cf Mon Sep 17 00:00:00 2001 From: letusfly85 Date: Sun, 26 Jul 2015 08:22:16 +0900 Subject: [PATCH 08/11] remove merge request scope --- pkg/remote/gitlab/gitlab.go | 53 +++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 29 deletions(-) diff --git a/pkg/remote/gitlab/gitlab.go b/pkg/remote/gitlab/gitlab.go index b1d51b4d6..8ee9363a7 100644 --- a/pkg/remote/gitlab/gitlab.go +++ b/pkg/remote/gitlab/gitlab.go @@ -214,9 +214,16 @@ func (r *Gitlab) Deactivate(user *common.User, repo *common.Repo, link string) e // ParseHook parses the post-commit hook from the Request body // and returns the required data in a standard format. func (r *Gitlab) Hook(req *http.Request) (*common.Hook, error) { - + defer req.Body.Close() var payload, _ = ioutil.ReadAll(req.Body) - var parsed, _ = gogitlab.ParseHook(payload) + var parsed, err = gogitlab.ParseHook(payload) + if err != nil { + return nil, err + } + + if len(parsed.After) == 0 || parsed.TotalCommitsCount == 0 { + return nil, nil + } if parsed.ObjectKind == "merge_request" { // NOTE: in gitlab 8.0, gitlab will get same MR models as github @@ -224,41 +231,29 @@ func (r *Gitlab) Hook(req *http.Request) (*common.Hook, error) { return nil, nil } - obj := parsed.ObjectAttributes - if !(obj.State == "opened" && obj.MergeStatus == "unchecked") { + if len(parsed.After) == 0 { return nil, nil } var hook = new(common.Hook) + hook.Repo.Owner = req.FormValue("owner") + hook.Repo.Name = req.FormValue("name") + hook.Commit.Sha = parsed.After + hook.Commit.Branch = parsed.Branch() - hook.Repo.Name = obj.Source.Name - hook.Repo.Owner = obj.Source.Namespace + var head = parsed.Head() + hook.Commit.Message = head.Message + hook.Commit.Timestamp = head.Timestamp - // Check pull request comes from public fork - if obj.Source.VisibilityLevel < 20 { - //hook.SourceRemote = obj.Source.SshUrl - // If pull request source repo is not a public - // check for non-internal pull request - if obj.Source.Name != obj.Target.Name || obj.Source.Namespace != obj.Target.Namespace { - return nil, nil - } + // extracts the commit author (ideally email) + // from the post-commit hook + switch { + case head.Author != nil: + hook.Commit.Author.Email = head.Author.Email + case head.Author == nil: + hook.Commit.Author.Login = parsed.UserName } - hook.Commit.Author.Login = req.FormValue("owner") - hook.Commit.Sha = obj.LastCommit.Id - hook.Commit.Branch = obj.TargetBranch - hook.Commit.Timestamp = obj.LastCommit.Timestamp - hook.Commit.Message = obj.Title - - if obj.LastCommit.Author == nil { - // Waiting for merge https://github.com/gitlabhq/gitlabhq/pull/7967 - hook.Commit.Author.Email = "" - } else { - hook.Commit.Author.Email = obj.LastCommit.Author.Email - } - - hook.PullRequest.Number = obj.IId - return hook, nil } From f889a294827650e1e6b78403703e1962194068b2 Mon Sep 17 00:00:00 2001 From: Kirill Zaitsev Date: Sun, 26 Jul 2015 15:32:49 +0300 Subject: [PATCH 09/11] More fixes for gitlab --- Makefile | 1 + cmd/drone-server/drone.go | 2 +- pkg/config/config.go | 2 +- pkg/remote/{ => builtin}/gitlab/gitlab.go | 42 ++++++++++++++++++----- pkg/remote/{ => builtin}/gitlab/helper.go | 0 5 files changed, 37 insertions(+), 10 deletions(-) rename pkg/remote/{ => builtin}/gitlab/gitlab.go (87%) rename pkg/remote/{ => builtin}/gitlab/helper.go (100%) diff --git a/Makefile b/Makefile index f54edc7ad..713be80d6 100644 --- a/Makefile +++ b/Makefile @@ -7,6 +7,7 @@ all: concat bindata build deps: go get github.com/jteeuwen/go-bindata/... + go get github.com/Bugagazavr/go-gitlab-client... test: go vet github.com/drone/drone/pkg/... diff --git a/cmd/drone-server/drone.go b/cmd/drone-server/drone.go index 1ce65b988..159e80de7 100644 --- a/cmd/drone-server/drone.go +++ b/cmd/drone-server/drone.go @@ -20,7 +20,7 @@ import ( "github.com/drone/drone/pkg/store" _ "github.com/drone/drone/pkg/remote/builtin/github" - _ "github.com/drone/drone/pkg/remote/gitlab" + _ "github.com/drone/drone/pkg/remote/builtin/gitlab" _ "github.com/drone/drone/pkg/store/builtin" _ "net/http/pprof" diff --git a/pkg/config/config.go b/pkg/config/config.go index cf477566f..b84abe04d 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -85,7 +85,7 @@ type Config struct { } Gitlab struct { - URL string `envconfig:"optional"` + Host string `envconfig:"optional"` Client string `envconfig:"optional"` Secret string `envconfig:"optional"` SkipVerify bool `envconfig:"optional"` diff --git a/pkg/remote/gitlab/gitlab.go b/pkg/remote/builtin/gitlab/gitlab.go similarity index 87% rename from pkg/remote/gitlab/gitlab.go rename to pkg/remote/builtin/gitlab/gitlab.go index 8ee9363a7..cf5ae7192 100644 --- a/pkg/remote/gitlab/gitlab.go +++ b/pkg/remote/builtin/gitlab/gitlab.go @@ -18,7 +18,7 @@ import ( ) const ( - DefaultScope = "repo" + DefaultScope = "api" ) type Gitlab struct { @@ -39,7 +39,7 @@ func init() { func NewDriver(conf *config.Config) (remote.Remote, error) { var gitlab = Gitlab{ - URL: conf.Gitlab.URL, + URL: conf.Gitlab.Host, Client: conf.Gitlab.Client, Secret: conf.Gitlab.Secret, AllowedOrgs: conf.Gitlab.Orgs, @@ -151,7 +151,7 @@ func (r *Gitlab) Netrc(u *common.User) (*common.Netrc, error) { // Activate activates a repository by adding a Post-commit hook and // a Public Deploy key, if applicable. -func (r *Gitlab) Activate(user *common.User, repo *common.Repo, keys *common.Keypair, link string) error { +func (r *Gitlab) Activate(user *common.User, repo *common.Repo, k *common.Keypair, link string) error { var client = NewClient(r.URL, user.Token, r.SkipVerify) var path = ns(repo.Owner, repo.Name) var title, err = GetKeyTitle(link) @@ -162,15 +162,14 @@ func (r *Gitlab) Activate(user *common.User, repo *common.Repo, keys *common.Key // if the repository is private we'll need // to upload a github key to the repository if repo.Private { - var err = client.AddProjectDeployKey(path, title, repo.Keys.Public) - if err != nil { + if err := client.AddProjectDeployKey(path, title, k.Public); err != nil { return err } } // append the repo owner / name to the hook url since gitlab // doesn't send this detail in the post-commit hook - link += "?owner=" + repo.Owner + "&name=" + repo.Name + link += "&owner=" + repo.Owner + "&name=" + repo.Name // add the hook return client.AddProjectHook(path, link, true, false, true) @@ -199,7 +198,7 @@ func (r *Gitlab) Deactivate(user *common.User, repo *common.Repo, link string) e if err != nil { return err } - link += "?owner=" + repo.Owner + "&name=" + repo.Name + link += "&owner=" + repo.Owner + "&name=" + repo.Name for _, h := range hooks { if link == h.Url { if err := client.RemoveProjectHook(path, strconv.Itoa(h.Id)); err != nil { @@ -235,21 +234,48 @@ func (r *Gitlab) Hook(req *http.Request) (*common.Hook, error) { return nil, nil } + var cloneUrl = parsed.Repository.GitHttpUrl + + if parsed.Repository.VisibilityLevel < 20 { + cloneUrl = parsed.Repository.GitSshUrl + } + var hook = new(common.Hook) + hook.Repo = &common.Repo{} hook.Repo.Owner = req.FormValue("owner") hook.Repo.Name = req.FormValue("name") + hook.Repo.Link = parsed.Repository.URL + hook.Repo.Clone = cloneUrl + hook.Repo.Branch = "master" + + switch parsed.Repository.VisibilityLevel { + case 0: + hook.Repo.Private = true + case 10: + hook.Repo.Private = true + case 20: + hook.Repo.Private = false + } + + hook.Repo.FullName = fmt.Sprintf("%s/%s", req.FormValue("owner"), req.FormValue("name")) + + hook.Commit = &common.Commit{} hook.Commit.Sha = parsed.After hook.Commit.Branch = parsed.Branch() + hook.Commit.Ref = parsed.Ref + hook.Commit.Remote = cloneUrl var head = parsed.Head() hook.Commit.Message = head.Message hook.Commit.Timestamp = head.Timestamp + hook.Commit.Author = &common.Author{} // extracts the commit author (ideally email) // from the post-commit hook switch { case head.Author != nil: hook.Commit.Author.Email = head.Author.Email + hook.Commit.Author.Login = parsed.UserName case head.Author == nil: hook.Commit.Author.Login = parsed.UserName } @@ -265,7 +291,7 @@ func (g *Gitlab) Oauth2Transport(r *http.Request) *oauth2.Transport { ClientSecret: g.Secret, Scope: DefaultScope, AuthURL: fmt.Sprintf("%s/oauth/authorize", g.URL), - TokenURL: fmt.Sprintf("%s/oauth/access_token", g.URL), + TokenURL: fmt.Sprintf("%s/oauth/token", g.URL), RedirectURL: fmt.Sprintf("%s/authorize", httputil.GetURL(r)), //settings.Server.Scheme, settings.Server.Hostname), }, diff --git a/pkg/remote/gitlab/helper.go b/pkg/remote/builtin/gitlab/helper.go similarity index 100% rename from pkg/remote/gitlab/helper.go rename to pkg/remote/builtin/gitlab/helper.go From da597626ddfbf8970ff28728d1183b7b719c9c4f Mon Sep 17 00:00:00 2001 From: Kirill Zaitsev Date: Mon, 27 Jul 2015 01:29:51 +0300 Subject: [PATCH 10/11] Fix private repo detection --- pkg/remote/builtin/gitlab/gitlab.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pkg/remote/builtin/gitlab/gitlab.go b/pkg/remote/builtin/gitlab/gitlab.go index cf5ae7192..4e5e4c5af 100644 --- a/pkg/remote/builtin/gitlab/gitlab.go +++ b/pkg/remote/builtin/gitlab/gitlab.go @@ -101,7 +101,10 @@ func (r *Gitlab) Repo(u *common.User, owner, name string) (*common.Repo, error) if r.PrivateMode { repo.Private = true + } else { + repo.Private = !repo_.Public } + return repo, err } From 4f0267e47e3c008f96257ed4936cd230916af761 Mon Sep 17 00:00:00 2001 From: Kirill Zaitsev Date: Mon, 27 Jul 2015 02:25:20 +0300 Subject: [PATCH 11/11] Vendoring gitlab client --- Godeps/Godeps.json | 6 +- .../Bugagazavr/go-gitlab-client/.gitignore | 3 + .../Bugagazavr/go-gitlab-client/.travis.yml | 6 + .../Bugagazavr/go-gitlab-client/Makefile | 8 + .../Bugagazavr/go-gitlab-client/README.md | 78 ++++++ .../go-gitlab-client/deploy_keys.go | 118 +++++++++ .../Bugagazavr/go-gitlab-client/events.go | 72 +++++ .../examples/config.json.sample | 5 + .../examples/projects/main.go | 195 ++++++++++++++ .../examples/repositories/main.go | 96 +++++++ .../Bugagazavr/go-gitlab-client/gitlab.go | 191 ++++++++++++++ .../go-gitlab-client/gitlab_test.go | 13 + .../go-gitlab-client/helper_test.go | 16 ++ .../go-gitlab-client/hook_payload.go | 154 +++++++++++ .../go-gitlab-client/hook_payload_test.go | 45 ++++ .../Bugagazavr/go-gitlab-client/hooks.go | 183 +++++++++++++ .../Bugagazavr/go-gitlab-client/hooks_test.go | 16 ++ .../Bugagazavr/go-gitlab-client/projects.go | 245 +++++++++++++++++ .../go-gitlab-client/projects_test.go | 70 +++++ .../go-gitlab-client/public_keys.go | 69 +++++ .../go-gitlab-client/public_keys_test.go | 50 ++++ .../go-gitlab-client/repositories.go | 246 ++++++++++++++++++ .../go-gitlab-client/repositories_test.go | 43 +++ .../Bugagazavr/go-gitlab-client/session.go | 54 ++++ .../go-gitlab-client/session_test.go | 33 +++ .../stubs/branches/index.json | 26 ++ .../go-gitlab-client/stubs/branches/show.json | 24 ++ .../go-gitlab-client/stubs/commits/index.json | 18 ++ .../stubs/hook_payloads/issue.json | 18 ++ .../stubs/hook_payloads/merge_request.json | 22 ++ .../stubs/hook_payloads/push.json | 42 +++ .../go-gitlab-client/stubs/hooks/show.json | 9 + .../stubs/projects/branches/index.json | 50 ++++ .../stubs/projects/index.json | 72 +++++ .../stubs/projects/merge_requests/index.json | 30 +++ .../go-gitlab-client/stubs/projects/show.json | 45 ++++ .../stubs/public_keys/index.json | 12 + .../stubs/public_keys/show.json | 5 + .../go-gitlab-client/stubs/session/index.json | 23 ++ .../go-gitlab-client/stubs/tags/index.json | 22 ++ .../go-gitlab-client/stubs/users/current.json | 19 ++ .../go-gitlab-client/stubs/users/index.json | 41 +++ .../go-gitlab-client/stubs/users/show.json | 15 ++ .../Bugagazavr/go-gitlab-client/users.go | 97 +++++++ .../Bugagazavr/go-gitlab-client/users_test.go | 53 ++++ .../Bugagazavr/go-gitlab-client/util.go | 10 + .../Bugagazavr/go-gitlab-client/util_test.go | 11 + Makefile | 1 - pkg/remote/builtin/gitlab/gitlab.go | 2 +- pkg/remote/builtin/gitlab/helper.go | 2 +- 50 files changed, 2680 insertions(+), 4 deletions(-) create mode 100644 Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/.gitignore create mode 100644 Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/.travis.yml create mode 100644 Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/Makefile create mode 100644 Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/README.md create mode 100644 Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/deploy_keys.go create mode 100644 Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/events.go create mode 100644 Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/examples/config.json.sample create mode 100644 Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/examples/projects/main.go create mode 100644 Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/examples/repositories/main.go create mode 100644 Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/gitlab.go create mode 100644 Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/gitlab_test.go create mode 100644 Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/helper_test.go create mode 100644 Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/hook_payload.go create mode 100644 Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/hook_payload_test.go create mode 100644 Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/hooks.go create mode 100644 Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/hooks_test.go create mode 100644 Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/projects.go create mode 100644 Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/projects_test.go create mode 100644 Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/public_keys.go create mode 100644 Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/public_keys_test.go create mode 100644 Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/repositories.go create mode 100644 Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/repositories_test.go create mode 100644 Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/session.go create mode 100644 Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/session_test.go create mode 100644 Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/stubs/branches/index.json create mode 100644 Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/stubs/branches/show.json create mode 100644 Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/stubs/commits/index.json create mode 100644 Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/stubs/hook_payloads/issue.json create mode 100644 Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/stubs/hook_payloads/merge_request.json create mode 100644 Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/stubs/hook_payloads/push.json create mode 100644 Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/stubs/hooks/show.json create mode 100644 Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/stubs/projects/branches/index.json create mode 100644 Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/stubs/projects/index.json create mode 100644 Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/stubs/projects/merge_requests/index.json create mode 100644 Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/stubs/projects/show.json create mode 100644 Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/stubs/public_keys/index.json create mode 100644 Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/stubs/public_keys/show.json create mode 100644 Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/stubs/session/index.json create mode 100644 Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/stubs/tags/index.json create mode 100644 Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/stubs/users/current.json create mode 100644 Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/stubs/users/index.json create mode 100644 Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/stubs/users/show.json create mode 100644 Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/users.go create mode 100644 Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/users_test.go create mode 100644 Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/util.go create mode 100644 Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/util_test.go diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index 9e5bdd3c2..827cac72f 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -2,7 +2,7 @@ "ImportPath": "github.com/drone/drone", "GoVersion": "go1.4.2", "Packages": [ - "github.com/drone/drone/..." + "github.com/drone/drone..." ], "Deps": [ { @@ -10,6 +10,10 @@ "Comment": "null-236", "Rev": "69e2a90ed92d03812364aeb947b7068dc42e561e" }, + { + "ImportPath": "github.com/Bugagazavr/go-gitlab-client", + "Rev": "912567bb7e65212c910733b3bfa178b11049a70e" + }, { "ImportPath": "github.com/BurntSushi/migration", "Rev": "c45b897f13350786ccaf2b7403b92b1c7ad85844" diff --git a/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/.gitignore b/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/.gitignore new file mode 100644 index 000000000..c444d00ec --- /dev/null +++ b/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/.gitignore @@ -0,0 +1,3 @@ +.DS_Store +.idea +examples/config.json \ No newline at end of file diff --git a/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/.travis.yml b/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/.travis.yml new file mode 100644 index 000000000..a8de6ec6e --- /dev/null +++ b/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/.travis.yml @@ -0,0 +1,6 @@ +language: go +go: + - 1.4 + +script: + - make deps test \ No newline at end of file diff --git a/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/Makefile b/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/Makefile new file mode 100644 index 000000000..b1ee855db --- /dev/null +++ b/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/Makefile @@ -0,0 +1,8 @@ +all:deps test + +deps: + go get github.com/stretchr/testify + go get ./... + +test: + go test -cover -short ./... diff --git a/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/README.md b/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/README.md new file mode 100644 index 000000000..4e716ee32 --- /dev/null +++ b/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/README.md @@ -0,0 +1,78 @@ +go-gitlab-client +================ + +This is a fork of project https://github.com/plouc/go-gitlab-client + +go-gitlab-client is a simple client written in golang to consume gitlab API. + +[![Build Status](https://travis-ci.org/Bugagazavr/go-gitlab-client.svg?branch=master)](https://travis-ci.org/Bugagazavr/go-gitlab-client) + + +##features + +* + ###Session [gitlab api doc](http://doc.gitlab.com/ce/api/session.html) + * get session + +* + ###Projects [gitlab api doc](http://doc.gitlab.com/ce/api/projects.html) + * list projects + * get single project + +* + ###Repositories [gitlab api doc](http://doc.gitlab.com/ce/api/repositories.html) + * list repository branches + * get single repository branch + * list project repository tags + * list repository commits + * list project hooks + * add/get/edit/rm project hook + +* + ###Users [gitlab api doc](http://doc.gitlab.com/ce/api/users.html) + * get single user + * manage user keys + +* + ###Deploy Keys [gitlab api doc](http://doc.gitlab.com/ce/api/deploy_keys.html) + * list project deploy keys + * add/get/rm project deploy key + + + + +##Installation + +To install go-gitlab-client, use `go get`: + + go get github.com/bugagazavr/go-gitlab-client + +Import the `go-gitlab-client` package into your code: + +```go +package whatever + +import ( + "github.com/bugagazavr/go-gitlab-client" +) +``` + + +##Update + +To update `go-gitlab-client`, use `go get -u`: + + go get -u github.com/bugagazavr/go-gitlab-client + + +##Documentation + +Visit the docs at http://godoc.org/github.com/Bugagazavr/go-gitlab-client + + +## Examples + +You can play with the examples located in the `examples` directory + +* [projects](https://github.com/Bugagazavr/go-gitlab-client/tree/master/examples/projects) +* [repositories](https://github.com/Bugagazavr/go-gitlab-client/tree/master/examples/repositories) diff --git a/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/deploy_keys.go b/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/deploy_keys.go new file mode 100644 index 000000000..38fe671c3 --- /dev/null +++ b/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/deploy_keys.go @@ -0,0 +1,118 @@ +package gogitlab + +import ( + "encoding/json" + "net/url" +) + +const ( + // ID + project_url_deploy_keys = "/projects/:id/keys" // Get list of project deploy keys + // PROJECT ID AND KEY ID + project_url_deploy_key = "/projects/:id/keys/:key_id" // Get single project deploy key +) + +/* +Get list of project deploy keys. + + GET /projects/:id/keys + +Parameters: + + id The ID of a project + +*/ +func (g *Gitlab) ProjectDeployKeys(id string) ([]*PublicKey, error) { + + url, opaque := g.ResourceUrlRaw(project_url_deploy_keys, map[string]string{":id": id}) + + var deployKeys []*PublicKey + + contents, err := g.buildAndExecRequestRaw("GET", url, opaque, nil) + if err == nil { + err = json.Unmarshal(contents, &deployKeys) + } + + return deployKeys, err +} + +/* +Get single project deploy key. + + GET /projects/:id/keys/:key_id + +Parameters: + + id The ID of a project + key_id The ID of a key + +*/ +func (g *Gitlab) ProjectDeployKey(id, key_id string) (*PublicKey, error) { + + url, opaque := g.ResourceUrlRaw(project_url_deploy_key, map[string]string{ + ":id": id, + ":key_id": key_id, + }) + + var deployKey *PublicKey + + contents, err := g.buildAndExecRequestRaw("GET", url, opaque, nil) + if err == nil { + err = json.Unmarshal(contents, &deployKey) + } + + return deployKey, err +} + +/* +Add deploy key to project. + + POST /projects/:id/keys + +Parameters: + + id The ID of a project + title The key title + key The key value + +*/ +func (g *Gitlab) AddProjectDeployKey(id, title, key string) error { + var err error + + path, opaque := g.ResourceUrlRaw(project_url_deploy_keys, map[string]string{":id": id}) + + v := url.Values{} + v.Set("title", title) + v.Set("key", key) + + body := v.Encode() + + _, err = g.buildAndExecRequestRaw("POST", path, opaque, []byte(body)) + + return err +} + +/* +Remove deploy key from project + + DELETE /projects/:id/keys/:key_id + +Parameters: + + id The ID of a project + key_id The ID of a key + +*/ +func (g *Gitlab) RemoveProjectDeployKey(id, key_id string) error { + + url, opaque := g.ResourceUrlRaw(project_url_deploy_key, map[string]string{ + ":id": id, + ":key_id": key_id, + }) + + var err error + + _, err = g.buildAndExecRequestRaw("DELETE", url, opaque, nil) + + return err +} diff --git a/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/events.go b/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/events.go new file mode 100644 index 000000000..82ac9c78f --- /dev/null +++ b/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/events.go @@ -0,0 +1,72 @@ +package gogitlab + +import ( + "encoding/xml" + "fmt" + "time" +) + +type Person struct { + Name string `xml:"name"json:"name"` + Email string `xml:"email"json:"email"` +} + +type Link struct { + Rel string `xml:"rel,attr,omitempty"json:"rel"` + Href string `xml:"href,attr"json:"href"` +} + +type ActivityFeed struct { + Title string `xml:"title"json:"title"` + Id string `xml:"id"json:"id"` + Link []Link `xml:"link"json:"link"` + Updated time.Time `xml:"updated,attr"json:"updated"` + Entries []*FeedCommit `xml:"entry"json:"entries"` +} + +type FeedCommit struct { + Id string `xml:"id"json:"id"` + Title string `xml:"title"json:"title"` + Link []Link `xml:"link"json:"link"` + Updated time.Time `xml:"updated"json:"updated"` + Author Person `xml:"author"json:"author"` + Summary string `xml:"summary"json:"summary"` + // +} + +func (g *Gitlab) Activity() (ActivityFeed, error) { + + url := g.BaseUrl + dasboard_feed_path + "?private_token=" + g.Token + fmt.Println(url) + + contents, err := g.buildAndExecRequest("GET", url, nil) + if err != nil { + fmt.Println("%s", err) + } + + var activity ActivityFeed + err = xml.Unmarshal(contents, &activity) + if err != nil { + fmt.Println("%s", err) + } + + return activity, err +} + +func (g *Gitlab) RepoActivityFeed(feedPath string) ActivityFeed { + + url := g.BaseUrl + g.RepoFeedPath + "?private_token=" + g.Token + + contents, err := g.buildAndExecRequest("GET", url, nil) + if err != nil { + fmt.Println("%s", err) + } + + var activity ActivityFeed + err = xml.Unmarshal(contents, &activity) + if err != nil { + fmt.Println("%s", err) + } + + return activity +} diff --git a/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/examples/config.json.sample b/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/examples/config.json.sample new file mode 100644 index 000000000..b7285af65 --- /dev/null +++ b/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/examples/config.json.sample @@ -0,0 +1,5 @@ +{ + "host": "https://gitlab.domain.com", + "api_path": "/api/v3", + "token": "TOKEN" +} \ No newline at end of file diff --git a/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/examples/projects/main.go b/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/examples/projects/main.go new file mode 100644 index 000000000..26325c19d --- /dev/null +++ b/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/examples/projects/main.go @@ -0,0 +1,195 @@ +package main + +import ( + "encoding/json" + "flag" + "fmt" + "github.com/bugagazavr/go-gitlab-client" + "io/ioutil" + "os" + "strconv" + "time" +) + +type Config struct { + Host string `json:"host"` + ApiPath string `json:"api_path"` + Token string `json:"token"` +} + +func main() { + help := flag.Bool("help", false, "Show usage") + + file, e := ioutil.ReadFile("../config.json") + if e != nil { + fmt.Printf("Config file error: %v\n", e) + os.Exit(1) + } + + var config Config + json.Unmarshal(file, &config) + fmt.Printf("Results: %+v\n", config) + + var gitlab *gogitlab.Gitlab + + gitlab = gogitlab.NewGitlab(config.Host, config.ApiPath, config.Token) + + var method string + flag.StringVar(&method, "m", "", "Specify method to retrieve projects infos, available methods:\n"+ + " > -m projects\n"+ + " > -m project -id PROJECT_ID\n"+ + " > -m hooks -id PROJECT_ID\n"+ + " > -m branches -id PROJECT_ID\n"+ + " > -m merge_requests -id PROJECT_ID\n"+ + " > -m team -id PROJECT_ID") + + var id string + flag.StringVar(&id, "id", "", "Specify repository id") + + flag.Usage = func() { + fmt.Printf("Usage:\n") + flag.PrintDefaults() + } + flag.Parse() + + if *help == true || method == "" { + flag.Usage() + return + } + + startedAt := time.Now() + defer func() { + fmt.Printf("processed in %v\n", time.Now().Sub(startedAt)) + }() + + switch method { + case "projects": + fmt.Println("Fetching projects…") + + projects, err := gitlab.Projects(1, 100) + if err != nil { + fmt.Println(err.Error()) + return + } + + for _, project := range projects { + fmt.Printf("> %6d | %s\n", project.Id, project.Name) + } + + case "project": + fmt.Println("Fetching project…") + + if id == "" { + flag.Usage() + return + } + + project, err := gitlab.Project(id) + if err != nil { + fmt.Println(err.Error()) + return + } + + format := "> %-23s: %s\n" + + fmt.Printf("%s\n", project.Name) + fmt.Printf(format, "id", strconv.Itoa(project.Id)) + fmt.Printf(format, "name", project.Name) + fmt.Printf(format, "description", project.Description) + fmt.Printf(format, "default branch", project.DefaultBranch) + if project.Owner != nil { + fmt.Printf(format, "owner.name", project.Owner.Username) + } + fmt.Printf(format, "public", strconv.FormatBool(project.Public)) + fmt.Printf(format, "path", project.Path) + fmt.Printf(format, "path with namespace", project.PathWithNamespace) + fmt.Printf(format, "issues enabled", strconv.FormatBool(project.IssuesEnabled)) + fmt.Printf(format, "merge requests enabled", strconv.FormatBool(project.MergeRequestsEnabled)) + fmt.Printf(format, "wall enabled", strconv.FormatBool(project.WallEnabled)) + fmt.Printf(format, "wiki enabled", strconv.FormatBool(project.WikiEnabled)) + fmt.Printf(format, "created at", project.CreatedAtRaw) + //fmt.Printf(format, "namespace", project.Namespace) + + case "branches": + fmt.Println("Fetching project branches…") + + if id == "" { + flag.Usage() + return + } + + branches, err := gitlab.ProjectBranches(id) + if err != nil { + fmt.Println(err.Error()) + return + } + + for _, branch := range branches { + fmt.Printf("> %s\n", branch.Name) + } + + case "merge_requests": + fmt.Println("Fetching project merge_requests…") + + if id == "" { + flag.Usage() + return + } + + mrs, err := gitlab.ProjectMergeRequests(id, 0, 30, "opened") + if err != nil { + fmt.Println(err.Error()) + return + } + + for _, mr := range mrs { + author := "" + if mr.Author != nil { + author = mr.Author.Username + } + assignee := "" + if mr.Assignee != nil { + assignee = mr.Assignee.Username + } + fmt.Printf(" %s -> %s [%s] author[%s] assignee[%s]\n", + mr.SourceBranch, mr.TargetBranch, mr.State, + author, assignee) + } + + case "hooks": + fmt.Println("Fetching project hooks…") + + if id == "" { + flag.Usage() + return + } + + hooks, err := gitlab.ProjectHooks(id) + if err != nil { + fmt.Println(err.Error()) + return + } + + for _, hook := range hooks { + fmt.Printf("> [%d] %s, created on %s\n", hook.Id, hook.Url, hook.CreatedAtRaw) + } + + case "team": + fmt.Println("Fetching project team members…") + + if id == "" { + flag.Usage() + return + } + + members, err := gitlab.ProjectMembers(id) + if err != nil { + fmt.Println(err.Error()) + return + } + + for _, member := range members { + fmt.Printf("> [%d] %s (%s) since %s\n", member.Id, member.Username, member.Name, member.CreatedAt) + } + } +} diff --git a/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/examples/repositories/main.go b/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/examples/repositories/main.go new file mode 100644 index 000000000..e6591bb91 --- /dev/null +++ b/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/examples/repositories/main.go @@ -0,0 +1,96 @@ +package main + +import ( + "encoding/json" + "flag" + "fmt" + "github.com/bugagazavr/go-gitlab-client" + "io/ioutil" + "os" + "time" +) + +type Config struct { + Host string `json:"host"` + ApiPath string `json:"api_path"` + Token string `json:"token"` +} + +func main() { + help := flag.Bool("help", false, "Show usage") + + file, e := ioutil.ReadFile("../config.json") + if e != nil { + fmt.Printf("Config file error: %v\n", e) + os.Exit(1) + } + + var config Config + json.Unmarshal(file, &config) + fmt.Printf("Results: %+v\n", config) + + gitlab := gogitlab.NewGitlab(config.Host, config.ApiPath, config.Token) + + var method string + flag.StringVar(&method, "m", "", "Specify method to retrieve repositories, available methods:\n"+ + " > branches\n"+ + " > branch\n"+ + " > tags\n"+ + " > commits") + + var id string + flag.StringVar(&id, "id", "", "Specify repository id") + + flag.Usage = func() { + fmt.Printf("Usage:\n") + flag.PrintDefaults() + } + flag.Parse() + + if *help == true || method == "" || id == "" { + flag.Usage() + return + } + + startedAt := time.Now() + defer func() { + fmt.Printf("processed in %v\n", time.Now().Sub(startedAt)) + }() + + switch method { + case "branches": + fmt.Println("Fetching repository branches…") + + branches, err := gitlab.RepoBranches(id) + if err != nil { + fmt.Println(err.Error()) + } + + for _, branch := range branches { + fmt.Printf("> %s\n", branch.Name) + } + case "branch": + case "tags": + fmt.Println("Fetching repository tags…") + + tags, err := gitlab.RepoTags(id) + if err != nil { + fmt.Println(err.Error()) + } + + for _, tag := range tags { + fmt.Printf("> %s\n", tag.Name) + } + case "commits": + fmt.Println("Fetching repository commits…") + + commits, err := gitlab.RepoCommits(id) + if err != nil { + fmt.Println(err.Error()) + } + + for _, commit := range commits { + fmt.Printf("%s > [%s] %s\n", commit.CreatedAt.Format("Mon 02 Jan 15:04"), commit.Author_Name, commit.Title) + } + } +} diff --git a/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/gitlab.go b/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/gitlab.go new file mode 100644 index 000000000..415dc5ed6 --- /dev/null +++ b/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/gitlab.go @@ -0,0 +1,191 @@ +// Package github implements a simple client to consume gitlab API. +package gogitlab + +import ( + "bytes" + "crypto/tls" + "flag" + "fmt" + "io/ioutil" + "net/http" + "net/url" + "strings" +) + +const ( + dasboard_feed_path = "/dashboard.atom" +) + +type Gitlab struct { + BaseUrl string + ApiPath string + RepoFeedPath string + Token string + Bearer bool + Client *http.Client +} + +const ( + dateLayout = "2006-01-02T15:04:05-07:00" +) + +var ( + skipCertVerify = flag.Bool("gitlab.skip-cert-check", false, + `If set to true, gitlab client will skip certificate checking for https, possibly exposing your system to MITM attack.`) +) + +func NewGitlab(baseUrl, apiPath, token string) *Gitlab { + return NewGitlabCert(baseUrl, apiPath, token, *skipCertVerify) +} + +func NewGitlabCert(baseUrl, apiPath, token string, skipVerify bool) *Gitlab { + config := &tls.Config{InsecureSkipVerify: skipVerify} + tr := &http.Transport{ + Proxy: http.ProxyFromEnvironment, + TLSClientConfig: config, + } + client := &http.Client{Transport: tr} + + return &Gitlab{ + BaseUrl: baseUrl, + ApiPath: apiPath, + Token: token, + Client: client, + } +} + +func (g *Gitlab) ResourceUrl(url string, params map[string]string) string { + + if params != nil { + for key, val := range params { + url = strings.Replace(url, key, encodeParameter(val), -1) + } + } + + url = g.BaseUrl + g.ApiPath + url + if !g.Bearer { + url = url + "?private_token=" + g.Token + } + return url +} + +func (g *Gitlab) buildAndExecRequest(method, url string, body []byte) ([]byte, error) { + + var req *http.Request + var err error + + if body != nil { + reader := bytes.NewReader(body) + req, err = http.NewRequest(method, url, reader) + } else { + req, err = http.NewRequest(method, url, nil) + } + if err != nil { + panic("Error while building gitlab request") + } + + if g.Bearer { + req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", g.Token)) + } + + resp, err := g.Client.Do(req) + if err != nil { + return nil, fmt.Errorf("Client.Do error: %q", err) + } + defer resp.Body.Close() + contents, err := ioutil.ReadAll(resp.Body) + if err != nil { + fmt.Printf("%s", err) + } + + if resp.StatusCode >= 400 { + err = fmt.Errorf("*Gitlab.buildAndExecRequest failed: <%d> %s", resp.StatusCode, req.URL) + } + + return contents, err +} + +func (g *Gitlab) ResourceUrlQuery(u string, params, query map[string]string) string { + if params != nil { + for key, val := range params { + u = strings.Replace(u, key, encodeParameter(val), -1) + } + } + + query_params := url.Values{} + if !g.Bearer { + query_params.Add("private_token", g.Token) + } + + if query != nil { + for key, val := range query { + query_params.Set(key, val) + } + } + + u = g.BaseUrl + g.ApiPath + u + "?" + query_params.Encode() + return u + +} + +func (g *Gitlab) ResourceUrlRaw(u string, params map[string]string) (string, string) { + + if params != nil { + for key, val := range params { + u = strings.Replace(u, key, encodeParameter(val), -1) + } + } + + path := u + u = g.BaseUrl + g.ApiPath + path + if !g.Bearer { + u = u + "?private_token=" + g.Token + } + + p, err := url.Parse(u) + if err != nil { + return u, "" + } + opaque := "//" + p.Host + p.Path + return u, opaque +} + +func (g *Gitlab) buildAndExecRequestRaw(method, url, opaque string, body []byte) ([]byte, error) { + + var req *http.Request + var err error + + if body != nil { + reader := bytes.NewReader(body) + req, err = http.NewRequest(method, url, reader) + } else { + req, err = http.NewRequest(method, url, nil) + } + if err != nil { + panic("Error while building gitlab request") + } + + if g.Bearer { + req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", g.Token)) + } + + if len(opaque) > 0 { + req.URL.Opaque = opaque + } + + resp, err := g.Client.Do(req) + if err != nil { + return nil, fmt.Errorf("Client.Do error: %q", err) + } + defer resp.Body.Close() + contents, err := ioutil.ReadAll(resp.Body) + if err != nil { + fmt.Printf("%s", err) + } + + if resp.StatusCode >= 400 { + err = fmt.Errorf("*Gitlab.buildAndExecRequestRaw failed: <%d> %s", resp.StatusCode, req.URL) + } + + return contents, err +} diff --git a/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/gitlab_test.go b/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/gitlab_test.go new file mode 100644 index 000000000..f4088e457 --- /dev/null +++ b/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/gitlab_test.go @@ -0,0 +1,13 @@ +package gogitlab + +import ( + "github.com/stretchr/testify/assert" + "testing" +) + +func TestResourceUrl(t *testing.T) { + gitlab := NewGitlab("http://base_url/", "api_path", "token") + + assert.Equal(t, gitlab.ResourceUrl(projects_url, nil), "http://base_url/api_path/projects?private_token=token") + assert.Equal(t, gitlab.ResourceUrl(project_url, map[string]string{":id": "123"}), "http://base_url/api_path/projects/123?private_token=token") +} diff --git a/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/helper_test.go b/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/helper_test.go new file mode 100644 index 000000000..2bac5df5e --- /dev/null +++ b/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/helper_test.go @@ -0,0 +1,16 @@ +package gogitlab + +import ( + "io/ioutil" + "net/http" + "net/http/httptest" +) + +func Stub(filename string) (*httptest.Server, *Gitlab) { + stub, _ := ioutil.ReadFile(filename) + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte(stub)) + })) + gitlab := NewGitlab(ts.URL, "", "") + return ts, gitlab +} diff --git a/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/hook_payload.go b/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/hook_payload.go new file mode 100644 index 000000000..8bb7e9add --- /dev/null +++ b/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/hook_payload.go @@ -0,0 +1,154 @@ +package gogitlab + +import ( + "encoding/json" + "fmt" + "strings" +) + +type HookObjAttr struct { + Id int `json:"id,omitempty"` + Title string `json:"title,omitempty"` + AssigneeId int `json:"assignee_id,omitempty"` + AuthorId int `json:"author_id,omitempty"` + ProjectId int `json:"project_id,omitempty"` + CreatedAt string `json:"created_at,omitempty"` + UpdatedAt string `json:"updated_at,omitempty"` + Position int `json:"position,omitempty"` + BranchName string `json:"branch_name,omitempty"` + Description string `json:"description,omitempty"` + MilestoneId int `json:"milestone_id,omitempty"` + State string `json:"state,omitempty"` + IId int `json:"iid,omitempty"` + TargetBranch string `json:"target_branch,omitempty"` + SourceBranch string `json:"source_branch,omitempty"` + SourceProjectId int `json:"source_project_id,omitempty"` + StCommits string `json:"st_commits,omitempty"` + StDiffs string `json:"st_diffs,omitempty"` + MergeStatus string `json:"merge_status,omitempty"` + TargetProjectId int `json:"target_project_id,omitempty"` + Source *hProject `json:"source,omitempty"` + Target *hProject `json:"target,omitempty"` + LastCommit *hCommit `json:"last_commit,omitempty"` +} + +type hProject struct { + Name string `json:"name"` + SshUrl string `json:"ssh_url"` + HttpUrl string `json:"http_url"` + VisibilityLevel int `json:"visibility_level"` + Namespace string `json:"namespace"` +} + +type hRepository struct { + Name string `json:"name,omitempty"` + URL string `json:"url,omitempty"` + Description string `json:"description,omitempty"` + Homepage string `json:"homepage,omitempty"` + GitHttpUrl string `json:"git_http_url,omitempty"` + GitSshUrl string `json:"git_ssh_url,omitempty"` + VisibilityLevel int `json:"visibility_level,omitempty"` +} + +type hCommit struct { + Id string `json:"id,omitempty"` + Message string `json:"message,omitempty"` + Timestamp string `json:"timestamp,omitempty"` + URL string `json:"url,omitempty"` + Author *Person `json:"author,omitempty"` +} + +type HookPayload struct { + Before string `json:"before,omitempty"` + After string `json:"after,omitempty"` + Ref string `json:"ref,omitempty"` + UserId int `json:"user_id,omitempty"` + UserName string `json:"user_name,omitempty"` + ProjectId int `json:"project_id,omitempty"` + Repository *hRepository `json:"repository,omitempty"` + Commits []hCommit `json:"commits,omitempty"` + TotalCommitsCount int `json:"total_commits_count,omitempty"` + ObjectKind string `json:"object_kind,omitempty"` + ObjectAttributes *HookObjAttr `json:"object_attributes,omitempty"` +} + +// ParseHook parses hook payload from GitLab +func ParseHook(payload []byte) (*HookPayload, error) { + hp := HookPayload{} + if err := json.Unmarshal(payload, &hp); err != nil { + return nil, err + } + + // Basic sanity check + switch { + case len(hp.ObjectKind) == 0: + // Assume this is a post-receive within repository + if len(hp.After) == 0 { + return nil, fmt.Errorf("Invalid hook received, commit hash not found.") + } + case hp.ObjectKind == "push": + if hp.Repository == nil { + return nil, fmt.Errorf("Invalid push hook received, attributes not found") + } + case hp.ObjectKind == "tag_push": + if hp.Repository == nil { + return nil, fmt.Errorf("Invalid tag push hook received, attributes not found") + } + case hp.ObjectKind == "issue": + fallthrough + case hp.ObjectKind == "merge_request": + if hp.ObjectAttributes == nil { + return nil, fmt.Errorf("Invalid hook received, attributes not found.") + } + default: + return nil, fmt.Errorf("Invalid hook received, payload format not recognized.") + } + + return &hp, nil +} + +// Type return current event type +// This function returns "unknown" type if event not supported +func (h *HookPayload) Type() string { + switch { + case strings.HasPrefix(h.Ref, "refs/heads/") && len(h.After) == 0: + return "branch_deleted" + case strings.HasPrefix(h.Ref, "refs/heads/") && len(h.Before) == 0: + return "branch" + case strings.HasPrefix(h.Ref, "refs/heads/"): + return "commit" + case strings.HasPrefix(h.Ref, "refs/tags/") && len(h.After) == 0: + return "tag_deleted" + case strings.HasPrefix(h.Ref, "refs/tags/"): + return "tag" + case h.ObjectKind == "issue": + return "issue" + case h.ObjectKind == "merge_request": + return "merge_request" + default: + return "unknown" + } +} + +// Tag returns current tag for push event hook payload +// This function returns empty string for any other events +func (h *HookPayload) Tag() string { + return strings.TrimPrefix(h.Ref, "refs/tags/") +} + +// Branch returns current branch for push event hook payload +// This function returns empty string for any other events +func (h *HookPayload) Branch() string { + return strings.TrimPrefix(h.Ref, "refs/heads/") +} + +// Head returns the latest changeset for push event hook payload +func (h *HookPayload) Head() hCommit { + c := hCommit{} + for _, cm := range h.Commits { + if h.After == cm.Id { + return cm + } + } + return c +} diff --git a/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/hook_payload_test.go b/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/hook_payload_test.go new file mode 100644 index 000000000..483d1bbae --- /dev/null +++ b/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/hook_payload_test.go @@ -0,0 +1,45 @@ +package gogitlab + +import ( + "github.com/stretchr/testify/assert" + "io/ioutil" + "testing" +) + +func TestParsePushHook(t *testing.T) { + stub, _ := ioutil.ReadFile("stubs/hook_payloads/push.json") + p, err := ParseHook([]byte(stub)) + + assert.Equal(t, err, nil) + assert.Equal(t, p.ObjectKind, "push") + assert.IsType(t, new(HookPayload), p) + assert.Equal(t, p.After, "da1560886d4f094c3e6c9ef40349f7d38b5d27d7") + assert.Equal(t, p.Repository.URL, "git@example.com:mike/diasporadiaspora.git") + assert.Equal(t, p.Repository.GitHttpUrl, "http://example.com/mike/diaspora.git") + assert.Equal(t, p.Repository.GitSshUrl, "git@example.com:mike/diaspora.git") + assert.Equal(t, p.Repository.VisibilityLevel, 0) + assert.Equal(t, len(p.Commits), 2) + assert.Equal(t, p.Commits[0].Author.Email, "jordi@softcatala.org") + assert.Equal(t, p.Commits[1].Id, "da1560886d4f094c3e6c9ef40349f7d38b5d27d7") + assert.Equal(t, p.Branch(), "master") + assert.Equal(t, p.Head().Message, "fixed readme") +} + +func TestParseIssueHook(t *testing.T) { + stub, _ := ioutil.ReadFile("stubs/hook_payloads/issue.json") + p, err := ParseHook([]byte(stub)) + + assert.Equal(t, err, nil) + assert.Equal(t, p.ObjectKind, "issue") + assert.Equal(t, p.ObjectAttributes.Id, 301) +} + +func TestParseMergeRequestHook(t *testing.T) { + stub, _ := ioutil.ReadFile("stubs/hook_payloads/merge_request.json") + p, err := ParseHook([]byte(stub)) + + assert.Equal(t, err, nil) + assert.Equal(t, p.ObjectKind, "merge_request") + assert.Equal(t, p.ObjectAttributes.TargetBranch, "master") + assert.Equal(t, p.ObjectAttributes.SourceProjectId, p.ObjectAttributes.TargetProjectId) +} diff --git a/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/hooks.go b/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/hooks.go new file mode 100644 index 000000000..9dd0fe4bb --- /dev/null +++ b/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/hooks.go @@ -0,0 +1,183 @@ +package gogitlab + +import ( + "encoding/json" + "net/url" +) + +const ( + project_url_hooks = "/projects/:id/hooks" // Get list of project hooks + project_url_hook = "/projects/:id/hooks/:hook_id" // Get single project hook +) + +type Hook struct { + Id int `json:"id,omitempty"` + Url string `json:"url,omitempty"` + CreatedAtRaw string `json:"created_at,omitempty"` +} + +/* +Get list of project hooks. + + GET /projects/:id/hooks + +Parameters: + + id The ID of a project + +*/ +func (g *Gitlab) ProjectHooks(id string) ([]*Hook, error) { + + url, opaque := g.ResourceUrlRaw(project_url_hooks, map[string]string{":id": id}) + + var err error + var hooks []*Hook + + contents, err := g.buildAndExecRequestRaw("GET", url, opaque, nil) + if err != nil { + return hooks, err + } + + err = json.Unmarshal(contents, &hooks) + + return hooks, err +} + +/* +Get single project hook. + + GET /projects/:id/hooks/:hook_id + +Parameters: + + id The ID of a project + hook_id The ID of a hook + +*/ +func (g *Gitlab) ProjectHook(id, hook_id string) (*Hook, error) { + + url, opaque := g.ResourceUrlRaw(project_url_hook, map[string]string{ + ":id": id, + ":hook_id": hook_id, + }) + + var err error + hook := new(Hook) + + contents, err := g.buildAndExecRequestRaw("GET", url, opaque, nil) + if err != nil { + return hook, err + } + + err = json.Unmarshal(contents, &hook) + + return hook, err +} + +/* +Add new project hook. + + POST /projects/:id/hooks + +Parameters: + + id The ID or NAMESPACE/PROJECT_NAME of a project + hook_url The hook URL + push_events Trigger hook on push events + issues_events Trigger hook on issues events + merge_requests_events Trigger hook on merge_requests events + +*/ +func (g *Gitlab) AddProjectHook(id, hook_url string, push_events, issues_events, merge_requests_events bool) error { + + url, opaque := g.ResourceUrlRaw(project_url_hooks, map[string]string{":id": id}) + + var err error + + body := buildHookQuery(hook_url, push_events, issues_events, merge_requests_events) + _, err = g.buildAndExecRequestRaw("POST", url, opaque, []byte(body)) + + return err +} + +/* +Edit existing project hook. + + PUT /projects/:id/hooks/:hook_id + +Parameters: + + id The ID or NAMESPACE/PROJECT_NAME of a project + hook_id The ID of a project hook + hook_url The hook URL + push_events Trigger hook on push events + issues_events Trigger hook on issues events + merge_requests_events Trigger hook on merge_requests events + +*/ +func (g *Gitlab) EditProjectHook(id, hook_id, hook_url string, push_events, issues_events, merge_requests_events bool) error { + + url, opaque := g.ResourceUrlRaw(project_url_hook, map[string]string{ + ":id": id, + ":hook_id": hook_id, + }) + + var err error + + body := buildHookQuery(hook_url, push_events, issues_events, merge_requests_events) + _, err = g.buildAndExecRequestRaw("PUT", url, opaque, []byte(body)) + + return err +} + +/* +Remove hook from project. + + DELETE /projects/:id/hooks/:hook_id + +Parameters: + + id The ID or NAMESPACE/PROJECT_NAME of a project + hook_id The ID of hook to delete + +*/ +func (g *Gitlab) RemoveProjectHook(id, hook_id string) error { + + url, opaque := g.ResourceUrlRaw(project_url_hook, map[string]string{ + ":id": id, + ":hook_id": hook_id, + }) + + var err error + + _, err = g.buildAndExecRequestRaw("DELETE", url, opaque, nil) + + return err +} + +/* +Build HTTP query to add or edit hook +*/ +func buildHookQuery(hook_url string, push_events, issues_events, merge_requests_events bool) string { + + v := url.Values{} + v.Set("url", hook_url) + + if push_events { + v.Set("push_events", "true") + } else { + v.Set("push_events", "false") + } + if issues_events { + v.Set("issues_events", "true") + } else { + v.Set("issues_events", "false") + } + if merge_requests_events { + v.Set("merge_requests_events", "true") + } else { + v.Set("merge_requests_events", "false") + } + + return v.Encode() +} diff --git a/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/hooks_test.go b/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/hooks_test.go new file mode 100644 index 000000000..18ae7fc1f --- /dev/null +++ b/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/hooks_test.go @@ -0,0 +1,16 @@ +package gogitlab + +import ( + "github.com/stretchr/testify/assert" + "testing" +) + +func TestHook(t *testing.T) { + ts, gitlab := Stub("stubs/hooks/show.json") + hook, err := gitlab.ProjectHook("1", "2") + + assert.Equal(t, err, nil) + assert.IsType(t, new(Hook), hook) + assert.Equal(t, hook.Url, "http://example.com/hook") + defer ts.Close() +} diff --git a/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/projects.go b/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/projects.go new file mode 100644 index 000000000..9de030ec0 --- /dev/null +++ b/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/projects.go @@ -0,0 +1,245 @@ +package gogitlab + +import ( + "encoding/json" + "strconv" + "strings" +) + +const ( + projects_url = "/projects" // Get a list of projects owned by the authenticated user + projects_search_url = "/projects/search/:query" // Search for projects by name + project_url = "/projects/:id" // Get a specific project, identified by project ID or NAME + project_url_events = "/projects/:id/events" // Get project events + project_url_branches = "/projects/:id/repository/branches" // Lists all branches of a project + project_url_members = "/projects/:id/members" // List project team members + project_url_member = "/projects/:id/members/:user_id" // Get project team member + project_url_merge_requests = "/projects/:id/merge_requests" // List all merge requests of a project +) + +type Member struct { + Id int + Username string + Email string + Name string + State string + CreatedAt string `json:"created_at,omitempty"` + // AccessLevel int +} + +type Namespace struct { + Id int + Name string + Path string + Description string + Owner_Id int + Created_At string + Updated_At string +} + +type ProjectAccess struct { + AccessLevel int `json:"access_level,omitempty"` + NotificationLevel int `json:"notification_level,omitempty"` +} + +type GroupAccess struct { + AccessLevel int `json:"access_level,omitempty"` + NotificationLevel int `json:"notification_level,omitempty"` +} + +type Permissions struct { + ProjectAccess *ProjectAccess `json:"project_access,omitempty"` + GroupAccess *GroupAccess `json:"group_access,omitempty"` +} + +// A gitlab project +type Project struct { + Id int `json:"id,omitempty"` + Name string `json:"name,omitempty"` + Description string `json:"description,omitempty"` + DefaultBranch string `json:"default_branch,omitempty"` + Owner *Member `json:"owner,omitempty"` + Public bool `json:"public,omitempty"` + Path string `json:"path,omitempty"` + PathWithNamespace string `json:"path_with_namespace,omitempty"` + IssuesEnabled bool `json:"issues_enabled,omitempty"` + MergeRequestsEnabled bool `json:"merge_requests_enabled,omitempty"` + WallEnabled bool `json:"wall_enabled,omitempty"` + WikiEnabled bool `json:"wiki_enabled,omitempty"` + CreatedAtRaw string `json:"created_at,omitempty"` + Namespace *Namespace `json:"namespace,omitempty"` + SshRepoUrl string `json:"ssh_url_to_repo"` + HttpRepoUrl string `json:"http_url_to_repo"` + Url string `json:"web_url"` + Permissions *Permissions `json:"permissions,omitempty"` +} + +type MergeRequest struct { + Id int `json:"id,omitempty"` + // IId + TargetBranch string `json:"target_branch,omitempty"` + SourceBranch string `json:"source_branch,omitempty"` + ProjectId int `json:"project_id,omitempty"` + Title string `json:"title,omitempty"` + State string `json:"state,omitempty"` + Upvotes int `json:"upvotes,omitempty"` + Downvotes int `json:"downvotes,omitempty"` + Author *Member `json:"author,omitempty"` + Assignee *Member `json:"assignee,omitempty"` + Description string `json:"description,omitempty"` +} + +/* +Get a list of all projects owned by the authenticated user. +*/ +func (g *Gitlab) AllProjects() ([]*Project, error) { + var per_page = 100 + var projects []*Project + + for i := 1; true; i++ { + contents, err := g.Projects(i, per_page) + if err != nil { + return projects, err + } + + for _, value := range contents { + projects = append(projects, value) + } + + if len(projects) == 0 { + break + } + + if len(projects)/i < per_page { + break + } + } + + return projects, nil +} + +/* +Get a list of projects owned by the authenticated user. +*/ +func (g *Gitlab) Projects(page int, per_page int) ([]*Project, error) { + + url := g.ResourceUrlQuery(projects_url, nil, map[string]string{"page": strconv.Itoa(page), "per_page": strconv.Itoa(per_page)}) + + var projects []*Project + + contents, err := g.buildAndExecRequest("GET", url, nil) + if err == nil { + err = json.Unmarshal(contents, &projects) + } + + return projects, err +} + +/* +Get a specific project, identified by project ID or NAME, +which is owned by the authentication user. +Namespaced project may be retrieved by specifying the namespace +and its project name like this: + + `namespace%2Fproject-name` + +*/ +func (g *Gitlab) Project(id string) (*Project, error) { + + url, opaque := g.ResourceUrlRaw(project_url, map[string]string{":id": id}) + + var project *Project + + contents, err := g.buildAndExecRequestRaw("GET", url, opaque, nil) + if err == nil { + err = json.Unmarshal(contents, &project) + } + + return project, err +} + +/* +Lists all branches of a project. +*/ +func (g *Gitlab) ProjectBranches(id string) ([]*Branch, error) { + + url, opaque := g.ResourceUrlRaw(project_url_branches, map[string]string{":id": id}) + + var branches []*Branch + + contents, err := g.buildAndExecRequestRaw("GET", url, opaque, nil) + if err == nil { + err = json.Unmarshal(contents, &branches) + } + + return branches, err +} + +func (g *Gitlab) ProjectMembers(id string) ([]*Member, error) { + url, opaque := g.ResourceUrlRaw(project_url_members, map[string]string{":id": id}) + + var members []*Member + + contents, err := g.buildAndExecRequestRaw("GET", url, opaque, nil) + if err == nil { + err = json.Unmarshal(contents, &members) + } + + return members, err +} + +/* +Lists all merge requests of a project. +*/ +func (g *Gitlab) ProjectMergeRequests(id string, page int, per_page int, state string) ([]*MergeRequest, error) { + par := map[string]string{":id": id} + qry := map[string]string{ + "state": state, + "page": strconv.Itoa(page), + "per_page": strconv.Itoa(per_page)} + url := g.ResourceUrlQuery(project_url_merge_requests, par, qry) + + var mr []*MergeRequest + + contents, err := g.buildAndExecRequest("GET", url, nil) + if err == nil { + err = json.Unmarshal(contents, &mr) + } + + return mr, err +} + +/* +Get single project id. + + GET /projects/search/:query + +Parameters: + + namespace The namespace of a project + name The id of a project + +*/ +func (g *Gitlab) SearchProjectId(namespace string, name string) (id int, err error) { + + url, opaque := g.ResourceUrlRaw(projects_search_url, map[string]string{ + ":query": strings.ToLower(name), + }) + + var projects []*Project + + contents, err := g.buildAndExecRequestRaw("GET", url, opaque, nil) + if err == nil { + err = json.Unmarshal(contents, &projects) + } else { + return id, err + } + + for _, project := range projects { + if project.Namespace.Name == namespace { + id = project.Id + } + } + + return id, err +} diff --git a/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/projects_test.go b/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/projects_test.go new file mode 100644 index 000000000..56511ce5e --- /dev/null +++ b/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/projects_test.go @@ -0,0 +1,70 @@ +package gogitlab + +import ( + "github.com/stretchr/testify/assert" + "testing" +) + +func TestALlProjects(t *testing.T) { + ts, gitlab := Stub("stubs/projects/index.json") + projects, err := gitlab.AllProjects() + + assert.Equal(t, err, nil) + assert.Equal(t, len(projects), 2) + defer ts.Close() +} + +func TestProjects(t *testing.T) { + ts, gitlab := Stub("stubs/projects/index.json") + projects, err := gitlab.Projects(1, 100) + + assert.Equal(t, err, nil) + assert.Equal(t, len(projects), 2) + defer ts.Close() +} + +func TestProject(t *testing.T) { + ts, gitlab := Stub("stubs/projects/show.json") + project, err := gitlab.Project("1") + + assert.Equal(t, err, nil) + assert.IsType(t, new(Project), project) + assert.Equal(t, project.SshRepoUrl, "git@example.com:diaspora/diaspora-project-site.git") + assert.Equal(t, project.HttpRepoUrl, "http://example.com/diaspora/diaspora-project-site.git") + defer ts.Close() +} + +func TestProjectBranches(t *testing.T) { + ts, gitlab := Stub("stubs/projects/branches/index.json") + branches, err := gitlab.ProjectBranches("1") + + assert.Equal(t, err, nil) + assert.Equal(t, len(branches), 2) + defer ts.Close() +} + +func TestProjectMergeRequests(t *testing.T) { + ts, gitlab := Stub("stubs/projects/merge_requests/index.json") + defer ts.Close() + mr, err := gitlab.ProjectMergeRequests("1", 0, 30, "all") + + assert.Equal(t, err, nil) + assert.Equal(t, len(mr), 1) + + if len(mr) > 0 { + assert.Equal(t, mr[0].TargetBranch, "master") + assert.Equal(t, mr[0].SourceBranch, "test1") + } +} + +func TestSearchProjectId(t *testing.T) { + ts, gitlab := Stub("stubs/projects/index.json") + + namespace := "Brightbox" + name := "Puppet" + id, err := gitlab.SearchProjectId(namespace, name) + + assert.Equal(t, err, nil) + assert.Equal(t, id, 6) + defer ts.Close() +} diff --git a/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/public_keys.go b/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/public_keys.go new file mode 100644 index 000000000..e3b7156b8 --- /dev/null +++ b/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/public_keys.go @@ -0,0 +1,69 @@ +package gogitlab + +import ( + "encoding/json" + "net/url" +) + +const ( + // ID + user_keys = "/user/keys" // Get current user keys + user_key = "/user/keys/:id" // Get user key by id + custom_user_keys = "/user/:id/keys" // Create key for user with :id +) + +type PublicKey struct { + Id int `json:"id,omitempty"` + Title string `json:"title,omitempty"` + Key string `json:"key,omitempty"` + CreatedAtRaw string `json:"created_at,omitempty"` +} + +func (g *Gitlab) UserKeys() ([]*PublicKey, error) { + url := g.ResourceUrl(user_keys, nil) + var keys []*PublicKey + contents, err := g.buildAndExecRequest("GET", url, nil) + if err == nil { + err = json.Unmarshal(contents, &keys) + } + return keys, err +} + +func (g *Gitlab) UserKey(id string) (*PublicKey, error) { + url := g.ResourceUrl(user_key, map[string]string{":id": id}) + var key *PublicKey + contents, err := g.buildAndExecRequest("GET", url, nil) + if err == nil { + err = json.Unmarshal(contents, &key) + } + return key, err +} + +func (g *Gitlab) AddKey(title, key string) error { + path := g.ResourceUrl(user_keys, nil) + var err error + v := url.Values{} + v.Set("title", title) + v.Set("key", key) + body := v.Encode() + _, err = g.buildAndExecRequest("POST", path, []byte(body)) + return err +} + +func (g *Gitlab) AddUserKey(id, title, key string) error { + path := g.ResourceUrl(user_keys, map[string]string{":id": id}) + var err error + v := url.Values{} + v.Set("title", title) + v.Set("key", key) + body := v.Encode() + _, err = g.buildAndExecRequest("POST", path, []byte(body)) + return err +} + +func (g *Gitlab) DeleteKey(id string) error { + url := g.ResourceUrl(user_key, map[string]string{":id": id}) + var err error + _, err = g.buildAndExecRequest("DELETE", url, nil) + return err +} diff --git a/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/public_keys_test.go b/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/public_keys_test.go new file mode 100644 index 000000000..d3be64a1f --- /dev/null +++ b/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/public_keys_test.go @@ -0,0 +1,50 @@ +package gogitlab + +import ( + "github.com/stretchr/testify/assert" + "testing" +) + +func TestGetUserKeys(t *testing.T) { + ts, gitlab := Stub("stubs/public_keys/index.json") + keys, err := gitlab.UserKeys() + + assert.Equal(t, err, nil) + assert.Equal(t, len(keys), 2) + defer ts.Close() +} + +func TestGetUserKey(t *testing.T) { + ts, gitlab := Stub("stubs/public_keys/show.json") + key, err := gitlab.UserKey("1") + + assert.Equal(t, err, nil) + assert.IsType(t, new(PublicKey), key) + assert.Equal(t, key.Title, "Public key") + assert.Equal(t, key.Key, "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=") + defer ts.Close() +} + +func TestAddKey(t *testing.T) { + ts, gitlab := Stub("") + err := gitlab.AddKey("Public key", "stubbed key") + + assert.Equal(t, err, nil) + defer ts.Close() +} + +func TestAddUserKey(t *testing.T) { + ts, gitlab := Stub("") + err := gitlab.AddUserKey("1", "Public key", "stubbed key") + + assert.Equal(t, err, nil) + defer ts.Close() +} + +func TestDeleteKey(t *testing.T) { + ts, gitlab := Stub("") + err := gitlab.DeleteKey("1") + + assert.Equal(t, err, nil) + defer ts.Close() +} diff --git a/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/repositories.go b/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/repositories.go new file mode 100644 index 000000000..694100e0e --- /dev/null +++ b/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/repositories.go @@ -0,0 +1,246 @@ +package gogitlab + +import ( + "encoding/json" + "net/url" + "time" +) + +const ( + repo_url_branches = "/projects/:id/repository/branches" // List repository branches + repo_url_branch = "/projects/:id/repository/branches/:branch" // Get a specific branch of a project. + repo_url_tags = "/projects/:id/repository/tags" // List project repository tags + repo_url_commits = "/projects/:id/repository/commits" // List repository commits + repo_url_tree = "/projects/:id/repository/tree" // List repository tree + repo_url_raw_file = "/projects/:id/repository/blobs/:sha" // Get raw file content for specific commit/branch +) + +type BranchCommit struct { + Id string `json:"id,omitempty"` + Tree string `json:"tree,omitempty"` + AuthoredDateRaw string `json:"authored_date,omitempty"` + CommittedDateRaw string `json:"committed_date,omitempty"` + Message string `json:"message,omitempty"` + Author *Person `json:"author,omitempty"` + Committer *Person `json:"committer,omitempty"` + /* + "parents": [ + {"id": "9b0c4b08e7890337fc8111e66f809c8bbec467a9"}, + {"id": "3ac634dca850cab70ab14b43ad6073d1e0a7827f"} + ] + */ +} + +type Branch struct { + Name string `json:"name,omitempty"` + Protected bool `json:"protected,omitempty"` + Commit *BranchCommit `json:"commit,omitempty"` +} + +type Tag struct { + Name string `json:"name,omitempty"` + Protected bool `json:"protected,omitempty"` + Commit *BranchCommit `json:"commit,omitempty"` +} + +type Commit struct { + Id string + Short_Id string + Title string + Author_Name string + Author_Email string + Created_At string + CreatedAt time.Time +} + +type File struct { + Id string `json:"id,omitempty"` + Name string `json:"name,omitempty"` + Type string `json:"type,omitempty"` + Mode string `json:"mode,omitempty"` + + Children []*File +} + +/* +Get a list of repository branches from a project, sorted by name alphabetically. + + GET /projects/:id/repository/branches + +Parameters: + + id The ID of a project + +Usage: + + branches, err := gitlab.RepoBranches("your_projet_id") + if err != nil { + fmt.Println(err.Error()) + } + for _, branch := range branches { + fmt.Printf("%+v\n", branch) + } +*/ +func (g *Gitlab) RepoBranches(id string) ([]*Branch, error) { + + url, opaque := g.ResourceUrlRaw(repo_url_branches, map[string]string{":id": id}) + + var branches []*Branch + + contents, err := g.buildAndExecRequestRaw("GET", url, opaque, nil) + if err == nil { + err = json.Unmarshal(contents, &branches) + } + + return branches, err +} + +/* +Get a single project repository branch. + + GET /projects/:id/repository/branches/:branch + +Parameters: + + id The ID of a project + branch The name of the branch + +*/ +func (g *Gitlab) RepoBranch(id, refName string) (*Branch, error) { + + url, opaque := g.ResourceUrlRaw(repo_url_branch, map[string]string{ + ":id": id, + ":branch": refName, + }) + + branch := new(Branch) + + contents, err := g.buildAndExecRequestRaw("GET", url, opaque, nil) + if err == nil { + err = json.Unmarshal(contents, &branch) + } + return branch, err +} + +/* +Get a list of repository tags from a project, sorted by name in reverse alphabetical order. + + GET /projects/:id/repository/tags + +Parameters: + + id The ID of a project + +Usage: + + tags, err := gitlab.RepoTags("your_projet_id") + if err != nil { + fmt.Println(err.Error()) + } + for _, tag := range tags { + fmt.Printf("%+v\n", tag) + } +*/ +func (g *Gitlab) RepoTags(id string) ([]*Tag, error) { + + url, opaque := g.ResourceUrlRaw(repo_url_tags, map[string]string{":id": id}) + + var tags []*Tag + + contents, err := g.buildAndExecRequestRaw("GET", url, opaque, nil) + if err == nil { + err = json.Unmarshal(contents, &tags) + } + + return tags, err +} + +/* +Get a list of repository commits in a project. + + GET /projects/:id/repository/commits + +Parameters: + + id The ID of a project + refName The name of a repository branch or tag or if not given the default branch + +Usage: + + commits, err := gitlab.RepoCommits("your_projet_id") + if err != nil { + fmt.Println(err.Error()) + } + for _, commit := range commits { + fmt.Printf("%+v\n", commit) + } +*/ +func (g *Gitlab) RepoCommits(id string) ([]*Commit, error) { + + url, opaque := g.ResourceUrlRaw(repo_url_commits, map[string]string{":id": id}) + + var commits []*Commit + + contents, err := g.buildAndExecRequestRaw("GET", url, opaque, nil) + if err == nil { + err = json.Unmarshal(contents, &commits) + if err == nil { + for _, commit := range commits { + t, _ := time.Parse(dateLayout, commit.Created_At) + commit.CreatedAt = t + } + } + } + + return commits, err +} + +/* +Get Raw file content +*/ +func (g *Gitlab) RepoRawFile(id, sha, filepath string) ([]byte, error) { + url_ := g.ResourceUrlQuery(repo_url_raw_file, map[string]string{ + ":id": id, + ":sha": sha, + }, map[string]string{ + "filepath": filepath, + }) + + p, err := url.Parse(url_) + if err != nil { + return nil, err + } + + opaque := "//" + p.Host + p.Path + contents, err := g.buildAndExecRequestRaw("GET", url_, opaque, nil) + + return contents, err +} + +/* +Get Raw file content +*/ +func (g *Gitlab) RepoTree(id, ref, path string) ([]*File, error) { + + url := g.ResourceUrlQuery(repo_url_tree, map[string]string{ + ":id": id, + }, map[string]string{ + "ref": ref, + "path": path, + }) + + var files []*File + + contents, err := g.buildAndExecRequest("GET", url, nil) + if err == nil { + err = json.Unmarshal(contents, &files) + } + + for _, f := range files { + if f.Type == "tree" { + f.Children, err = g.RepoTree(id, ref, path+"/"+f.Name) + } + } + + return files, err +} diff --git a/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/repositories_test.go b/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/repositories_test.go new file mode 100644 index 000000000..f009fb23f --- /dev/null +++ b/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/repositories_test.go @@ -0,0 +1,43 @@ +package gogitlab + +import ( + "github.com/stretchr/testify/assert" + "testing" +) + +func TestRepoBranches(t *testing.T) { + ts, gitlab := Stub("stubs/branches/index.json") + branches, err := gitlab.RepoBranches("1") + + assert.Equal(t, err, nil) + assert.Equal(t, len(branches), 1) + defer ts.Close() +} + +func TestRepoBranch(t *testing.T) { + ts, gitlab := Stub("stubs/branches/show.json") + branch, err := gitlab.RepoBranch("1", "master") + + assert.Equal(t, err, nil) + assert.IsType(t, new(Branch), branch) + assert.Equal(t, branch.Name, "master") + defer ts.Close() +} + +func TestRepoTags(t *testing.T) { + ts, gitlab := Stub("stubs/tags/index.json") + tags, err := gitlab.RepoTags("1") + + assert.Equal(t, err, nil) + assert.Equal(t, len(tags), 1) + defer ts.Close() +} + +func TestRepoCommits(t *testing.T) { + ts, gitlab := Stub("stubs/commits/index.json") + commits, err := gitlab.RepoCommits("1") + + assert.Equal(t, err, nil) + assert.Equal(t, len(commits), 2) + defer ts.Close() +} diff --git a/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/session.go b/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/session.go new file mode 100644 index 000000000..5626ade26 --- /dev/null +++ b/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/session.go @@ -0,0 +1,54 @@ +package gogitlab + +import ( + "encoding/json" + "net/url" +) + +const ( + session_path = "/session" +) + +type Session struct { + Id int `json:"id"` + UserName string `json:"username"` + Name string `json:"name"` + Blocked bool `json:"blocked"` + State string `json:"state"` + AvatarURL string `json:"avatar_url",omitempty` + IsAdmin bool `json:"is_admin"` + Bio string `json:"bio",omitempty` + Email string `json:"email"` + ThemeId int `json:"theme_id",omitempty` + ColorSchemeId int `json:"color_scheme_id",omitempty` + ExternUid string `json:"extern_uid",omitempty` + Provider string `json:"provider",omitempty` + CanCreateGroup bool `json:"can_create_group"` + CanCreateProject bool `json:"can_create_project"` + Skype string `json:"skype",omitempty` + Twitter string `json:"twitter",omitempty` + LinkedIn string `json:"linkedin",omitempty` + WebsiteURL string `json:"website_url",omitempty` + PrivateToken string `json:"private_token"` +} + +func (g *Gitlab) GetSession(email string, password string) (*Session, error) { + session_url := g.ResourceUrl(session_path, map[string]string{}) + + var session *Session + + v := url.Values{} + v.Set("email", email) + v.Set("password", password) + + body := v.Encode() + + contents, err := g.buildAndExecRequest("POST", session_url, []byte(body)) + if err != nil { + return session, err + } + + err = json.Unmarshal(contents, &session) + + return session, err +} diff --git a/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/session_test.go b/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/session_test.go new file mode 100644 index 000000000..28665563b --- /dev/null +++ b/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/session_test.go @@ -0,0 +1,33 @@ +package gogitlab + +import ( + "github.com/stretchr/testify/assert" + "testing" +) + +func TestGetSesison(t *testing.T) { + ts, gitlab := Stub("stubs/session/index.json") + session, err := gitlab.GetSession("john@example.com", "samplepassword") + + assert.Equal(t, err, nil) + assert.Equal(t, session.Id, 1) + assert.Equal(t, session.UserName, "john_smith") + assert.Equal(t, session.Name, "John Smith") + assert.Equal(t, session.State, "active") + assert.Equal(t, session.AvatarURL, "http://someurl.com/avatar.png") + assert.Equal(t, session.IsAdmin, false) + assert.Equal(t, session.Bio, "somebio") + assert.Equal(t, session.Skype, "someskype") + assert.Equal(t, session.LinkedIn, "somelinkedin") + assert.Equal(t, session.Twitter, "sometwitter") + assert.Equal(t, session.WebsiteURL, "http://example.com") + assert.Equal(t, session.Email, "john@example.com") + assert.Equal(t, session.ThemeId, 1) + assert.Equal(t, session.ColorSchemeId, 1) + assert.Equal(t, session.ExternUid, "someuid") + assert.Equal(t, session.Provider, "github.com") + assert.Equal(t, session.CanCreateGroup, true) + assert.Equal(t, session.CanCreateProject, true) + assert.Equal(t, session.PrivateToken, "dd34asd13as") + defer ts.Close() +} diff --git a/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/stubs/branches/index.json b/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/stubs/branches/index.json new file mode 100644 index 000000000..c3f72112b --- /dev/null +++ b/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/stubs/branches/index.json @@ -0,0 +1,26 @@ +[ + { + "name": "master", + "commit": { + "id": "7b5c3cc8be40ee161ae89a06bba6229da1032a0c", + "parents": [ + { + "id": "4ad91d3c1144c406e50c7b33bae684bd6837faf8" + } + ], + "tree": "46e82de44b1061621357f24c05515327f2795a95", + "message": "add projects API", + "author": { + "name": "John Smith", + "email": "john@example.com" + }, + "committer": { + "name": "John Smith", + "email": "john@example.com" + }, + "authored_date": "2012-06-27T05:51:39-07:00", + "committed_date": "2012-06-28T03:44:20-07:00" + }, + "protected": true + } +] \ No newline at end of file diff --git a/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/stubs/branches/show.json b/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/stubs/branches/show.json new file mode 100644 index 000000000..4bf5b39ef --- /dev/null +++ b/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/stubs/branches/show.json @@ -0,0 +1,24 @@ +{ + "name": "master", + "commit": { + "id": "7b5c3cc8be40ee161ae89a06bba6229da1032a0c", + "parents": [ + { + "id": "4ad91d3c1144c406e50c7b33bae684bd6837faf8" + } + ], + "tree": "46e82de44b1061621357f24c05515327f2795a95", + "message": "add projects API", + "author": { + "name": "John Smith", + "email": "john@example.com" + }, + "committer": { + "name": "John Smith", + "email": "john@example.com" + }, + "authored_date": "2012-06-27T05:51:39-07:00", + "committed_date": "2012-06-28T03:44:20-07:00" + }, + "protected": true +} \ No newline at end of file diff --git a/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/stubs/commits/index.json b/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/stubs/commits/index.json new file mode 100644 index 000000000..53eb0c558 --- /dev/null +++ b/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/stubs/commits/index.json @@ -0,0 +1,18 @@ +[ + { + "id": "ed899a2f4b50b4370feeea94676502b42383c746", + "short_id": "ed899a2f4b5", + "title": "Replace sanitize with escape once", + "author_name": "Dmitriy Zaporozhets", + "author_email": "dzaporozhets@sphereconsultinginc.com", + "created_at": "2012-09-20T11:50:22+03:00" + }, + { + "id": "6104942438c14ec7bd21c6cd5bd995272b3faff6", + "short_id": "6104942438c", + "title": "Sanitize for network graph", + "author_name": "randx", + "author_email": "dmitriy.zaporozhets@gmail.com", + "created_at": "2012-09-20T09:06:12+03:00" + } +] \ No newline at end of file diff --git a/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/stubs/hook_payloads/issue.json b/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/stubs/hook_payloads/issue.json new file mode 100644 index 000000000..e0842c1c9 --- /dev/null +++ b/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/stubs/hook_payloads/issue.json @@ -0,0 +1,18 @@ +{ + "object_kind": "issue", + "object_attributes": { + "id": 301, + "title": "New API: create/update/delete file", + "assignee_id": 51, + "author_id": 51, + "project_id": 14, + "created_at": "2013-12-03T17:15:43Z", + "updated_at": "2013-12-03T17:15:43Z", + "position": 0, + "branch_name": null, + "description": "Create new API for manipulations with repository", + "milestone_id": null, + "state": "opened", + "iid": 23 + } +} diff --git a/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/stubs/hook_payloads/merge_request.json b/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/stubs/hook_payloads/merge_request.json new file mode 100644 index 000000000..490719e63 --- /dev/null +++ b/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/stubs/hook_payloads/merge_request.json @@ -0,0 +1,22 @@ +{ + "object_kind": "merge_request", + "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": "" + } +} diff --git a/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/stubs/hook_payloads/push.json b/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/stubs/hook_payloads/push.json new file mode 100644 index 000000000..02e7f6485 --- /dev/null +++ b/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/stubs/hook_payloads/push.json @@ -0,0 +1,42 @@ +{ + "object_kind": "push", + "before": "95790bf891e76fee5e1747ab589903a6a1f80f22", + "after": "da1560886d4f094c3e6c9ef40349f7d38b5d27d7", + "ref": "refs/heads/master", + "user_id": 4, + "user_name": "John Smith", + "user_email": "john@example.com", + "project_id": 15, + "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" + } + }, + { + "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)" + } + } + ], + "total_commits_count": 4 +} diff --git a/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/stubs/hooks/show.json b/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/stubs/hooks/show.json new file mode 100644 index 000000000..ce7f3aca2 --- /dev/null +++ b/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/stubs/hooks/show.json @@ -0,0 +1,9 @@ +{ + "id": 1, + "url": "http://example.com/hook", + "project_id": 3, + "push_events": "true", + "issues_events": "true", + "merge_requests_events": "true", + "created_at": "2012-10-12T17:04:47Z" +} \ No newline at end of file diff --git a/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/stubs/projects/branches/index.json b/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/stubs/projects/branches/index.json new file mode 100644 index 000000000..0e62917a0 --- /dev/null +++ b/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/stubs/projects/branches/index.json @@ -0,0 +1,50 @@ +[ + { + "name": "async", + "commit": { + "id": "a2b702edecdf41f07b42653eb1abe30ce98b9fca", + "parents": [ + { + "id": "3f94fc7c85061973edc9906ae170cc269b07ca55" + } + ], + "tree": "c68537c6534a02cc2b176ca1549f4ffa190b58ee", + "message": "give caolan credit where it's due (up top)", + "author": { + "name": "Jeremy Ashkenas", + "email": "jashkenas@example.com" + }, + "committer": { + "name": "Jeremy Ashkenas", + "email": "jashkenas@example.com" + }, + "authored_date": "2010-12-08T21:28:50+00:00", + "committed_date": "2010-12-08T21:28:50+00:00" + }, + "protected": false + }, + { + "name": "gh-pages", + "commit": { + "id": "101c10a60019fe870d21868835f65c25d64968fc", + "parents": [ + { + "id": "9c15d2e26945a665131af5d7b6d30a06ba338aaa" + } + ], + "tree": "fb5cc9d45da3014b17a876ad539976a0fb9b352a", + "message": "Underscore.js 1.5.2", + "author": { + "name": "Jeremy Ashkenas", + "email": "jashkenas@example.com" + }, + "committer": { + "name": "Jeremy Ashkenas", + "email": "jashkenas@example.com" + }, + "authored_date": "2013-09-07T12: 58: 21+00: 00", + "committed_date": "2013-09-07T12: 58: 21+00: 00" + }, + "protected": false + } +] \ No newline at end of file diff --git a/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/stubs/projects/index.json b/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/stubs/projects/index.json new file mode 100644 index 000000000..6528d6cc4 --- /dev/null +++ b/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/stubs/projects/index.json @@ -0,0 +1,72 @@ +[ + { + "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", + "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, + "wall_enabled": false, + "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" + } + }, + { + "id": 6, + "description": null, + "default_branch": "master", + "public": false, + "visibility_level": 0, + "ssh_url_to_repo": "git@example.com:brightbox/puppet.git", + "http_url_to_repo": "http://example.com/brightbox/puppet.git", + "web_url": "http://example.com/brightbox/puppet", + "owner": { + "id": 4, + "name": "Brightbox", + "created_at": "2013-09-30T13:46:02Z" + }, + "name": "Puppet", + "name_with_namespace": "Brightbox / Puppet", + "path": "puppet", + "path_with_namespace": "brightbox/puppet", + "issues_enabled": true, + "merge_requests_enabled": true, + "wall_enabled": false, + "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": 4, + "name": "Brightbox", + "owner_id": 1, + "path": "brightbox", + "updated_at": "2013-09-30T13:46:02Z" + } + } +] \ No newline at end of file diff --git a/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/stubs/projects/merge_requests/index.json b/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/stubs/projects/merge_requests/index.json new file mode 100644 index 000000000..b9cbbe5a5 --- /dev/null +++ b/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/stubs/projects/merge_requests/index.json @@ -0,0 +1,30 @@ +[ + { + "id": 1, + "iid": 1, + "target_branch": "master", + "source_branch": "test1", + "project_id": 3, + "title": "test1", + "state": "opened", + "upvotes": 0, + "downvotes": 0, + "author": { + "id": 1, + "username": "admin", + "email": "admin@example.com", + "name": "Administrator", + "state": "active", + "created_at": "2012-04-29T08:46:00Z" + }, + "assignee": { + "id": 1, + "username": "admin", + "email": "admin@example.com", + "name": "Administrator", + "state": "active", + "created_at": "2012-04-29T08:46:00Z" + }, + "description":"fixed login page css paddings" + } +] diff --git a/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/stubs/projects/show.json b/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/stubs/projects/show.json new file mode 100644 index 000000000..cabb359ec --- /dev/null +++ b/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/stubs/projects/show.json @@ -0,0 +1,45 @@ +{ + "id": 3, + "description": null, + "default_branch": "master", + "public": false, + "visibility_level": 0, + "ssh_url_to_repo": "git@example.com:diaspora/diaspora-project-site.git", + "http_url_to_repo": "http://example.com/diaspora/diaspora-project-site.git", + "web_url": "http://example.com/diaspora/diaspora-project-site", + "owner": { + "id": 3, + "name": "Diaspora", + "created_at": "2013-09-30T13: 46: 02Z" + }, + "name": "Diaspora Project Site", + "name_with_namespace": "Diaspora / Diaspora Project Site", + "path": "diaspora-project-site", + "path_with_namespace": "diaspora/diaspora-project-site", + "issues_enabled": true, + "merge_requests_enabled": true, + "wall_enabled": false, + "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" + }, + "permissions": { + "project_access": { + "access_level": 10, + "notification_level": 3 + }, + "group_access": { + "access_level": 50, + "notification_level": 3 + } + } +} \ No newline at end of file diff --git a/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/stubs/public_keys/index.json b/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/stubs/public_keys/index.json new file mode 100644 index 000000000..67a9f4795 --- /dev/null +++ b/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/stubs/public_keys/index.json @@ -0,0 +1,12 @@ +[ + { + "id": 1, + "title": "Public key", + "key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=" + }, + { + "id": 3, + "title": "Another Public key", + "key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=" + } +] \ No newline at end of file diff --git a/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/stubs/public_keys/show.json b/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/stubs/public_keys/show.json new file mode 100644 index 000000000..ef2868b36 --- /dev/null +++ b/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/stubs/public_keys/show.json @@ -0,0 +1,5 @@ +{ + "id": 1, + "title": "Public key", + "key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=" +} \ No newline at end of file diff --git a/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/stubs/session/index.json b/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/stubs/session/index.json new file mode 100644 index 000000000..bf55d8bd0 --- /dev/null +++ b/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/stubs/session/index.json @@ -0,0 +1,23 @@ +{ + "id": 1, + "username": "john_smith", + "email": "john@example.com", + "name": "John Smith", + "avatar_url": "http://someurl.com/avatar.png", + "state": "active", + "created_at": "2012-05-23T08:00:58Z", + "bio": "somebio", + "skype": "someskype", + "linkedin": "somelinkedin", + "twitter": "sometwitter", + "website_url": "http://example.com", + "theme_id": 1, + "color_scheme_id": 1, + "extern_uid": "someuid", + "provider": "github.com", + "is_admin": false, + "can_create_group": true, + "can_create_team": true, + "can_create_project": true, + "private_token": "dd34asd13as" +} diff --git a/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/stubs/tags/index.json b/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/stubs/tags/index.json new file mode 100644 index 000000000..0e7474cbf --- /dev/null +++ b/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/stubs/tags/index.json @@ -0,0 +1,22 @@ +[ + { + "name": "v1.0.0", + "commit": { + "id": "2695effb5807a22ff3d138d593fd856244e155e7", + "parents": [], + "tree": "38017f2f189336fe4497e9d230c5bb1bf873f08d", + "message": "Initial commit", + "author": { + "name": "John Smith", + "email": "john@example.com" + }, + "committer": { + "name": "Jack Smith", + "email": "jack@example.com" + }, + "authored_date": "2012-05-28T04:42:42-07:00", + "committed_date": "2012-05-28T04:42:42-07:00" + }, + "protected": null + } +] \ No newline at end of file diff --git a/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/stubs/users/current.json b/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/stubs/users/current.json new file mode 100644 index 000000000..e39d69667 --- /dev/null +++ b/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/stubs/users/current.json @@ -0,0 +1,19 @@ +{ + "id": 1, + "username": "john_smith", + "email": "john@example.com", + "name": "John Smith", + "private_token": "dd34asd13as", + "state": "active", + "created_at": "2012-05-23T08:00:58Z", + "bio": null, + "skype": "", + "linkedin": "", + "twitter": "", + "website_url": "", + "theme_id": 1, + "color_scheme_id": 2, + "is_admin": false, + "can_create_group": true, + "can_create_project": true +} \ No newline at end of file diff --git a/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/stubs/users/index.json b/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/stubs/users/index.json new file mode 100644 index 000000000..5a14aa5d5 --- /dev/null +++ b/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/stubs/users/index.json @@ -0,0 +1,41 @@ +[ + { + "id": 1, + "username": "john_smith", + "email": "john@example.com", + "name": "John Smith", + "state": "active", + "created_at": "2012-05-23T08:00:58Z", + "bio": null, + "skype": "", + "linkedin": "", + "twitter": "", + "website_url": "", + "extern_uid": "john.smith", + "provider": "provider_name", + "theme_id": 1, + "color_scheme_id": 2, + "is_admin": false, + "can_create_group": true + }, + { + "id": 2, + "username": "jack_smith", + "email": "jack@example.com", + "name": "Jack Smith", + "state": "blocked", + "created_at": "2012-05-23T08:01:01Z", + "bio": null, + "skype": "", + "linkedin": "", + "twitter": "", + "website_url": "", + "extern_uid": "jack.smith", + "provider": "provider_name", + "theme_id": 1, + "color_scheme_id": 3, + "is_admin": false, + "can_create_group": true, + "can_create_project": true + } +] \ No newline at end of file diff --git a/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/stubs/users/show.json b/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/stubs/users/show.json new file mode 100644 index 000000000..af1d2d1bb --- /dev/null +++ b/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/stubs/users/show.json @@ -0,0 +1,15 @@ +{ + "id": 6, + "username": "plouc", + "email": "plouc@plouc.com", + "name": "Raphaël Benitte", + "bio": null, + "skype": "", + "linkedin": "", + "twitter": "", + "theme_id": 2, + "state": "active", + "created_at": "2001-01-01T00:00:00Z", + "extern_uid": "uid=plouc", + "provider": "ldap" +} \ No newline at end of file diff --git a/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/users.go b/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/users.go new file mode 100644 index 000000000..6c2c9460f --- /dev/null +++ b/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/users.go @@ -0,0 +1,97 @@ +package gogitlab + +import ( + "encoding/json" + "strconv" +) + +const ( + users_url = "/users" // Get users list + user_url = "/users/:id" // Get a single user. + current_user_url = "/user" // Get current user +) + +type User struct { + Id int `json:"id,omitempty"` + Username string `json:"username,omitempty"` + Email string `json:"email,omitempty"` + AvatarUrl string `json:"avatar_url,omitempty"` + Name string `json:"name,omitempty"` + State string `json:"state,omitempty"` + CreatedAt string `json:"created_at,omitempty"` + Bio string `json:"bio,omitempty"` + Skype string `json:"skype,omitempty"` + LinkedIn string `json:"linkedin,omitempty"` + Twitter string `json:"twitter,omitempty"` + ExternUid string `json:"extern_uid,omitempty"` + Provider string `json:"provider,omitempty"` + ThemeId int `json:"theme_id,omitempty"` + ColorSchemeId int `json:"color_scheme_id,color_scheme_id"` +} + +func (g *Gitlab) Users(page int, per_page int) ([]*User, error) { + + qry := map[string]string{ + "page": strconv.Itoa(page), + "per_page": strconv.Itoa(per_page)} + url := g.ResourceUrlQuery(users_url, nil, qry) + + var users []*User + + contents, err := g.buildAndExecRequest("GET", url, nil) + if err == nil { + err = json.Unmarshal(contents, &users) + } + + return users, err +} + +/* +Get a single user. + + GET /users/:id + +Parameters: + + id The ID of a user + +Usage: + + user, err := gitlab.User("your_user_id") + if err != nil { + fmt.Println(err.Error()) + } + fmt.Printf("%+v\n", user) +*/ +func (g *Gitlab) User(id string) (*User, error) { + + url := g.ResourceUrl(user_url, map[string]string{":id": id}) + + user := new(User) + + contents, err := g.buildAndExecRequest("GET", url, nil) + if err == nil { + err = json.Unmarshal(contents, &user) + } + + return user, err +} + +func (g *Gitlab) DeleteUser(id string) error { + url := g.ResourceUrl(user_url, map[string]string{":id": id}) + var err error + _, err = g.buildAndExecRequest("DELETE", url, nil) + return err +} + +func (g *Gitlab) CurrentUser() (User, error) { + url := g.ResourceUrl(current_user_url, nil) + var user User + + contents, err := g.buildAndExecRequest("GET", url, nil) + if err == nil { + err = json.Unmarshal(contents, &user) + } + + return user, err +} diff --git a/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/users_test.go b/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/users_test.go new file mode 100644 index 000000000..0102697a5 --- /dev/null +++ b/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/users_test.go @@ -0,0 +1,53 @@ +package gogitlab + +import ( + "github.com/stretchr/testify/assert" + "testing" +) + +func TestUsers(t *testing.T) { + ts, gitlab := Stub("stubs/users/index.json") + users, err := gitlab.Users(0, 100) + + assert.Equal(t, err, nil) + assert.Equal(t, len(users), 2) + defer ts.Close() +} + +func TestUser(t *testing.T) { + ts, gitlab := Stub("stubs/users/show.json") + user, err := gitlab.User("plouc") + + assert.Equal(t, err, nil) + assert.IsType(t, new(User), user) + assert.Equal(t, user.Id, 6) + assert.Equal(t, user.Username, "plouc") + assert.Equal(t, user.Name, "Raphaël Benitte") + assert.Equal(t, user.Bio, "") + assert.Equal(t, user.Skype, "") + assert.Equal(t, user.LinkedIn, "") + assert.Equal(t, user.Twitter, "") + assert.Equal(t, user.ThemeId, 2) + assert.Equal(t, user.State, "active") + assert.Equal(t, user.CreatedAt, "2001-01-01T00:00:00Z") + assert.Equal(t, user.ExternUid, "uid=plouc") + assert.Equal(t, user.Provider, "ldap") + defer ts.Close() +} + +func TestDeleteUser(t *testing.T) { + ts, gitlab := Stub("") + err := gitlab.DeleteUser("1") + + assert.Equal(t, err, nil) + defer ts.Close() +} + +func TestCurrentUser(t *testing.T) { + ts, gitlab := Stub("stubs/users/current.json") + user, err := gitlab.CurrentUser() + + assert.Equal(t, err, nil) + assert.Equal(t, user.Username, "john_smith") + defer ts.Close() +} diff --git a/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/util.go b/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/util.go new file mode 100644 index 000000000..8e47c681f --- /dev/null +++ b/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/util.go @@ -0,0 +1,10 @@ +package gogitlab + +import ( + "net/url" + "strings" +) + +func encodeParameter(value string) string { + return strings.Replace(url.QueryEscape(value), "/", "%2F", 0) +} diff --git a/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/util_test.go b/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/util_test.go new file mode 100644 index 000000000..fdaed9cd3 --- /dev/null +++ b/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client/util_test.go @@ -0,0 +1,11 @@ +package gogitlab + +import ( + "github.com/stretchr/testify/assert" + "testing" +) + +func TestParameterEncoding(t *testing.T) { + assert.Equal(t, encodeParameter("namespace/project"), "namespace%2Fproject") + assert.Equal(t, encodeParameter("14"), "14") +} diff --git a/Makefile b/Makefile index 713be80d6..f54edc7ad 100644 --- a/Makefile +++ b/Makefile @@ -7,7 +7,6 @@ all: concat bindata build deps: go get github.com/jteeuwen/go-bindata/... - go get github.com/Bugagazavr/go-gitlab-client... test: go vet github.com/drone/drone/pkg/... diff --git a/pkg/remote/builtin/gitlab/gitlab.go b/pkg/remote/builtin/gitlab/gitlab.go index 4e5e4c5af..29920914f 100644 --- a/pkg/remote/builtin/gitlab/gitlab.go +++ b/pkg/remote/builtin/gitlab/gitlab.go @@ -8,7 +8,7 @@ import ( "strconv" "strings" - "github.com/Bugagazavr/go-gitlab-client" + "github.com/drone/drone/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client" "github.com/drone/drone/Godeps/_workspace/src/github.com/hashicorp/golang-lru" "github.com/drone/drone/pkg/config" "github.com/drone/drone/pkg/oauth2" diff --git a/pkg/remote/builtin/gitlab/helper.go b/pkg/remote/builtin/gitlab/helper.go index 39339196e..ac7f49f5d 100644 --- a/pkg/remote/builtin/gitlab/helper.go +++ b/pkg/remote/builtin/gitlab/helper.go @@ -4,7 +4,7 @@ import ( "fmt" "net/url" - "github.com/Bugagazavr/go-gitlab-client" + "github.com/drone/drone/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client" ) // NewClient is a helper function that returns a new GitHub