Merge pull request #1969 from bradrydzewski/master
implement gating / remove signature
This commit is contained in:
commit
57ac00665a
30 changed files with 583 additions and 95 deletions
|
@ -102,6 +102,12 @@ type Client interface {
|
||||||
// the prior history.
|
// the prior history.
|
||||||
BuildFork(string, string, int, map[string]string) (*model.Build, error)
|
BuildFork(string, string, int, map[string]string) (*model.Build, error)
|
||||||
|
|
||||||
|
// BuildApprove approves a blocked build.
|
||||||
|
BuildApprove(string, string, int) (*model.Build, error)
|
||||||
|
|
||||||
|
// BuildDecline declines a blocked build.
|
||||||
|
BuildDecline(string, string, int) (*model.Build, error)
|
||||||
|
|
||||||
// BuildLogs returns the build logs for the specified job.
|
// BuildLogs returns the build logs for the specified job.
|
||||||
BuildLogs(string, string, int, int) (io.ReadCloser, error)
|
BuildLogs(string, string, int, int) (io.ReadCloser, error)
|
||||||
|
|
||||||
|
|
|
@ -34,6 +34,8 @@ const (
|
||||||
pathEncrypt = "%s/api/repos/%s/%s/encrypt"
|
pathEncrypt = "%s/api/repos/%s/%s/encrypt"
|
||||||
pathBuilds = "%s/api/repos/%s/%s/builds"
|
pathBuilds = "%s/api/repos/%s/%s/builds"
|
||||||
pathBuild = "%s/api/repos/%s/%s/builds/%v"
|
pathBuild = "%s/api/repos/%s/%s/builds/%v"
|
||||||
|
pathApprove = "%s/api/repos/%s/%s/builds/%d/approve"
|
||||||
|
pathDecline = "%s/api/repos/%s/%s/builds/%d/decline"
|
||||||
pathJob = "%s/api/repos/%s/%s/builds/%d/%d"
|
pathJob = "%s/api/repos/%s/%s/builds/%d/%d"
|
||||||
pathLog = "%s/api/repos/%s/%s/logs/%d/%d"
|
pathLog = "%s/api/repos/%s/%s/logs/%d/%d"
|
||||||
pathKey = "%s/api/repos/%s/%s/key"
|
pathKey = "%s/api/repos/%s/%s/key"
|
||||||
|
@ -257,6 +259,22 @@ func (c *client) BuildFork(owner, name string, num int, params map[string]string
|
||||||
return out, err
|
return out, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BuildApprove approves a blocked build.
|
||||||
|
func (c *client) BuildApprove(owner, name string, num int) (*model.Build, error) {
|
||||||
|
out := new(model.Build)
|
||||||
|
uri := fmt.Sprintf(pathApprove, c.base, owner, name, num)
|
||||||
|
err := c.post(uri, nil, out)
|
||||||
|
return out, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// BuildDecline declines a blocked build.
|
||||||
|
func (c *client) BuildDecline(owner, name string, num int) (*model.Build, error) {
|
||||||
|
out := new(model.Build)
|
||||||
|
uri := fmt.Sprintf(pathDecline, c.base, owner, name, num)
|
||||||
|
err := c.post(uri, nil, out)
|
||||||
|
return out, err
|
||||||
|
}
|
||||||
|
|
||||||
// BuildLogs returns the build logs for the specified job.
|
// BuildLogs returns the build logs for the specified job.
|
||||||
func (c *client) BuildLogs(owner, name string, num, job int) (io.ReadCloser, error) {
|
func (c *client) BuildLogs(owner, name string, num, job int) (io.ReadCloser, error) {
|
||||||
uri := fmt.Sprintf(pathLog, c.base, owner, name, num, job)
|
uri := fmt.Sprintf(pathLog, c.base, owner, name, num, job)
|
||||||
|
|
|
@ -12,6 +12,8 @@ var buildCmd = cli.Command{
|
||||||
buildInfoCmd,
|
buildInfoCmd,
|
||||||
buildStopCmd,
|
buildStopCmd,
|
||||||
buildStartCmd,
|
buildStartCmd,
|
||||||
|
buildApproveCmd,
|
||||||
|
buildDeclineCmd,
|
||||||
buildQueueCmd,
|
buildQueueCmd,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
39
drone/build_approve.go
Normal file
39
drone/build_approve.go
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/urfave/cli"
|
||||||
|
)
|
||||||
|
|
||||||
|
var buildApproveCmd = cli.Command{
|
||||||
|
Name: "approve",
|
||||||
|
Usage: "approve a build",
|
||||||
|
Action: buildApprove,
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildApprove(c *cli.Context) (err error) {
|
||||||
|
repo := c.Args().First()
|
||||||
|
owner, name, err := parseRepo(repo)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
number, err := strconv.Atoi(c.Args().Get(1))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
client, err := newClient(c)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = client.BuildApprove(owner, name, number)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("Approving build %s/%s#%d\n", owner, name, number)
|
||||||
|
return nil
|
||||||
|
}
|
39
drone/build_decline.go
Normal file
39
drone/build_decline.go
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/urfave/cli"
|
||||||
|
)
|
||||||
|
|
||||||
|
var buildDeclineCmd = cli.Command{
|
||||||
|
Name: "decline",
|
||||||
|
Usage: "decline a build",
|
||||||
|
Action: buildDecline,
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildDecline(c *cli.Context) (err error) {
|
||||||
|
repo := c.Args().First()
|
||||||
|
owner, name, err := parseRepo(repo)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
number, err := strconv.Atoi(c.Args().Get(1))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
client, err := newClient(c)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = client.BuildDecline(owner, name, number)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("Declining build %s/%s#%d\n", owner, name, number)
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -22,12 +22,15 @@ type Build struct {
|
||||||
Title string `json:"title" meddler:"build_title"`
|
Title string `json:"title" meddler:"build_title"`
|
||||||
Message string `json:"message" meddler:"build_message"`
|
Message string `json:"message" meddler:"build_message"`
|
||||||
Timestamp int64 `json:"timestamp" meddler:"build_timestamp"`
|
Timestamp int64 `json:"timestamp" meddler:"build_timestamp"`
|
||||||
|
Sender string `json:"sender" meddler:"build_sender"`
|
||||||
Author string `json:"author" meddler:"build_author"`
|
Author string `json:"author" meddler:"build_author"`
|
||||||
Avatar string `json:"author_avatar" meddler:"build_avatar"`
|
Avatar string `json:"author_avatar" meddler:"build_avatar"`
|
||||||
Email string `json:"author_email" meddler:"build_email"`
|
Email string `json:"author_email" meddler:"build_email"`
|
||||||
Link string `json:"link_url" meddler:"build_link"`
|
Link string `json:"link_url" meddler:"build_link"`
|
||||||
Signed bool `json:"signed" meddler:"build_signed"`
|
Signed bool `json:"signed" meddler:"build_signed"` // deprecate
|
||||||
Verified bool `json:"verified" meddler:"build_verified"`
|
Verified bool `json:"verified" meddler:"build_verified"` // deprecate
|
||||||
|
Reviewer string `json:"reviewed_by" meddler:"build_reviewer"`
|
||||||
|
Reviewed int64 `json:"reviewed_at" meddler:"build_reviewed"`
|
||||||
Jobs []*Job `json:"jobs,omitempty" meddler:"-"`
|
Jobs []*Job `json:"jobs,omitempty" meddler:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,8 @@ const (
|
||||||
StatusFailure = "failure"
|
StatusFailure = "failure"
|
||||||
StatusKilled = "killed"
|
StatusKilled = "killed"
|
||||||
StatusError = "error"
|
StatusError = "error"
|
||||||
|
StatusBlocked = "blocked"
|
||||||
|
StatusDeclined = "declined"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
|
@ -29,5 +29,6 @@ type Repo struct {
|
||||||
AllowPush bool `json:"allow_push" meddler:"repo_allow_push"`
|
AllowPush bool `json:"allow_push" meddler:"repo_allow_push"`
|
||||||
AllowDeploy bool `json:"allow_deploys" meddler:"repo_allow_deploys"`
|
AllowDeploy bool `json:"allow_deploys" meddler:"repo_allow_deploys"`
|
||||||
AllowTag bool `json:"allow_tags" meddler:"repo_allow_tags"`
|
AllowTag bool `json:"allow_tags" meddler:"repo_allow_tags"`
|
||||||
|
Config string `json:"config_path" meddler:"repo_config_path"`
|
||||||
Hash string `json:"-" meddler:"repo_hash"`
|
Hash string `json:"-" meddler:"repo_hash"`
|
||||||
}
|
}
|
||||||
|
|
|
@ -183,7 +183,12 @@ func (c *config) Perm(u *model.User, owner, name string) (*model.Perm, error) {
|
||||||
|
|
||||||
// File fetches the file from the Bitbucket repository and returns its contents.
|
// File fetches the file from the Bitbucket repository and returns its contents.
|
||||||
func (c *config) File(u *model.User, r *model.Repo, b *model.Build, f string) ([]byte, error) {
|
func (c *config) File(u *model.User, r *model.Repo, b *model.Build, f string) ([]byte, error) {
|
||||||
config, err := c.newClient(u).FindSource(r.Owner, r.Name, b.Commit, f)
|
return c.FileRef(u, r, b.Commit, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FileRef fetches the file from the Bitbucket repository and returns its contents.
|
||||||
|
func (c *config) FileRef(u *model.User, r *model.Repo, ref, f string) ([]byte, error) {
|
||||||
|
config, err := c.newClient(u).FindSource(r.Owner, r.Name, ref, f)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,8 +2,8 @@ package bitbucket
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"regexp"
|
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/drone/drone/model"
|
"github.com/drone/drone/model"
|
||||||
|
@ -22,6 +22,8 @@ const (
|
||||||
descPending = "this build is pending"
|
descPending = "this build is pending"
|
||||||
descSuccess = "the build was successful"
|
descSuccess = "the build was successful"
|
||||||
descFailure = "the build failed"
|
descFailure = "the build failed"
|
||||||
|
descBlocked = "the build requires approval"
|
||||||
|
descDeclined = "the build was rejected"
|
||||||
descError = "oops, something went wrong"
|
descError = "oops, something went wrong"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -29,7 +31,7 @@ const (
|
||||||
// Bitbucket commit status.
|
// Bitbucket commit status.
|
||||||
func convertStatus(status string) string {
|
func convertStatus(status string) string {
|
||||||
switch status {
|
switch status {
|
||||||
case model.StatusPending, model.StatusRunning:
|
case model.StatusPending, model.StatusRunning, model.StatusBlocked:
|
||||||
return statusPending
|
return statusPending
|
||||||
case model.StatusSuccess:
|
case model.StatusSuccess:
|
||||||
return statusSuccess
|
return statusSuccess
|
||||||
|
@ -48,6 +50,10 @@ func convertDesc(status string) string {
|
||||||
return descSuccess
|
return descSuccess
|
||||||
case model.StatusFailure:
|
case model.StatusFailure:
|
||||||
return descFailure
|
return descFailure
|
||||||
|
case model.StatusBlocked:
|
||||||
|
return descBlocked
|
||||||
|
case model.StatusDeclined:
|
||||||
|
return descDeclined
|
||||||
default:
|
default:
|
||||||
return descError
|
return descError
|
||||||
}
|
}
|
||||||
|
@ -163,6 +169,7 @@ func convertPullHook(from *internal.PullRequestHook) *model.Build {
|
||||||
Message: from.PullRequest.Desc,
|
Message: from.PullRequest.Desc,
|
||||||
Avatar: from.Actor.Links.Avatar.Href,
|
Avatar: from.Actor.Links.Avatar.Href,
|
||||||
Author: from.Actor.Login,
|
Author: from.Actor.Login,
|
||||||
|
Sender: from.Actor.Login,
|
||||||
Timestamp: from.PullRequest.Updated.UTC().Unix(),
|
Timestamp: from.PullRequest.Updated.UTC().Unix(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -177,6 +184,7 @@ func convertPushHook(hook *internal.PushHook, change *internal.Change) *model.Bu
|
||||||
Message: change.New.Target.Message,
|
Message: change.New.Target.Message,
|
||||||
Avatar: hook.Actor.Links.Avatar.Href,
|
Avatar: hook.Actor.Links.Avatar.Href,
|
||||||
Author: hook.Actor.Login,
|
Author: hook.Actor.Login,
|
||||||
|
Sender: hook.Actor.Login,
|
||||||
Timestamp: change.New.Target.Date.UTC().Unix(),
|
Timestamp: change.New.Target.Date.UTC().Unix(),
|
||||||
}
|
}
|
||||||
switch change.New.Type {
|
switch change.New.Type {
|
||||||
|
@ -198,7 +206,7 @@ var reGitMail = regexp.MustCompile("<(.*)>")
|
||||||
|
|
||||||
// extracts the email from a git commit author string
|
// extracts the email from a git commit author string
|
||||||
func extractEmail(gitauthor string) (author string) {
|
func extractEmail(gitauthor string) (author string) {
|
||||||
matches := reGitMail.FindAllStringSubmatch(gitauthor,-1)
|
matches := reGitMail.FindAllStringSubmatch(gitauthor, -1)
|
||||||
if len(matches) == 1 {
|
if len(matches) == 1 {
|
||||||
author = matches[0][1]
|
author = matches[0][1]
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,14 +9,15 @@ import (
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/drone/drone/model"
|
|
||||||
"github.com/drone/drone/remote"
|
|
||||||
"github.com/drone/drone/remote/bitbucketserver/internal"
|
|
||||||
"github.com/mrjones/oauth"
|
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/drone/drone/model"
|
||||||
|
"github.com/drone/drone/remote"
|
||||||
|
"github.com/drone/drone/remote/bitbucketserver/internal"
|
||||||
|
"github.com/mrjones/oauth"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -164,6 +165,12 @@ func (c *Config) File(u *model.User, r *model.Repo, b *model.Build, f string) ([
|
||||||
return client.FindFileForRepo(r.Owner, r.Name, f, b.Ref)
|
return client.FindFileForRepo(r.Owner, r.Name, f, b.Ref)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Config) FileRef(u *model.User, r *model.Repo, ref, f string) ([]byte, error) {
|
||||||
|
client := internal.NewClientWithToken(c.URL, c.Consumer, u.Token)
|
||||||
|
|
||||||
|
return client.FindFileForRepo(r.Owner, r.Name, f, ref)
|
||||||
|
}
|
||||||
|
|
||||||
// Status is not supported by the bitbucketserver driver.
|
// Status is not supported by the bitbucketserver driver.
|
||||||
func (c *Config) Status(u *model.User, r *model.Repo, b *model.Build, link string) error {
|
func (c *Config) Status(u *model.User, r *model.Repo, b *model.Build, link string) error {
|
||||||
status := internal.BuildStatus{
|
status := internal.BuildStatus{
|
||||||
|
|
|
@ -94,6 +94,11 @@ func (c *client) File(u *model.User, r *model.Repo, b *model.Build, f string) ([
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// File is not supported by the Gerrit driver.
|
||||||
|
func (c *client) FileRef(u *model.User, r *model.Repo, ref, f string) ([]byte, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Status is not supported by the Gogs driver.
|
// Status is not supported by the Gogs driver.
|
||||||
func (c *client) Status(u *model.User, r *model.Repo, b *model.Build, link string) error {
|
func (c *client) Status(u *model.User, r *model.Repo, b *model.Build, link string) error {
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -22,6 +22,8 @@ const (
|
||||||
descPending = "this build is pending"
|
descPending = "this build is pending"
|
||||||
descSuccess = "the build was successful"
|
descSuccess = "the build was successful"
|
||||||
descFailure = "the build failed"
|
descFailure = "the build failed"
|
||||||
|
descBlocked = "the build requires approval"
|
||||||
|
descDeclined = "the build was rejected"
|
||||||
descError = "oops, something went wrong"
|
descError = "oops, something went wrong"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -35,12 +37,12 @@ const (
|
||||||
// GitHub commit status.
|
// GitHub commit status.
|
||||||
func convertStatus(status string) string {
|
func convertStatus(status string) string {
|
||||||
switch status {
|
switch status {
|
||||||
case model.StatusPending, model.StatusRunning:
|
case model.StatusPending, model.StatusRunning, model.StatusBlocked:
|
||||||
return statusPending
|
return statusPending
|
||||||
|
case model.StatusFailure, model.StatusDeclined:
|
||||||
|
return statusFailure
|
||||||
case model.StatusSuccess:
|
case model.StatusSuccess:
|
||||||
return statusSuccess
|
return statusSuccess
|
||||||
case model.StatusFailure:
|
|
||||||
return statusFailure
|
|
||||||
default:
|
default:
|
||||||
return statusError
|
return statusError
|
||||||
}
|
}
|
||||||
|
@ -56,6 +58,10 @@ func convertDesc(status string) string {
|
||||||
return descSuccess
|
return descSuccess
|
||||||
case model.StatusFailure:
|
case model.StatusFailure:
|
||||||
return descFailure
|
return descFailure
|
||||||
|
case model.StatusBlocked:
|
||||||
|
return descBlocked
|
||||||
|
case model.StatusDeclined:
|
||||||
|
return descDeclined
|
||||||
default:
|
default:
|
||||||
return descError
|
return descError
|
||||||
}
|
}
|
||||||
|
@ -185,6 +191,7 @@ func convertPushHook(from *webhook) *model.Build {
|
||||||
Avatar: from.Sender.Avatar,
|
Avatar: from.Sender.Avatar,
|
||||||
Author: from.Sender.Login,
|
Author: from.Sender.Login,
|
||||||
Remote: from.Repo.CloneURL,
|
Remote: from.Repo.CloneURL,
|
||||||
|
Sender: from.Sender.Login,
|
||||||
}
|
}
|
||||||
if len(build.Author) == 0 {
|
if len(build.Author) == 0 {
|
||||||
build.Author = from.Head.Author.Username
|
build.Author = from.Head.Author.Username
|
||||||
|
@ -213,6 +220,7 @@ func convertDeployHook(from *webhook) *model.Build {
|
||||||
Ref: from.Deployment.Ref,
|
Ref: from.Deployment.Ref,
|
||||||
Branch: from.Deployment.Ref,
|
Branch: from.Deployment.Ref,
|
||||||
Deploy: from.Deployment.Env,
|
Deploy: from.Deployment.Env,
|
||||||
|
Sender: from.Sender.Login,
|
||||||
}
|
}
|
||||||
// if the ref is a sha or short sha we need to manuallyconstruct the ref.
|
// if the ref is a sha or short sha we need to manuallyconstruct the ref.
|
||||||
if strings.HasPrefix(build.Commit, build.Ref) || build.Commit == build.Ref {
|
if strings.HasPrefix(build.Commit, build.Ref) || build.Commit == build.Ref {
|
||||||
|
@ -242,6 +250,7 @@ func convertPullHook(from *webhook, merge bool) *model.Build {
|
||||||
Author: from.PullRequest.User.Login,
|
Author: from.PullRequest.User.Login,
|
||||||
Avatar: from.PullRequest.User.Avatar,
|
Avatar: from.PullRequest.User.Avatar,
|
||||||
Title: from.PullRequest.Title,
|
Title: from.PullRequest.Title,
|
||||||
|
Sender: from.Sender.Login,
|
||||||
Remote: from.PullRequest.Head.Repo.CloneURL,
|
Remote: from.PullRequest.Head.Repo.CloneURL,
|
||||||
Refspec: fmt.Sprintf(refspec,
|
Refspec: fmt.Sprintf(refspec,
|
||||||
from.PullRequest.Head.Ref,
|
from.PullRequest.Head.Ref,
|
||||||
|
|
|
@ -181,6 +181,7 @@ func Test_helper(t *testing.T) {
|
||||||
from.PullRequest.Title = "Updated README.md"
|
from.PullRequest.Title = "Updated README.md"
|
||||||
from.PullRequest.User.Login = "octocat"
|
from.PullRequest.User.Login = "octocat"
|
||||||
from.PullRequest.User.Avatar = "https://avatars1.githubusercontent.com/u/583231"
|
from.PullRequest.User.Avatar = "https://avatars1.githubusercontent.com/u/583231"
|
||||||
|
from.Sender.Login = "octocat"
|
||||||
|
|
||||||
build := convertPullHook(from, true)
|
build := convertPullHook(from, true)
|
||||||
g.Assert(build.Event).Equal(model.EventPull)
|
g.Assert(build.Event).Equal(model.EventPull)
|
||||||
|
@ -193,6 +194,7 @@ func Test_helper(t *testing.T) {
|
||||||
g.Assert(build.Title).Equal(from.PullRequest.Title)
|
g.Assert(build.Title).Equal(from.PullRequest.Title)
|
||||||
g.Assert(build.Author).Equal(from.PullRequest.User.Login)
|
g.Assert(build.Author).Equal(from.PullRequest.User.Login)
|
||||||
g.Assert(build.Avatar).Equal(from.PullRequest.User.Avatar)
|
g.Assert(build.Avatar).Equal(from.PullRequest.User.Avatar)
|
||||||
|
g.Assert(build.Sender).Equal(from.Sender.Login)
|
||||||
})
|
})
|
||||||
|
|
||||||
g.It("should convert a deployment from webhook", func() {
|
g.It("should convert a deployment from webhook", func() {
|
||||||
|
|
|
@ -95,7 +95,7 @@ const HookPullRequest = `
|
||||||
"default_branch": "master"
|
"default_branch": "master"
|
||||||
},
|
},
|
||||||
"sender": {
|
"sender": {
|
||||||
"login": "baxterthehacker",
|
"login": "octocat",
|
||||||
"avatar_url": "https://avatars.githubusercontent.com/u/6752317?v=3"
|
"avatar_url": "https://avatars.githubusercontent.com/u/6752317?v=3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -219,12 +219,17 @@ func (c *client) Perm(u *model.User, owner, name string) (*model.Perm, error) {
|
||||||
return convertPerm(repo), nil
|
return convertPerm(repo), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// File fetches the file from the Bitbucket repository and returns its contents.
|
// File fetches the file from the GitHub repository and returns its contents.
|
||||||
func (c *client) File(u *model.User, r *model.Repo, b *model.Build, f string) ([]byte, error) {
|
func (c *client) File(u *model.User, r *model.Repo, b *model.Build, f string) ([]byte, error) {
|
||||||
|
return c.FileRef(u, r, b.Commit, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FileRef fetches the file from the GitHub repository and returns its contents.
|
||||||
|
func (c *client) FileRef(u *model.User, r *model.Repo, ref, f string) ([]byte, error) {
|
||||||
client := c.newClientToken(u.Token)
|
client := c.newClientToken(u.Token)
|
||||||
|
|
||||||
opts := new(github.RepositoryContentGetOptions)
|
opts := new(github.RepositoryContentGetOptions)
|
||||||
opts.Ref = b.Commit
|
opts.Ref = ref
|
||||||
data, _, _, err := client.Repositories.GetContents(r.Owner, r.Name, f, opts)
|
data, _, _, err := client.Repositories.GetContents(r.Owner, r.Name, f, opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -11,6 +11,7 @@ const (
|
||||||
projectsUrl = "/projects"
|
projectsUrl = "/projects"
|
||||||
projectUrl = "/projects/:id"
|
projectUrl = "/projects/:id"
|
||||||
repoUrlRawFile = "/projects/:id/repository/blobs/:sha"
|
repoUrlRawFile = "/projects/:id/repository/blobs/:sha"
|
||||||
|
repoUrlRawFileRef = "/projects/:id/repository/files"
|
||||||
commitStatusUrl = "/projects/:id/statuses/:sha"
|
commitStatusUrl = "/projects/:id/statuses/:sha"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -96,6 +97,23 @@ func (c *Client) RepoRawFile(id, sha, filepath string) ([]byte, error) {
|
||||||
return contents, err
|
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 {
|
func (c *Client) SetStatus(id, sha, state, desc, ref, link string) error {
|
||||||
url, opaque := c.ResourceUrl(
|
url, opaque := c.ResourceUrl(
|
||||||
|
|
|
@ -321,6 +321,21 @@ func (g *Gitlab) File(user *model.User, repo *model.Repo, build *model.Build, f
|
||||||
return out, err
|
return out, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FileRef fetches the file from the GitHub repository and returns its contents.
|
||||||
|
func (g *Gitlab) FileRef(u *model.User, r *model.Repo, ref, f string) ([]byte, error) {
|
||||||
|
var client = NewClient(g.URL, u.Token, g.SkipVerify)
|
||||||
|
id, err := GetProjectId(g, client, r.Owner, r.Name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
out, err := client.RepoRawFileRef(id, ref, f)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, err
|
||||||
|
}
|
||||||
|
|
||||||
// NOTE Currently gitlab doesn't support status for commits and events,
|
// 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,
|
// 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.
|
// gitlab uses API to fetch build status on client side. But for now we skip this.
|
||||||
|
@ -629,18 +644,20 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
DescPending = "this build is pending"
|
DescPending = "the build is pending"
|
||||||
DescRunning = "this buils is running"
|
DescRunning = "the buils is running"
|
||||||
DescSuccess = "the build was successful"
|
DescSuccess = "the build was successful"
|
||||||
DescFailure = "the build failed"
|
DescFailure = "the build failed"
|
||||||
DescCanceled = "the build canceled"
|
DescCanceled = "the build canceled"
|
||||||
|
DescBlocked = "the build is pending approval"
|
||||||
|
DescDeclined = "the build was rejected"
|
||||||
)
|
)
|
||||||
|
|
||||||
// getStatus is a helper functin that converts a Drone
|
// getStatus is a helper functin that converts a Drone
|
||||||
// status to a GitHub status.
|
// status to a GitHub status.
|
||||||
func getStatus(status string) string {
|
func getStatus(status string) string {
|
||||||
switch status {
|
switch status {
|
||||||
case model.StatusPending:
|
case model.StatusPending, model.StatusBlocked:
|
||||||
return StatusPending
|
return StatusPending
|
||||||
case model.StatusRunning:
|
case model.StatusRunning:
|
||||||
return StatusRunning
|
return StatusRunning
|
||||||
|
@ -669,6 +686,10 @@ func getDesc(status string) string {
|
||||||
return DescFailure
|
return DescFailure
|
||||||
case model.StatusKilled:
|
case model.StatusKilled:
|
||||||
return DescCanceled
|
return DescCanceled
|
||||||
|
case model.StatusBlocked:
|
||||||
|
return DescBlocked
|
||||||
|
case model.StatusDeclined:
|
||||||
|
return DescDeclined
|
||||||
default:
|
default:
|
||||||
return DescFailure
|
return DescFailure
|
||||||
}
|
}
|
||||||
|
|
|
@ -129,6 +129,7 @@ var HookPullRequest = `{
|
||||||
},
|
},
|
||||||
"sender": {
|
"sender": {
|
||||||
"id": 1,
|
"id": 1,
|
||||||
|
"login": "gordon",
|
||||||
"username": "gordon",
|
"username": "gordon",
|
||||||
"full_name": "Gordon the Gopher",
|
"full_name": "Gordon the Gopher",
|
||||||
"email": "gordon@golang.org",
|
"email": "gordon@golang.org",
|
||||||
|
|
|
@ -190,6 +190,11 @@ func (c *client) File(u *model.User, r *model.Repo, b *model.Build, f string) ([
|
||||||
return cfg, err
|
return cfg, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FileRef fetches the file from the Gogs repository and returns its contents.
|
||||||
|
func (c *client) FileRef(u *model.User, r *model.Repo, ref, f string) ([]byte, error) {
|
||||||
|
return c.newClientToken(u.Token).GetFile(r.Owner, r.Name, ref, f)
|
||||||
|
}
|
||||||
|
|
||||||
// Status is not supported by the Gogs driver.
|
// Status is not supported by the Gogs driver.
|
||||||
func (c *client) Status(u *model.User, r *model.Repo, b *model.Build, link string) error {
|
func (c *client) Status(u *model.User, r *model.Repo, b *model.Build, link string) error {
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -74,6 +74,10 @@ func buildFromPush(hook *pushHook) *model.Build {
|
||||||
if author == "" {
|
if author == "" {
|
||||||
author = hook.Sender.Username
|
author = hook.Sender.Username
|
||||||
}
|
}
|
||||||
|
sender := hook.Sender.Username
|
||||||
|
if sender == "" {
|
||||||
|
sender = hook.Sender.Login
|
||||||
|
}
|
||||||
|
|
||||||
return &model.Build{
|
return &model.Build{
|
||||||
Event: model.EventPush,
|
Event: model.EventPush,
|
||||||
|
@ -85,6 +89,7 @@ func buildFromPush(hook *pushHook) *model.Build {
|
||||||
Avatar: avatar,
|
Avatar: avatar,
|
||||||
Author: author,
|
Author: author,
|
||||||
Timestamp: time.Now().UTC().Unix(),
|
Timestamp: time.Now().UTC().Unix(),
|
||||||
|
Sender: sender,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,6 +103,10 @@ func buildFromTag(hook *pushHook) *model.Build {
|
||||||
if author == "" {
|
if author == "" {
|
||||||
author = hook.Sender.Username
|
author = hook.Sender.Username
|
||||||
}
|
}
|
||||||
|
sender := hook.Sender.Username
|
||||||
|
if sender == "" {
|
||||||
|
sender = hook.Sender.Login
|
||||||
|
}
|
||||||
|
|
||||||
return &model.Build{
|
return &model.Build{
|
||||||
Event: model.EventTag,
|
Event: model.EventTag,
|
||||||
|
@ -108,6 +117,7 @@ func buildFromTag(hook *pushHook) *model.Build {
|
||||||
Message: fmt.Sprintf("created tag %s", hook.Ref),
|
Message: fmt.Sprintf("created tag %s", hook.Ref),
|
||||||
Avatar: avatar,
|
Avatar: avatar,
|
||||||
Author: author,
|
Author: author,
|
||||||
|
Sender: sender,
|
||||||
Timestamp: time.Now().UTC().Unix(),
|
Timestamp: time.Now().UTC().Unix(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -118,6 +128,10 @@ func buildFromPullRequest(hook *pullRequestHook) *model.Build {
|
||||||
hook.Repo.URL,
|
hook.Repo.URL,
|
||||||
fixMalformedAvatar(hook.PullRequest.User.Avatar),
|
fixMalformedAvatar(hook.PullRequest.User.Avatar),
|
||||||
)
|
)
|
||||||
|
sender := hook.Sender.Username
|
||||||
|
if sender == "" {
|
||||||
|
sender = hook.Sender.Login
|
||||||
|
}
|
||||||
build := &model.Build{
|
build := &model.Build{
|
||||||
Event: model.EventPull,
|
Event: model.EventPull,
|
||||||
Commit: hook.PullRequest.Head.Sha,
|
Commit: hook.PullRequest.Head.Sha,
|
||||||
|
@ -127,6 +141,7 @@ func buildFromPullRequest(hook *pullRequestHook) *model.Build {
|
||||||
Message: hook.PullRequest.Title,
|
Message: hook.PullRequest.Title,
|
||||||
Author: hook.PullRequest.User.Username,
|
Author: hook.PullRequest.User.Username,
|
||||||
Avatar: avatar,
|
Avatar: avatar,
|
||||||
|
Sender: sender,
|
||||||
Title: hook.PullRequest.Title,
|
Title: hook.PullRequest.Title,
|
||||||
Refspec: fmt.Sprintf("%s:%s",
|
Refspec: fmt.Sprintf("%s:%s",
|
||||||
hook.PullRequest.Head.Ref,
|
hook.PullRequest.Head.Ref,
|
||||||
|
|
|
@ -116,6 +116,7 @@ type pullRequestHook struct {
|
||||||
} `json:"repository"`
|
} `json:"repository"`
|
||||||
Sender struct {
|
Sender struct {
|
||||||
ID int64 `json:"id"`
|
ID int64 `json:"id"`
|
||||||
|
Login string `json:"login"`
|
||||||
Username string `json:"username"`
|
Username string `json:"username"`
|
||||||
Name string `json:"full_name"`
|
Name string `json:"full_name"`
|
||||||
Email string `json:"email"`
|
Email string `json:"email"`
|
||||||
|
|
|
@ -7,7 +7,7 @@ import (
|
||||||
"github.com/stretchr/testify/mock"
|
"github.com/stretchr/testify/mock"
|
||||||
)
|
)
|
||||||
|
|
||||||
// This is an autogenerated mock type for the Remote type
|
// Remote is an autogenerated mock type for the Remote type
|
||||||
type Remote struct {
|
type Remote struct {
|
||||||
mock.Mock
|
mock.Mock
|
||||||
}
|
}
|
||||||
|
@ -84,6 +84,29 @@ func (_m *Remote) File(u *model.User, r *model.Repo, b *model.Build, f string) (
|
||||||
return r0, r1
|
return r0, r1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FileRef provides a mock function with given fields: u, r, ref, f
|
||||||
|
func (_m *Remote) FileRef(u *model.User, r *model.Repo, ref string, f string) ([]byte, error) {
|
||||||
|
ret := _m.Called(u, r, ref, f)
|
||||||
|
|
||||||
|
var r0 []byte
|
||||||
|
if rf, ok := ret.Get(0).(func(*model.User, *model.Repo, string, string) []byte); ok {
|
||||||
|
r0 = rf(u, r, ref, f)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).([]byte)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var r1 error
|
||||||
|
if rf, ok := ret.Get(1).(func(*model.User, *model.Repo, string, string) error); ok {
|
||||||
|
r1 = rf(u, r, ref, f)
|
||||||
|
} else {
|
||||||
|
r1 = ret.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0, r1
|
||||||
|
}
|
||||||
|
|
||||||
// Hook provides a mock function with given fields: r
|
// Hook provides a mock function with given fields: r
|
||||||
func (_m *Remote) Hook(r *http.Request) (*model.Repo, *model.Build, error) {
|
func (_m *Remote) Hook(r *http.Request) (*model.Repo, *model.Build, error) {
|
||||||
ret := _m.Called(r)
|
ret := _m.Called(r)
|
||||||
|
@ -245,29 +268,6 @@ func (_m *Remote) Status(u *model.User, r *model.Repo, b *model.Build, link stri
|
||||||
return r0
|
return r0
|
||||||
}
|
}
|
||||||
|
|
||||||
// Teams provides a mock function with given fields: u
|
|
||||||
func (_m *Remote) Teams(u *model.User) ([]*model.Team, error) {
|
|
||||||
ret := _m.Called(u)
|
|
||||||
|
|
||||||
var r0 []*model.Team
|
|
||||||
if rf, ok := ret.Get(0).(func(*model.User) []*model.Team); ok {
|
|
||||||
r0 = rf(u)
|
|
||||||
} else {
|
|
||||||
if ret.Get(0) != nil {
|
|
||||||
r0 = ret.Get(0).([]*model.Team)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var r1 error
|
|
||||||
if rf, ok := ret.Get(1).(func(*model.User) error); ok {
|
|
||||||
r1 = rf(u)
|
|
||||||
} else {
|
|
||||||
r1 = ret.Error(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
return r0, r1
|
|
||||||
}
|
|
||||||
|
|
||||||
// TeamPerm provides a mock function with given fields: u, org
|
// TeamPerm provides a mock function with given fields: u, org
|
||||||
func (_m *Remote) TeamPerm(u *model.User, org string) (*model.Perm, error) {
|
func (_m *Remote) TeamPerm(u *model.User, org string) (*model.Perm, error) {
|
||||||
ret := _m.Called(u, org)
|
ret := _m.Called(u, org)
|
||||||
|
@ -290,3 +290,26 @@ func (_m *Remote) TeamPerm(u *model.User, org string) (*model.Perm, error) {
|
||||||
|
|
||||||
return r0, r1
|
return r0, r1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Teams provides a mock function with given fields: u
|
||||||
|
func (_m *Remote) Teams(u *model.User) ([]*model.Team, error) {
|
||||||
|
ret := _m.Called(u)
|
||||||
|
|
||||||
|
var r0 []*model.Team
|
||||||
|
if rf, ok := ret.Get(0).(func(*model.User) []*model.Team); ok {
|
||||||
|
r0 = rf(u)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).([]*model.Team)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var r1 error
|
||||||
|
if rf, ok := ret.Get(1).(func(*model.User) error); ok {
|
||||||
|
r1 = rf(u)
|
||||||
|
} else {
|
||||||
|
r1 = ret.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0, r1
|
||||||
|
}
|
||||||
|
|
|
@ -41,6 +41,10 @@ type Remote interface {
|
||||||
// format.
|
// format.
|
||||||
File(u *model.User, r *model.Repo, b *model.Build, f string) ([]byte, error)
|
File(u *model.User, r *model.Repo, b *model.Build, f string) ([]byte, error)
|
||||||
|
|
||||||
|
// FileRef fetches a file from the remote repository for the given ref
|
||||||
|
// and returns in string format.
|
||||||
|
FileRef(u *model.User, r *model.Repo, ref, f string) ([]byte, error)
|
||||||
|
|
||||||
// Status sends the commit status to the remote system.
|
// Status sends the commit status to the remote system.
|
||||||
// An example would be the GitHub pull request status.
|
// An example would be the GitHub pull request status.
|
||||||
Status(u *model.User, r *model.Repo, b *model.Build, link string) error
|
Status(u *model.User, r *model.Repo, b *model.Build, link string) error
|
||||||
|
@ -109,12 +113,12 @@ func Perm(c context.Context, u *model.User, owner, repo string) (*model.Perm, er
|
||||||
|
|
||||||
// File fetches a file from the remote repository and returns in string format.
|
// File fetches a file from the remote repository and returns in string format.
|
||||||
func File(c context.Context, u *model.User, r *model.Repo, b *model.Build, f string) (out []byte, err error) {
|
func File(c context.Context, u *model.User, r *model.Repo, b *model.Build, f string) (out []byte, err error) {
|
||||||
for i:=0;i<5;i++ {
|
for i := 0; i < 5; i++ {
|
||||||
out, err = FromContext(c).File(u, r, b, f)
|
out, err = FromContext(c).File(u, r, b, f)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
time.Sleep(1*time.Second)
|
time.Sleep(1 * time.Second)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -108,6 +108,8 @@ func Load(middleware ...gin.HandlerFunc) http.Handler {
|
||||||
repo.POST("/chown", session.MustRepoAdmin(), server.ChownRepo)
|
repo.POST("/chown", session.MustRepoAdmin(), server.ChownRepo)
|
||||||
|
|
||||||
repo.POST("/builds/:number", session.MustPush, server.PostBuild)
|
repo.POST("/builds/:number", session.MustPush, server.PostBuild)
|
||||||
|
repo.POST("/builds/:number/approve", session.MustPush, server.PostApproval)
|
||||||
|
repo.POST("/builds/:number/decline", session.MustPush, server.PostDecline)
|
||||||
repo.DELETE("/builds/:number/:job", session.MustPush, server.DeleteBuild)
|
repo.DELETE("/builds/:number/:job", session.MustPush, server.DeleteBuild)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
186
server/build.go
186
server/build.go
|
@ -10,7 +10,7 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
log "github.com/Sirupsen/logrus"
|
"github.com/Sirupsen/logrus"
|
||||||
"github.com/cncd/pipeline/pipeline/rpc"
|
"github.com/cncd/pipeline/pipeline/rpc"
|
||||||
"github.com/cncd/pubsub"
|
"github.com/cncd/pubsub"
|
||||||
"github.com/cncd/queue"
|
"github.com/cncd/queue"
|
||||||
|
@ -156,6 +156,178 @@ func DeleteBuild(c *gin.Context) {
|
||||||
c.String(204, "")
|
c.String(204, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func PostApproval(c *gin.Context) {
|
||||||
|
var (
|
||||||
|
remote_ = remote.FromContext(c)
|
||||||
|
repo = session.Repo(c)
|
||||||
|
user = session.User(c)
|
||||||
|
num, _ = strconv.Atoi(
|
||||||
|
c.Params.ByName("number"),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
build, err := store.GetBuildNumber(c, repo, num)
|
||||||
|
if err != nil {
|
||||||
|
c.AbortWithError(404, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if build.Status != model.StatusBlocked {
|
||||||
|
c.String(500, "cannot decline a build with status %s", build.Status)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
build.Status = model.StatusPending
|
||||||
|
build.Reviewed = time.Now().Unix()
|
||||||
|
build.Reviewer = user.Login
|
||||||
|
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// This code is copied pasted until I have a chance
|
||||||
|
// to refactor into a proper function. Lots of changes
|
||||||
|
// and technical debt. No judgement please!
|
||||||
|
//
|
||||||
|
//
|
||||||
|
|
||||||
|
// fetch the build file from the database
|
||||||
|
cfg := ToConfig(c)
|
||||||
|
raw, err := remote_.File(user, repo, build, cfg.Yaml)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Errorf("failure to get build config for %s. %s", repo.FullName, err)
|
||||||
|
c.AbortWithError(404, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
netrc, err := remote_.Netrc(user, repo)
|
||||||
|
if err != nil {
|
||||||
|
c.String(500, "Failed to generate netrc file. %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if uerr := store.UpdateBuild(c, build); err != nil {
|
||||||
|
c.String(500, "error updating build. %s", uerr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(200, build)
|
||||||
|
|
||||||
|
// get the previous build so that we can send
|
||||||
|
// on status change notifications
|
||||||
|
last, _ := store.GetBuildLastBefore(c, repo, build.Branch, build.ID)
|
||||||
|
secs, err := store.GetMergedSecretList(c, repo)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Debugf("Error getting secrets for %s#%d. %s", repo.FullName, build.Number, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
uri := fmt.Sprintf("%s/%s/%d", httputil.GetURL(c.Request), repo.FullName, build.Number)
|
||||||
|
err = remote_.Status(user, repo, build, uri)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Errorf("error setting commit status for %s/%d", repo.FullName, build.Number)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
b := builder{
|
||||||
|
Repo: repo,
|
||||||
|
Curr: build,
|
||||||
|
Last: last,
|
||||||
|
Netrc: netrc,
|
||||||
|
Secs: secs,
|
||||||
|
Link: httputil.GetURL(c.Request),
|
||||||
|
Yaml: string(raw),
|
||||||
|
}
|
||||||
|
items, err := b.Build()
|
||||||
|
if err != nil {
|
||||||
|
build.Status = model.StatusError
|
||||||
|
build.Started = time.Now().Unix()
|
||||||
|
build.Finished = build.Started
|
||||||
|
build.Error = err.Error()
|
||||||
|
store.UpdateBuild(c, build)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, item := range items {
|
||||||
|
build.Jobs = append(build.Jobs, item.Job)
|
||||||
|
store.CreateJob(c, item.Job)
|
||||||
|
// TODO err
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// publish topic
|
||||||
|
//
|
||||||
|
message := pubsub.Message{
|
||||||
|
Labels: map[string]string{
|
||||||
|
"repo": repo.FullName,
|
||||||
|
"private": strconv.FormatBool(repo.IsPrivate),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
message.Data, _ = json.Marshal(model.Event{
|
||||||
|
Type: model.Enqueued,
|
||||||
|
Repo: *repo,
|
||||||
|
Build: *build,
|
||||||
|
})
|
||||||
|
// TODO remove global reference
|
||||||
|
config.pubsub.Publish(c, "topic/events", message)
|
||||||
|
//
|
||||||
|
// end publish topic
|
||||||
|
//
|
||||||
|
|
||||||
|
for _, item := range items {
|
||||||
|
task := new(queue.Task)
|
||||||
|
task.ID = fmt.Sprint(item.Job.ID)
|
||||||
|
task.Labels = map[string]string{}
|
||||||
|
task.Labels["platform"] = item.Platform
|
||||||
|
for k, v := range item.Labels {
|
||||||
|
task.Labels[k] = v
|
||||||
|
}
|
||||||
|
|
||||||
|
task.Data, _ = json.Marshal(rpc.Pipeline{
|
||||||
|
ID: fmt.Sprint(item.Job.ID),
|
||||||
|
Config: item.Config,
|
||||||
|
Timeout: b.Repo.Timeout,
|
||||||
|
})
|
||||||
|
|
||||||
|
config.logger.Open(context.Background(), task.ID)
|
||||||
|
config.queue.Push(context.Background(), task)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func PostDecline(c *gin.Context) {
|
||||||
|
var (
|
||||||
|
remote_ = remote.FromContext(c)
|
||||||
|
repo = session.Repo(c)
|
||||||
|
user = session.User(c)
|
||||||
|
num, _ = strconv.Atoi(
|
||||||
|
c.Params.ByName("number"),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
build, err := store.GetBuildNumber(c, repo, num)
|
||||||
|
if err != nil {
|
||||||
|
c.AbortWithError(404, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if build.Status != model.StatusBlocked {
|
||||||
|
c.String(500, "cannot decline a build with status %s", build.Status)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
build.Status = model.StatusDeclined
|
||||||
|
build.Reviewed = time.Now().Unix()
|
||||||
|
build.Reviewer = user.Login
|
||||||
|
|
||||||
|
err = store.UpdateBuild(c, build)
|
||||||
|
if err != nil {
|
||||||
|
c.String(500, "error updating build. %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
uri := fmt.Sprintf("%s/%s/%d", httputil.GetURL(c.Request), repo.FullName, build.Number)
|
||||||
|
err = remote_.Status(user, repo, build, uri)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Errorf("error setting commit status for %s/%d", repo.FullName, build.Number)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(200, build)
|
||||||
|
}
|
||||||
|
|
||||||
func GetBuildQueue(c *gin.Context) {
|
func GetBuildQueue(c *gin.Context) {
|
||||||
out, err := store.GetBuildQueue(c)
|
out, err := store.GetBuildQueue(c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -203,14 +375,14 @@ func PostBuild(c *gin.Context) {
|
||||||
|
|
||||||
user, err := store.GetUser(c, repo.UserID)
|
user, err := store.GetUser(c, repo.UserID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("failure to find repo owner %s. %s", repo.FullName, err)
|
logrus.Errorf("failure to find repo owner %s. %s", repo.FullName, err)
|
||||||
c.AbortWithError(500, err)
|
c.AbortWithError(500, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
build, err := store.GetBuildNumber(c, repo, num)
|
build, err := store.GetBuildNumber(c, repo, num)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("failure to get build %d. %s", num, err)
|
logrus.Errorf("failure to get build %d. %s", num, err)
|
||||||
c.AbortWithError(404, err)
|
c.AbortWithError(404, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -229,21 +401,21 @@ func PostBuild(c *gin.Context) {
|
||||||
cfg := ToConfig(c)
|
cfg := ToConfig(c)
|
||||||
raw, err := remote_.File(user, repo, build, cfg.Yaml)
|
raw, err := remote_.File(user, repo, build, cfg.Yaml)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("failure to get build config for %s. %s", repo.FullName, err)
|
logrus.Errorf("failure to get build config for %s. %s", repo.FullName, err)
|
||||||
c.AbortWithError(404, err)
|
c.AbortWithError(404, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
netrc, err := remote_.Netrc(user, repo)
|
netrc, err := remote_.Netrc(user, repo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("failure to generate netrc for %s. %s", repo.FullName, err)
|
logrus.Errorf("failure to generate netrc for %s. %s", repo.FullName, err)
|
||||||
c.AbortWithError(500, err)
|
c.AbortWithError(500, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
jobs, err := store.GetJobList(c, build)
|
jobs, err := store.GetJobList(c, build)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("failure to get build %d jobs. %s", build.Number, err)
|
logrus.Errorf("failure to get build %d jobs. %s", build.Number, err)
|
||||||
c.AbortWithError(404, err)
|
c.AbortWithError(404, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -326,7 +498,7 @@ func PostBuild(c *gin.Context) {
|
||||||
last, _ := store.GetBuildLastBefore(c, repo, build.Branch, build.ID)
|
last, _ := store.GetBuildLastBefore(c, repo, build.Branch, build.ID)
|
||||||
secs, err := store.GetMergedSecretList(c, repo)
|
secs, err := store.GetMergedSecretList(c, repo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debugf("Error getting secrets for %s#%d. %s", repo.FullName, build.Number, err)
|
logrus.Debugf("Error getting secrets for %s#%d. %s", repo.FullName, build.Number, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
b := builder{
|
b := builder{
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -9,7 +10,6 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/square/go-jose"
|
|
||||||
|
|
||||||
"github.com/Sirupsen/logrus"
|
"github.com/Sirupsen/logrus"
|
||||||
"github.com/drone/drone/model"
|
"github.com/drone/drone/model"
|
||||||
|
@ -139,10 +139,6 @@ func PostHook(c *gin.Context) {
|
||||||
c.AbortWithError(404, err)
|
c.AbortWithError(404, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
sec, err := remote_.File(user, repo, build, cfg.Shasum)
|
|
||||||
if err != nil {
|
|
||||||
logrus.Debugf("cannot find yaml signature for %s. %s", repo.FullName, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
netrc, err := remote_.Netrc(user, repo)
|
netrc, err := remote_.Netrc(user, repo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -159,26 +155,47 @@ func PostHook(c *gin.Context) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
signature, err := jose.ParseSigned(string(sec))
|
// TODO default logic should avoid the approval if all
|
||||||
if err != nil {
|
// secrets have skip-verify flag
|
||||||
logrus.Debugf("cannot parse .drone.yml.sig file. %s", err)
|
|
||||||
} else if len(sec) == 0 {
|
if build.Event == model.EventPull {
|
||||||
logrus.Debugf("cannot parse .drone.yml.sig file. empty file")
|
old, ferr := remote_.FileRef(user, repo, build.Ref, cfg.Yaml)
|
||||||
|
if ferr != nil {
|
||||||
|
build.Status = model.StatusBlocked
|
||||||
|
} else if bytes.Equal(old, raw) {
|
||||||
|
build.Status = model.StatusPending
|
||||||
} else {
|
} else {
|
||||||
build.Signed = true
|
// this block is executed if the target yaml file
|
||||||
output, verr := signature.Verify([]byte(repo.Hash))
|
// does not match the base yaml.
|
||||||
if verr != nil {
|
|
||||||
logrus.Debugf("cannot verify .drone.yml.sig file. %s", verr)
|
// TODO unfortunately we have no good way to get the
|
||||||
} else if string(output) != string(raw) {
|
// sender repository permissions unless the user is
|
||||||
logrus.Debugf("cannot verify .drone.yml.sig file. no match")
|
// a registered drone user.
|
||||||
|
sender, uerr := store.GetUserLogin(c, build.Sender)
|
||||||
|
if uerr != nil {
|
||||||
|
build.Status = model.StatusBlocked
|
||||||
} else {
|
} else {
|
||||||
build.Verified = true
|
if refresher, ok := remote_.(remote.Refresher); ok {
|
||||||
|
ok, _ := refresher.Refresh(sender)
|
||||||
|
if ok {
|
||||||
|
store.UpdateUser(c, sender)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// if the sender does not have push access to the
|
||||||
|
// repository the pull request should be blocked.
|
||||||
|
perm, perr := remote_.Perm(sender, repo.Owner, repo.Name)
|
||||||
|
if perr != nil || perm.Push == false {
|
||||||
|
build.Status = model.StatusBlocked
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
build.Status = model.StatusPending
|
||||||
|
}
|
||||||
|
|
||||||
// update some build fields
|
// update some build fields
|
||||||
build.Status = model.StatusPending
|
|
||||||
build.RepoID = repo.ID
|
build.RepoID = repo.ID
|
||||||
|
build.Verified = true
|
||||||
|
|
||||||
if err := store.CreateBuild(c, build, build.Jobs...); err != nil {
|
if err := store.CreateBuild(c, build, build.Jobs...); err != nil {
|
||||||
logrus.Errorf("failure to save commit for %s. %s", repo.FullName, err)
|
logrus.Errorf("failure to save commit for %s. %s", repo.FullName, err)
|
||||||
|
@ -188,6 +205,10 @@ func PostHook(c *gin.Context) {
|
||||||
|
|
||||||
c.JSON(200, build)
|
c.JSON(200, build)
|
||||||
|
|
||||||
|
if build.Status == model.StatusBlocked {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// get the previous build so that we can send
|
// get the previous build so that we can send
|
||||||
// on status change notifications
|
// on status change notifications
|
||||||
last, _ := store.GetBuildLastBefore(c, repo, build.Branch, build.ID)
|
last, _ := store.GetBuildLastBefore(c, repo, build.Branch, build.ID)
|
||||||
|
|
18
store/datastore/ddl/mysql/12.sql
Normal file
18
store/datastore/ddl/mysql/12.sql
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
-- +migrate Up
|
||||||
|
|
||||||
|
ALTER TABLE repos ADD COLUMN repo_config_path VARCHAR(255);
|
||||||
|
ALTER TABLE builds ADD COLUMN build_sender VARCHAR(255);
|
||||||
|
ALTER TABLE builds ADD COLUMN build_reviewer VARCHAR(255);
|
||||||
|
ALTER TABLE builds ADD COLUMN build_reviewed INTEGER;
|
||||||
|
|
||||||
|
UPDATE repos SET repo_config_path = '';
|
||||||
|
UPDATE builds SET build_reviewer = '';
|
||||||
|
UPDATE builds SET build_reviewed = 0;
|
||||||
|
UPDATE builds SET build_sender = '';
|
||||||
|
|
||||||
|
-- +migrate Down
|
||||||
|
|
||||||
|
ALTER TABLE repos DROP COLUMN repo_config_path;
|
||||||
|
ALTER TABLE builds DROP COLUMN build_sender;
|
||||||
|
ALTER TABLE builds DROP COLUMN build_reviewer;
|
||||||
|
ALTER TABLE builds DROP COLUMN build_reviewed;
|
18
store/datastore/ddl/postgres/12.sql
Normal file
18
store/datastore/ddl/postgres/12.sql
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
-- +migrate Up
|
||||||
|
|
||||||
|
ALTER TABLE repos ADD COLUMN repo_config_path VARCHAR(255);
|
||||||
|
ALTER TABLE builds ADD COLUMN build_reviewer VARCHAR(255);
|
||||||
|
ALTER TABLE builds ADD COLUMN build_reviewed INTEGER;
|
||||||
|
ALTER TABLE builds ADD COLUMN build_sender VARCHAR(255);
|
||||||
|
|
||||||
|
UPDATE repos SET repo_config_path = '';
|
||||||
|
UPDATE builds SET build_reviewer = '';
|
||||||
|
UPDATE builds SET build_reviewed = 0;
|
||||||
|
UPDATE builds SET build_sender = '';
|
||||||
|
|
||||||
|
-- +migrate Down
|
||||||
|
|
||||||
|
ALTER TABLE repos DROP COLUMN repo_config_path;
|
||||||
|
ALTER TABLE builds DROP COLUMN build_reviewer;
|
||||||
|
ALTER TABLE builds DROP COLUMN build_reviewed;
|
||||||
|
ALTER TABLE builds DROP COLUMN build_sender;
|
18
store/datastore/ddl/sqlite3/12.sql
Normal file
18
store/datastore/ddl/sqlite3/12.sql
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
-- +migrate Up
|
||||||
|
|
||||||
|
ALTER TABLE repos ADD COLUMN repo_config_path TEXT;
|
||||||
|
ALTER TABLE builds ADD COLUMN build_reviewer TEXT;
|
||||||
|
ALTER TABLE builds ADD COLUMN build_reviewed INTEGER;
|
||||||
|
ALTER TABLE builds ADD COLUMN build_sender TEXT;
|
||||||
|
|
||||||
|
UPDATE repos SET repo_config_path = '';
|
||||||
|
UPDATE builds SET build_reviewer = '';
|
||||||
|
UPDATE builds SET build_reviewed = 0;
|
||||||
|
UPDATE builds SET build_sender = '';
|
||||||
|
|
||||||
|
-- +migrate Down
|
||||||
|
|
||||||
|
ALTER TABLE repos DROP COLUMN repo_config_path;
|
||||||
|
ALTER TABLE builds DROP COLUMN build_reviewer;
|
||||||
|
ALTER TABLE builds DROP COLUMN build_reviewed;
|
||||||
|
ALTER TABLE builds DROP COLUMN build_sender;
|
Loading…
Reference in a new issue