Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Brad Rydzewski 2016-03-03 14:40:50 -08:00
commit 6769eed65d
20 changed files with 722 additions and 146 deletions

8
ISSUE_TEMPLATE.md Normal file
View file

@ -0,0 +1,8 @@
Thank you for taking the time to use Drone and file an issue or feature request. Before filing an issue please ensure the following boxes are checked, if applicable:
- [ ] I have searched for existing issues
- [ ] I have discussed the issue with the community at https://gitter.im/drone/drone
- [ ] I have provided a sample `.drone.yml` file to help the team reproduce
- [ ] I have provided details from the build logs
- [ ] I have provided details from the server logs by running `docker logs drone`
- [ ] I am not using the issue tracker to ask why my build failed

View file

@ -33,6 +33,7 @@ This section lists all connection options used in the connection string format.
* `open=false` allows users to self-register. Defaults to false for security reasons.
* `orgs=drone&orgs=docker` restricts access to these GitLab organizations. **Optional**
* `skip_verify=false` skip ca verification if self-signed certificate. Defaults to false for security reasons.
* `hide_archives=false` hide projects archived in GitLab from the listing.
* `clone_mode=token` a strategy for clone authorization, by default use repo token, but can be changed to `oauth` ( This is not secure, because your user token, with full access to your gitlab account will be written to .netrc, and it can be read by all who have access to project builds )
## Gitlab registration

View file

