mirror of
https://github.com/woodpecker-ci/woodpecker.git
synced 2026-03-16 17:54:07 +01:00
Refactor Gitlab Remote (#358)
- Replace custom client - Update Docs - Test if it works - Update Tests close #285
This commit is contained in:
@@ -1,41 +0,0 @@
|
||||
// Copyright 2018 Drone.IO Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package 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
|
||||
}
|
||||
@@ -1,110 +0,0 @@
|
||||
// Copyright 2018 Drone.IO Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package 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
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
// Copyright 2018 Drone.IO Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package 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
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
// Copyright 2018 Drone.IO Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package 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
|
||||
}
|
||||
@@ -1,168 +0,0 @@
|
||||
// Copyright 2018 Drone.IO Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
projectsUrl = "/projects"
|
||||
projectUrl = "/projects/:id"
|
||||
repoUrlRawFileRef = "/projects/:id/repository/files/:filepath"
|
||||
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),
|
||||
"membership": "true",
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
func (c *Client) RepoRawFileRef(id, ref, filepath string) ([]byte, error) {
|
||||
var fileRef FileRef
|
||||
url, opaque := c.ResourceUrl(
|
||||
repoUrlRawFileRef,
|
||||
QMap{
|
||||
":id": id,
|
||||
":filepath": filepath,
|
||||
},
|
||||
QMap{
|
||||
"ref": ref,
|
||||
},
|
||||
)
|
||||
|
||||
contents, err := c.Do("GET", url, opaque, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = json.Unmarshal(contents, &fileRef)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fileRawContent, err := base64.StdEncoding.DecodeString(fileRef.Content)
|
||||
return fileRawContent, 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(projectsUrl, nil, QMap{
|
||||
"query": strings.ToLower(name),
|
||||
"membership": "true",
|
||||
})
|
||||
|
||||
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
|
||||
}
|
||||
@@ -1,164 +0,0 @@
|
||||
// Copyright 2018 Drone.IO Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package 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"`
|
||||
}
|
||||
|
||||
type FileRef struct {
|
||||
FileName string `json:"file_name,omitempty"`
|
||||
FilePath string `json:"file_path,omitempty"`
|
||||
Size int `json:"size,omitempty"`
|
||||
Encoding string `json:"encoding,omitempty"`
|
||||
Content string `json:"content"`
|
||||
Ref string `json:"ref,omitempty"`
|
||||
BlobId string `json:"blob_id,omitempty"`
|
||||
CommitId string `json:"commit_id,omitempty"`
|
||||
LastCommitId string `json:"last_commit_id,omitempty"`
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
// Copyright 2018 Drone.IO Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package 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
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
// Copyright 2018 Drone.IO Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package 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
|
||||
}
|
||||
243
server/remote/gitlab/convert.go
Normal file
243
server/remote/gitlab/convert.go
Normal file
@@ -0,0 +1,243 @@
|
||||
// Copyright 2021 Woodpecker Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package gitlab
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/woodpecker-ci/woodpecker/server/model"
|
||||
|
||||
"github.com/xanzy/go-gitlab"
|
||||
)
|
||||
|
||||
func (g *Gitlab) convertGitlabRepo(repo_ *gitlab.Project) (*model.Repo, error) {
|
||||
parts := strings.Split(repo_.PathWithNamespace, "/")
|
||||
// TODO: save repo id (support nested repos)
|
||||
var owner = parts[0]
|
||||
var name = parts[1]
|
||||
repo := &model.Repo{
|
||||
Owner: owner,
|
||||
Name: name,
|
||||
FullName: repo_.PathWithNamespace,
|
||||
Avatar: repo_.AvatarURL,
|
||||
Link: repo_.WebURL,
|
||||
Clone: repo_.HTTPURLToRepo,
|
||||
Branch: repo_.DefaultBranch,
|
||||
Visibility: string(repo_.Visibility),
|
||||
}
|
||||
|
||||
if len(repo.Branch) == 0 { // TODO: do we need that?
|
||||
repo.Branch = "master"
|
||||
}
|
||||
|
||||
if len(repo.Avatar) != 0 && !strings.HasPrefix(repo.Avatar, "http") {
|
||||
repo.Avatar = fmt.Sprintf("%s/%s", g.URL, repo.Avatar)
|
||||
}
|
||||
|
||||
if g.PrivateMode {
|
||||
repo.IsPrivate = true
|
||||
} else {
|
||||
repo.IsPrivate = !repo_.Public
|
||||
}
|
||||
|
||||
return repo, nil
|
||||
}
|
||||
|
||||
func convertMergeRequestHock(hook *gitlab.MergeEvent, req *http.Request) (*model.Repo, *model.Build, error) {
|
||||
repo := &model.Repo{}
|
||||
build := &model.Build{}
|
||||
|
||||
target := hook.ObjectAttributes.Target
|
||||
source := hook.ObjectAttributes.Source
|
||||
obj := hook.ObjectAttributes
|
||||
|
||||
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.Event = model.EventPull
|
||||
|
||||
lastCommit := obj.LastCommit
|
||||
|
||||
build.Message = lastCommit.Message
|
||||
build.Commit = lastCommit.ID
|
||||
build.Remote = obj.Source.HTTPURL
|
||||
|
||||
build.Ref = fmt.Sprintf("refs/merge-requests/%d/head", obj.IID)
|
||||
|
||||
build.Branch = obj.SourceBranch
|
||||
|
||||
author := lastCommit.Author
|
||||
|
||||
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 convertPushHock(hook *gitlab.PushEvent) (*model.Repo, *model.Build, error) {
|
||||
repo := &model.Repo{}
|
||||
build := &model.Build{}
|
||||
|
||||
var err error
|
||||
if repo.Owner, repo.Name, err = extractFromPath(hook.Project.PathWithNamespace); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
repo.Avatar = hook.Project.AvatarURL
|
||||
repo.Link = hook.Project.WebURL
|
||||
repo.Clone = hook.Project.GitHTTPURL
|
||||
repo.FullName = hook.Project.PathWithNamespace
|
||||
repo.Branch = hook.Project.DefaultBranch
|
||||
|
||||
switch hook.Project.Visibility {
|
||||
case gitlab.PrivateVisibility:
|
||||
repo.IsPrivate = true
|
||||
case gitlab.InternalVisibility:
|
||||
repo.IsPrivate = true
|
||||
case gitlab.PublicVisibility:
|
||||
repo.IsPrivate = false
|
||||
}
|
||||
|
||||
build.Event = model.EventPush
|
||||
build.Commit = hook.After
|
||||
build.Branch = strings.TrimPrefix(hook.Ref, "refs/heads/")
|
||||
build.Ref = hook.Ref
|
||||
|
||||
for _, cm := range hook.Commits {
|
||||
if hook.After == cm.ID {
|
||||
build.Author = cm.Author.Name
|
||||
build.Email = cm.Author.Email
|
||||
build.Message = cm.Message
|
||||
build.Timestamp = cm.Timestamp.Unix()
|
||||
if len(build.Email) != 0 {
|
||||
build.Avatar = getUserAvatar(build.Email)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return repo, build, nil
|
||||
}
|
||||
|
||||
func convertTagHock(hook *gitlab.TagEvent) (*model.Repo, *model.Build, error) {
|
||||
repo := &model.Repo{}
|
||||
build := &model.Build{}
|
||||
|
||||
var err error
|
||||
if repo.Owner, repo.Name, err = extractFromPath(hook.Project.PathWithNamespace); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
repo.Avatar = hook.Project.AvatarURL
|
||||
repo.Link = hook.Project.WebURL
|
||||
repo.Clone = hook.Project.GitHTTPURL
|
||||
repo.FullName = hook.Project.PathWithNamespace
|
||||
repo.Branch = hook.Project.DefaultBranch
|
||||
|
||||
switch hook.Project.Visibility {
|
||||
case gitlab.PrivateVisibility:
|
||||
repo.IsPrivate = true
|
||||
case gitlab.InternalVisibility:
|
||||
repo.IsPrivate = true
|
||||
case gitlab.PublicVisibility:
|
||||
repo.IsPrivate = false
|
||||
}
|
||||
|
||||
build.Event = model.EventTag
|
||||
build.Commit = hook.After
|
||||
build.Branch = strings.TrimPrefix(hook.Ref, "refs/heads/")
|
||||
build.Ref = hook.Ref
|
||||
|
||||
for _, cm := range hook.Commits {
|
||||
if hook.After == cm.ID {
|
||||
build.Author = cm.Author.Name
|
||||
build.Email = cm.Author.Email
|
||||
build.Message = cm.Message
|
||||
build.Timestamp = cm.Timestamp.Unix()
|
||||
if len(build.Email) != 0 {
|
||||
build.Avatar = getUserAvatar(build.Email)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return repo, build, nil
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
@@ -17,12 +17,12 @@ package gitlab
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/woodpecker-ci/woodpecker/server"
|
||||
@@ -30,20 +30,38 @@ import (
|
||||
"github.com/woodpecker-ci/woodpecker/server/remote"
|
||||
"github.com/woodpecker-ci/woodpecker/shared/oauth2"
|
||||
|
||||
"github.com/woodpecker-ci/woodpecker/server/remote/gitlab/client"
|
||||
"github.com/xanzy/go-gitlab"
|
||||
)
|
||||
|
||||
const DefaultScope = "api"
|
||||
const (
|
||||
defaultScope = "api"
|
||||
perPage = 100
|
||||
statusContext = "ci/drone"
|
||||
)
|
||||
|
||||
// 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.
|
||||
URL string // Gitlab server url.
|
||||
ClientID string // Oauth2 client id.
|
||||
ClientSecret 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.
|
||||
}
|
||||
|
||||
// Gitlab implements "Remote" interface
|
||||
type Gitlab struct {
|
||||
URL string
|
||||
ClientID string
|
||||
ClientSecret string
|
||||
Machine string
|
||||
Username string
|
||||
Password string
|
||||
PrivateMode bool
|
||||
SkipVerify bool
|
||||
HideArchives bool
|
||||
Search bool
|
||||
}
|
||||
|
||||
// New returns a Remote implementation that integrates with Gitlab, an open
|
||||
@@ -58,78 +76,29 @@ func New(opts Opts) (remote.Remote, error) {
|
||||
u.Host = host
|
||||
}
|
||||
return &Gitlab{
|
||||
URL: opts.URL,
|
||||
Client: opts.Client,
|
||||
Secret: opts.Secret,
|
||||
Machine: u.Host,
|
||||
Username: opts.Username,
|
||||
Password: opts.Password,
|
||||
PrivateMode: opts.PrivateMode,
|
||||
SkipVerify: opts.SkipVerify,
|
||||
URL: opts.URL,
|
||||
ClientID: opts.ClientID,
|
||||
ClientSecret: opts.ClientSecret,
|
||||
Machine: u.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(ctx context.Context, res http.ResponseWriter, req *http.Request) (*model.User, error) {
|
||||
|
||||
var config = &oauth2.Config{
|
||||
ClientId: g.Client,
|
||||
ClientSecret: g.Secret,
|
||||
Scope: DefaultScope,
|
||||
ClientId: g.ClientID,
|
||||
ClientSecret: g.ClientSecret,
|
||||
Scope: defaultScope,
|
||||
AuthURL: fmt.Sprintf("%s/oauth/authorize", g.URL),
|
||||
TokenURL: fmt.Sprintf("%s/oauth/token", g.URL),
|
||||
RedirectURL: fmt.Sprintf("%s/authorize", server.Config.Server.Host),
|
||||
}
|
||||
|
||||
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{
|
||||
@@ -146,248 +115,262 @@ func (g *Gitlab) Login(ctx context.Context, res http.ResponseWriter, req *http.R
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var trans = &oauth2.Transport{Config: config, Transport: trans_}
|
||||
var trans = &oauth2.Transport{Config: config, Transport: &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: g.SkipVerify},
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
}}
|
||||
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()
|
||||
client, err := newClient(g.URL, token_.AccessToken, g.SkipVerify)
|
||||
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)
|
||||
// }
|
||||
// }
|
||||
login, _, err := client.Users.CurrentUser(gitlab.WithContext(ctx))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
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
|
||||
user := &model.User{
|
||||
Login: login.Username,
|
||||
Email: login.Email,
|
||||
Avatar: login.AvatarURL,
|
||||
Token: token_.AccessToken,
|
||||
Secret: token_.RefreshToken,
|
||||
}
|
||||
if !strings.HasPrefix(user.Avatar, "http") {
|
||||
user.Avatar = g.URL + "/" + login.AvatarURL
|
||||
}
|
||||
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func (g *Gitlab) Auth(ctx context.Context, token, secret string) (string, error) {
|
||||
client := NewClient(g.URL, token, g.SkipVerify)
|
||||
login, err := client.CurrentUser()
|
||||
// Auth authenticates the session and returns the remote user login for the given token
|
||||
func (g *Gitlab) Auth(ctx context.Context, token, _ string) (string, error) {
|
||||
client, err := newClient(g.URL, token, g.SkipVerify)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
login, _, err := client.Users.CurrentUser(gitlab.WithContext(ctx))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return login.Username, nil
|
||||
}
|
||||
|
||||
func (g *Gitlab) Teams(ctx context.Context, u *model.User) ([]*model.Team, error) {
|
||||
client := NewClient(g.URL, u.Token, g.SkipVerify)
|
||||
groups, err := client.AllGroups()
|
||||
// Teams fetches a list of team memberships from the remote system.
|
||||
func (g *Gitlab) Teams(ctx context.Context, user *model.User) ([]*model.Team, error) {
|
||||
client, err := newClient(g.URL, user.Token, g.SkipVerify)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var teams []*model.Team
|
||||
for _, group := range groups {
|
||||
teams = append(teams, &model.Team{
|
||||
Login: group.Name,
|
||||
})
|
||||
|
||||
teams := make([]*model.Team, 0, perPage)
|
||||
|
||||
for i := 1; true; i++ {
|
||||
batch, _, err := client.Groups.ListGroups(&gitlab.ListGroupsOptions{
|
||||
ListOptions: gitlab.ListOptions{Page: i, PerPage: perPage},
|
||||
AllAvailable: gitlab.Bool(false),
|
||||
MinAccessLevel: gitlab.AccessLevel(gitlab.DeveloperPermissions), // TODO: check whats best here
|
||||
}, gitlab.WithContext(ctx))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for i := range batch {
|
||||
teams = append(teams, &model.Team{
|
||||
Login: batch[i].Name,
|
||||
Avatar: batch[i].AvatarURL,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
if len(batch) < perPage {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return teams, nil
|
||||
}
|
||||
|
||||
// getProject fetches the named repository from the remote system.
|
||||
func (g *Gitlab) getProject(ctx context.Context, client *gitlab.Client, owner, name string) (*gitlab.Project, error) {
|
||||
repo, _, err := client.Projects.GetProject(fmt.Sprintf("%s/%s", owner, name), nil, gitlab.WithContext(ctx))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return repo, nil
|
||||
}
|
||||
|
||||
// Repo fetches the named repository from the remote system.
|
||||
func (g *Gitlab) Repo(ctx context.Context, 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)
|
||||
func (g *Gitlab) Repo(ctx context.Context, user *model.User, owner, name string) (*model.Repo, error) {
|
||||
client, err := newClient(g.URL, user.Token, g.SkipVerify)
|
||||
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)
|
||||
repo_, err := g.getProject(ctx, client, owner, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if repo_.DefaultBranch != "" {
|
||||
repo.Branch = repo_.DefaultBranch
|
||||
}
|
||||
|
||||
if g.PrivateMode {
|
||||
repo.IsPrivate = true
|
||||
} else {
|
||||
repo.IsPrivate = !repo_.Public
|
||||
}
|
||||
|
||||
return repo, err
|
||||
return g.convertGitlabRepo(repo_)
|
||||
}
|
||||
|
||||
// Repos fetches a list of repos from the remote system.
|
||||
func (g *Gitlab) Repos(ctx context.Context, u *model.User) ([]*model.Repo, error) {
|
||||
client := NewClient(g.URL, u.Token, g.SkipVerify)
|
||||
|
||||
var repos = []*model.Repo{}
|
||||
|
||||
all, err := client.AllProjects(g.HideArchives)
|
||||
func (g *Gitlab) Repos(ctx context.Context, user *model.User) ([]*model.Repo, error) {
|
||||
client, err := newClient(g.URL, user.Token, g.SkipVerify)
|
||||
if err != nil {
|
||||
return repos, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, repo_ := range all {
|
||||
var parts = strings.Split(repo_.PathWithNamespace, "/")
|
||||
var owner = parts[0]
|
||||
var name = parts[1]
|
||||
repos := make([]*model.Repo, 0, perPage)
|
||||
opts := &gitlab.ListProjectsOptions{
|
||||
ListOptions: gitlab.ListOptions{PerPage: perPage},
|
||||
MinAccessLevel: gitlab.AccessLevel(gitlab.DeveloperPermissions), // TODO: check whats best here
|
||||
}
|
||||
if g.HideArchives {
|
||||
opts.Archived = gitlab.Bool(false)
|
||||
}
|
||||
|
||||
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
|
||||
for i := 1; true; i++ {
|
||||
opts.Page = i
|
||||
batch, _, err := client.Projects.ListProjects(opts, gitlab.WithContext(ctx))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if g.PrivateMode {
|
||||
repo.IsPrivate = true
|
||||
} else {
|
||||
repo.IsPrivate = !repo_.Public
|
||||
for i := range batch {
|
||||
repo, err := g.convertGitlabRepo(batch[i])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
repos = append(repos, repo)
|
||||
}
|
||||
|
||||
repos = append(repos, repo)
|
||||
if len(batch) < perPage {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return repos, err
|
||||
}
|
||||
|
||||
// Perm fetches the named repository from the remote system.
|
||||
func (g *Gitlab) Perm(ctx context.Context, u *model.User, owner, name string) (*model.Perm, error) {
|
||||
|
||||
client := NewClient(g.URL, u.Token, g.SkipVerify)
|
||||
id, err := GetProjectId(g, client, owner, name)
|
||||
func (g *Gitlab) Perm(ctx context.Context, user *model.User, owner, name string) (*model.Perm, error) {
|
||||
client, err := newClient(g.URL, user.Token, g.SkipVerify)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
repo, err := client.Project(id)
|
||||
repo, err := g.getProject(ctx, client, owner, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// repo owner is granted full access
|
||||
if repo.Owner != nil && repo.Owner.Username == u.Login {
|
||||
if repo.Owner != nil && repo.Owner.Username == user.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
|
||||
// return permission for current user
|
||||
return &model.Perm{
|
||||
Pull: isRead(repo),
|
||||
Push: isWrite(repo),
|
||||
Admin: isAdmin(repo),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// File fetches a file from the remote repository and returns in string format.
|
||||
func (g *Gitlab) File(ctx context.Context, u *model.User, r *model.Repo, b *model.Build, f string) ([]byte, error) {
|
||||
var client = NewClient(g.URL, u.Token, g.SkipVerify)
|
||||
id, err := GetProjectId(g, client, r.Owner, r.Name)
|
||||
func (g *Gitlab) File(ctx context.Context, user *model.User, repo *model.Repo, build *model.Build, fileName string) ([]byte, error) {
|
||||
client, err := newClient(g.URL, user.Token, g.SkipVerify)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
repo_, err := g.getProject(ctx, client, repo.Owner, repo.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
file, _, err := client.RepositoryFiles.GetRawFile(repo_.ID, fileName, &gitlab.GetRawFileOptions{Ref: &build.Commit}, gitlab.WithContext(ctx))
|
||||
return file, err
|
||||
}
|
||||
|
||||
// Dir fetches a folder from the remote repository
|
||||
func (g *Gitlab) Dir(ctx context.Context, user *model.User, repo *model.Repo, build *model.Build, path string) ([]*remote.FileMeta, error) {
|
||||
client, err := newClient(g.URL, user.Token, g.SkipVerify)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
out, err := client.RepoRawFileRef(id, b.Commit, f)
|
||||
files := make([]*remote.FileMeta, 0, perPage)
|
||||
repo_, err := g.getProject(ctx, client, repo.Owner, repo.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, err
|
||||
opts := &gitlab.ListTreeOptions{
|
||||
ListOptions: gitlab.ListOptions{PerPage: perPage},
|
||||
Path: &path,
|
||||
Ref: &build.Commit,
|
||||
Recursive: gitlab.Bool(false),
|
||||
}
|
||||
|
||||
for i := 1; true; i++ {
|
||||
opts.Page = 1
|
||||
batch, _, err := client.Repositories.ListTree(repo_.ID, opts, gitlab.WithContext(ctx))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for i := range batch {
|
||||
if batch[i].Type != "blob" { // no file
|
||||
continue
|
||||
}
|
||||
data, err := g.File(ctx, user, repo, build, batch[i].Path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
files = append(files, &remote.FileMeta{
|
||||
Name: batch[i].Path,
|
||||
Data: data,
|
||||
})
|
||||
}
|
||||
|
||||
if len(batch) < perPage {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return files, nil
|
||||
}
|
||||
|
||||
func (c *Gitlab) Dir(ctx context.Context, u *model.User, r *model.Repo, b *model.Build, f string) ([]*remote.FileMeta, error) {
|
||||
return nil, fmt.Errorf("Not implemented")
|
||||
// Status sends the commit status back to gitlab.
|
||||
func (g *Gitlab) Status(ctx context.Context, user *model.User, repo *model.Repo, build *model.Build, link string, proc *model.Proc) error {
|
||||
client, err := newClient(g.URL, user.Token, g.SkipVerify)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
repo_, err := g.getProject(ctx, client, repo.Owner, repo.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, _, err = client.Commits.SetCommitStatus(repo_.ID, build.Commit, &gitlab.SetCommitStatusOptions{
|
||||
Ref: gitlab.String(strings.ReplaceAll(build.Ref, "refs/heads/", "")),
|
||||
State: getStatus(build.Status),
|
||||
Description: gitlab.String(getDesc(build.Status)),
|
||||
TargetURL: &link,
|
||||
Name: nil,
|
||||
Context: gitlab.String(statusContext),
|
||||
}, gitlab.WithContext(ctx))
|
||||
|
||||
return 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(ctx context.Context, u *model.User, r *model.Repo, b *model.Build, link string, proc *model.Proc) error {
|
||||
client := NewClient(g.URL, u.Token, g.SkipVerify)
|
||||
|
||||
status := getStatus(b.Status)
|
||||
desc := getDesc(b.Status)
|
||||
|
||||
client.SetStatus(
|
||||
ns(r.Owner, r.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.
|
||||
@@ -408,296 +391,90 @@ func (g *Gitlab) Netrc(u *model.User, r *model.Repo) (*model.Netrc, error) {
|
||||
|
||||
// Activate activates a repository by adding a Post-commit hook and
|
||||
// a Public Deploy key, if applicable.
|
||||
func (g *Gitlab) Activate(ctx context.Context, u *model.User, r *model.Repo, link string) error {
|
||||
var client = NewClient(g.URL, u.Token, g.SkipVerify)
|
||||
id, err := GetProjectId(g, client, r.Owner, r.Name)
|
||||
func (g *Gitlab) Activate(ctx context.Context, user *model.User, repo *model.Repo, link string) error {
|
||||
client, err := newClient(g.URL, user.Token, g.SkipVerify)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
uri, err := url.Parse(link)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
token := uri.Query().Get("access_token")
|
||||
webUrl := fmt.Sprintf("%s://%s", uri.Scheme, uri.Host)
|
||||
|
||||
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,
|
||||
})
|
||||
repo_, err := g.getProject(ctx, client, repo.Owner, repo.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// TODO: "WoodpeckerCIService"
|
||||
_, err = client.Services.SetDroneCIService(repo_.ID, &gitlab.SetDroneCIServiceOptions{
|
||||
Token: &token,
|
||||
DroneURL: &webUrl,
|
||||
EnableSSLVerification: gitlab.Bool(!g.SkipVerify),
|
||||
}, gitlab.WithContext(ctx))
|
||||
return err
|
||||
}
|
||||
|
||||
// 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(ctx context.Context, u *model.User, r *model.Repo, link string) error {
|
||||
var client = NewClient(g.URL, u.Token, g.SkipVerify)
|
||||
id, err := GetProjectId(g, client, r.Owner, r.Name)
|
||||
func (g *Gitlab) Deactivate(ctx context.Context, user *model.User, repo *model.Repo, link string) error {
|
||||
client, err := newClient(g.URL, user.Token, g.SkipVerify)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return client.DeleteDroneService(id)
|
||||
repo_, err := g.getProject(ctx, client, repo.Owner, repo.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// TODO: "WoodpeckerCIService"
|
||||
_, err = client.Services.DeleteDroneCIService(repo_.ID, gitlab.WithContext(ctx))
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// ParseHook parses the post-commit hook from the Request body
|
||||
// Hook 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)
|
||||
payload, err := ioutil.ReadAll(req.Body)
|
||||
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)
|
||||
eventType := gitlab.WebhookEventType(req)
|
||||
// TODO: Fix Upstream: We get `Service Hook` - which the library do not understand
|
||||
if eventType == "Service Hook" {
|
||||
e := struct {
|
||||
ObjectKind string `json:"object_kind"`
|
||||
}{}
|
||||
if err := json.Unmarshal(payload, &e); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
switch e.ObjectKind {
|
||||
case "push":
|
||||
eventType = gitlab.EventTypePush
|
||||
case "tag_push":
|
||||
eventType = gitlab.EventTypeTagPush
|
||||
case "merge_request":
|
||||
eventType = gitlab.EventTypeMergeRequest
|
||||
}
|
||||
}
|
||||
|
||||
parsed, err := gitlab.ParseWebhook(eventType, payload)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
switch event := parsed.(type) {
|
||||
case *gitlab.MergeEvent:
|
||||
return convertMergeRequestHock(event, req)
|
||||
case *gitlab.PushEvent:
|
||||
return convertPushHock(event)
|
||||
case *gitlab.TagEvent:
|
||||
return convertTagHock(event)
|
||||
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", server.Config.Server.Host),
|
||||
//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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,21 +18,46 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/franela/goblin"
|
||||
"github.com/woodpecker-ci/woodpecker/server/model"
|
||||
"github.com/woodpecker-ci/woodpecker/server/remote/gitlab/testdata"
|
||||
|
||||
"github.com/franela/goblin"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
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.ClientID = params.Get("client_id")
|
||||
gitlab.ClientSecret = params.Get("client_secret")
|
||||
gitlab.SkipVerify, _ = strconv.ParseBool(params.Get("skip_verify"))
|
||||
gitlab.HideArchives, _ = strconv.ParseBool(params.Get("hide_archives"))
|
||||
|
||||
// this is a temp workaround
|
||||
gitlab.Search, _ = strconv.ParseBool(params.Get("search"))
|
||||
|
||||
return &gitlab
|
||||
}
|
||||
|
||||
func Test_Gitlab(t *testing.T) {
|
||||
// setup a dummy github server
|
||||
var server = testdata.NewServer()
|
||||
var server = testdata.NewServer(t)
|
||||
defer server.Close()
|
||||
|
||||
env := server.URL + "?client_id=test&client_secret=test"
|
||||
|
||||
gitlab := Load(env)
|
||||
client := load(env)
|
||||
|
||||
var user = model.User{
|
||||
Login: "test_user",
|
||||
@@ -50,16 +75,15 @@ func Test_Gitlab(t *testing.T) {
|
||||
// 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(ctx, &user)
|
||||
|
||||
g.Assert(err == nil).IsTrue()
|
||||
g.Assert(len(_projects)).Equal(1)
|
||||
client.HideArchives = true
|
||||
_projects, err := client.Repos(ctx, &user)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, _projects, 1)
|
||||
})
|
||||
|
||||
g.It("Should return all the projects", func() {
|
||||
gitlab.HideArchives = false
|
||||
_projects, err := gitlab.Repos(ctx, &user)
|
||||
client.HideArchives = false
|
||||
_projects, err := client.Repos(ctx, &user)
|
||||
|
||||
g.Assert(err == nil).IsTrue()
|
||||
g.Assert(len(_projects)).Equal(2)
|
||||
@@ -69,39 +93,37 @@ func Test_Gitlab(t *testing.T) {
|
||||
// Test repository method
|
||||
g.Describe("Repo", func() {
|
||||
g.It("Should return valid repo", func() {
|
||||
_repo, err := gitlab.Repo(ctx, &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)
|
||||
_repo, err := client.Repo(ctx, &user, "diaspora", "diaspora-client")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "diaspora-client", _repo.Name)
|
||||
assert.Equal(t, "diaspora", _repo.Owner)
|
||||
assert.True(t, _repo.IsPrivate)
|
||||
})
|
||||
|
||||
g.It("Should return error, when repo not exist", func() {
|
||||
_, err := gitlab.Repo(ctx, &user, "not-existed", "not-existed")
|
||||
|
||||
g.Assert(err != nil).IsTrue()
|
||||
_, err := client.Repo(ctx, &user, "not-existed", "not-existed")
|
||||
assert.Error(t, err)
|
||||
})
|
||||
})
|
||||
|
||||
// Test permissions method
|
||||
g.Describe("Perm", func() {
|
||||
g.It("Should return repo permissions", func() {
|
||||
perm, err := gitlab.Perm(ctx, &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)
|
||||
perm, err := client.Perm(ctx, &user, "diaspora", "diaspora-client")
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, perm.Admin)
|
||||
assert.True(t, perm.Pull)
|
||||
assert.True(t, perm.Push)
|
||||
})
|
||||
g.It("Should return repo permissions when user is admin", func() {
|
||||
perm, err := gitlab.Perm(ctx, &user, "brightbox", "puppet")
|
||||
g.Assert(err == nil).IsTrue()
|
||||
perm, err := client.Perm(ctx, &user, "brightbox", "puppet")
|
||||
assert.NoError(t, err)
|
||||
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(ctx, &user, "not-existed", "not-existed")
|
||||
_, err := client.Perm(ctx, &user, "not-existed", "not-existed")
|
||||
|
||||
g.Assert(err != nil).IsTrue()
|
||||
})
|
||||
@@ -110,13 +132,12 @@ func Test_Gitlab(t *testing.T) {
|
||||
// Test activate method
|
||||
g.Describe("Activate", func() {
|
||||
g.It("Should be success", func() {
|
||||
err := gitlab.Activate(ctx, &user, &repo, "http://example.com/api/hook/test/test?access_token=token")
|
||||
|
||||
g.Assert(err == nil).IsTrue()
|
||||
err := client.Activate(ctx, &user, &repo, "http://example.com/api/hook/test/test?access_token=token")
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
|
||||
g.It("Should be failed, when token not given", func() {
|
||||
err := gitlab.Activate(ctx, &user, &repo, "http://example.com/api/hook/test/test")
|
||||
err := client.Activate(ctx, &user, &repo, "http://example.com/api/hook/test/test")
|
||||
|
||||
g.Assert(err != nil).IsTrue()
|
||||
})
|
||||
@@ -125,137 +146,75 @@ func Test_Gitlab(t *testing.T) {
|
||||
// Test deactivate method
|
||||
g.Describe("Deactivate", func() {
|
||||
g.It("Should be success", func() {
|
||||
err := gitlab.Deactivate(ctx, &user, &repo, "http://example.com/api/hook/test/test?access_token=token")
|
||||
err := client.Deactivate(ctx, &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),
|
||||
testdata.ServiceHookMethod,
|
||||
testdata.ServiceHookURL.String(),
|
||||
bytes.NewReader(testdata.ServiceHookPushBody),
|
||||
)
|
||||
req.Header = testdata.ServiceHookHeaders
|
||||
|
||||
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")
|
||||
|
||||
hookRepo, build, err := client.Hook(req)
|
||||
assert.NoError(t, err)
|
||||
if assert.NotNil(t, hookRepo) && assert.NotNil(t, build) {
|
||||
assert.Equal(t, build.Event, model.EventPush)
|
||||
assert.Equal(t, "test", hookRepo.Owner)
|
||||
assert.Equal(t, "woodpecker", hookRepo.Name)
|
||||
assert.Equal(t, "http://example.com/uploads/project/avatar/555/Outh-20-Logo.jpg", hookRepo.Avatar)
|
||||
assert.Equal(t, "develop", hookRepo.Branch)
|
||||
assert.Equal(t, "refs/heads/master", build.Ref)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
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),
|
||||
testdata.ServiceHookMethod,
|
||||
testdata.ServiceHookURL.String(),
|
||||
bytes.NewReader(testdata.ServiceHookTagPushBody),
|
||||
)
|
||||
req.Header = testdata.ServiceHookHeaders
|
||||
|
||||
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")
|
||||
|
||||
hookRepo, build, err := client.Hook(req)
|
||||
assert.NoError(t, err)
|
||||
if assert.NotNil(t, hookRepo) && assert.NotNil(t, build) {
|
||||
assert.Equal(t, "test", hookRepo.Owner)
|
||||
assert.Equal(t, "woodpecker", hookRepo.Name)
|
||||
assert.Equal(t, "http://example.com/uploads/project/avatar/555/Outh-20-Logo.jpg", hookRepo.Avatar)
|
||||
assert.Equal(t, "develop", hookRepo.Branch)
|
||||
assert.Equal(t, "refs/tags/v22", build.Ref)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
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),
|
||||
testdata.ServiceHookMethod,
|
||||
testdata.ServiceHookURL.String(),
|
||||
bytes.NewReader(testdata.ServiceHookMergeRequestBody),
|
||||
)
|
||||
req.Header = testdata.ServiceHookHeaders
|
||||
|
||||
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")
|
||||
hookRepo, build, err := client.Hook(req)
|
||||
assert.NoError(t, err)
|
||||
if assert.NotNil(t, hookRepo) && assert.NotNil(t, build) {
|
||||
assert.Equal(t, "http://example.com/uploads/project/avatar/555/Outh-20-Logo.jpg", hookRepo.Avatar)
|
||||
assert.Equal(t, "develop", hookRepo.Branch)
|
||||
assert.Equal(t, "test", hookRepo.Owner)
|
||||
assert.Equal(t, "woodpecker", hookRepo.Name)
|
||||
assert.Equal(t, "Update client.go 🎉", build.Title)
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -15,30 +15,30 @@
|
||||
package gitlab
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"crypto/tls"
|
||||
"net/http"
|
||||
|
||||
"github.com/woodpecker-ci/woodpecker/server/remote/gitlab/client"
|
||||
"github.com/xanzy/go-gitlab"
|
||||
)
|
||||
|
||||
const (
|
||||
gravatarBase = "https://www.gravatar.com/avatar"
|
||||
)
|
||||
|
||||
// NewClient is a helper function that returns a new GitHub
|
||||
// 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/v4", accessToken, skipVerify)
|
||||
return client
|
||||
func newClient(url, accessToken string, skipVerify bool) (*gitlab.Client, error) {
|
||||
return gitlab.NewOAuthClient(accessToken, gitlab.WithBaseURL(url), gitlab.WithHTTPClient(&http.Client{
|
||||
Transport: &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: skipVerify},
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
},
|
||||
}))
|
||||
}
|
||||
|
||||
// IsRead is a helper function that returns true if the
|
||||
// isRead is a helper function that returns true if the
|
||||
// user has Read-only access to the repository.
|
||||
func IsRead(proj *client.Project) bool {
|
||||
func isRead(proj *gitlab.Project) bool {
|
||||
var user = proj.Permissions.ProjectAccess
|
||||
var group = proj.Permissions.GroupAccess
|
||||
|
||||
@@ -54,9 +54,9 @@ func IsRead(proj *client.Project) bool {
|
||||
}
|
||||
}
|
||||
|
||||
// IsWrite is a helper function that returns true if the
|
||||
// isWrite is a helper function that returns true if the
|
||||
// user has Read-Write access to the repository.
|
||||
func IsWrite(proj *client.Project) bool {
|
||||
func isWrite(proj *gitlab.Project) bool {
|
||||
var user = proj.Permissions.ProjectAccess
|
||||
var group = proj.Permissions.GroupAccess
|
||||
|
||||
@@ -70,9 +70,9 @@ func IsWrite(proj *client.Project) bool {
|
||||
}
|
||||
}
|
||||
|
||||
// IsAdmin is a helper function that returns true if the
|
||||
// isAdmin is a helper function that returns true if the
|
||||
// user has Admin access to the repository.
|
||||
func IsAdmin(proj *client.Project) bool {
|
||||
func isAdmin(proj *gitlab.Project) bool {
|
||||
var user = proj.Permissions.ProjectAccess
|
||||
var group = proj.Permissions.GroupAccess
|
||||
|
||||
@@ -85,55 +85,3 @@ func IsAdmin(proj *client.Project) bool {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
72
server/remote/gitlab/status.go
Normal file
72
server/remote/gitlab/status.go
Normal file
@@ -0,0 +1,72 @@
|
||||
// Copyright 2021 Woodpecker Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package gitlab
|
||||
|
||||
import (
|
||||
"github.com/woodpecker-ci/woodpecker/server/model"
|
||||
|
||||
"github.com/xanzy/go-gitlab"
|
||||
)
|
||||
|
||||
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 that converts a Woodpecker status to a Gitlab status.
|
||||
func getStatus(status string) gitlab.BuildStateValue {
|
||||
switch status {
|
||||
case model.StatusPending, model.StatusBlocked:
|
||||
return gitlab.Pending
|
||||
case model.StatusRunning:
|
||||
return gitlab.Running
|
||||
case model.StatusSuccess:
|
||||
return gitlab.Success
|
||||
case model.StatusFailure, model.StatusError:
|
||||
return gitlab.Failed
|
||||
case model.StatusKilled:
|
||||
return gitlab.Canceled
|
||||
default:
|
||||
return gitlab.Failed
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
}
|
||||
567
server/remote/gitlab/testdata/hooks.go
vendored
567
server/remote/gitlab/testdata/hooks.go
vendored
@@ -1,4 +1,4 @@
|
||||
// Copyright 2018 Drone.IO Inc.
|
||||
// Copyright 2021 Woodpecker Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@@ -14,335 +14,292 @@
|
||||
|
||||
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
|
||||
}
|
||||
`)
|
||||
import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
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",
|
||||
var (
|
||||
ServiceHookMethod = http.MethodPost
|
||||
ServiceHookURL, _ = url.Parse(
|
||||
"http://10.40.8.5:8000/hook?owner=test&name=woodpecker&access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9." +
|
||||
"eyJ0ZXh0IjoidGVzdC93b29kcGVja2VyIiwidHlwZSI6Imhvb2sifQ.x3kPnmZtxZQ_9_eMhfQ1HSmj_SLhdT_Lu2hMczWjKh0")
|
||||
ServiceHookHeaders = http.Header{
|
||||
"Content-Type": []string{"application/json"},
|
||||
"User-Agent": []string{"GitLab/14.3.0"},
|
||||
"X-Gitlab-Event": []string{"Service Hook"},
|
||||
}
|
||||
)
|
||||
|
||||
// ServiceHookPushBody is payload of ServiceHook: Push
|
||||
var ServiceHookPushBody = []byte(`{
|
||||
"object_kind": "push",
|
||||
"event_name": "push",
|
||||
"before": "ffe8eb4f91d1fe6bc49f1e610e50e4b5767f0104",
|
||||
"after": "16862e368d8ab812e48833b741dad720d6e2cb7f",
|
||||
"ref": "refs/heads/master",
|
||||
"checkout_sha": "16862e368d8ab812e48833b741dad720d6e2cb7f",
|
||||
"message": null,
|
||||
"user_id": 2,
|
||||
"user_name": "te st",
|
||||
"user_username": "test",
|
||||
"user_email": "",
|
||||
"user_avatar": "https://www.gravatar.com/avatar/dd46a756faad4727fb679320751f6dea?s=80&d=identicon",
|
||||
"project_id": 2,
|
||||
"project": {
|
||||
"id": 2,
|
||||
"name": "Woodpecker",
|
||||
"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
|
||||
"web_url": "http://10.40.8.5:3200/test/woodpecker",
|
||||
"avatar_url": "http://example.com/uploads/project/avatar/555/Outh-20-Logo.jpg",
|
||||
"git_ssh_url": "git@10.40.8.5:test/woodpecker.git",
|
||||
"git_http_url": "http://10.40.8.5:3200/test/woodpecker.git",
|
||||
"namespace": "te st",
|
||||
"visibility_level": 20,
|
||||
"path_with_namespace": "test/woodpecker",
|
||||
"default_branch": "develop",
|
||||
"ci_config_path": null,
|
||||
"homepage": "http://10.40.8.5:3200/test/woodpecker",
|
||||
"url": "git@10.40.8.5:test/woodpecker.git",
|
||||
"ssh_url": "git@10.40.8.5:test/woodpecker.git",
|
||||
"http_url": "http://10.40.8.5:3200/test/woodpecker.git"
|
||||
},
|
||||
"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",
|
||||
"id": "16862e368d8ab812e48833b741dad720d6e2cb7f",
|
||||
"message": "Update main.go",
|
||||
"title": "Update main.go",
|
||||
"timestamp": "2021-09-27T04:46:14+00:00",
|
||||
"url": "http://10.40.8.5:3200/test/woodpecker/-/commit/16862e368d8ab812e48833b741dad720d6e2cb7f",
|
||||
"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)"
|
||||
}
|
||||
"name": "te st",
|
||||
"email": "test@test.test"
|
||||
},
|
||||
"added": [
|
||||
|
||||
],
|
||||
"modified": [
|
||||
"cmd/cli/main.go"
|
||||
],
|
||||
"removed": [
|
||||
|
||||
]
|
||||
}
|
||||
],
|
||||
"total_commits_count": 4
|
||||
}
|
||||
`)
|
||||
"total_commits_count": 1,
|
||||
"push_options": {
|
||||
},
|
||||
"repository": {
|
||||
"name": "Woodpecker",
|
||||
"url": "git@10.40.8.5:test/woodpecker.git",
|
||||
"description": "",
|
||||
"homepage": "http://10.40.8.5:3200/test/woodpecker",
|
||||
"git_http_url": "http://10.40.8.5:3200/test/woodpecker.git",
|
||||
"git_ssh_url": "git@10.40.8.5:test/woodpecker.git",
|
||||
"visibility_level": 20
|
||||
}
|
||||
}`)
|
||||
|
||||
var MergeRequestHook = []byte(`
|
||||
{
|
||||
// ServiceHookTagPushBody is payload of ServiceHook: TagPush
|
||||
var ServiceHookTagPushBody = []byte(`{
|
||||
"object_kind": "tag_push",
|
||||
"event_name": "tag_push",
|
||||
"before": "0000000000000000000000000000000000000000",
|
||||
"after": "fabed3d94cd03e6c2b7958afa9569c18a24d301f",
|
||||
"ref": "refs/tags/v22",
|
||||
"checkout_sha": "16862e368d8ab812e48833b741dad720d6e2cb7f",
|
||||
"message": "hi",
|
||||
"user_id": 2,
|
||||
"user_name": "te st",
|
||||
"user_username": "test",
|
||||
"user_email": "",
|
||||
"user_avatar": "https://www.gravatar.com/avatar/dd46a756faad4727fb679320751f6dea?s=80&d=identicon",
|
||||
"project_id": 2,
|
||||
"project": {
|
||||
"id": 2,
|
||||
"name": "Woodpecker",
|
||||
"description": "",
|
||||
"web_url": "http://10.40.8.5:3200/test/woodpecker",
|
||||
"avatar_url": "http://example.com/uploads/project/avatar/555/Outh-20-Logo.jpg",
|
||||
"git_ssh_url": "git@10.40.8.5:test/woodpecker.git",
|
||||
"git_http_url": "http://10.40.8.5:3200/test/woodpecker.git",
|
||||
"namespace": "te st",
|
||||
"visibility_level": 20,
|
||||
"path_with_namespace": "test/woodpecker",
|
||||
"default_branch": "develop",
|
||||
"ci_config_path": null,
|
||||
"homepage": "http://10.40.8.5:3200/test/woodpecker",
|
||||
"url": "git@10.40.8.5:test/woodpecker.git",
|
||||
"ssh_url": "git@10.40.8.5:test/woodpecker.git",
|
||||
"http_url": "http://10.40.8.5:3200/test/woodpecker.git"
|
||||
},
|
||||
"commits": [
|
||||
{
|
||||
"id": "16862e368d8ab812e48833b741dad720d6e2cb7f",
|
||||
"message": "Update main.go",
|
||||
"title": "Update main.go",
|
||||
"timestamp": "2021-09-27T04:46:14+00:00",
|
||||
"url": "http://10.40.8.5:3200/test/woodpecker/-/commit/16862e368d8ab812e48833b741dad720d6e2cb7f",
|
||||
"author": {
|
||||
"name": "te st",
|
||||
"email": "test@test.test"
|
||||
},
|
||||
"added": [
|
||||
|
||||
],
|
||||
"modified": [
|
||||
"cmd/cli/main.go"
|
||||
],
|
||||
"removed": [
|
||||
|
||||
]
|
||||
}
|
||||
],
|
||||
"total_commits_count": 1,
|
||||
"push_options": {
|
||||
},
|
||||
"repository": {
|
||||
"name": "Woodpecker",
|
||||
"url": "git@10.40.8.5:test/woodpecker.git",
|
||||
"description": "",
|
||||
"homepage": "http://10.40.8.5:3200/test/woodpecker",
|
||||
"git_http_url": "http://10.40.8.5:3200/test/woodpecker.git",
|
||||
"git_ssh_url": "git@10.40.8.5:test/woodpecker.git",
|
||||
"visibility_level": 20
|
||||
}
|
||||
}`)
|
||||
|
||||
// ServiceHookMergeRequestBody is payload of ServiceHook: MergeRequest
|
||||
var ServiceHookMergeRequestBody = []byte(`{
|
||||
"object_kind": "merge_request",
|
||||
"event_type": "merge_request",
|
||||
"user": {
|
||||
"name": "Administrator",
|
||||
"username": "root",
|
||||
"avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=40\u0026d=identicon"
|
||||
"id": 2,
|
||||
"name": "te st",
|
||||
"username": "test",
|
||||
"avatar_url": "https://www.gravatar.com/avatar/dd46a756faad4727fb679320751f6dea?s=80&d=identicon",
|
||||
"email": "test@test.test"
|
||||
},
|
||||
"project": {
|
||||
"id": 2,
|
||||
"name": "Woodpecker",
|
||||
"description": "",
|
||||
"web_url": "http://10.40.8.5:3200/test/woodpecker",
|
||||
"avatar_url": null,
|
||||
"git_ssh_url": "git@10.40.8.5:test/woodpecker.git",
|
||||
"git_http_url": "http://10.40.8.5:3200/test/woodpecker.git",
|
||||
"namespace": "te st",
|
||||
"visibility_level": 20,
|
||||
"path_with_namespace": "test/woodpecker",
|
||||
"default_branch": "master",
|
||||
"ci_config_path": null,
|
||||
"homepage": "http://10.40.8.5:3200/test/woodpecker",
|
||||
"url": "git@10.40.8.5:test/woodpecker.git",
|
||||
"ssh_url": "git@10.40.8.5:test/woodpecker.git",
|
||||
"http_url": "http://10.40.8.5:3200/test/woodpecker.git"
|
||||
},
|
||||
"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,
|
||||
"assignee_id": null,
|
||||
"author_id": 2,
|
||||
"created_at": "2021-09-27 05:00:01 UTC",
|
||||
"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"
|
||||
"head_pipeline_id": 5,
|
||||
"id": 2,
|
||||
"iid": 2,
|
||||
"last_edited_at": null,
|
||||
"last_edited_by_id": null,
|
||||
"merge_commit_sha": null,
|
||||
"merge_error": null,
|
||||
"merge_params": {
|
||||
"force_remove_source_branch": "1"
|
||||
},
|
||||
"merge_status": "unchecked",
|
||||
"merge_user_id": null,
|
||||
"merge_when_pipeline_succeeds": false,
|
||||
"milestone_id": null,
|
||||
"source_branch": "masterfdsafds",
|
||||
"source_project_id": 2,
|
||||
"state_id": 1,
|
||||
"target_branch": "master",
|
||||
"target_project_id": 2,
|
||||
"time_estimate": 0,
|
||||
"title": "Update client.go 🎉",
|
||||
"updated_at": "2021-09-27 05:01:21 UTC",
|
||||
"updated_by_id": null,
|
||||
"url": "http://10.40.8.5:3200/test/woodpecker/-/merge_requests/2",
|
||||
"source": {
|
||||
"id": 2,
|
||||
"name": "Woodpecker",
|
||||
"description": "",
|
||||
"web_url": "http://10.40.8.5:3200/test/woodpecker",
|
||||
"avatar_url": "http://example.com/uploads/project/avatar/555/Outh-20-Logo.jpg",
|
||||
"git_ssh_url": "git@10.40.8.5:test/woodpecker.git",
|
||||
"git_http_url": "http://10.40.8.5:3200/test/woodpecker.git",
|
||||
"namespace": "te st",
|
||||
"visibility_level": 20,
|
||||
"path_with_namespace": "test/woodpecker",
|
||||
"default_branch": "develop",
|
||||
"ci_config_path": null,
|
||||
"homepage": "http://10.40.8.5:3200/test/woodpecker",
|
||||
"url": "git@10.40.8.5:test/woodpecker.git",
|
||||
"ssh_url": "git@10.40.8.5:test/woodpecker.git",
|
||||
"http_url": "http://10.40.8.5:3200/test/woodpecker.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"
|
||||
"id": 2,
|
||||
"name": "Woodpecker",
|
||||
"description": "",
|
||||
"web_url": "http://10.40.8.5:3200/test/woodpecker",
|
||||
"avatar_url": "http://example.com/uploads/project/avatar/555/Outh-20-Logo.jpg",
|
||||
"git_ssh_url": "git@10.40.8.5:test/woodpecker.git",
|
||||
"git_http_url": "http://10.40.8.5:3200/test/woodpecker.git",
|
||||
"namespace": "te st",
|
||||
"visibility_level": 20,
|
||||
"path_with_namespace": "test/woodpecker",
|
||||
"default_branch": "develop",
|
||||
"ci_config_path": null,
|
||||
"homepage": "http://10.40.8.5:3200/test/woodpecker",
|
||||
"url": "git@10.40.8.5:test/woodpecker.git",
|
||||
"ssh_url": "git@10.40.8.5:test/woodpecker.git",
|
||||
"http_url": "http://10.40.8.5:3200/test/woodpecker.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",
|
||||
"id": "0ab96a10266b95b4b533dcfd98738015fbe70889",
|
||||
"message": "Update state.go",
|
||||
"title": "Update state.go",
|
||||
"timestamp": "2021-09-27T05:01:20+00:00",
|
||||
"url": "http://10.40.8.5:3200/test/woodpecker/-/commit/0ab96a10266b95b4b533dcfd98738015fbe70889",
|
||||
"author": {
|
||||
"name": "GitLab dev user",
|
||||
"email": "gitlabdev@dv6700.(none)"
|
||||
"name": "te st",
|
||||
"email": "test@test.test"
|
||||
}
|
||||
},
|
||||
"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"
|
||||
}
|
||||
}
|
||||
}
|
||||
`)
|
||||
"total_time_spent": 0,
|
||||
"time_change": 0,
|
||||
"human_total_time_spent": null,
|
||||
"human_time_change": null,
|
||||
"human_time_estimate": null,
|
||||
"assignee_ids": [
|
||||
|
||||
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"
|
||||
}
|
||||
}
|
||||
`)
|
||||
"action": "update",
|
||||
"oldrev": "6ef047571374c96a2bf13c361efd1fb008b0063e"
|
||||
},
|
||||
"labels": [
|
||||
|
||||
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,
|
||||
"changes": {
|
||||
"updated_at": {
|
||||
"previous": "2021-09-27 05:00:01 UTC",
|
||||
"current": "2021-09-27 05:01:21 UTC"
|
||||
}
|
||||
},
|
||||
"repository": {
|
||||
"name": "Diaspora",
|
||||
"url": "git@example.com:mike/diasporadiaspora.git",
|
||||
"name": "Woodpecker",
|
||||
"url": "git@10.40.8.5:test/woodpecker.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
|
||||
}
|
||||
`)
|
||||
"homepage": "http://10.40.8.5:3200/test/woodpecker"
|
||||
}
|
||||
}`)
|
||||
|
||||
30
server/remote/gitlab/testdata/projects.go
vendored
30
server/remote/gitlab/testdata/projects.go
vendored
@@ -30,7 +30,7 @@ var allProjectsPayload = []byte(`
|
||||
"id": 3,
|
||||
"name": "Diaspora",
|
||||
"username": "some_user",
|
||||
"created_at": "2013-09-30T13: 46: 02Z"
|
||||
"created_at": "2013-09-30T13:46:02Z"
|
||||
},
|
||||
"name": "Diaspora Client",
|
||||
"name_with_namespace": "Diaspora / Diaspora Client",
|
||||
@@ -40,16 +40,16 @@ var allProjectsPayload = []byte(`
|
||||
"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",
|
||||
"created_at": "2013-09-30T13:46:02Z",
|
||||
"last_activity_at": "2013-09-30T13:46:02Z",
|
||||
"namespace": {
|
||||
"created_at": "2013-09-30T13: 46: 02Z",
|
||||
"created_at": "2013-09-30T13:46:02Z",
|
||||
"description": "",
|
||||
"id": 3,
|
||||
"name": "Diaspora",
|
||||
"owner_id": 1,
|
||||
"path": "diaspora",
|
||||
"updated_at": "2013-09-30T13: 46: 02Z"
|
||||
"updated_at": "2013-09-30T13:46:02Z"
|
||||
},
|
||||
"archived": false
|
||||
},
|
||||
@@ -107,7 +107,7 @@ var notArchivedProjectsPayload = []byte(`
|
||||
"id": 3,
|
||||
"name": "Diaspora",
|
||||
"username": "some_user",
|
||||
"created_at": "2013-09-30T13: 46: 02Z"
|
||||
"created_at": "2013-09-30T13:46:02Z"
|
||||
},
|
||||
"name": "Diaspora Client",
|
||||
"name_with_namespace": "Diaspora / Diaspora Client",
|
||||
@@ -117,16 +117,16 @@ var notArchivedProjectsPayload = []byte(`
|
||||
"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",
|
||||
"created_at": "2013-09-30T13:46:02Z",
|
||||
"last_activity_at": "2013-09-30T13:46:02Z",
|
||||
"namespace": {
|
||||
"created_at": "2013-09-30T13: 46: 02Z",
|
||||
"created_at": "2013-09-30T13:46:02Z",
|
||||
"description": "",
|
||||
"id": 3,
|
||||
"name": "Diaspora",
|
||||
"owner_id": 1,
|
||||
"path": "diaspora",
|
||||
"updated_at": "2013-09-30T13: 46: 02Z"
|
||||
"updated_at": "2013-09-30T13:46:02Z"
|
||||
},
|
||||
"archived": false
|
||||
}
|
||||
@@ -147,7 +147,7 @@ var project4Paylod = []byte(`
|
||||
"id": 3,
|
||||
"name": "Diaspora",
|
||||
"username": "some_user",
|
||||
"created_at": "2013-09-30T13: 46: 02Z"
|
||||
"created_at": "2013-09-30T13:46:02Z"
|
||||
},
|
||||
"name": "Diaspora Client",
|
||||
"name_with_namespace": "Diaspora / Diaspora Client",
|
||||
@@ -157,16 +157,16 @@ var project4Paylod = []byte(`
|
||||
"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",
|
||||
"created_at": "2013-09-30T13:46:02Z",
|
||||
"last_activity_at": "2013-09-30T13:46:02Z",
|
||||
"namespace": {
|
||||
"created_at": "2013-09-30T13: 46: 02Z",
|
||||
"created_at": "2013-09-30T13:46:02Z",
|
||||
"description": "",
|
||||
"id": 3,
|
||||
"name": "Diaspora",
|
||||
"owner_id": 1,
|
||||
"path": "diaspora",
|
||||
"updated_at": "2013-09-30T13: 46: 02Z"
|
||||
"updated_at": "2013-09-30T13:46:02Z"
|
||||
},
|
||||
"archived": false,
|
||||
"permissions": {
|
||||
|
||||
26
server/remote/gitlab/testdata/testdata.go
vendored
26
server/remote/gitlab/testdata/testdata.go
vendored
@@ -15,22 +15,31 @@
|
||||
package testdata
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// setup a mock server for testing purposes.
|
||||
func NewServer() *httptest.Server {
|
||||
// NewServer setup a mock server for testing purposes.
|
||||
func NewServer(t *testing.T) *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)
|
||||
t.Logf("gitlab remote mock server: [%s] %s", r.Method, r.URL.Path)
|
||||
// evaluate the path to serve a dummy data file
|
||||
|
||||
// TODO: find source of "/api/v4/" requests
|
||||
// assert.EqualValues(t, "go-gitlab", r.Header.Get("user-agent"), "on request: "+r.URL.Path)
|
||||
|
||||
switch r.URL.Path {
|
||||
case "/api/v4/projects":
|
||||
if r.URL.Query().Get("archived") == "false" {
|
||||
if r.FormValue("archived") == "false" {
|
||||
w.Write(notArchivedProjectsPayload)
|
||||
} else {
|
||||
w.Write(allProjectsPayload)
|
||||
@@ -43,10 +52,15 @@ func NewServer() *httptest.Server {
|
||||
case "/api/v4/projects/brightbox/puppet":
|
||||
w.Write(project6Paylod)
|
||||
return
|
||||
case "/api/v4/projects/diaspora/diaspora-client/services/drone-ci":
|
||||
case "/api/v4/projects/4/services/drone-ci":
|
||||
switch r.Method {
|
||||
case "PUT":
|
||||
if r.FormValue("token") == "" {
|
||||
body, _ := io.ReadAll(r.Body)
|
||||
opts := make(map[string]interface{})
|
||||
assert.NoError(t, json.Unmarshal(body, &opts))
|
||||
token, ok := opts["token"].(string)
|
||||
assert.True(t, ok)
|
||||
if token == "" {
|
||||
w.WriteHeader(404)
|
||||
} else {
|
||||
w.WriteHeader(201)
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
// Copyright 2018 Drone.IO Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package 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
|
||||
}
|
||||
@@ -1,110 +0,0 @@
|
||||
// Copyright 2018 Drone.IO Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package 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
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
// Copyright 2018 Drone.IO Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package 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
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
// Copyright 2018 Drone.IO Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package 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
|
||||
}
|
||||
@@ -1,175 +0,0 @@
|
||||
// Copyright 2018 Drone.IO Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package 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
|
||||
}
|
||||
@@ -1,152 +0,0 @@
|
||||
// Copyright 2018 Drone.IO Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package 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"`
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
// Copyright 2018 Drone.IO Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package 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
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
// Copyright 2018 Drone.IO Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package 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
|
||||
}
|
||||
@@ -1,703 +0,0 @@
|
||||
// Copyright 2018 Drone.IO Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package gitlab3
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/woodpecker-ci/woodpecker/server"
|
||||
"github.com/woodpecker-ci/woodpecker/server/model"
|
||||
"github.com/woodpecker-ci/woodpecker/server/remote"
|
||||
"github.com/woodpecker-ci/woodpecker/shared/oauth2"
|
||||
|
||||
"github.com/woodpecker-ci/woodpecker/server/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) {
|
||||
u, err := url.Parse(opts.URL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
host, _, err := net.SplitHostPort(u.Host)
|
||||
if err == nil {
|
||||
u.Host = host
|
||||
}
|
||||
return &Gitlab{
|
||||
URL: opts.URL,
|
||||
Client: opts.Client,
|
||||
Secret: opts.Secret,
|
||||
Machine: u.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(ctx context.Context, 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", server.Config.Server.Host),
|
||||
}
|
||||
|
||||
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(ctx context.Context, 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(ctx context.Context, 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(ctx context.Context, 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(ctx context.Context, 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(ctx context.Context, 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(ctx context.Context, 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
|
||||
}
|
||||
|
||||
func (c *Gitlab) Dir(ctx context.Context, u *model.User, r *model.Repo, b *model.Build, f string) ([]*remote.FileMeta, error) {
|
||||
return nil, fmt.Errorf("Not implemented")
|
||||
}
|
||||
|
||||
// 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(ctx context.Context, u *model.User, r *model.Repo, b *model.Build, link string, proc *model.Proc) error {
|
||||
client := NewClient(g.URL, u.Token, g.SkipVerify)
|
||||
|
||||
status := getStatus(b.Status)
|
||||
desc := getDesc(b.Status)
|
||||
|
||||
client.SetStatus(
|
||||
ns(r.Owner, r.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(ctx context.Context, u *model.User, r *model.Repo, link string) error {
|
||||
var client = NewClient(g.URL, u.Token, g.SkipVerify)
|
||||
id, err := GetProjectId(g, client, r.Owner, r.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(ctx context.Context, u *model.User, r *model.Repo, link string) error {
|
||||
var client = NewClient(g.URL, u.Token, g.SkipVerify)
|
||||
id, err := GetProjectId(g, client, r.Owner, r.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", server.Config.Server.Host),
|
||||
//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
|
||||
}
|
||||
}
|
||||
@@ -1,261 +0,0 @@
|
||||
// Copyright 2018 Drone.IO Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package gitlab3
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/franela/goblin"
|
||||
"github.com/woodpecker-ci/woodpecker/server/model"
|
||||
"github.com/woodpecker-ci/woodpecker/server/remote/gitlab3/testdata"
|
||||
)
|
||||
|
||||
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(nil, &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(nil, &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(nil, &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(nil, &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(nil, &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(nil, &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(nil, &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(nil, &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(nil, &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(nil, &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")
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -1,139 +0,0 @@
|
||||
// Copyright 2018 Drone.IO Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package gitlab3
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/woodpecker-ci/woodpecker/server/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
|
||||
}
|
||||
}
|
||||
348
server/remote/gitlab3/testdata/hooks.go
vendored
348
server/remote/gitlab3/testdata/hooks.go
vendored
@@ -1,348 +0,0 @@
|
||||
// Copyright 2018 Drone.IO Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package 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
|
||||
}
|
||||
`)
|
||||
17
server/remote/gitlab3/testdata/oauth.go
vendored
17
server/remote/gitlab3/testdata/oauth.go
vendored
@@ -1,17 +0,0 @@
|
||||
// Copyright 2018 Drone.IO Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package testdata
|
||||
|
||||
var accessTokenPayload = []byte(`access_token=sekret&scope=api&token_type=bearer`)
|
||||
226
server/remote/gitlab3/testdata/projects.go
vendored
226
server/remote/gitlab3/testdata/projects.go
vendored
@@ -1,226 +0,0 @@
|
||||
// Copyright 2018 Drone.IO Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package 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
|
||||
}
|
||||
}
|
||||
`)
|
||||
74
server/remote/gitlab3/testdata/testdata.go
vendored
74
server/remote/gitlab3/testdata/testdata.go
vendored
@@ -1,74 +0,0 @@
|
||||
// Copyright 2018 Drone.IO Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package 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
|
||||
}
|
||||
38
server/remote/gitlab3/testdata/users.go
vendored
38
server/remote/gitlab3/testdata/users.go
vendored
@@ -1,38 +0,0 @@
|
||||
// Copyright 2018 Drone.IO Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package 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
|
||||
}
|
||||
`)
|
||||
Reference in New Issue
Block a user