mirror of
https://github.com/woodpecker-ci/woodpecker.git
synced 2024-11-25 19:31:05 +00:00
Merge pull request #2215 from bradrydzewski/master
add gitlab v3 option for backward compat
This commit is contained in:
commit
bf454572f2
20 changed files with 2304 additions and 3 deletions
|
@ -80,7 +80,7 @@ pipeline:
|
|||
image: plugins/docker
|
||||
repo: drone/drone
|
||||
secrets: [ docker_username, docker_password ]
|
||||
tag: [ 0.8, 0.8.0, 0.8.0-rc.5 ]
|
||||
tag: [ 0.8, 0.8.0 ]
|
||||
when:
|
||||
event: tag
|
||||
|
||||
|
@ -89,7 +89,7 @@ pipeline:
|
|||
repo: drone/agent
|
||||
dockerfile: Dockerfile.agent
|
||||
secrets: [ docker_username, docker_password ]
|
||||
tag: [ 0.8, 0.8.0, 0.8.0-rc.5 ]
|
||||
tag: [ 0.8, 0.8.0 ]
|
||||
when:
|
||||
event: tag
|
||||
|
||||
|
|
|
@ -365,6 +365,11 @@ var flags = []cli.Flag{
|
|||
Name: "gitlab-private-mode",
|
||||
Usage: "gitlab is running in private mode",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
EnvVar: "DRONE_GITLAB_V3_API",
|
||||
Name: "gitlab-v3-api",
|
||||
Usage: "gitlab is running the v3 api",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
EnvVar: "DRONE_STASH",
|
||||
Name: "stash",
|
||||
|
|
|
@ -15,6 +15,7 @@ import (
|
|||
"github.com/drone/drone/remote/gitea"
|
||||
"github.com/drone/drone/remote/github"
|
||||
"github.com/drone/drone/remote/gitlab"
|
||||
"github.com/drone/drone/remote/gitlab3"
|
||||
"github.com/drone/drone/remote/gogs"
|
||||
"github.com/drone/drone/server/web"
|
||||
"github.com/drone/drone/store"
|
||||
|
@ -121,6 +122,17 @@ func setupStash(c *cli.Context) (remote.Remote, error) {
|
|||
|
||||
// helper function to setup the Gitlab remote from the CLI arguments.
|
||||
func setupGitlab(c *cli.Context) (remote.Remote, error) {
|
||||
if c.Bool("gitlab-v3-api") {
|
||||
return gitlab3.New(gitlab3.Opts{
|
||||
URL: c.String("gitlab-server"),
|
||||
Client: c.String("gitlab-client"),
|
||||
Secret: c.String("gitlab-secret"),
|
||||
Username: c.String("gitlab-git-username"),
|
||||
Password: c.String("gitlab-git-password"),
|
||||
PrivateMode: c.Bool("gitlab-private-mode"),
|
||||
SkipVerify: c.Bool("gitlab-skip-verify"),
|
||||
})
|
||||
}
|
||||
return gitlab.New(gitlab.Opts{
|
||||
URL: c.String("gitlab-server"),
|
||||
Client: c.String("gitlab-client"),
|
||||
|
|
27
remote/gitlab3/client/drone.go
Normal file
27
remote/gitlab3/client/drone.go
Normal file
|
@ -0,0 +1,27 @@
|
|||
package client
|
||||
|
||||
const (
|
||||
droneServiceUrl = "/projects/:id/services/drone-ci"
|
||||
)
|
||||
|
||||
func (c *Client) AddDroneService(id string, params QMap) error {
|
||||
url, opaque := c.ResourceUrl(
|
||||
droneServiceUrl,
|
||||
QMap{":id": id},
|
||||
params,
|
||||
)
|
||||
|
||||
_, err := c.Do("PUT", url, opaque, nil)
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Client) DeleteDroneService(id string) error {
|
||||
url, opaque := c.ResourceUrl(
|
||||
droneServiceUrl,
|
||||
QMap{":id": id},
|
||||
nil,
|
||||
)
|
||||
|
||||
_, err := c.Do("DELETE", url, opaque, nil)
|
||||
return err
|
||||
}
|
96
remote/gitlab3/client/gitlab.go
Normal file
96
remote/gitlab3/client/gitlab.go
Normal file
|
@ -0,0 +1,96 @@
|
|||
package client
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
BaseUrl string
|
||||
ApiPath string
|
||||
Token string
|
||||
Client *http.Client
|
||||
}
|
||||
|
||||
func New(baseUrl, apiPath, token string, skipVerify bool) *Client {
|
||||
config := &tls.Config{InsecureSkipVerify: skipVerify}
|
||||
tr := &http.Transport{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
TLSClientConfig: config,
|
||||
}
|
||||
client := &http.Client{Transport: tr}
|
||||
|
||||
return &Client{
|
||||
BaseUrl: baseUrl,
|
||||
ApiPath: apiPath,
|
||||
Token: token,
|
||||
Client: client,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) ResourceUrl(u string, params, query QMap) (string, string) {
|
||||
if params != nil {
|
||||
for key, val := range params {
|
||||
u = strings.Replace(u, key, encodeParameter(val), -1)
|
||||
}
|
||||
}
|
||||
|
||||
query_params := url.Values{}
|
||||
|
||||
if query != nil {
|
||||
for key, val := range query {
|
||||
query_params.Set(key, val)
|
||||
}
|
||||
}
|
||||
|
||||
u = c.BaseUrl + c.ApiPath + u + "?" + query_params.Encode()
|
||||
p, err := url.Parse(u)
|
||||
if err != nil {
|
||||
return u, ""
|
||||
}
|
||||
|
||||
opaque := "//" + p.Host + p.Path
|
||||
return u, opaque
|
||||
}
|
||||
|
||||
func (c *Client) Do(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 {
|
||||
return nil, fmt.Errorf("Error while building gitlab request")
|
||||
}
|
||||
|
||||
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", c.Token))
|
||||
|
||||
if len(opaque) > 0 {
|
||||
req.URL.Opaque = opaque
|
||||
}
|
||||
|
||||
resp, err := c.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
|
||||
}
|
53
remote/gitlab3/client/groups.go
Normal file
53
remote/gitlab3/client/groups.go
Normal file
|
@ -0,0 +1,53 @@
|
|||
package client
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
const (
|
||||
groupsUrl = "/groups"
|
||||
)
|
||||
|
||||
// Get a list of all projects owned by the authenticated user.
|
||||
func (g *Client) AllGroups() ([]*Namespace, error) {
|
||||
var perPage = 100
|
||||
var groups []*Namespace
|
||||
|
||||
for i := 1; true; i++ {
|
||||
contents, err := g.Groups(i, perPage)
|
||||
if err != nil {
|
||||
return groups, err
|
||||
}
|
||||
|
||||
for _, value := range contents {
|
||||
groups = append(groups, value)
|
||||
}
|
||||
|
||||
if len(groups) == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
if len(groups)/i < perPage {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return groups, nil
|
||||
}
|
||||
|
||||
func (g *Client) Groups(page, perPage int) ([]*Namespace, error) {
|
||||
url, opaque := g.ResourceUrl(groupsUrl, nil, QMap{
|
||||
"page": strconv.Itoa(page),
|
||||
"per_page": strconv.Itoa(perPage),
|
||||
})
|
||||
|
||||
var groups []*Namespace
|
||||
|
||||
contents, err := g.Do("GET", url, opaque, nil)
|
||||
if err == nil {
|
||||
err = json.Unmarshal(contents, &groups)
|
||||
}
|
||||
|
||||
return groups, err
|
||||
}
|
41
remote/gitlab3/client/hook.go
Normal file
41
remote/gitlab3/client/hook.go
Normal file
|
@ -0,0 +1,41 @@
|
|||
package client
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// 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
|
||||
}
|
161
remote/gitlab3/client/project.go
Normal file
161
remote/gitlab3/client/project.go
Normal file
|
@ -0,0 +1,161 @@
|
|||
package client
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
searchUrl = "/projects/search/:query"
|
||||
projectsUrl = "/projects"
|
||||
projectUrl = "/projects/:id"
|
||||
repoUrlRawFile = "/projects/:id/repository/blobs/:sha"
|
||||
repoUrlRawFileRef = "/projects/:id/repository/files"
|
||||
commitStatusUrl = "/projects/:id/statuses/:sha"
|
||||
)
|
||||
|
||||
// Get a list of all projects owned by the authenticated user.
|
||||
func (g *Client) AllProjects(hide_archives bool) ([]*Project, error) {
|
||||
var per_page = 100
|
||||
var projects []*Project
|
||||
|
||||
for i := 1; true; i++ {
|
||||
contents, err := g.Projects(i, per_page, hide_archives)
|
||||
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 (c *Client) Projects(page int, per_page int, hide_archives bool) ([]*Project, error) {
|
||||
projectsOptions := QMap{
|
||||
"page": strconv.Itoa(page),
|
||||
"per_page": strconv.Itoa(per_page),
|
||||
}
|
||||
|
||||
if hide_archives {
|
||||
projectsOptions["archived"] = "false"
|
||||
}
|
||||
|
||||
url, opaque := c.ResourceUrl(projectsUrl, nil, projectsOptions)
|
||||
|
||||
var projects []*Project
|
||||
|
||||
contents, err := c.Do("GET", url, opaque, nil)
|
||||
if err == nil {
|
||||
err = json.Unmarshal(contents, &projects)
|
||||
}
|
||||
|
||||
return projects, err
|
||||
}
|
||||
|
||||
// Get a project by id
|
||||
func (c *Client) Project(id string) (*Project, error) {
|
||||
url, opaque := c.ResourceUrl(projectUrl, QMap{":id": id}, nil)
|
||||
|
||||
var project *Project
|
||||
|
||||
contents, err := c.Do("GET", url, opaque, nil)
|
||||
if err == nil {
|
||||
err = json.Unmarshal(contents, &project)
|
||||
}
|
||||
|
||||
return project, err
|
||||
}
|
||||
|
||||
// Get Raw file content
|
||||
func (c *Client) RepoRawFile(id, sha, filepath string) ([]byte, error) {
|
||||
url, opaque := c.ResourceUrl(
|
||||
repoUrlRawFile,
|
||||
QMap{
|
||||
":id": id,
|
||||
":sha": sha,
|
||||
},
|
||||
QMap{
|
||||
"filepath": filepath,
|
||||
},
|
||||
)
|
||||
|
||||
contents, err := c.Do("GET", url, opaque, nil)
|
||||
|
||||
return contents, err
|
||||
}
|
||||
|
||||
func (c *Client) RepoRawFileRef(id, ref, filepath string) ([]byte, error) {
|
||||
url, opaque := c.ResourceUrl(
|
||||
repoUrlRawFileRef,
|
||||
QMap{
|
||||
":id": id,
|
||||
},
|
||||
QMap{
|
||||
"filepath": filepath,
|
||||
"ref": ref,
|
||||
},
|
||||
)
|
||||
|
||||
contents, err := c.Do("GET", url, opaque, nil)
|
||||
|
||||
return contents, err
|
||||
}
|
||||
|
||||
//
|
||||
func (c *Client) SetStatus(id, sha, state, desc, ref, link string) error {
|
||||
url, opaque := c.ResourceUrl(
|
||||
commitStatusUrl,
|
||||
QMap{
|
||||
":id": id,
|
||||
":sha": sha,
|
||||
},
|
||||
QMap{
|
||||
"state": state,
|
||||
"ref": ref,
|
||||
"target_url": link,
|
||||
"description": desc,
|
||||
"context": "ci/drone",
|
||||
},
|
||||
)
|
||||
|
||||
_, err := c.Do("POST", url, opaque, nil)
|
||||
return err
|
||||
}
|
||||
|
||||
// Get a list of projects by query owned by the authenticated user.
|
||||
func (c *Client) SearchProjectId(namespace string, name string) (id int, err error) {
|
||||
|
||||
url, opaque := c.ResourceUrl(searchUrl, nil, QMap{
|
||||
":query": strings.ToLower(name),
|
||||
})
|
||||
|
||||
var projects []*Project
|
||||
|
||||
contents, err := c.Do("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 && strings.ToLower(project.Name) == strings.ToLower(name) {
|
||||
id = project.Id
|
||||
}
|
||||
}
|
||||
|
||||
return id, err
|
||||
}
|
138
remote/gitlab3/client/types.go
Normal file
138
remote/gitlab3/client/types.go
Normal file
|
@ -0,0 +1,138 @@
|
|||
package client
|
||||
|
||||
type QMap map[string]string
|
||||
|
||||
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"`
|
||||
}
|
||||
|
||||
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"`
|
||||
}
|
||||
|
||||
type Member struct {
|
||||
Id int
|
||||
Username string
|
||||
Email string
|
||||
Name string
|
||||
State string
|
||||
CreatedAt string `json:"created_at,omitempty"`
|
||||
// AccessLevel int
|
||||
}
|
||||
|
||||
type Project struct {
|
||||
Id int `json:"id,omitempty"`
|
||||
Owner *Member `json:"owner,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
DefaultBranch string `json:"default_branch,omitempty"`
|
||||
Public bool `json:"public,omitempty"`
|
||||
Path string `json:"path,omitempty"`
|
||||
PathWithNamespace string `json:"path_with_namespace,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"`
|
||||
AvatarUrl string `json:"avatar_url"`
|
||||
Permissions *Permissions `json:"permissions,omitempty"`
|
||||
}
|
||||
|
||||
type Namespace struct {
|
||||
Id int `json:"id,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Path string `json:"path,omitempty"`
|
||||
}
|
||||
|
||||
type Person struct {
|
||||
Name string `json:"name"`
|
||||
Email string `json:"email"`
|
||||
}
|
||||
|
||||
type hProject struct {
|
||||
Name string `json:"name"`
|
||||
SshUrl string `json:"ssh_url"`
|
||||
HttpUrl string `json:"http_url"`
|
||||
GitSshUrl string `json:"git_ssh_url"`
|
||||
GitHttpUrl string `json:"git_http_url"`
|
||||
AvatarUrl string `json:"avatar_url"`
|
||||
VisibilityLevel int `json:"visibility_level"`
|
||||
WebUrl string `json:"web_url"`
|
||||
PathWithNamespace string `json:"path_with_namespace"`
|
||||
DefaultBranch string `json:"default_branch"`
|
||||
Namespace string `json:"namespace"`
|
||||
}
|
||||
|
||||
type hRepository struct {
|
||||
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 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"`
|
||||
Url string `json:"url,omiyempty"`
|
||||
Source *hProject `json:"source,omitempty"`
|
||||
Target *hProject `json:"target,omitempty"`
|
||||
LastCommit *hCommit `json:"last_commit,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"`
|
||||
Project *hProject `json:"project,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"`
|
||||
}
|
21
remote/gitlab3/client/user.go
Normal file
21
remote/gitlab3/client/user.go
Normal file
|
@ -0,0 +1,21 @@
|
|||
package client
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
const (
|
||||
currentUserUrl = "/user"
|
||||
)
|
||||
|
||||
func (c *Client) CurrentUser() (User, error) {
|
||||
url, opaque := c.ResourceUrl(currentUserUrl, nil, nil)
|
||||
var user User
|
||||
|
||||
contents, err := c.Do("GET", url, opaque, nil)
|
||||
if err == nil {
|
||||
err = json.Unmarshal(contents, &user)
|
||||
}
|
||||
|
||||
return user, err
|
||||
}
|
43
remote/gitlab3/client/util.go
Normal file
43
remote/gitlab3/client/util.go
Normal file
|
@ -0,0 +1,43 @@
|
|||
package client
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var encodeMap = map[string]string{
|
||||
".": "%252E",
|
||||
}
|
||||
|
||||
func encodeParameter(value string) string {
|
||||
value = url.QueryEscape(value)
|
||||
|
||||
for before, after := range encodeMap {
|
||||
value = strings.Replace(value, before, after, -1)
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
// Tag returns current tag for push event hook payload
|
||||
// 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
|
||||
}
|
699
remote/gitlab3/gitlab.go
Normal file
699
remote/gitlab3/gitlab.go
Normal file
|
@ -0,0 +1,699 @@
|
|||
package gitlab3
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/drone/drone/model"
|
||||
"github.com/drone/drone/remote"
|
||||
"github.com/drone/drone/shared/httputil"
|
||||
"github.com/drone/drone/shared/oauth2"
|
||||
|
||||
"github.com/drone/drone/remote/gitlab3/client"
|
||||
)
|
||||
|
||||
const DefaultScope = "api"
|
||||
|
||||
// Opts defines configuration options.
|
||||
type Opts struct {
|
||||
URL string // Gogs server url.
|
||||
Client string // Oauth2 client id.
|
||||
Secret string // Oauth2 client secret.
|
||||
Username string // Optional machine account username.
|
||||
Password string // Optional machine account password.
|
||||
PrivateMode bool // Gogs is running in private mode.
|
||||
SkipVerify bool // Skip ssl verification.
|
||||
}
|
||||
|
||||
// New returns a Remote implementation that integrates with Gitlab, an open
|
||||
// source Git service. See https://gitlab.com
|
||||
func New(opts Opts) (remote.Remote, error) {
|
||||
url, err := url.Parse(opts.URL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
host, _, err := net.SplitHostPort(url.Host)
|
||||
if err == nil {
|
||||
url.Host = host
|
||||
}
|
||||
return &Gitlab{
|
||||
URL: opts.URL,
|
||||
Client: opts.Client,
|
||||
Secret: opts.Secret,
|
||||
Machine: url.Host,
|
||||
Username: opts.Username,
|
||||
Password: opts.Password,
|
||||
PrivateMode: opts.PrivateMode,
|
||||
SkipVerify: opts.SkipVerify,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type Gitlab struct {
|
||||
URL string
|
||||
Client string
|
||||
Secret string
|
||||
Machine string
|
||||
Username string
|
||||
Password string
|
||||
PrivateMode bool
|
||||
SkipVerify bool
|
||||
HideArchives bool
|
||||
Search bool
|
||||
}
|
||||
|
||||
func Load(config string) *Gitlab {
|
||||
url_, err := url.Parse(config)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
params := url_.Query()
|
||||
url_.RawQuery = ""
|
||||
|
||||
gitlab := Gitlab{}
|
||||
gitlab.URL = url_.String()
|
||||
gitlab.Client = params.Get("client_id")
|
||||
gitlab.Secret = params.Get("client_secret")
|
||||
// gitlab.AllowedOrgs = params["orgs"]
|
||||
gitlab.SkipVerify, _ = strconv.ParseBool(params.Get("skip_verify"))
|
||||
gitlab.HideArchives, _ = strconv.ParseBool(params.Get("hide_archives"))
|
||||
// gitlab.Open, _ = strconv.ParseBool(params.Get("open"))
|
||||
|
||||
// switch params.Get("clone_mode") {
|
||||
// case "oauth":
|
||||
// gitlab.CloneMode = "oauth"
|
||||
// default:
|
||||
// gitlab.CloneMode = "token"
|
||||
// }
|
||||
|
||||
// this is a temp workaround
|
||||
gitlab.Search, _ = strconv.ParseBool(params.Get("search"))
|
||||
|
||||
return &gitlab
|
||||
}
|
||||
|
||||
// Login authenticates the session and returns the
|
||||
// remote user details.
|
||||
func (g *Gitlab) Login(res http.ResponseWriter, req *http.Request) (*model.User, error) {
|
||||
|
||||
var config = &oauth2.Config{
|
||||
ClientId: g.Client,
|
||||
ClientSecret: g.Secret,
|
||||
Scope: DefaultScope,
|
||||
AuthURL: fmt.Sprintf("%s/oauth/authorize", g.URL),
|
||||
TokenURL: fmt.Sprintf("%s/oauth/token", g.URL),
|
||||
RedirectURL: fmt.Sprintf("%s/authorize", httputil.GetURL(req)),
|
||||
}
|
||||
|
||||
trans_ := &http.Transport{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: g.SkipVerify},
|
||||
}
|
||||
|
||||
// get the OAuth errors
|
||||
if err := req.FormValue("error"); err != "" {
|
||||
return nil, &remote.AuthError{
|
||||
Err: err,
|
||||
Description: req.FormValue("error_description"),
|
||||
URI: req.FormValue("error_uri"),
|
||||
}
|
||||
}
|
||||
|
||||
// get the OAuth code
|
||||
var code = req.FormValue("code")
|
||||
if len(code) == 0 {
|
||||
http.Redirect(res, req, config.AuthCodeURL("drone"), http.StatusSeeOther)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var trans = &oauth2.Transport{Config: config, Transport: trans_}
|
||||
var token_, err = trans.Exchange(code)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error exchanging token. %s", err)
|
||||
}
|
||||
|
||||
client := NewClient(g.URL, token_.AccessToken, g.SkipVerify)
|
||||
login, err := client.CurrentUser()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// if len(g.AllowedOrgs) != 0 {
|
||||
// groups, err := client.AllGroups()
|
||||
// if err != nil {
|
||||
// return nil, fmt.Errorf("Could not check org membership. %s", err)
|
||||
// }
|
||||
//
|
||||
// var member bool
|
||||
// for _, group := range groups {
|
||||
// for _, allowedOrg := range g.AllowedOrgs {
|
||||
// if group.Path == allowedOrg {
|
||||
// member = true
|
||||
// break
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// if !member {
|
||||
// return nil, false, fmt.Errorf("User does not belong to correct group. Must belong to %v", g.AllowedOrgs)
|
||||
// }
|
||||
// }
|
||||
|
||||
user := &model.User{}
|
||||
user.Login = login.Username
|
||||
user.Email = login.Email
|
||||
user.Token = token_.AccessToken
|
||||
user.Secret = token_.RefreshToken
|
||||
|
||||
if strings.HasPrefix(login.AvatarUrl, "http") {
|
||||
user.Avatar = login.AvatarUrl
|
||||
} else {
|
||||
user.Avatar = g.URL + "/" + login.AvatarUrl
|
||||
}
|
||||
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func (g *Gitlab) Auth(token, secret string) (string, error) {
|
||||
client := NewClient(g.URL, token, g.SkipVerify)
|
||||
login, err := client.CurrentUser()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return login.Username, nil
|
||||
}
|
||||
|
||||
func (g *Gitlab) Teams(u *model.User) ([]*model.Team, error) {
|
||||
client := NewClient(g.URL, u.Token, g.SkipVerify)
|
||||
groups, err := client.AllGroups()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var teams []*model.Team
|
||||
for _, group := range groups {
|
||||
teams = append(teams, &model.Team{
|
||||
Login: group.Name,
|
||||
})
|
||||
}
|
||||
return teams, nil
|
||||
}
|
||||
|
||||
// Repo fetches the named repository from the remote system.
|
||||
func (g *Gitlab) Repo(u *model.User, owner, name string) (*model.Repo, error) {
|
||||
client := NewClient(g.URL, u.Token, g.SkipVerify)
|
||||
id, err := GetProjectId(g, client, owner, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
repo_, err := client.Project(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
repo := &model.Repo{}
|
||||
repo.Owner = owner
|
||||
repo.Name = name
|
||||
repo.FullName = repo_.PathWithNamespace
|
||||
repo.Link = repo_.Url
|
||||
repo.Clone = repo_.HttpRepoUrl
|
||||
repo.Branch = "master"
|
||||
|
||||
repo.Avatar = repo_.AvatarUrl
|
||||
|
||||
if len(repo.Avatar) != 0 && !strings.HasPrefix(repo.Avatar, "http") {
|
||||
repo.Avatar = fmt.Sprintf("%s/%s", g.URL, repo.Avatar)
|
||||
}
|
||||
|
||||
if repo_.DefaultBranch != "" {
|
||||
repo.Branch = repo_.DefaultBranch
|
||||
}
|
||||
|
||||
if g.PrivateMode {
|
||||
repo.IsPrivate = true
|
||||
} else {
|
||||
repo.IsPrivate = !repo_.Public
|
||||
}
|
||||
|
||||
return repo, err
|
||||
}
|
||||
|
||||
// Repos fetches a list of repos from the remote system.
|
||||
func (g *Gitlab) Repos(u *model.User) ([]*model.Repo, error) {
|
||||
client := NewClient(g.URL, u.Token, g.SkipVerify)
|
||||
|
||||
var repos = []*model.Repo{}
|
||||
|
||||
all, err := client.AllProjects(g.HideArchives)
|
||||
if err != nil {
|
||||
return repos, err
|
||||
}
|
||||
|
||||
for _, repo_ := range all {
|
||||
var parts = strings.Split(repo_.PathWithNamespace, "/")
|
||||
var owner = parts[0]
|
||||
var name = parts[1]
|
||||
|
||||
repo := &model.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 g.PrivateMode {
|
||||
repo.IsPrivate = true
|
||||
} else {
|
||||
repo.IsPrivate = !repo_.Public
|
||||
}
|
||||
|
||||
repos = append(repos, repo)
|
||||
}
|
||||
|
||||
return repos, err
|
||||
}
|
||||
|
||||
// Perm fetches the named repository from the remote system.
|
||||
func (g *Gitlab) Perm(u *model.User, owner, name string) (*model.Perm, error) {
|
||||
|
||||
client := NewClient(g.URL, u.Token, g.SkipVerify)
|
||||
id, err := GetProjectId(g, client, owner, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
repo, err := client.Project(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// repo owner is granted full access
|
||||
if repo.Owner != nil && repo.Owner.Username == u.Login {
|
||||
return &model.Perm{Push: true, Pull: true, Admin: true}, nil
|
||||
}
|
||||
|
||||
// check permission for current user
|
||||
m := &model.Perm{}
|
||||
m.Admin = IsAdmin(repo)
|
||||
m.Pull = IsRead(repo)
|
||||
m.Push = IsWrite(repo)
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// File fetches a file from the remote repository and returns in string format.
|
||||
func (g *Gitlab) File(user *model.User, repo *model.Repo, build *model.Build, f string) ([]byte, error) {
|
||||
var client = NewClient(g.URL, user.Token, g.SkipVerify)
|
||||
id, err := GetProjectId(g, client, repo.Owner, repo.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
out, err := client.RepoRawFile(id, build.Commit, f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, err
|
||||
}
|
||||
|
||||
// FileRef fetches the file from the GitHub repository and returns its contents.
|
||||
func (g *Gitlab) FileRef(u *model.User, r *model.Repo, ref, f string) ([]byte, error) {
|
||||
var client = NewClient(g.URL, u.Token, g.SkipVerify)
|
||||
id, err := GetProjectId(g, client, r.Owner, r.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
out, err := client.RepoRawFileRef(id, ref, f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, err
|
||||
}
|
||||
|
||||
// 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 (g *Gitlab) Status(u *model.User, repo *model.Repo, b *model.Build, link string) error {
|
||||
client := NewClient(g.URL, u.Token, g.SkipVerify)
|
||||
|
||||
status := getStatus(b.Status)
|
||||
desc := getDesc(b.Status)
|
||||
|
||||
client.SetStatus(
|
||||
ns(repo.Owner, repo.Name),
|
||||
b.Commit,
|
||||
status,
|
||||
desc,
|
||||
strings.Replace(b.Ref, "refs/heads/", "", -1),
|
||||
link,
|
||||
)
|
||||
|
||||
// Gitlab statuses it's a new feature, just ignore error
|
||||
// if gitlab version not support this
|
||||
return nil
|
||||
}
|
||||
|
||||
// Netrc returns a .netrc file that can be used to clone
|
||||
// private repositories from a remote system.
|
||||
// func (g *Gitlab) Netrc(u *model.User, r *model.Repo) (*model.Netrc, error) {
|
||||
// url_, err := url.Parse(g.URL)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
// netrc := &model.Netrc{}
|
||||
// netrc.Machine = url_.Host
|
||||
//
|
||||
// switch g.CloneMode {
|
||||
// case "oauth":
|
||||
// netrc.Login = "oauth2"
|
||||
// netrc.Password = u.Token
|
||||
// case "token":
|
||||
// t := token.New(token.HookToken, r.FullName)
|
||||
// netrc.Login = "drone-ci-token"
|
||||
// netrc.Password, err = t.Sign(r.Hash)
|
||||
// }
|
||||
// return netrc, err
|
||||
// }
|
||||
|
||||
// Netrc returns a netrc file capable of authenticating Gitlab requests and
|
||||
// cloning Gitlab repositories. The netrc will use the global machine account
|
||||
// when configured.
|
||||
func (g *Gitlab) Netrc(u *model.User, r *model.Repo) (*model.Netrc, error) {
|
||||
if g.Password != "" {
|
||||
return &model.Netrc{
|
||||
Login: g.Username,
|
||||
Password: g.Password,
|
||||
Machine: g.Machine,
|
||||
}, nil
|
||||
}
|
||||
return &model.Netrc{
|
||||
Login: "oauth2",
|
||||
Password: u.Token,
|
||||
Machine: g.Machine,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Activate activates a repository by adding a Post-commit hook and
|
||||
// a Public Deploy key, if applicable.
|
||||
func (g *Gitlab) Activate(user *model.User, repo *model.Repo, link string) error {
|
||||
var client = NewClient(g.URL, user.Token, g.SkipVerify)
|
||||
id, err := GetProjectId(g, client, repo.Owner, repo.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
uri, err := url.Parse(link)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
droneUrl := fmt.Sprintf("%s://%s", uri.Scheme, uri.Host)
|
||||
droneToken := uri.Query().Get("access_token")
|
||||
ssl_verify := strconv.FormatBool(!g.SkipVerify)
|
||||
|
||||
return client.AddDroneService(id, map[string]string{
|
||||
"token": droneToken,
|
||||
"drone_url": droneUrl,
|
||||
"enable_ssl_verification": ssl_verify,
|
||||
})
|
||||
}
|
||||
|
||||
// Deactivate removes a repository by removing all the post-commit hooks
|
||||
// which are equal to link and removing the SSH deploy key.
|
||||
func (g *Gitlab) Deactivate(user *model.User, repo *model.Repo, link string) error {
|
||||
var client = NewClient(g.URL, user.Token, g.SkipVerify)
|
||||
id, err := GetProjectId(g, client, repo.Owner, repo.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return client.DeleteDroneService(id)
|
||||
}
|
||||
|
||||
// ParseHook parses the post-commit hook from the Request body
|
||||
// and returns the required data in a standard format.
|
||||
func (g *Gitlab) Hook(req *http.Request) (*model.Repo, *model.Build, error) {
|
||||
defer req.Body.Close()
|
||||
var payload, _ = ioutil.ReadAll(req.Body)
|
||||
var parsed, err = client.ParseHook(payload)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
switch parsed.ObjectKind {
|
||||
case "merge_request":
|
||||
return mergeRequest(parsed, req)
|
||||
case "tag_push", "push":
|
||||
return push(parsed, req)
|
||||
default:
|
||||
return nil, nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
func mergeRequest(parsed *client.HookPayload, req *http.Request) (*model.Repo, *model.Build, error) {
|
||||
|
||||
repo := &model.Repo{}
|
||||
|
||||
obj := parsed.ObjectAttributes
|
||||
if obj == nil {
|
||||
return nil, nil, fmt.Errorf("object_attributes key expected in merge request hook")
|
||||
}
|
||||
|
||||
target := obj.Target
|
||||
source := obj.Source
|
||||
|
||||
if target == nil && source == nil {
|
||||
return nil, nil, fmt.Errorf("target and source keys expected in merge request hook")
|
||||
} else if target == nil {
|
||||
return nil, nil, fmt.Errorf("target key expected in merge request hook")
|
||||
} else if source == nil {
|
||||
return nil, nil, fmt.Errorf("source key exptected in merge request hook")
|
||||
}
|
||||
|
||||
if target.PathWithNamespace != "" {
|
||||
var err error
|
||||
if repo.Owner, repo.Name, err = ExtractFromPath(target.PathWithNamespace); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
repo.FullName = target.PathWithNamespace
|
||||
} else {
|
||||
repo.Owner = req.FormValue("owner")
|
||||
repo.Name = req.FormValue("name")
|
||||
repo.FullName = fmt.Sprintf("%s/%s", repo.Owner, repo.Name)
|
||||
}
|
||||
|
||||
repo.Link = target.WebUrl
|
||||
|
||||
if target.GitHttpUrl != "" {
|
||||
repo.Clone = target.GitHttpUrl
|
||||
} else {
|
||||
repo.Clone = target.HttpUrl
|
||||
}
|
||||
|
||||
if target.DefaultBranch != "" {
|
||||
repo.Branch = target.DefaultBranch
|
||||
} else {
|
||||
repo.Branch = "master"
|
||||
}
|
||||
|
||||
if target.AvatarUrl != "" {
|
||||
repo.Avatar = target.AvatarUrl
|
||||
}
|
||||
|
||||
build := &model.Build{}
|
||||
build.Event = "pull_request"
|
||||
|
||||
lastCommit := obj.LastCommit
|
||||
if lastCommit == nil {
|
||||
return nil, nil, fmt.Errorf("last_commit key expected in merge request hook")
|
||||
}
|
||||
|
||||
build.Message = lastCommit.Message
|
||||
build.Commit = lastCommit.Id
|
||||
//build.Remote = parsed.ObjectAttributes.Source.HttpUrl
|
||||
|
||||
build.Ref = fmt.Sprintf("refs/merge-requests/%d/head", obj.IId)
|
||||
|
||||
build.Branch = obj.SourceBranch
|
||||
|
||||
author := lastCommit.Author
|
||||
if author == nil {
|
||||
return nil, nil, fmt.Errorf("author key expected in merge request hook")
|
||||
}
|
||||
|
||||
build.Author = author.Name
|
||||
build.Email = author.Email
|
||||
|
||||
if len(build.Email) != 0 {
|
||||
build.Avatar = GetUserAvatar(build.Email)
|
||||
}
|
||||
|
||||
build.Title = obj.Title
|
||||
build.Link = obj.Url
|
||||
|
||||
return repo, build, nil
|
||||
}
|
||||
|
||||
func push(parsed *client.HookPayload, req *http.Request) (*model.Repo, *model.Build, error) {
|
||||
repo := &model.Repo{}
|
||||
|
||||
// Since gitlab 8.5, used project instead repository key
|
||||
// see https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/web_hooks/web_hooks.md#web-hooks
|
||||
if project := parsed.Project; project != nil {
|
||||
var err error
|
||||
if repo.Owner, repo.Name, err = ExtractFromPath(project.PathWithNamespace); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
repo.Avatar = project.AvatarUrl
|
||||
repo.Link = project.WebUrl
|
||||
repo.Clone = project.GitHttpUrl
|
||||
repo.FullName = project.PathWithNamespace
|
||||
repo.Branch = project.DefaultBranch
|
||||
|
||||
switch project.VisibilityLevel {
|
||||
case 0:
|
||||
repo.IsPrivate = true
|
||||
case 10:
|
||||
repo.IsPrivate = true
|
||||
case 20:
|
||||
repo.IsPrivate = false
|
||||
}
|
||||
} else if repository := parsed.Repository; repository != nil {
|
||||
repo.Owner = req.FormValue("owner")
|
||||
repo.Name = req.FormValue("name")
|
||||
repo.Link = repository.URL
|
||||
repo.Clone = repository.GitHttpUrl
|
||||
repo.Branch = "master"
|
||||
repo.FullName = fmt.Sprintf("%s/%s", req.FormValue("owner"), req.FormValue("name"))
|
||||
|
||||
switch repository.VisibilityLevel {
|
||||
case 0:
|
||||
repo.IsPrivate = true
|
||||
case 10:
|
||||
repo.IsPrivate = true
|
||||
case 20:
|
||||
repo.IsPrivate = false
|
||||
}
|
||||
} else {
|
||||
return nil, nil, fmt.Errorf("No project/repository keys given")
|
||||
}
|
||||
|
||||
build := &model.Build{}
|
||||
build.Event = model.EventPush
|
||||
build.Commit = parsed.After
|
||||
build.Branch = parsed.Branch()
|
||||
build.Ref = parsed.Ref
|
||||
// hook.Commit.Remote = cloneUrl
|
||||
|
||||
var head = parsed.Head()
|
||||
build.Message = head.Message
|
||||
// build.Timestamp = head.Timestamp
|
||||
|
||||
// extracts the commit author (ideally email)
|
||||
// from the post-commit hook
|
||||
switch {
|
||||
case head.Author != nil:
|
||||
build.Email = head.Author.Email
|
||||
build.Author = parsed.UserName
|
||||
if len(build.Email) != 0 {
|
||||
build.Avatar = GetUserAvatar(build.Email)
|
||||
}
|
||||
case head.Author == nil:
|
||||
build.Author = parsed.UserName
|
||||
}
|
||||
|
||||
if strings.HasPrefix(build.Ref, "refs/tags/") {
|
||||
build.Event = model.EventTag
|
||||
}
|
||||
|
||||
return repo, build, 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/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},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
StatusPending = "pending"
|
||||
StatusRunning = "running"
|
||||
StatusSuccess = "success"
|
||||
StatusFailure = "failed"
|
||||
StatusCanceled = "canceled"
|
||||
)
|
||||
|
||||
const (
|
||||
DescPending = "the build is pending"
|
||||
DescRunning = "the buils is running"
|
||||
DescSuccess = "the build was successful"
|
||||
DescFailure = "the build failed"
|
||||
DescCanceled = "the build canceled"
|
||||
DescBlocked = "the build is pending approval"
|
||||
DescDeclined = "the build was rejected"
|
||||
)
|
||||
|
||||
// getStatus is a helper functin that converts a Drone
|
||||
// status to a GitHub status.
|
||||
func getStatus(status string) string {
|
||||
switch status {
|
||||
case model.StatusPending, model.StatusBlocked:
|
||||
return StatusPending
|
||||
case model.StatusRunning:
|
||||
return StatusRunning
|
||||
case model.StatusSuccess:
|
||||
return StatusSuccess
|
||||
case model.StatusFailure, model.StatusError:
|
||||
return StatusFailure
|
||||
case model.StatusKilled:
|
||||
return StatusCanceled
|
||||
default:
|
||||
return StatusFailure
|
||||
}
|
||||
}
|
||||
|
||||
// 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 model.StatusPending:
|
||||
return DescPending
|
||||
case model.StatusRunning:
|
||||
return DescRunning
|
||||
case model.StatusSuccess:
|
||||
return DescSuccess
|
||||
case model.StatusFailure, model.StatusError:
|
||||
return DescFailure
|
||||
case model.StatusKilled:
|
||||
return DescCanceled
|
||||
case model.StatusBlocked:
|
||||
return DescBlocked
|
||||
case model.StatusDeclined:
|
||||
return DescDeclined
|
||||
default:
|
||||
return DescFailure
|
||||
}
|
||||
}
|
247
remote/gitlab3/gitlab_test.go
Normal file
247
remote/gitlab3/gitlab_test.go
Normal file
|
@ -0,0 +1,247 @@
|
|||
package gitlab3
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/drone/drone/model"
|
||||
"github.com/drone/drone/remote/gitlab3/testdata"
|
||||
"github.com/franela/goblin"
|
||||
)
|
||||
|
||||
func Test_Gitlab(t *testing.T) {
|
||||
// setup a dummy github server
|
||||
var server = testdata.NewServer()
|
||||
defer server.Close()
|
||||
|
||||
env := server.URL + "?client_id=test&client_secret=test"
|
||||
|
||||
gitlab := Load(env)
|
||||
|
||||
var user = model.User{
|
||||
Login: "test_user",
|
||||
Token: "e3b0c44298fc1c149afbf4c8996fb",
|
||||
}
|
||||
|
||||
var repo = model.Repo{
|
||||
Name: "diaspora-client",
|
||||
Owner: "diaspora",
|
||||
}
|
||||
|
||||
g := goblin.Goblin(t)
|
||||
g.Describe("Gitlab Plugin", func() {
|
||||
// Test projects method
|
||||
g.Describe("AllProjects", func() {
|
||||
g.It("Should return only non-archived projects is hidden", func() {
|
||||
gitlab.HideArchives = true
|
||||
_projects, err := gitlab.Repos(&user)
|
||||
|
||||
g.Assert(err == nil).IsTrue()
|
||||
g.Assert(len(_projects)).Equal(1)
|
||||
})
|
||||
|
||||
g.It("Should return all the projects", func() {
|
||||
gitlab.HideArchives = false
|
||||
_projects, err := gitlab.Repos(&user)
|
||||
|
||||
g.Assert(err == nil).IsTrue()
|
||||
g.Assert(len(_projects)).Equal(2)
|
||||
})
|
||||
})
|
||||
|
||||
// Test repository method
|
||||
g.Describe("Repo", func() {
|
||||
g.It("Should return valid repo", func() {
|
||||
_repo, err := gitlab.Repo(&user, "diaspora", "diaspora-client")
|
||||
|
||||
g.Assert(err == nil).IsTrue()
|
||||
g.Assert(_repo.Name).Equal("diaspora-client")
|
||||
g.Assert(_repo.Owner).Equal("diaspora")
|
||||
g.Assert(_repo.IsPrivate).Equal(true)
|
||||
})
|
||||
|
||||
g.It("Should return error, when repo not exist", func() {
|
||||
_, err := gitlab.Repo(&user, "not-existed", "not-existed")
|
||||
|
||||
g.Assert(err != nil).IsTrue()
|
||||
})
|
||||
})
|
||||
|
||||
// Test permissions method
|
||||
g.Describe("Perm", func() {
|
||||
g.It("Should return repo permissions", func() {
|
||||
perm, err := gitlab.Perm(&user, "diaspora", "diaspora-client")
|
||||
g.Assert(err == nil).IsTrue()
|
||||
g.Assert(perm.Admin).Equal(true)
|
||||
g.Assert(perm.Pull).Equal(true)
|
||||
g.Assert(perm.Push).Equal(true)
|
||||
})
|
||||
g.It("Should return repo permissions when user is admin", func() {
|
||||
perm, err := gitlab.Perm(&user, "brightbox", "puppet")
|
||||
g.Assert(err == nil).IsTrue()
|
||||
g.Assert(perm.Admin).Equal(true)
|
||||
g.Assert(perm.Pull).Equal(true)
|
||||
g.Assert(perm.Push).Equal(true)
|
||||
})
|
||||
g.It("Should return error, when repo is not exist", func() {
|
||||
_, err := gitlab.Perm(&user, "not-existed", "not-existed")
|
||||
|
||||
g.Assert(err != nil).IsTrue()
|
||||
})
|
||||
})
|
||||
|
||||
// Test activate method
|
||||
g.Describe("Activate", func() {
|
||||
g.It("Should be success", func() {
|
||||
err := gitlab.Activate(&user, &repo, "http://example.com/api/hook/test/test?access_token=token")
|
||||
|
||||
g.Assert(err == nil).IsTrue()
|
||||
})
|
||||
|
||||
g.It("Should be failed, when token not given", func() {
|
||||
err := gitlab.Activate(&user, &repo, "http://example.com/api/hook/test/test")
|
||||
|
||||
g.Assert(err != nil).IsTrue()
|
||||
})
|
||||
})
|
||||
|
||||
// Test deactivate method
|
||||
g.Describe("Deactivate", func() {
|
||||
g.It("Should be success", func() {
|
||||
err := gitlab.Deactivate(&user, &repo, "http://example.com/api/hook/test/test?access_token=token")
|
||||
|
||||
g.Assert(err == nil).IsTrue()
|
||||
})
|
||||
})
|
||||
|
||||
// Test login method
|
||||
// g.Describe("Login", func() {
|
||||
// g.It("Should return user", func() {
|
||||
// user, err := gitlab.Login("valid_token", "")
|
||||
|
||||
// g.Assert(err == nil).IsTrue()
|
||||
// g.Assert(user == nil).IsFalse()
|
||||
// })
|
||||
|
||||
// g.It("Should return error, when token is invalid", func() {
|
||||
// _, err := gitlab.Login("invalid_token", "")
|
||||
|
||||
// g.Assert(err != nil).IsTrue()
|
||||
// })
|
||||
// })
|
||||
|
||||
// Test hook method
|
||||
g.Describe("Hook", func() {
|
||||
g.Describe("Push hook", func() {
|
||||
g.It("Should parse actual push hoook", func() {
|
||||
req, _ := http.NewRequest(
|
||||
"POST",
|
||||
"http://example.com/api/hook?owner=diaspora&name=diaspora-client",
|
||||
bytes.NewReader(testdata.PushHook),
|
||||
)
|
||||
|
||||
repo, build, err := gitlab.Hook(req)
|
||||
|
||||
g.Assert(err == nil).IsTrue()
|
||||
g.Assert(repo.Owner).Equal("mike")
|
||||
g.Assert(repo.Name).Equal("diaspora")
|
||||
g.Assert(repo.Avatar).Equal("http://example.com/uploads/project/avatar/555/Outh-20-Logo.jpg")
|
||||
g.Assert(repo.Branch).Equal("develop")
|
||||
g.Assert(build.Ref).Equal("refs/heads/master")
|
||||
|
||||
})
|
||||
|
||||
g.It("Should parse legacy push hoook", func() {
|
||||
req, _ := http.NewRequest(
|
||||
"POST",
|
||||
"http://example.com/api/hook?owner=diaspora&name=diaspora-client",
|
||||
bytes.NewReader(testdata.LegacyPushHook),
|
||||
)
|
||||
|
||||
repo, build, err := gitlab.Hook(req)
|
||||
|
||||
g.Assert(err == nil).IsTrue()
|
||||
g.Assert(repo.Owner).Equal("diaspora")
|
||||
g.Assert(repo.Name).Equal("diaspora-client")
|
||||
g.Assert(repo.Avatar).Equal("")
|
||||
g.Assert(repo.Branch).Equal("master")
|
||||
g.Assert(build.Ref).Equal("refs/heads/master")
|
||||
|
||||
})
|
||||
})
|
||||
|
||||
g.Describe("Tag push hook", func() {
|
||||
g.It("Should parse tag push hook", func() {
|
||||
req, _ := http.NewRequest(
|
||||
"POST",
|
||||
"http://example.com/api/hook?owner=diaspora&name=diaspora-client",
|
||||
bytes.NewReader(testdata.TagHook),
|
||||
)
|
||||
|
||||
repo, build, err := gitlab.Hook(req)
|
||||
|
||||
g.Assert(err == nil).IsTrue()
|
||||
g.Assert(repo.Owner).Equal("jsmith")
|
||||
g.Assert(repo.Name).Equal("example")
|
||||
g.Assert(repo.Avatar).Equal("http://example.com/uploads/project/avatar/555/Outh-20-Logo.jpg")
|
||||
g.Assert(repo.Branch).Equal("develop")
|
||||
g.Assert(build.Ref).Equal("refs/tags/v1.0.0")
|
||||
|
||||
})
|
||||
|
||||
g.It("Should parse legacy tag push hook", func() {
|
||||
req, _ := http.NewRequest(
|
||||
"POST",
|
||||
"http://example.com/api/hook?owner=diaspora&name=diaspora-client",
|
||||
bytes.NewReader(testdata.LegacyTagHook),
|
||||
)
|
||||
|
||||
repo, build, err := gitlab.Hook(req)
|
||||
|
||||
g.Assert(err == nil).IsTrue()
|
||||
g.Assert(repo.Owner).Equal("diaspora")
|
||||
g.Assert(repo.Name).Equal("diaspora-client")
|
||||
g.Assert(build.Ref).Equal("refs/tags/v1.0.0")
|
||||
|
||||
})
|
||||
})
|
||||
|
||||
g.Describe("Merge request hook", func() {
|
||||
g.It("Should parse merge request hook", func() {
|
||||
req, _ := http.NewRequest(
|
||||
"POST",
|
||||
"http://example.com/api/hook?owner=diaspora&name=diaspora-client",
|
||||
bytes.NewReader(testdata.MergeRequestHook),
|
||||
)
|
||||
|
||||
repo, build, err := gitlab.Hook(req)
|
||||
|
||||
g.Assert(err == nil).IsTrue()
|
||||
g.Assert(repo.Avatar).Equal("http://example.com/uploads/project/avatar/555/Outh-20-Logo.jpg")
|
||||
g.Assert(repo.Branch).Equal("develop")
|
||||
g.Assert(repo.Owner).Equal("awesome_space")
|
||||
g.Assert(repo.Name).Equal("awesome_project")
|
||||
|
||||
g.Assert(build.Title).Equal("MS-Viewport")
|
||||
})
|
||||
|
||||
g.It("Should parse legacy merge request hook", func() {
|
||||
req, _ := http.NewRequest(
|
||||
"POST",
|
||||
"http://example.com/api/hook?owner=diaspora&name=diaspora-client",
|
||||
bytes.NewReader(testdata.LegacyMergeRequestHook),
|
||||
)
|
||||
|
||||
repo, build, err := gitlab.Hook(req)
|
||||
|
||||
g.Assert(err == nil).IsTrue()
|
||||
g.Assert(repo.Owner).Equal("diaspora")
|
||||
g.Assert(repo.Name).Equal("diaspora-client")
|
||||
|
||||
g.Assert(build.Title).Equal("MS-Viewport")
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
125
remote/gitlab3/helper.go
Normal file
125
remote/gitlab3/helper.go
Normal file
|
@ -0,0 +1,125 @@
|
|||
package gitlab3
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/drone/drone/remote/gitlab3/client"
|
||||
)
|
||||
|
||||
const (
|
||||
gravatarBase = "https://www.gravatar.com/avatar"
|
||||
)
|
||||
|
||||
// NewClient is a helper function that returns a new GitHub
|
||||
// client using the provided OAuth token.
|
||||
func NewClient(url, accessToken string, skipVerify bool) *client.Client {
|
||||
client := client.New(url, "/api/v3", accessToken, skipVerify)
|
||||
return client
|
||||
}
|
||||
|
||||
// IsRead is a helper function that returns true if the
|
||||
// user has Read-only access to the repository.
|
||||
func IsRead(proj *client.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 *client.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 *client.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 GetUserAvatar(email string) string {
|
||||
hasher := md5.New()
|
||||
hasher.Write([]byte(email))
|
||||
|
||||
return fmt.Sprintf(
|
||||
"%s/%v.jpg?s=%s",
|
||||
gravatarBase,
|
||||
hex.EncodeToString(hasher.Sum(nil)),
|
||||
"128",
|
||||
)
|
||||
}
|
||||
|
||||
func ExtractFromPath(str string) (string, string, error) {
|
||||
s := strings.Split(str, "/")
|
||||
if len(s) < 2 {
|
||||
return "", "", fmt.Errorf("Minimum match not found")
|
||||
}
|
||||
return s[0], s[1], nil
|
||||
}
|
||||
|
||||
func GetUserEmail(c *client.Client, defaultURL string) (*client.Client, error) {
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func GetProjectId(r *Gitlab, c *client.Client, owner, name string) (projectId string, err error) {
|
||||
if r.Search {
|
||||
_projectId, err := c.SearchProjectId(owner, name)
|
||||
if err != nil || _projectId == 0 {
|
||||
return "", err
|
||||
}
|
||||
projectId := strconv.Itoa(_projectId)
|
||||
return projectId, nil
|
||||
} else {
|
||||
projectId := ns(owner, name)
|
||||
return projectId, nil
|
||||
}
|
||||
}
|
334
remote/gitlab3/testdata/hooks.go
vendored
Normal file
334
remote/gitlab3/testdata/hooks.go
vendored
Normal file
|
@ -0,0 +1,334 @@
|
|||
package testdata
|
||||
|
||||
var TagHook = []byte(`
|
||||
{
|
||||
"object_kind": "tag_push",
|
||||
"ref": "refs/tags/v1.0.0",
|
||||
"before": "0000000000000000000000000000000000000000",
|
||||
"after": "82b3d5ae55f7080f1e6022629cdb57bfae7cccc7",
|
||||
"user_id": 1,
|
||||
"user_name": "John Smith",
|
||||
"user_avatar": "https://s.gravatar.com/avatar/d4c74594d841139328695756648b6bd6?s=8://s=80",
|
||||
"project_id": 1,
|
||||
"project":{
|
||||
"name":"Example",
|
||||
"description":"",
|
||||
"web_url":"http://example.com/jsmith/example",
|
||||
"avatar_url":"http://example.com/uploads/project/avatar/555/Outh-20-Logo.jpg",
|
||||
"git_ssh_url":"git@example.com:jsmith/example.git",
|
||||
"git_http_url":"http://example.com/jsmith/example.git",
|
||||
"namespace":"Jsmith",
|
||||
"visibility_level":0,
|
||||
"path_with_namespace":"jsmith/example",
|
||||
"default_branch":"develop",
|
||||
"homepage":"http://example.com/jsmith/example",
|
||||
"url":"git@example.com:jsmith/example.git",
|
||||
"ssh_url":"git@example.com:jsmith/example.git",
|
||||
"http_url":"http://example.com/jsmith/example.git"
|
||||
},
|
||||
"repository":{
|
||||
"name": "jsmith",
|
||||
"url": "ssh://git@example.com/jsmith/example.git",
|
||||
"description": "",
|
||||
"homepage": "http://example.com/jsmith/example",
|
||||
"git_http_url":"http://example.com/jsmith/example.git",
|
||||
"git_ssh_url":"git@example.com:jsmith/example.git",
|
||||
"visibility_level":0
|
||||
},
|
||||
"commits": [],
|
||||
"total_commits_count": 0
|
||||
}
|
||||
`)
|
||||
|
||||
var LegacyTagHook = []byte(`
|
||||
{
|
||||
"object_kind": "tag_push",
|
||||
"ref": "refs/tags/v1.0.0",
|
||||
"before": "0000000000000000000000000000000000000000",
|
||||
"after": "82b3d5ae55f7080f1e6022629cdb57bfae7cccc7",
|
||||
"user_id": 1,
|
||||
"user_name": "John Smith",
|
||||
"project_id": 1,
|
||||
"repository": {
|
||||
"name": "jsmith",
|
||||
"url": "ssh://git@example.com/jsmith/example.git",
|
||||
"description": "",
|
||||
"homepage": "http://example.com/jsmith/example",
|
||||
"git_http_url":"http://example.com/jsmith/example.git",
|
||||
"git_ssh_url":"git@example.com:jsmith/example.git",
|
||||
"visibility_level":0
|
||||
},
|
||||
"commits": [
|
||||
{
|
||||
"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
|
||||
}
|
||||
`)
|
||||
|
||||
var MergeRequestHook = []byte(`
|
||||
{
|
||||
"object_kind": "merge_request",
|
||||
"user": {
|
||||
"name": "Administrator",
|
||||
"username": "root",
|
||||
"avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=40\u0026d=identicon"
|
||||
},
|
||||
"object_attributes": {
|
||||
"id": 99,
|
||||
"target_branch": "master",
|
||||
"source_branch": "ms-viewport",
|
||||
"source_project_id": 14,
|
||||
"author_id": 51,
|
||||
"assignee_id": 6,
|
||||
"title": "MS-Viewport",
|
||||
"created_at": "2013-12-03T17:23:34Z",
|
||||
"updated_at": "2013-12-03T17:23:34Z",
|
||||
"st_commits": null,
|
||||
"st_diffs": null,
|
||||
"milestone_id": null,
|
||||
"state": "opened",
|
||||
"merge_status": "unchecked",
|
||||
"target_project_id": 14,
|
||||
"iid": 1,
|
||||
"description": "",
|
||||
"source":{
|
||||
"name":"Awesome Project",
|
||||
"description":"Aut reprehenderit ut est.",
|
||||
"web_url":"http://example.com/awesome_space/awesome_project",
|
||||
"avatar_url":"http://example.com/uploads/project/avatar/555/Outh-20-Logo.jpg",
|
||||
"git_ssh_url":"git@example.com:awesome_space/awesome_project.git",
|
||||
"git_http_url":"http://example.com/awesome_space/awesome_project.git",
|
||||
"namespace":"Awesome Space",
|
||||
"visibility_level":20,
|
||||
"path_with_namespace":"awesome_space/awesome_project",
|
||||
"default_branch":"master",
|
||||
"homepage":"http://example.com/awesome_space/awesome_project",
|
||||
"url":"http://example.com/awesome_space/awesome_project.git",
|
||||
"ssh_url":"git@example.com:awesome_space/awesome_project.git",
|
||||
"http_url":"http://example.com/awesome_space/awesome_project.git"
|
||||
},
|
||||
"target": {
|
||||
"name":"Awesome Project",
|
||||
"description":"Aut reprehenderit ut est.",
|
||||
"web_url":"http://example.com/awesome_space/awesome_project",
|
||||
"avatar_url":"http://example.com/uploads/project/avatar/555/Outh-20-Logo.jpg",
|
||||
"git_ssh_url":"git@example.com:awesome_space/awesome_project.git",
|
||||
"git_http_url":"http://example.com/awesome_space/awesome_project.git",
|
||||
"namespace":"Awesome Space",
|
||||
"visibility_level":20,
|
||||
"path_with_namespace":"awesome_space/awesome_project",
|
||||
"default_branch":"develop",
|
||||
"homepage":"http://example.com/awesome_space/awesome_project",
|
||||
"url":"http://example.com/awesome_space/awesome_project.git",
|
||||
"ssh_url":"git@example.com:awesome_space/awesome_project.git",
|
||||
"http_url":"http://example.com/awesome_space/awesome_project.git"
|
||||
},
|
||||
"last_commit": {
|
||||
"id": "da1560886d4f094c3e6c9ef40349f7d38b5d27d7",
|
||||
"message": "fixed readme",
|
||||
"timestamp": "2012-01-03T23:36:29+02:00",
|
||||
"url": "http://example.com/awesome_space/awesome_project/commits/da1560886d4f094c3e6c9ef40349f7d38b5d27d7",
|
||||
"author": {
|
||||
"name": "GitLab dev user",
|
||||
"email": "gitlabdev@dv6700.(none)"
|
||||
}
|
||||
},
|
||||
"work_in_progress": false,
|
||||
"url": "http://example.com/diaspora/merge_requests/1",
|
||||
"action": "open",
|
||||
"assignee": {
|
||||
"name": "User1",
|
||||
"username": "user1",
|
||||
"avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=40\u0026d=identicon"
|
||||
}
|
||||
}
|
||||
}
|
||||
`)
|
||||
|
||||
var LegacyMergeRequestHook = []byte(`
|
||||
{
|
||||
"object_kind": "merge_request",
|
||||
"user": {
|
||||
"name": "Administrator",
|
||||
"username": "root",
|
||||
"avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=40\u0026d=identicon"
|
||||
},
|
||||
"object_attributes": {
|
||||
"id": 99,
|
||||
"target_branch": "master",
|
||||
"source_branch": "ms-viewport",
|
||||
"source_project_id": 14,
|
||||
"author_id": 51,
|
||||
"assignee_id": 6,
|
||||
"title": "MS-Viewport",
|
||||
"created_at": "2013-12-03T17:23:34Z",
|
||||
"updated_at": "2013-12-03T17:23:34Z",
|
||||
"st_commits": null,
|
||||
"st_diffs": null,
|
||||
"milestone_id": null,
|
||||
"state": "opened",
|
||||
"merge_status": "unchecked",
|
||||
"target_project_id": 14,
|
||||
"iid": 1,
|
||||
"description": "",
|
||||
"source": {
|
||||
"name": "awesome_project",
|
||||
"ssh_url": "ssh://git@example.com/awesome_space/awesome_project.git",
|
||||
"http_url": "http://example.com/awesome_space/awesome_project.git",
|
||||
"visibility_level": 20,
|
||||
"namespace": "awesome_space"
|
||||
},
|
||||
"target": {
|
||||
"name": "awesome_project",
|
||||
"ssh_url": "ssh://git@example.com/awesome_space/awesome_project.git",
|
||||
"http_url": "http://example.com/awesome_space/awesome_project.git",
|
||||
"visibility_level": 20,
|
||||
"namespace": "awesome_space"
|
||||
},
|
||||
"last_commit": {
|
||||
"id": "da1560886d4f094c3e6c9ef40349f7d38b5d27d7",
|
||||
"message": "fixed readme",
|
||||
"timestamp": "2012-01-03T23:36:29+02:00",
|
||||
"url": "http://example.com/awesome_space/awesome_project/commits/da1560886d4f094c3e6c9ef40349f7d38b5d27d7",
|
||||
"author": {
|
||||
"name": "GitLab dev user",
|
||||
"email": "gitlabdev@dv6700.(none)"
|
||||
}
|
||||
},
|
||||
"url": "http://example.com/diaspora/merge_requests/1",
|
||||
"action": "open"
|
||||
}
|
||||
}
|
||||
`)
|
||||
|
||||
var PushHook = []byte(`
|
||||
{
|
||||
"object_kind": "push",
|
||||
"before": "95790bf891e76fee5e1747ab589903a6a1f80f22",
|
||||
"after": "da1560886d4f094c3e6c9ef40349f7d38b5d27d7",
|
||||
"ref": "refs/heads/master",
|
||||
"user_id": 4,
|
||||
"user_name": "John Smith",
|
||||
"user_email": "john@example.com",
|
||||
"user_avatar": "https://s.gravatar.com/avatar/d4c74594d841139328695756648b6bd6?s=80",
|
||||
"project_id": 15,
|
||||
"project":{
|
||||
"name":"Diaspora",
|
||||
"description":"",
|
||||
"web_url":"http://example.com/mike/diaspora",
|
||||
"avatar_url":"http://example.com/uploads/project/avatar/555/Outh-20-Logo.jpg",
|
||||
"git_ssh_url":"git@example.com:mike/diaspora.git",
|
||||
"git_http_url":"http://example.com/mike/diaspora.git",
|
||||
"namespace":"Mike",
|
||||
"visibility_level":0,
|
||||
"path_with_namespace":"mike/diaspora",
|
||||
"default_branch":"develop",
|
||||
"homepage":"http://example.com/mike/diaspora",
|
||||
"url":"git@example.com:mike/diasporadiaspora.git",
|
||||
"ssh_url":"git@example.com:mike/diaspora.git",
|
||||
"http_url":"http://example.com/mike/diaspora.git"
|
||||
},
|
||||
"repository":{
|
||||
"name": "Diaspora",
|
||||
"url": "git@example.com:mike/diasporadiaspora.git",
|
||||
"description": "",
|
||||
"homepage": "http://example.com/mike/diaspora",
|
||||
"git_http_url":"http://example.com/mike/diaspora.git",
|
||||
"git_ssh_url":"git@example.com:mike/diaspora.git",
|
||||
"visibility_level":0
|
||||
},
|
||||
"commits": [
|
||||
{
|
||||
"id": "b6568db1bc1dcd7f8b4d5a946b0b91f9dacd7327",
|
||||
"message": "Update Catalan translation to e38cb41.",
|
||||
"timestamp": "2011-12-12T14:27:31+02:00",
|
||||
"url": "http://example.com/mike/diaspora/commit/b6568db1bc1dcd7f8b4d5a946b0b91f9dacd7327",
|
||||
"author": {
|
||||
"name": "Jordi Mallach",
|
||||
"email": "jordi@softcatala.org"
|
||||
},
|
||||
"added": ["CHANGELOG"],
|
||||
"modified": ["app/controller/application.rb"],
|
||||
"removed": []
|
||||
},
|
||||
{
|
||||
"id": "da1560886d4f094c3e6c9ef40349f7d38b5d27d7",
|
||||
"message": "fixed readme",
|
||||
"timestamp": "2012-01-03T23:36:29+02:00",
|
||||
"url": "http://example.com/mike/diaspora/commit/da1560886d4f094c3e6c9ef40349f7d38b5d27d7",
|
||||
"author": {
|
||||
"name": "GitLab dev user",
|
||||
"email": "gitlabdev@dv6700.(none)"
|
||||
},
|
||||
"added": ["CHANGELOG"],
|
||||
"modified": ["app/controller/application.rb"],
|
||||
"removed": []
|
||||
}
|
||||
],
|
||||
"total_commits_count": 4
|
||||
}
|
||||
`)
|
||||
|
||||
var LegacyPushHook = []byte(`
|
||||
{
|
||||
"object_kind": "push",
|
||||
"before": "95790bf891e76fee5e1747ab589903a6a1f80f22",
|
||||
"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
|
||||
}
|
||||
`)
|
3
remote/gitlab3/testdata/oauth.go
vendored
Normal file
3
remote/gitlab3/testdata/oauth.go
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
package testdata
|
||||
|
||||
var accessTokenPayload = []byte(`access_token=sekret&scope=api&token_type=bearer`)
|
212
remote/gitlab3/testdata/projects.go
vendored
Normal file
212
remote/gitlab3/testdata/projects.go
vendored
Normal file
|
@ -0,0 +1,212 @@
|
|||
package testdata
|
||||
|
||||
// sample repository list
|
||||
var allProjectsPayload = []byte(`
|
||||
[
|
||||
{
|
||||
"id": 4,
|
||||
"description": null,
|
||||
"default_branch": "master",
|
||||
"public": false,
|
||||
"visibility_level": 0,
|
||||
"ssh_url_to_repo": "git@example.com:diaspora/diaspora-client.git",
|
||||
"http_url_to_repo": "http://example.com/diaspora/diaspora-client.git",
|
||||
"web_url": "http://example.com/diaspora/diaspora-client",
|
||||
"owner": {
|
||||
"id": 3,
|
||||
"name": "Diaspora",
|
||||
"username": "some_user",
|
||||
"created_at": "2013-09-30T13: 46: 02Z"
|
||||
},
|
||||
"name": "Diaspora Client",
|
||||
"name_with_namespace": "Diaspora / Diaspora Client",
|
||||
"path": "diaspora-client",
|
||||
"path_with_namespace": "diaspora/diaspora-client",
|
||||
"issues_enabled": true,
|
||||
"merge_requests_enabled": true,
|
||||
"wiki_enabled": true,
|
||||
"snippets_enabled": false,
|
||||
"created_at": "2013-09-30T13: 46: 02Z",
|
||||
"last_activity_at": "2013-09-30T13: 46: 02Z",
|
||||
"namespace": {
|
||||
"created_at": "2013-09-30T13: 46: 02Z",
|
||||
"description": "",
|
||||
"id": 3,
|
||||
"name": "Diaspora",
|
||||
"owner_id": 1,
|
||||
"path": "diaspora",
|
||||
"updated_at": "2013-09-30T13: 46: 02Z"
|
||||
},
|
||||
"archived": false
|
||||
},
|
||||
{
|
||||
"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": 1,
|
||||
"name": "Brightbox",
|
||||
"username": "test_user",
|
||||
"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,
|
||||
"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"
|
||||
},
|
||||
"archived": true
|
||||
}
|
||||
]
|
||||
`)
|
||||
|
||||
var notArchivedProjectsPayload = []byte(`
|
||||
[
|
||||
{
|
||||
"id": 4,
|
||||
"description": null,
|
||||
"default_branch": "master",
|
||||
"public": false,
|
||||
"visibility_level": 0,
|
||||
"ssh_url_to_repo": "git@example.com:diaspora/diaspora-client.git",
|
||||
"http_url_to_repo": "http://example.com/diaspora/diaspora-client.git",
|
||||
"web_url": "http://example.com/diaspora/diaspora-client",
|
||||
"owner": {
|
||||
"id": 3,
|
||||
"name": "Diaspora",
|
||||
"username": "some_user",
|
||||
"created_at": "2013-09-30T13: 46: 02Z"
|
||||
},
|
||||
"name": "Diaspora Client",
|
||||
"name_with_namespace": "Diaspora / Diaspora Client",
|
||||
"path": "diaspora-client",
|
||||
"path_with_namespace": "diaspora/diaspora-client",
|
||||
"issues_enabled": true,
|
||||
"merge_requests_enabled": true,
|
||||
"wiki_enabled": true,
|
||||
"snippets_enabled": false,
|
||||
"created_at": "2013-09-30T13: 46: 02Z",
|
||||
"last_activity_at": "2013-09-30T13: 46: 02Z",
|
||||
"namespace": {
|
||||
"created_at": "2013-09-30T13: 46: 02Z",
|
||||
"description": "",
|
||||
"id": 3,
|
||||
"name": "Diaspora",
|
||||
"owner_id": 1,
|
||||
"path": "diaspora",
|
||||
"updated_at": "2013-09-30T13: 46: 02Z"
|
||||
},
|
||||
"archived": false
|
||||
}
|
||||
]
|
||||
`)
|
||||
|
||||
var project4Paylod = []byte(`
|
||||
{
|
||||
"id": 4,
|
||||
"description": null,
|
||||
"default_branch": "master",
|
||||
"public": false,
|
||||
"visibility_level": 0,
|
||||
"ssh_url_to_repo": "git@example.com:diaspora/diaspora-client.git",
|
||||
"http_url_to_repo": "http://example.com/diaspora/diaspora-client.git",
|
||||
"web_url": "http://example.com/diaspora/diaspora-client",
|
||||
"owner": {
|
||||
"id": 3,
|
||||
"name": "Diaspora",
|
||||
"username": "some_user",
|
||||
"created_at": "2013-09-30T13: 46: 02Z"
|
||||
},
|
||||
"name": "Diaspora Client",
|
||||
"name_with_namespace": "Diaspora / Diaspora Client",
|
||||
"path": "diaspora-client",
|
||||
"path_with_namespace": "diaspora/diaspora-client",
|
||||
"issues_enabled": true,
|
||||
"merge_requests_enabled": true,
|
||||
"wiki_enabled": true,
|
||||
"snippets_enabled": false,
|
||||
"created_at": "2013-09-30T13: 46: 02Z",
|
||||
"last_activity_at": "2013-09-30T13: 46: 02Z",
|
||||
"namespace": {
|
||||
"created_at": "2013-09-30T13: 46: 02Z",
|
||||
"description": "",
|
||||
"id": 3,
|
||||
"name": "Diaspora",
|
||||
"owner_id": 1,
|
||||
"path": "diaspora",
|
||||
"updated_at": "2013-09-30T13: 46: 02Z"
|
||||
},
|
||||
"archived": false,
|
||||
"permissions": {
|
||||
"project_access": {
|
||||
"access_level": 10,
|
||||
"notification_level": 3
|
||||
},
|
||||
"group_access": {
|
||||
"access_level": 50,
|
||||
"notification_level": 3
|
||||
}
|
||||
}
|
||||
}
|
||||
`)
|
||||
|
||||
var project6Paylod = []byte(`
|
||||
{
|
||||
"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": 1,
|
||||
"name": "Brightbox",
|
||||
"username": "test_user",
|
||||
"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,
|
||||
"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"
|
||||
},
|
||||
"archived": false,
|
||||
"permissions": {
|
||||
"project_access": null,
|
||||
"group_access": null
|
||||
}
|
||||
}
|
||||
`)
|
60
remote/gitlab3/testdata/testdata.go
vendored
Normal file
60
remote/gitlab3/testdata/testdata.go
vendored
Normal file
|
@ -0,0 +1,60 @@
|
|||
package testdata
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
)
|
||||
|
||||
// setup a mock server for testing purposes.
|
||||
func NewServer() *httptest.Server {
|
||||
mux := http.NewServeMux()
|
||||
server := httptest.NewServer(mux)
|
||||
|
||||
// handle requests and serve mock data
|
||||
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
//println(r.URL.Path + " " + r.Method)
|
||||
// evaluate the path to serve a dummy data file
|
||||
switch r.URL.Path {
|
||||
case "/api/v3/projects":
|
||||
if r.URL.Query().Get("archived") == "false" {
|
||||
w.Write(notArchivedProjectsPayload)
|
||||
} else {
|
||||
w.Write(allProjectsPayload)
|
||||
}
|
||||
|
||||
return
|
||||
case "/api/v3/projects/diaspora/diaspora-client":
|
||||
w.Write(project4Paylod)
|
||||
return
|
||||
case "/api/v3/projects/brightbox/puppet":
|
||||
w.Write(project6Paylod)
|
||||
return
|
||||
case "/api/v3/projects/diaspora/diaspora-client/services/drone-ci":
|
||||
switch r.Method {
|
||||
case "PUT":
|
||||
if r.FormValue("token") == "" {
|
||||
w.WriteHeader(404)
|
||||
} else {
|
||||
w.WriteHeader(201)
|
||||
}
|
||||
case "DELETE":
|
||||
w.WriteHeader(201)
|
||||
}
|
||||
|
||||
return
|
||||
case "/oauth/token":
|
||||
w.Write(accessTokenPayload)
|
||||
return
|
||||
case "/api/v3/user":
|
||||
w.Write(currentUserPayload)
|
||||
return
|
||||
}
|
||||
|
||||
// else return a 404
|
||||
http.NotFound(w, r)
|
||||
})
|
||||
|
||||
// return the server to the client which
|
||||
// will need to know the base URL path
|
||||
return server
|
||||
}
|
24
remote/gitlab3/testdata/users.go
vendored
Normal file
24
remote/gitlab3/testdata/users.go
vendored
Normal file
|
@ -0,0 +1,24 @@
|
|||
package testdata
|
||||
|
||||
var currentUserPayload = []byte(`
|
||||
{
|
||||
"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,
|
||||
"projects_limit": 100
|
||||
}
|
||||
`)
|
|
@ -10,7 +10,7 @@ var (
|
|||
// VersionPatch is for backwards-compatible bug fixes.
|
||||
VersionPatch int64 = 0
|
||||
// VersionPre indicates prerelease.
|
||||
VersionPre string = "rc.5"
|
||||
VersionPre string
|
||||
// VersionDev indicates development branch. Releases will be empty string.
|
||||
VersionDev string
|
||||
)
|
||||
|
|
Loading…
Reference in a new issue