@ -329,6 +329,11 @@ func (e *engine) runJob(c context.Context, r *Task, updater *updater, client doc
info, builderr := docker.Wait(client, name)
switch {
case info.State.Running:
// A build unblocked before actually being completed.
log.Errorf("incomplete build: %s", name)
r.Job.ExitCode = 1
r.Job.Status = model.StatusError
case info.State.ExitCode == 128:
r.Job.ExitCode = info.State.ExitCode
r.Job.Status = model.StatusKilled

View file

@ -0,0 +1,53 @@
package client
import (
"encoding/json"
"strconv"
)
const (
groupsUrl = "/groups"
)
// Get a list of all projects owned by the authenticated user.
func (g *Client) AllGroups() ([]*Namespace, error) {
var perPage = 100
var groups []*Namespace
for i := 1; true; i++ {
contents, err := g.Groups(i, perPage)
if err != nil {
return groups, err
}
for _, value := range contents {
groups = append(groups, value)
}
if len(groups) == 0 {
break
}
if len(groups)/i < perPage {
break
}
}
return groups, nil
}
func (g *Client) Groups(page, perPage int) ([]*Namespace, error) {
url, opaque := g.ResourceUrl(groupsUrl, nil, QMap{
"page": strconv.Itoa(page),
"per_page": strconv.Itoa(perPage),
})
var groups []*Namespace
contents, err := g.Do("GET", url, opaque, nil)
if err == nil {
err = json.Unmarshal(contents, &groups)
}
return groups, err
}

View file

@ -15,12 +15,12 @@ const (
)
// Get a list of all projects owned by the authenticated user.
func (g *Client) AllProjects() ([]*Project, error) {
func (g *Client) AllProjects(hide_archives bool) ([]*Project, error) {
var per_page = 100
var projects []*Project
for i := 1; true; i++ {
contents, err := g.Projects(i, per_page)
contents, err := g.Projects(i, per_page, hide_archives)
if err != nil {
return projects, err
}
@ -42,12 +42,17 @@ func (g *Client) AllProjects() ([]*Project, error) {
}
// Get a list of projects owned by the authenticated user.
func (c *Client) Projects(page int, per_page int) ([]*Project, error) {
url, opaque := c.ResourceUrl(projectsUrl, nil, QMap{
func (c *Client) Projects(page int, per_page int, hide_archives bool) ([]*Project, error) {
projectsOptions := QMap{
"page": strconv.Itoa(page),
"per_page": strconv.Itoa(per_page),
})
}
if hide_archives {
projectsOptions["archived"] = "false"
}
url, opaque := c.ResourceUrl(projectsUrl, nil, projectsOptions)
var projects []*Project

View file

@ -48,12 +48,14 @@ type Project struct {
SshRepoUrl string `json:"ssh_url_to_repo"`
HttpRepoUrl string `json:"http_url_to_repo"`
Url string `json:"web_url"`
AvatarUrl string `json:"avatar_url"`
Permissions *Permissions `json:"permissions,omitempty"`
}
type Namespace struct {
Id int `json:"id,omitempty"`
Name string `json:"name,omitempty"`
Path string `json:"path,omitempty"`
}
type Person struct {
@ -62,12 +64,17 @@ type Person struct {
}
type hProject struct {
Name string `json:"name"`
SshUrl string `json:"ssh_url"`
HttpUrl string `json:"http_url"`
VisibilityLevel int `json:"visibility_level"`
WebUrl string `json:"web_url"`
Namespace string `json:"namespace"`
Name string `json:"name"`
SshUrl string `json:"ssh_url"`
HttpUrl string `json:"http_url"`
GitSshUrl string `json:"git_ssh_url"`
GitHttpUrl string `json:"git_http_url"`
AvatarUrl string `json:"avatar_url"`
VisibilityLevel int `json:"visibility_level"`
WebUrl string `json:"web_url"`
PathWithNamespace string `json:"path_with_namespace"`
DefaultBranch string `json:"default_branch"`
Namespace string `json:"namespace"`
}
type hRepository struct {
@ -122,6 +129,7 @@ type HookPayload struct {
UserId int `json:"user_id,omitempty"`
UserName string `json:"user_name,omitempty"`
ProjectId int `json:"project_id,omitempty"`
Project *hProject `json:"project,omitempty"`
Repository *hRepository `json:"repository,omitempty"`
Commits []hCommit `json:"commits,omitempty"`
TotalCommitsCount int `json:"total_commits_count,omitempty"`

View file

@ -5,8 +5,18 @@ import (
"strings"
)
var encodeMap = map[string]string{
".": "%252E",
}
func encodeParameter(value string) string {
return strings.Replace(url.QueryEscape(value), "/", "%2F", 0)
value = url.QueryEscape(value)
for before, after := range encodeMap {
value = strings.Replace(value, before, after, -1)
}
return value
}
// Tag returns current tag for push event hook payload

View file

@ -23,15 +23,16 @@ const (
)
type Gitlab struct {
URL string
Client string
Secret string
AllowedOrgs []string
CloneMode string
Open bool
PrivateMode bool
SkipVerify bool
Search bool
URL string
Client string
Secret string
AllowedOrgs []string
CloneMode string
Open bool
PrivateMode bool
SkipVerify bool
HideArchives bool
Search bool
}
func Load(env envconfig.Env) *Gitlab {
@ -50,6 +51,7 @@ func Load(env envconfig.Env) *Gitlab {
gitlab.Secret = params.Get("client_secret")
gitlab.AllowedOrgs = params["orgs"]
gitlab.SkipVerify, _ = strconv.ParseBool(params.Get("skip_verify"))
gitlab.HideArchives, _ = strconv.ParseBool(params.Get("hide_archives"))
gitlab.Open, _ = strconv.ParseBool(params.Get("open"))
switch params.Get("clone_mode") {
@ -101,6 +103,28 @@ func (g *Gitlab) Login(res http.ResponseWriter, req *http.Request) (*model.User,
if err != nil {
return nil, false, err
}
if len(g.AllowedOrgs) != 0 {
groups, err := client.AllGroups()
if err != nil {
return nil, false, fmt.Errorf("Could not check org membership. %s", err)
}
var member bool
for _, group := range groups {
for _, allowedOrg := range g.AllowedOrgs {
if group.Path == allowedOrg {
member = true
break
}
}
}
if !member {
return nil, false, fmt.Errorf("User does not belong to correct group. Must belong to %v", g.AllowedOrgs)
}
}
user := &model.User{}
user.Login = login.Username
user.Email = login.Email
@ -113,7 +137,7 @@ func (g *Gitlab) Login(res http.ResponseWriter, req *http.Request) (*model.User,
user.Avatar = g.URL + "/" + login.AvatarUrl
}
return user, true, nil
return user, g.Open, nil
}
func (g *Gitlab) Auth(token, secret string) (string, error) {
@ -145,6 +169,12 @@ func (g *Gitlab) Repo(u *model.User, owner, name string) (*model.Repo, error) {
repo.Clone = repo_.HttpRepoUrl
repo.Branch = "master"
repo.Avatar = repo_.AvatarUrl
if len(repo.Avatar) != 0 && !strings.HasPrefix(repo.Avatar, "http") {
repo.Avatar = fmt.Sprintf("%s/%s", g.URL, repo.Avatar)
}
if repo_.DefaultBranch != "" {
repo.Branch = repo_.DefaultBranch
}
@ -164,7 +194,7 @@ func (g *Gitlab) Repos(u *model.User) ([]*model.RepoLite, error) {
var repos = []*model.RepoLite{}
all, err := client.AllProjects()
all, err := client.AllProjects(g.HideArchives)
if err != nil {
return repos, err
}
@ -173,15 +203,20 @@ func (g *Gitlab) Repos(u *model.User) ([]*model.RepoLite, error) {
var parts = strings.Split(repo.PathWithNamespace, "/")
var owner = parts[0]
var name = parts[1]
var avatar = repo.AvatarUrl
if len(avatar) != 0 && !strings.HasPrefix(avatar, "http") {
avatar = fmt.Sprintf("%s/%s", g.URL, avatar)
}
repos = append(repos, &model.RepoLite{
Owner: owner,
Name: name,
FullName: repo.PathWithNamespace,
Avatar: avatar,
})
// TODO: add repo.AvatarUrl
}
return repos, err
}
@ -201,7 +236,7 @@ func (g *Gitlab) Perm(u *model.User, owner, name string) (*model.Perm, error) {
// repo owner is granted full access
if repo.Owner != nil && repo.Owner.Username == u.Login {
return &model.Perm{true, true, true}, nil
return &model.Perm{true, true, true}, nil
}
// check permission for current user
@ -337,58 +372,136 @@ func (g *Gitlab) Hook(req *http.Request) (*model.Repo, *model.Build, error) {
func mergeRequest(parsed *client.HookPayload, req *http.Request) (*model.Repo, *model.Build, error) {
repo := &model.Repo{}
repo.Owner = req.FormValue("owner")
repo.Name = req.FormValue("name")
repo.FullName = fmt.Sprintf("%s/%s", repo.Owner, repo.Name)
repo.Link = parsed.ObjectAttributes.Target.WebUrl
repo.Clone = parsed.ObjectAttributes.Target.HttpUrl
repo.Branch = "master"
obj := parsed.ObjectAttributes
if obj == nil {
return nil, nil, fmt.Errorf("object_attributes key expected in merge request hook")
}
target := obj.Target
source := obj.Source
if target == nil && source == nil {
return nil, nil, fmt.Errorf("target and source keys expected in merge request hook")
} else if target == nil {
return nil, nil, fmt.Errorf("target key expected in merge request hook")
} else if source == nil {
return nil, nil, fmt.Errorf("source key exptected in merge request hook")
}
if target.PathWithNamespace != "" {
var err error
if repo.Owner, repo.Name, err = ExtractFromPath(target.PathWithNamespace); err != nil {
return nil, nil, err
}
repo.FullName = target.PathWithNamespace
} else {
repo.Owner = req.FormValue("owner")
repo.Name = req.FormValue("name")
repo.FullName = fmt.Sprintf("%s/%s", repo.Owner, repo.Name)
}
repo.Link = target.WebUrl
if target.GitHttpUrl != "" {
repo.Clone = target.GitHttpUrl
} else {
repo.Clone = target.HttpUrl
}
if target.DefaultBranch != "" {
repo.Branch = target.DefaultBranch
} else {
repo.Branch = "master"
}
if target.AvatarUrl != "" {
repo.Avatar = target.AvatarUrl
}
build := &model.Build{}
build.Event = "pull_request"
build.Message = parsed.ObjectAttributes.LastCommit.Message
build.Commit = parsed.ObjectAttributes.LastCommit.Id
//build.Remote = parsed.ObjectAttributes.Source.HttpUrl
if parsed.ObjectAttributes.SourceProjectId == parsed.ObjectAttributes.TargetProjectId {
build.Ref = fmt.Sprintf("refs/heads/%s", parsed.ObjectAttributes.SourceBranch)
} else {
build.Ref = fmt.Sprintf("refs/merge-requests/%d/head", parsed.ObjectAttributes.IId)
lastCommit := obj.LastCommit
if lastCommit == nil {
return nil, nil, fmt.Errorf("last_commit key expected in merge request hook")
}
build.Branch = parsed.ObjectAttributes.SourceBranch
// build.Timestamp = parsed.ObjectAttributes.LastCommit.Timestamp
build.Message = lastCommit.Message
build.Commit = lastCommit.Id
//build.Remote = parsed.ObjectAttributes.Source.HttpUrl
build.Author = parsed.ObjectAttributes.LastCommit.Author.Name
build.Email = parsed.ObjectAttributes.LastCommit.Author.Email
build.Title = parsed.ObjectAttributes.Title
build.Link = parsed.ObjectAttributes.Url
if obj.SourceProjectId == obj.TargetProjectId {
build.Ref = fmt.Sprintf("refs/heads/%s", obj.SourceBranch)
} else {
build.Ref = fmt.Sprintf("refs/merge-requests/%d/head", obj.IId)
}
build.Branch = obj.SourceBranch
author := lastCommit.Author
if author == nil {
return nil, nil, fmt.Errorf("author key expected in merge request hook")
}
build.Author = author.Name
build.Email = author.Email
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) {
var cloneUrl = parsed.Repository.GitHttpUrl
repo := &model.Repo{}
repo.Owner = req.FormValue("owner")
repo.Name = req.FormValue("name")
repo.FullName = fmt.Sprintf("%s/%s", repo.Owner, repo.Name)
repo.Link = parsed.Repository.URL
repo.Clone = cloneUrl
repo.Branch = "master"
switch parsed.Repository.VisibilityLevel {
case 0:
repo.IsPrivate = true
case 10:
repo.IsPrivate = true
case 20:
repo.IsPrivate = false
// Since gitlab 8.5, used project instead repository key
// see https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/web_hooks/web_hooks.md#web-hooks
if project := parsed.Project; project != nil {
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")
}
repo.FullName = fmt.Sprintf("%s/%s", req.FormValue("owner"), req.FormValue("name"))
build := &model.Build{}
build.Event = model.EventPush
build.Commit = parsed.After
@ -406,6 +519,9 @@ func push(parsed *client.HookPayload, req *http.Request) (*model.Repo, *model.Bu
case head.Author != nil:
build.Email = head.Author.Email
build.Author = parsed.UserName
if len(build.Email) != 0 {
build.Avatar = GetUserAvatar(build.Email)
}
case head.Author == nil:
build.Author = parsed.UserName
}
@ -436,25 +552,6 @@ func (g *Gitlab) Oauth2Transport(r *http.Request) *oauth2.Transport {
}
}
// Accessor method, to allowed remote organizations field.
func (g *Gitlab) GetOrgs() []string {
return g.AllowedOrgs
}
// Accessor method, to open field.
func (g *Gitlab) GetOpen() bool {
return g.Open
}
// return default scope for GitHub
func (g *Gitlab) Scope() string {
return DefaultScope
}
func (g *Gitlab) String() string {
return "gitlab"
}
const (
StatusPending = "pending"
StatusRunning = "running"

View file

@ -32,6 +32,25 @@ func Test_Gitlab(t *testing.T) {
g := goblin.Goblin(t)
g.Describe("Gitlab Plugin", func() {
// Test projects method
g.Describe("AllProjects", func() {
g.It("Should return only non-archived projects is hidden", func() {
gitlab.HideArchives = true
_projects, err := gitlab.Repos(&user)
g.Assert(err == nil).IsTrue()
g.Assert(len(_projects)).Equal(1)
})
g.It("Should return all the projects", func() {
gitlab.HideArchives = false
_projects, err := gitlab.Repos(&user)
g.Assert(err == nil).IsTrue()
g.Assert(len(_projects)).Equal(2)
})
})
// Test repository method
g.Describe("Repo", func() {
g.It("Should return valid repo", func() {
@ -115,52 +134,114 @@ func Test_Gitlab(t *testing.T) {
// Test hook method
g.Describe("Hook", func() {
g.It("Should parse push hoook", func() {
req, _ := http.NewRequest(
"POST",
"http://example.com/api/hook?owner=diaspora&name=diaspora-client",
bytes.NewReader(testdata.PushHook),
)
g.Describe("Push hook", func() {
g.It("Should parse actual push hoook", func() {
req, _ := http.NewRequest(
"POST",
"http://example.com/api/hook?owner=diaspora&name=diaspora-client",
bytes.NewReader(testdata.PushHook),
)
repo, build, err := gitlab.Hook(req)
repo, build, err := gitlab.Hook(req)
g.Assert(err == nil).IsTrue()
g.Assert(repo.Owner).Equal("diaspora")
g.Assert(repo.Name).Equal("diaspora-client")
g.Assert(build.Ref).Equal("refs/heads/master")
g.Assert(err == nil).IsTrue()
g.Assert(repo.Owner).Equal("mike")
g.Assert(repo.Name).Equal("diaspora")
g.Assert(repo.Avatar).Equal("http://example.com/uploads/project/avatar/555/Outh-20-Logo.jpg")
g.Assert(repo.Branch).Equal("develop")
g.Assert(build.Ref).Equal("refs/heads/master")
})
g.It("Should parse legacy push hoook", func() {
req, _ := http.NewRequest(
"POST",
"http://example.com/api/hook?owner=diaspora&name=diaspora-client",
bytes.NewReader(testdata.LegacyPushHook),
)
repo, build, err := gitlab.Hook(req)
g.Assert(err == nil).IsTrue()
g.Assert(repo.Owner).Equal("diaspora")
g.Assert(repo.Name).Equal("diaspora-client")
g.Assert(repo.Avatar).Equal("")
g.Assert(repo.Branch).Equal("master")
g.Assert(build.Ref).Equal("refs/heads/master")
})
})
g.It("Should parse tag push hook", func() {
req, _ := http.NewRequest(
"POST",
"http://example.com/api/hook?owner=diaspora&name=diaspora-client",
bytes.NewReader(testdata.TagHook),
)
g.Describe("Tag push hook", func() {
g.It("Should parse tag push hook", func() {
req, _ := http.NewRequest(
"POST",
"http://example.com/api/hook?owner=diaspora&name=diaspora-client",
bytes.NewReader(testdata.TagHook),
)
repo, build, err := gitlab.Hook(req)
repo, build, err := gitlab.Hook(req)
g.Assert(err == nil).IsTrue()
g.Assert(repo.Owner).Equal("diaspora")
g.Assert(repo.Name).Equal("diaspora-client")
g.Assert(build.Ref).Equal("refs/tags/v1.0.0")
g.Assert(err == nil).IsTrue()
g.Assert(repo.Owner).Equal("jsmith")
g.Assert(repo.Name).Equal("example")
g.Assert(repo.Avatar).Equal("http://example.com/uploads/project/avatar/555/Outh-20-Logo.jpg")
g.Assert(repo.Branch).Equal("develop")
g.Assert(build.Ref).Equal("refs/tags/v1.0.0")
})
g.It("Should parse legacy tag push hook", func() {
req, _ := http.NewRequest(
"POST",
"http://example.com/api/hook?owner=diaspora&name=diaspora-client",
bytes.NewReader(testdata.LegacyTagHook),
)
repo, build, err := gitlab.Hook(req)
g.Assert(err == nil).IsTrue()
g.Assert(repo.Owner).Equal("diaspora")
g.Assert(repo.Name).Equal("diaspora-client")
g.Assert(build.Ref).Equal("refs/tags/v1.0.0")
})
})
g.It("Should parse merge request hook", func() {
req, _ := http.NewRequest(
"POST",
"http://example.com/api/hook?owner=diaspora&name=diaspora-client",
bytes.NewReader(testdata.MergeRequestHook),
)
g.Describe("Merge request hook", func() {
g.It("Should parse merge request hook", func() {
req, _ := http.NewRequest(
"POST",
"http://example.com/api/hook?owner=diaspora&name=diaspora-client",
bytes.NewReader(testdata.MergeRequestHook),
)
repo, build, err := gitlab.Hook(req)
repo, build, err := gitlab.Hook(req)
g.Assert(err == nil).IsTrue()
g.Assert(repo.Owner).Equal("diaspora")
g.Assert(repo.Name).Equal("diaspora-client")
g.Assert(err == nil).IsTrue()
g.Assert(repo.Avatar).Equal("http://example.com/uploads/project/avatar/555/Outh-20-Logo.jpg")
g.Assert(repo.Branch).Equal("develop")
g.Assert(repo.Owner).Equal("awesome_space")
g.Assert(repo.Name).Equal("awesome_project")
g.Assert(build.Title).Equal("MS-Viewport")
g.Assert(build.Title).Equal("MS-Viewport")
})
g.It("Should parse legacy merge request hook", func() {
req, _ := http.NewRequest(
"POST",
"http://example.com/api/hook?owner=diaspora&name=diaspora-client",
bytes.NewReader(testdata.LegacyMergeRequestHook),
)
repo, build, err := gitlab.Hook(req)
g.Assert(err == nil).IsTrue()
g.Assert(repo.Owner).Equal("diaspora")
g.Assert(repo.Name).Equal("diaspora-client")
g.Assert(build.Title).Equal("MS-Viewport")
})
})
})
})

View file

@ -1,13 +1,20 @@
package gitlab
import (
"crypto/md5"
"encoding/hex"
"fmt"
"net/url"
"strconv"
"strings"
"github.com/drone/drone/remote/gitlab/client"
)
const (
gravatarBase = "https://www.gravatar.com/avatar"
)
// NewClient is a helper function that returns a new GitHub
// client using the provided OAuth token.
func NewClient(url, accessToken string, skipVerify bool) *client.Client {
@ -79,6 +86,26 @@ 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
}

View file

@ -1,6 +1,46 @@
package testdata
var TagHook = []byte(`
{
"object_kind": "tag_push",
"ref": "refs/tags/v1.0.0",
"before": "0000000000000000000000000000000000000000",
"after": "82b3d5ae55f7080f1e6022629cdb57bfae7cccc7",
"user_id": 1,
"user_name": "John Smith",
"user_avatar": "https://s.gravatar.com/avatar/d4c74594d841139328695756648b6bd6?s=8://s=80",
"project_id": 1,
"project":{
"name":"Example",
"description":"",
"web_url":"http://example.com/jsmith/example",
"avatar_url":"http://example.com/uploads/project/avatar/555/Outh-20-Logo.jpg",
"git_ssh_url":"git@example.com:jsmith/example.git",
"git_http_url":"http://example.com/jsmith/example.git",
"namespace":"Jsmith",
"visibility_level":0,
"path_with_namespace":"jsmith/example",
"default_branch":"develop",
"homepage":"http://example.com/jsmith/example",
"url":"git@example.com:jsmith/example.git",
"ssh_url":"git@example.com:jsmith/example.git",
"http_url":"http://example.com/jsmith/example.git"
},
"repository":{
"name": "jsmith",
"url": "ssh://git@example.com/jsmith/example.git",
"description": "",
"homepage": "http://example.com/jsmith/example",
"git_http_url":"http://example.com/jsmith/example.git",
"git_ssh_url":"git@example.com:jsmith/example.git",
"visibility_level":0
},
"commits": [],
"total_commits_count": 0
}
`)
var LegacyTagHook = []byte(`
{
"object_kind": "tag_push",
"ref": "refs/tags/v1.0.0",
@ -45,6 +85,86 @@ var TagHook = []byte(`
`)
var MergeRequestHook = []byte(`
{
"object_kind": "merge_request",
"user": {
"name": "Administrator",
"username": "root",
"avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=40\u0026d=identicon"
},
"object_attributes": {
"id": 99,
"target_branch": "master",
"source_branch": "ms-viewport",
"source_project_id": 14,
"author_id": 51,
"assignee_id": 6,
"title": "MS-Viewport",
"created_at": "2013-12-03T17:23:34Z",
"updated_at": "2013-12-03T17:23:34Z",
"st_commits": null,
"st_diffs": null,
"milestone_id": null,
"state": "opened",
"merge_status": "unchecked",
"target_project_id": 14,
"iid": 1,
"description": "",
"source":{
"name":"Awesome Project",
"description":"Aut reprehenderit ut est.",
"web_url":"http://example.com/awesome_space/awesome_project",
"avatar_url":"http://example.com/uploads/project/avatar/555/Outh-20-Logo.jpg",
"git_ssh_url":"git@example.com:awesome_space/awesome_project.git",
"git_http_url":"http://example.com/awesome_space/awesome_project.git",
"namespace":"Awesome Space",
"visibility_level":20,
"path_with_namespace":"awesome_space/awesome_project",
"default_branch":"master",
"homepage":"http://example.com/awesome_space/awesome_project",
"url":"http://example.com/awesome_space/awesome_project.git",
"ssh_url":"git@example.com:awesome_space/awesome_project.git",
"http_url":"http://example.com/awesome_space/awesome_project.git"
},
"target": {
"name":"Awesome Project",
"description":"Aut reprehenderit ut est.",
"web_url":"http://example.com/awesome_space/awesome_project",
"avatar_url":"http://example.com/uploads/project/avatar/555/Outh-20-Logo.jpg",
"git_ssh_url":"git@example.com:awesome_space/awesome_project.git",
"git_http_url":"http://example.com/awesome_space/awesome_project.git",
"namespace":"Awesome Space",
"visibility_level":20,
"path_with_namespace":"awesome_space/awesome_project",
"default_branch":"develop",
"homepage":"http://example.com/awesome_space/awesome_project",
"url":"http://example.com/awesome_space/awesome_project.git",
"ssh_url":"git@example.com:awesome_space/awesome_project.git",
"http_url":"http://example.com/awesome_space/awesome_project.git"
},
"last_commit": {
"id": "da1560886d4f094c3e6c9ef40349f7d38b5d27d7",
"message": "fixed readme",
"timestamp": "2012-01-03T23:36:29+02:00",
"url": "http://example.com/awesome_space/awesome_project/commits/da1560886d4f094c3e6c9ef40349f7d38b5d27d7",
"author": {
"name": "GitLab dev user",
"email": "gitlabdev@dv6700.(none)"
}
},
"work_in_progress": false,
"url": "http://example.com/diaspora/merge_requests/1",
"action": "open",
"assignee": {
"name": "User1",
"username": "user1",
"avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=40\u0026d=identicon"
}
}
}
`)
var LegacyMergeRequestHook = []byte(`
{
"object_kind": "merge_request",
"user": {
@ -101,6 +221,74 @@ var MergeRequestHook = []byte(`
`)
var PushHook = []byte(`
{
"object_kind": "push",
"before": "95790bf891e76fee5e1747ab589903a6a1f80f22",
"after": "da1560886d4f094c3e6c9ef40349f7d38b5d27d7",
"ref": "refs/heads/master",
"user_id": 4,
"user_name": "John Smith",
"user_email": "john@example.com",
"user_avatar": "https://s.gravatar.com/avatar/d4c74594d841139328695756648b6bd6?s=80",
"project_id": 15,
"project":{
"name":"Diaspora",
"description":"",
"web_url":"http://example.com/mike/diaspora",
"avatar_url":"http://example.com/uploads/project/avatar/555/Outh-20-Logo.jpg",
"git_ssh_url":"git@example.com:mike/diaspora.git",
"git_http_url":"http://example.com/mike/diaspora.git",
"namespace":"Mike",
"visibility_level":0,
"path_with_namespace":"mike/diaspora",
"default_branch":"develop",
"homepage":"http://example.com/mike/diaspora",
"url":"git@example.com:mike/diasporadiaspora.git",
"ssh_url":"git@example.com:mike/diaspora.git",
"http_url":"http://example.com/mike/diaspora.git"
},
"repository":{
"name": "Diaspora",
"url": "git@example.com:mike/diasporadiaspora.git",
"description": "",
"homepage": "http://example.com/mike/diaspora",
"git_http_url":"http://example.com/mike/diaspora.git",
"git_ssh_url":"git@example.com:mike/diaspora.git",
"visibility_level":0
},
"commits": [
{
"id": "b6568db1bc1dcd7f8b4d5a946b0b91f9dacd7327",
"message": "Update Catalan translation to e38cb41.",
"timestamp": "2011-12-12T14:27:31+02:00",
"url": "http://example.com/mike/diaspora/commit/b6568db1bc1dcd7f8b4d5a946b0b91f9dacd7327",
"author": {
"name": "Jordi Mallach",
"email": "jordi@softcatala.org"
},
"added": ["CHANGELOG"],
"modified": ["app/controller/application.rb"],
"removed": []
},
{
"id": "da1560886d4f094c3e6c9ef40349f7d38b5d27d7",
"message": "fixed readme",
"timestamp": "2012-01-03T23:36:29+02:00",
"url": "http://example.com/mike/diaspora/commit/da1560886d4f094c3e6c9ef40349f7d38b5d27d7",
"author": {
"name": "GitLab dev user",
"email": "gitlabdev@dv6700.(none)"
},
"added": ["CHANGELOG"],
"modified": ["app/controller/application.rb"],
"removed": []
}
],
"total_commits_count": 4
}
`)
var LegacyPushHook = []byte(`
{
"object_kind": "push",
"before": "95790bf891e76fee5e1747ab589903a6a1f80f22",
@ -114,7 +302,7 @@ var PushHook = []byte(`
"name": "Diaspora",
"url": "git@example.com:mike/diasporadiaspora.git",
"description": "",
"homepage": "http://example.com/mike/diaspora",
"homepage": "http://example.com/mike/diaspora",
"git_http_url":"http://example.com/mike/diaspora.git",
"git_ssh_url":"git@example.com:mike/diaspora.git",
"visibility_level":0

View file

@ -1,7 +1,7 @@
package testdata
// sample repository list
var projectsPayload = []byte(`
var allProjectsPayload = []byte(`
[
{
"id": 4,
@ -73,6 +73,47 @@ var projectsPayload = []byte(`
"path": "brightbox",
"updated_at": "2013-09-30T13:46:02Z"
},
"archived": true
}
]
`)
var notArchivedProjectsPayload = []byte(`
[
{
"id": 4,
"description": null,
"default_branch": "master",
"public": false,
"visibility_level": 0,
"ssh_url_to_repo": "git@example.com:diaspora/diaspora-client.git",
"http_url_to_repo": "http://example.com/diaspora/diaspora-client.git",
"web_url": "http://example.com/diaspora/diaspora-client",
"owner": {
"id": 3,
"name": "Diaspora",
"username": "some_user",
"created_at": "2013-09-30T13: 46: 02Z"
},
"name": "Diaspora Client",
"name_with_namespace": "Diaspora / Diaspora Client",
"path": "diaspora-client",
"path_with_namespace": "diaspora/diaspora-client",
"issues_enabled": true,
"merge_requests_enabled": true,
"wiki_enabled": true,
"snippets_enabled": false,
"created_at": "2013-09-30T13: 46: 02Z",
"last_activity_at": "2013-09-30T13: 46: 02Z",
"namespace": {
"created_at": "2013-09-30T13: 46: 02Z",
"description": "",
"id": 3,
"name": "Diaspora",
"owner_id": 1,
"path": "diaspora",
"updated_at": "2013-09-30T13: 46: 02Z"
},
"archived": false
}
]

View file

@ -16,7 +16,12 @@ func NewServer() *httptest.Server {
// evaluate the path to serve a dummy data file
switch r.URL.Path {
case "/api/v3/projects":
w.Write(projectsPayload)
if r.URL.Query().Get("archived") == "false" {
w.Write(notArchivedProjectsPayload)
} else {
w.Write(allProjectsPayload)
}
return
case "/api/v3/projects/diaspora/diaspora-client":
w.Write(project4Paylod)

View file

@ -25,8 +25,9 @@ func Options(c *gin.Context) {
if c.Request.Method != "OPTIONS" {
c.Next()
} else {
c.Header("Access-Control-Allow-Origin", "*")
c.Header("Access-Control-Allow-Methods", "GET,POST,PUT,PATCH,DELETE,OPTIONS")
c.Header("Access-Control-Allow-Headers", "Authorization")
c.Header("Access-Control-Allow-Headers", "authorization, origin, content-type, accept")
c.Header("Allow", "HEAD,GET,POST,PUT,PATCH,DELETE,OPTIONS")
c.Header("Content-Type", "application/json")
c.AbortWithStatus(200)

View file

@ -2,6 +2,7 @@ package session
import (
"net/http"
"os"
"github.com/drone/drone/model"
"github.com/drone/drone/remote"
@ -104,6 +105,8 @@ func Perm(c *gin.Context) *model.Perm {
}
func SetPerm() gin.HandlerFunc {
PUBLIC_MODE := os.Getenv("PUBLIC_MODE")
return func(c *gin.Context) {
user := User(c)
repo := Repo(c)
@ -164,6 +167,11 @@ func SetPerm() gin.HandlerFunc {
}
}
// all build logs are visible in public mode
if PUBLIC_MODE != "" {
perm.Pull = true
}
if user != nil {
log.Debugf("%s granted %+v permission to %s",
user.Login, perm, repo.FullName)

View file

@ -0,0 +1,44 @@
package session
import (
"os"
"testing"
"github.com/drone/drone/model"
"github.com/franela/goblin"
"github.com/gin-gonic/gin"
)
func TestSetPerm(t *testing.T) {
g := goblin.Goblin(t)
g.Describe("SetPerm", func() {
g.BeforeEach(func() {
os.Unsetenv("PUBLIC_MODE")
})
g.It("Should set pull to false (private repo, user not logged in)", func() {
c := gin.Context{}
c.Set("repo", &model.Repo{
IsPrivate: true,
})
SetPerm()(&c)
v, ok := c.Get("perm")
g.Assert(ok).IsTrue("perm was not set")
p, ok := v.(*model.Perm)
g.Assert(ok).IsTrue("perm was the wrong type")
g.Assert(p.Pull).IsFalse("pull should be false")
})
g.It("Should set pull to true (private repo, user not logged in, public mode)", func() {
os.Setenv("PUBLIC_MODE", "true")
c := gin.Context{}
c.Set("repo", &model.Repo{
IsPrivate: true,
})
SetPerm()(&c)
v, ok := c.Get("perm")
g.Assert(ok).IsTrue("perm was not set")
p, ok := v.(*model.Perm)
g.Assert(ok).IsTrue("perm was the wrong type")
g.Assert(p.Pull).IsTrue("pull should be true")
})
})
}

View file

@ -1,9 +1,9 @@
package docker
import (
"io"
"io/ioutil"
"errors"
log "github.com/Sirupsen/logrus"
"github.com/samalba/dockerclient"
)
@ -77,33 +77,21 @@ func Wait(client dockerclient.Client, name string) (*dockerclient.ContainerInfo,
client.KillContainer(name, "9")
}()
errc := make(chan error, 1)
infoc := make(chan *dockerclient.ContainerInfo, 1)
go func() {
// blocks and waits for the container to finish
// by streaming the logs (to /dev/null). Ideally
// we could use the `wait` function instead
rc, err := client.ContainerLogs(name, LogOptsTail)
if err != nil {
errc <- err
return
}
io.Copy(ioutil.Discard, rc)
rc.Close()
for attempts := 0; attempts < 5; attempts++ {
done := client.Wait(name)
<-done
info, err := client.InspectContainer(name)
if err != nil {
errc <- err
return
return nil, err
}
infoc <- info
}()
select {
case info := <-infoc:
return info, nil
case err := <-errc:
return nil, err
if !info.State.Running {
return info, nil
}
log.Debugf("attempting to resume waiting after %d attempts.\n", attempts)
}
return nil, errors.New("reached maximum wait attempts")
}

BIN
static/images/dummy.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View file

@ -32,7 +32,10 @@ block content
each $build in $group.Builds
a.card[href=$repo.Name+"/"+$build.Number][data-build=$build.Number]
div.card-header
img[src=$build.Avatar]
if $build.Avatar != ""
img[src=$build.Avatar]
else
img[src="/static/images/dummy.png"]
div.card-block
div
div.status[class=$build.Status] #{$build.Status}

View file

@ -20,7 +20,10 @@ block content
div.col-sm-4
a.card[href="/"+$repo.FullName]
div.card-header
img.avatar[src=$repo.Avatar]
if $repo.Avatar != ""
img.avatar[src=$repo.Avatar]
else
img.avatar[src="/static/images/dummy.png"]
div.card-block
h3.login #{$repo.Name}
div.full_name.hidden #{$repo.FullName}