refactoring input and configuration
This commit is contained in:
parent
4d4003a9a1
commit
082570fb5b
34 changed files with 1784 additions and 1345 deletions
43
api/repo.go
43
api/repo.go
|
@ -1,13 +1,11 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"gopkg.in/yaml.v2"
|
||||
|
||||
"github.com/drone/drone/cache"
|
||||
"github.com/drone/drone/model"
|
||||
|
@ -210,44 +208,3 @@ func DeleteRepo(c *gin.Context) {
|
|||
remote.Deactivate(user, repo, httputil.GetURL(c.Request))
|
||||
c.Writer.WriteHeader(http.StatusOK)
|
||||
}
|
||||
|
||||
func PostSecure(c *gin.Context) {
|
||||
repo := session.Repo(c)
|
||||
|
||||
in, err := ioutil.ReadAll(c.Request.Body)
|
||||
if err != nil {
|
||||
c.AbortWithError(http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
|
||||
// we found some strange characters included in
|
||||
// the yaml file when entered into a browser textarea.
|
||||
// these need to be removed
|
||||
in = bytes.Replace(in, []byte{'\xA0'}, []byte{' '}, -1)
|
||||
|
||||
// make sure the Yaml is valid format to prevent
|
||||
// a malformed value from being used in the build
|
||||
err = yaml.Unmarshal(in, &yaml.MapSlice{})
|
||||
if err != nil {
|
||||
c.String(http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
key, err := store.GetKey(c, repo)
|
||||
if err != nil {
|
||||
c.AbortWithError(http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
|
||||
// encrypts using go-jose
|
||||
out, err := crypto.Encrypt(string(in), key.Private)
|
||||
if err != nil {
|
||||
c.AbortWithError(http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
c.String(http.StatusOK, out)
|
||||
}
|
||||
|
||||
func PostReactivate(c *gin.Context) {
|
||||
|
||||
}
|
||||
|
|
|
@ -141,9 +141,9 @@ func start(c *cli.Context) {
|
|||
c.String("drone-token"),
|
||||
)
|
||||
|
||||
tls, _ := dockerclient.TLSConfigFromCertPath(c.String("docker-cert-path"))
|
||||
if c.Bool("docker-host") {
|
||||
tls.InsecureSkipVerify = true
|
||||
tls, err := dockerclient.TLSConfigFromCertPath(c.String("docker-cert-path"))
|
||||
if err == nil {
|
||||
tls.InsecureSkipVerify = c.Bool("docker-tls-verify")
|
||||
}
|
||||
docker, err := dockerclient.NewDockerClient(c.String("docker-host"), tls)
|
||||
if err != nil {
|
||||
|
|
457
drone/daemon.go
Normal file
457
drone/daemon.go
Normal file
|
@ -0,0 +1,457 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/drone/drone/bus"
|
||||
"github.com/drone/drone/cache"
|
||||
"github.com/drone/drone/queue"
|
||||
"github.com/drone/drone/remote"
|
||||
"github.com/drone/drone/remote/bitbucket"
|
||||
"github.com/drone/drone/remote/bitbucketserver"
|
||||
"github.com/drone/drone/remote/github"
|
||||
"github.com/drone/drone/remote/gitlab"
|
||||
"github.com/drone/drone/remote/gogs"
|
||||
"github.com/drone/drone/server"
|
||||
"github.com/drone/drone/shared/token"
|
||||
"github.com/drone/drone/store"
|
||||
"github.com/drone/drone/store/datastore"
|
||||
"github.com/drone/drone/stream"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/codegangsta/cli"
|
||||
)
|
||||
|
||||
// DaemonCmd is the exported command for starting the drone server daemon.
|
||||
var DaemonCmd = cli.Command{
|
||||
Name: "daemon",
|
||||
Usage: "starts the drone server daemon",
|
||||
Action: func(c *cli.Context) {
|
||||
if err := start(c); err != nil {
|
||||
logrus.Fatal(err)
|
||||
}
|
||||
},
|
||||
Flags: []cli.Flag{
|
||||
cli.BoolFlag{
|
||||
EnvVar: "DRONE_DEBUG",
|
||||
Name: "debug",
|
||||
Usage: "start the server in debug mode",
|
||||
},
|
||||
cli.StringFlag{
|
||||
EnvVar: "DRONE_SERVER_ADDR",
|
||||
Name: "server-addr",
|
||||
Usage: "server address",
|
||||
Value: ":8000",
|
||||
},
|
||||
cli.StringFlag{
|
||||
EnvVar: "DRONE_SERVER_CERT",
|
||||
Name: "server-cert",
|
||||
Usage: "server ssl cert",
|
||||
},
|
||||
cli.StringFlag{
|
||||
EnvVar: "DRONE_SERVER_KEY",
|
||||
Name: "server-key",
|
||||
Usage: "server ssl key",
|
||||
},
|
||||
cli.StringSliceFlag{
|
||||
EnvVar: "DRONE_ADMIN",
|
||||
Name: "admin",
|
||||
Usage: "list of admin users",
|
||||
},
|
||||
cli.StringSliceFlag{
|
||||
EnvVar: "DRONE_ORGS",
|
||||
Name: "orgs",
|
||||
Usage: "list of approved organizations",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
EnvVar: "DRONE_OPEN",
|
||||
Name: "open",
|
||||
Usage: "open user registration",
|
||||
},
|
||||
cli.StringFlag{
|
||||
EnvVar: "DRONE_YAML",
|
||||
Name: "yaml",
|
||||
Usage: "build configuraton file name",
|
||||
Value: ".drone.yml",
|
||||
},
|
||||
cli.DurationFlag{
|
||||
EnvVar: "DRONE_CACHE_TTY",
|
||||
Name: "cache-tty",
|
||||
Usage: "cache duration",
|
||||
Value: time.Minute * 15,
|
||||
},
|
||||
cli.StringFlag{
|
||||
EnvVar: "DRONE_AGENT_SECRET",
|
||||
Name: "agent-secret",
|
||||
Usage: "agent secret passcode",
|
||||
},
|
||||
cli.StringFlag{
|
||||
EnvVar: "DRONE_DATABASE_DRIVER,DATABASE_DRIVER",
|
||||
Name: "driver",
|
||||
Usage: "database driver",
|
||||
Value: "sqite3",
|
||||
},
|
||||
cli.StringFlag{
|
||||
EnvVar: "DRONE_DATABASE_DATASOURCE,DATABASE_CONFIG",
|
||||
Name: "datasource",
|
||||
Usage: "database driver configuration string",
|
||||
Value: "drone.sqlite",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
EnvVar: "DRONE_GITHUB",
|
||||
Name: "github",
|
||||
Usage: "github driver is enabled",
|
||||
},
|
||||
cli.StringFlag{
|
||||
EnvVar: "DRONE_GITHUB_URL",
|
||||
Name: "github-server",
|
||||
Usage: "github server address",
|
||||
Value: "https://github.com",
|
||||
},
|
||||
cli.StringFlag{
|
||||
EnvVar: "DRONE_GITHUB_CLIENT",
|
||||
Name: "github-client",
|
||||
Usage: "github oauth2 client id",
|
||||
},
|
||||
cli.StringFlag{
|
||||
EnvVar: "DRONE_GITHUB_SECRET",
|
||||
Name: "github-sercret",
|
||||
Usage: "github oauth2 client secret",
|
||||
},
|
||||
cli.StringSliceFlag{
|
||||
EnvVar: "DRONE_GITHUB_SCOPE",
|
||||
Name: "github-scope",
|
||||
Usage: "github oauth scope",
|
||||
Value: &cli.StringSlice{
|
||||
"repo",
|
||||
"repo:status",
|
||||
"user:email",
|
||||
"read:org",
|
||||
},
|
||||
},
|
||||
cli.BoolTFlag{
|
||||
EnvVar: "DRONE_GITHUB_MERGE_REF",
|
||||
Name: "github-merge-ref",
|
||||
Usage: "github pull requests use merge ref",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
EnvVar: "DRONE_GITHUB_PRIVATE_MODE",
|
||||
Name: "github-private-mode",
|
||||
Usage: "github is running in private mode",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
EnvVar: "DRONE_GITHUB_SKIP_VERIFY",
|
||||
Name: "github-skip-verify",
|
||||
Usage: "github skip ssl verification",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
EnvVar: "DRONE_GOGS",
|
||||
Name: "gogs",
|
||||
Usage: "gogs driver is enabled",
|
||||
},
|
||||
cli.StringFlag{
|
||||
EnvVar: "DRONE_GOGS_URL",
|
||||
Name: "gogs-server",
|
||||
Usage: "gogs server address",
|
||||
Value: "https://github.com",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
EnvVar: "DRONE_GOGS_PRIVATE_MODE",
|
||||
Name: "gogs-private-mode",
|
||||
Usage: "gogs private mode enabled",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
EnvVar: "DRONE_GOGS_SKIP_VERIFY",
|
||||
Name: "gogs-skip-verify",
|
||||
Usage: "gogs skip ssl verification",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
EnvVar: "DRONE_BITBUCKET",
|
||||
Name: "bitbucket",
|
||||
Usage: "bitbucket driver is enabled",
|
||||
},
|
||||
cli.StringFlag{
|
||||
EnvVar: "DRONE_BITBUCKET_CLIENT",
|
||||
Name: "bitbucket-client",
|
||||
Usage: "bitbucket oauth2 client id",
|
||||
},
|
||||
cli.StringFlag{
|
||||
EnvVar: "DRONE_BITBUCKET_SECRET",
|
||||
Name: "bitbucket-secret",
|
||||
Usage: "bitbucket oauth2 client secret",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
EnvVar: "DRONE_GITLAB",
|
||||
Name: "gitlab",
|
||||
Usage: "gitlab driver is enabled",
|
||||
},
|
||||
cli.StringFlag{
|
||||
EnvVar: "DRONE_GITLAB_URL",
|
||||
Name: "gitlab-server",
|
||||
Usage: "gitlab server address",
|
||||
Value: "https://gitlab.com",
|
||||
},
|
||||
cli.StringFlag{
|
||||
EnvVar: "DRONE_GITLAB_CLIENT",
|
||||
Name: "gitlab-client",
|
||||
Usage: "gitlab oauth2 client id",
|
||||
},
|
||||
cli.StringFlag{
|
||||
EnvVar: "DRONE_GITLAB_SECRET",
|
||||
Name: "gitlab-sercret",
|
||||
Usage: "gitlab oauth2 client secret",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
EnvVar: "DRONE_GITLAB_SKIP_VERIFY",
|
||||
Name: "gitlab-skip-verify",
|
||||
Usage: "gitlab skip ssl verification",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
EnvVar: "DRONE_GITLAB_PRIVATE_MODE",
|
||||
Name: "gitlab-private-mode",
|
||||
Usage: "gitlab is running in private mode",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
EnvVar: "DRONE_STASH",
|
||||
Name: "stash",
|
||||
Usage: "stash driver is enabled",
|
||||
},
|
||||
cli.StringFlag{
|
||||
EnvVar: "DRONE_STASH_URL",
|
||||
Name: "stash-server",
|
||||
Usage: "stash server address",
|
||||
},
|
||||
cli.StringFlag{
|
||||
EnvVar: "DRONE_STASH_CONSUMER_KEY",
|
||||
Name: "stash-consumer-key",
|
||||
Usage: "stash oauth1 consumer key",
|
||||
},
|
||||
cli.StringFlag{
|
||||
EnvVar: "DRONE_STASH_CONSUMER_RSA",
|
||||
Name: "stash-consumer-rsa",
|
||||
Usage: "stash oauth1 private key file",
|
||||
},
|
||||
cli.StringFlag{
|
||||
EnvVar: "DRONE_STASH_GIT_USERNAME",
|
||||
Name: "stash-git-username",
|
||||
Usage: "stash service account username",
|
||||
},
|
||||
cli.StringFlag{
|
||||
EnvVar: "DRONE_STASH_GIT_PASSWORD",
|
||||
Name: "stash-git-password",
|
||||
Usage: "stash service account password",
|
||||
},
|
||||
|
||||
//
|
||||
// remove these eventually
|
||||
//
|
||||
|
||||
cli.BoolFlag{
|
||||
Name: "agreement.ack",
|
||||
EnvVar: "I_UNDERSTAND_I_AM_USING_AN_UNSTABLE_VERSION",
|
||||
Usage: "agree to terms of use.",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "agreement.fix",
|
||||
EnvVar: "I_AGREE_TO_FIX_BUGS_AND_NOT_FILE_BUGS",
|
||||
Usage: "agree to terms of use.",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func start(c *cli.Context) error {
|
||||
|
||||
if c.Bool("agreement.ack") == false || c.Bool("agreement.fix") == false {
|
||||
fmt.Println(agreement)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// debug level if requested by user
|
||||
if c.Bool("debug") {
|
||||
logrus.SetLevel(logrus.DebugLevel)
|
||||
} else {
|
||||
logrus.SetLevel(logrus.WarnLevel)
|
||||
}
|
||||
|
||||
// print the agent secret to the console
|
||||
// TODO(bradrydzewski) this overall approach should be re-considered
|
||||
if err := printSecret(c); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// setup the server and start the listener
|
||||
server := server.Server{
|
||||
Bus: setupBus(c),
|
||||
Cache: setupCache(c),
|
||||
Config: setupConfig(c),
|
||||
Queue: setupQueue(c),
|
||||
Remote: setupRemote(c),
|
||||
Stream: setupStream(c),
|
||||
Store: setupStore(c),
|
||||
}
|
||||
|
||||
// start the server with tls enabled
|
||||
if c.String("server-cert") != "" {
|
||||
return http.ListenAndServeTLS(
|
||||
c.String("server-addr"),
|
||||
c.String("server-cert"),
|
||||
c.String("server-key"),
|
||||
server.Handler(),
|
||||
)
|
||||
}
|
||||
|
||||
// start the server without tls enabled
|
||||
return http.ListenAndServe(
|
||||
c.String("server-addr"),
|
||||
server.Handler(),
|
||||
)
|
||||
}
|
||||
|
||||
func setupConfig(c *cli.Context) *server.Config {
|
||||
return &server.Config{
|
||||
Open: c.Bool("open"),
|
||||
Yaml: c.String("yaml"),
|
||||
Secret: c.String("agent-secret"),
|
||||
Admins: c.StringSlice("admin"),
|
||||
Orgs: c.StringSlice("orgs"),
|
||||
}
|
||||
}
|
||||
|
||||
func setupCache(c *cli.Context) cache.Cache {
|
||||
return cache.NewTTL(
|
||||
c.Duration("cache-ttl"),
|
||||
)
|
||||
}
|
||||
|
||||
func setupBus(c *cli.Context) bus.Bus {
|
||||
return bus.New()
|
||||
}
|
||||
|
||||
func setupQueue(c *cli.Context) queue.Queue {
|
||||
return queue.New()
|
||||
}
|
||||
|
||||
func setupStream(c *cli.Context) stream.Stream {
|
||||
return stream.New()
|
||||
}
|
||||
|
||||
func setupStore(c *cli.Context) store.Store {
|
||||
return datastore.New(
|
||||
c.String("driver"),
|
||||
c.String("datasource"),
|
||||
)
|
||||
}
|
||||
|
||||
func setupRemote(c *cli.Context) remote.Remote {
|
||||
switch {
|
||||
case c.Bool("github"):
|
||||
return setupGithub(c)
|
||||
case c.Bool("gitlab"):
|
||||
return setupGitlab(c)
|
||||
case c.Bool("bitbucket"):
|
||||
return setupBitbucket(c)
|
||||
case c.Bool("stash"):
|
||||
return setupStash(c)
|
||||
case c.Bool("gogs"):
|
||||
return setupGogs(c)
|
||||
default:
|
||||
logrus.Fatalln("version control system not configured")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func setupBitbucket(c *cli.Context) remote.Remote {
|
||||
return bitbucket.New(
|
||||
c.String("bitbucket-client"),
|
||||
c.String("bitbucket-server"),
|
||||
)
|
||||
}
|
||||
|
||||
func setupGogs(c *cli.Context) remote.Remote {
|
||||
return gogs.New(
|
||||
c.String("gogs-server"),
|
||||
c.Bool("gogs-private-mode"),
|
||||
c.Bool("gogs-skip-verify"),
|
||||
)
|
||||
}
|
||||
|
||||
func setupStash(c *cli.Context) remote.Remote {
|
||||
return bitbucketserver.New(
|
||||
c.String("stash-server"),
|
||||
c.String("stash-consumer-key"),
|
||||
c.String("stash-consumer-rsa"),
|
||||
c.String("stash-git-username"),
|
||||
c.String("stash-git-password"),
|
||||
)
|
||||
}
|
||||
|
||||
func setupGitlab(c *cli.Context) remote.Remote {
|
||||
return gitlab.New(
|
||||
c.String("gitlab-server"),
|
||||
c.String("gitlab-client"),
|
||||
c.String("gitlab-sercret"),
|
||||
c.Bool("gitlab-private-mode"),
|
||||
c.Bool("gitlab-skip-verify"),
|
||||
)
|
||||
}
|
||||
|
||||
func setupGithub(c *cli.Context) remote.Remote {
|
||||
g, err := github.New(
|
||||
c.String("github-server"),
|
||||
c.String("github-client"),
|
||||
c.String("github-sercret"),
|
||||
c.StringSlice("github-scope"),
|
||||
c.Bool("github-private-mode"),
|
||||
c.Bool("github-skip-verify"),
|
||||
c.BoolT("github-merge-ref"),
|
||||
)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
return g
|
||||
}
|
||||
|
||||
func printSecret(c *cli.Context) error {
|
||||
secret := c.String("agent-secret")
|
||||
if secret == "" {
|
||||
return fmt.Errorf("missing DRONE_AGENT_SECRET configuration parameter")
|
||||
}
|
||||
t := token.New(secret, "")
|
||||
s, err := t.Sign(secret)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid value for DRONE_AGENT_SECRET. %s", s)
|
||||
}
|
||||
|
||||
logrus.Infof("using agent secret %s", secret)
|
||||
logrus.Warnf("agents can connect with token %s", s)
|
||||
return nil
|
||||
}
|
||||
|
||||
var agreement = `
|
||||
---
|
||||
|
||||
|
||||
You are attempting to use the unstable channel. This build is experimental and
|
||||
has known bugs and compatibility issues. It is not intended for general use.
|
||||
|
||||
Please consider using the latest stable release instead:
|
||||
|
||||
drone/drone:0.4.2
|
||||
|
||||
If you are attempting to build from source please use the latest stable tag:
|
||||
|
||||
v0.4.2
|
||||
|
||||
If you are interested in testing this experimental build AND assisting with
|
||||
development you may proceed by setting the following environment:
|
||||
|
||||
I_UNDERSTAND_I_AM_USING_AN_UNSTABLE_VERSION=true
|
||||
I_AGREE_TO_FIX_BUGS_AND_NOT_FILE_BUGS=true
|
||||
|
||||
|
||||
---
|
||||
`
|
|
@ -4,7 +4,6 @@ import (
|
|||
"os"
|
||||
|
||||
"github.com/drone/drone/drone/agent"
|
||||
"github.com/drone/drone/drone/server"
|
||||
"github.com/drone/drone/version"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
|
@ -35,7 +34,7 @@ func main() {
|
|||
}
|
||||
app.Commands = []cli.Command{
|
||||
agent.AgentCmd,
|
||||
server.ServeCmd,
|
||||
DaemonCmd,
|
||||
SignCmd,
|
||||
SecretCmd,
|
||||
}
|
||||
|
|
|
@ -1,129 +0,0 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/drone/drone/router"
|
||||
"github.com/drone/drone/router/middleware"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/codegangsta/cli"
|
||||
"github.com/gin-gonic/contrib/ginrus"
|
||||
)
|
||||
|
||||
// ServeCmd is the exported command for starting the drone server.
|
||||
var ServeCmd = cli.Command{
|
||||
Name: "serve",
|
||||
Usage: "starts the drone server",
|
||||
Action: func(c *cli.Context) {
|
||||
if err := start(c); err != nil {
|
||||
logrus.Fatal(err)
|
||||
}
|
||||
},
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
EnvVar: "SERVER_ADDR",
|
||||
Name: "server-addr",
|
||||
Usage: "server address",
|
||||
Value: ":8000",
|
||||
},
|
||||
cli.StringFlag{
|
||||
EnvVar: "SERVER_CERT",
|
||||
Name: "server-cert",
|
||||
Usage: "server ssl cert",
|
||||
},
|
||||
cli.StringFlag{
|
||||
EnvVar: "SERVER_KEY",
|
||||
Name: "server-key",
|
||||
Usage: "server ssl key",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
EnvVar: "DEBUG",
|
||||
Name: "debug",
|
||||
Usage: "start the server in debug mode",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
EnvVar: "EXPERIMENTAL",
|
||||
Name: "experimental",
|
||||
Usage: "start the server with experimental features",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "agreement.ack",
|
||||
EnvVar: "I_UNDERSTAND_I_AM_USING_AN_UNSTABLE_VERSION",
|
||||
Usage: "agree to terms of use.",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "agreement.fix",
|
||||
EnvVar: "I_AGREE_TO_FIX_BUGS_AND_NOT_FILE_BUGS",
|
||||
Usage: "agree to terms of use.",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func start(c *cli.Context) error {
|
||||
|
||||
if c.Bool("agreement.ack") == false || c.Bool("agreement.fix") == false {
|
||||
println(agreement)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// debug level if requested by user
|
||||
if c.Bool("debug") {
|
||||
logrus.SetLevel(logrus.DebugLevel)
|
||||
} else {
|
||||
logrus.SetLevel(logrus.WarnLevel)
|
||||
}
|
||||
|
||||
// setup the server and start the listener
|
||||
handler := router.Load(
|
||||
ginrus.Ginrus(logrus.StandardLogger(), time.RFC3339, true),
|
||||
middleware.Version,
|
||||
middleware.Queue(),
|
||||
middleware.Stream(),
|
||||
middleware.Bus(),
|
||||
middleware.Cache(),
|
||||
middleware.Store(),
|
||||
middleware.Remote(),
|
||||
)
|
||||
|
||||
if c.String("server-cert") != "" {
|
||||
return http.ListenAndServeTLS(
|
||||
c.String("server-addr"),
|
||||
c.String("server-cert"),
|
||||
c.String("server-key"),
|
||||
handler,
|
||||
)
|
||||
}
|
||||
|
||||
return http.ListenAndServe(
|
||||
c.String("server-addr"),
|
||||
handler,
|
||||
)
|
||||
}
|
||||
|
||||
var agreement = `
|
||||
---
|
||||
|
||||
|
||||
You are attempting to use the unstable channel. This build is experimental and
|
||||
has known bugs and compatibility issues. It is not intended for general use.
|
||||
|
||||
Please consider using the latest stable release instead:
|
||||
|
||||
drone/drone:0.4.2
|
||||
|
||||
If you are attempting to build from source please use the latest stable tag:
|
||||
|
||||
v0.4.2
|
||||
|
||||
If you are interested in testing this experimental build AND assisting with
|
||||
development you may proceed by setting the following environment:
|
||||
|
||||
I_UNDERSTAND_I_AM_USING_AN_UNSTABLE_VERSION=true
|
||||
I_AGREE_TO_FIX_BUGS_AND_NOT_FILE_BUGS=true
|
||||
|
||||
|
||||
---
|
||||
`
|
12
model/team.go
Normal file
12
model/team.go
Normal file
|
@ -0,0 +1,12 @@
|
|||
package model
|
||||
|
||||
// Team represents a team or organization in the remote version control system.
|
||||
//
|
||||
// swagger:model user
|
||||
type Team struct {
|
||||
// Login is the username for this team.
|
||||
Login string `json:"login"`
|
||||
|
||||
// the avatar url for this team.
|
||||
Avatar string `json:"avatar_url"`
|
||||
}
|
|
@ -6,128 +6,79 @@ import (
|
|||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
|
||||
"github.com/drone/drone/model"
|
||||
"github.com/drone/drone/remote"
|
||||
"github.com/drone/drone/remote/bitbucket/internal"
|
||||
"github.com/drone/drone/shared/httputil"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"golang.org/x/oauth2"
|
||||
"golang.org/x/oauth2/bitbucket"
|
||||
)
|
||||
|
||||
type Bitbucket struct {
|
||||
type config struct {
|
||||
Client string
|
||||
Secret string
|
||||
Orgs []string
|
||||
Open bool
|
||||
}
|
||||
|
||||
func Load(config string) *Bitbucket {
|
||||
|
||||
// parse the remote DSN configuration string
|
||||
url_, err := url.Parse(config)
|
||||
if err != nil {
|
||||
log.Fatalln("unable to parse remote dsn. %s", err)
|
||||
// New returns a new remote Configuration for integrating with the Bitbucket
|
||||
// repository hosting service at https://bitbucket.org
|
||||
func New(client, secret string) remote.Remote {
|
||||
return &config{
|
||||
Client: client,
|
||||
Secret: secret,
|
||||
}
|
||||
params := url_.Query()
|
||||
url_.Path = ""
|
||||
url_.RawQuery = ""
|
||||
|
||||
// create the Githbub remote using parameters from
|
||||
// the parsed DSN configuration string.
|
||||
bitbucket := Bitbucket{}
|
||||
bitbucket.Client = params.Get("client_id")
|
||||
bitbucket.Secret = params.Get("client_secret")
|
||||
bitbucket.Orgs = params["orgs"]
|
||||
bitbucket.Open, _ = strconv.ParseBool(params.Get("open"))
|
||||
|
||||
return &bitbucket
|
||||
}
|
||||
|
||||
// Login authenticates the session and returns the
|
||||
// remote user details.
|
||||
func (bb *Bitbucket) Login(res http.ResponseWriter, req *http.Request) (*model.User, bool, error) {
|
||||
// helper function to return the bitbucket oauth2 client
|
||||
func (c *config) newClient(u *model.User) *internal.Client {
|
||||
return internal.NewClientToken(
|
||||
c.Client,
|
||||
c.Secret,
|
||||
&oauth2.Token{
|
||||
AccessToken: u.Token,
|
||||
RefreshToken: u.Secret,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func (c *config) Login(res http.ResponseWriter, req *http.Request) (*model.User, error) {
|
||||
config := &oauth2.Config{
|
||||
ClientID: bb.Client,
|
||||
ClientSecret: bb.Secret,
|
||||
ClientID: c.Client,
|
||||
ClientSecret: c.Secret,
|
||||
Endpoint: bitbucket.Endpoint,
|
||||
RedirectURL: fmt.Sprintf("%s/authorize", httputil.GetURL(req)),
|
||||
}
|
||||
|
||||
// get the OAuth code
|
||||
var code = req.FormValue("code")
|
||||
if len(code) == 0 {
|
||||
http.Redirect(res, req, config.AuthCodeURL("drone"), http.StatusSeeOther)
|
||||
return nil, false, nil
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var token, err = config.Exchange(oauth2.NoContext, code)
|
||||
if err != nil {
|
||||
return nil, false, fmt.Errorf("Error exchanging token. %s", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
client := NewClient(config.Client(oauth2.NoContext, token))
|
||||
client := internal.NewClient(config.Client(oauth2.NoContext, token))
|
||||
curr, err := client.FindCurrent()
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// convers the current bitbucket user to the
|
||||
// common drone user structure.
|
||||
user := model.User{}
|
||||
user.Login = curr.Login
|
||||
user.Token = token.AccessToken
|
||||
user.Secret = token.RefreshToken
|
||||
user.Expiry = token.Expiry.UTC().Unix()
|
||||
user.Avatar = curr.Links.Avatar.Href
|
||||
|
||||
// gets the primary, confirmed email from bitbucket
|
||||
emails, err := client.ListEmail()
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
for _, email := range emails.Values {
|
||||
if email.IsPrimary && email.IsConfirmed {
|
||||
user.Email = email.Email
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// if the installation is restricted to a subset
|
||||
// of organizations, get the orgs and verify the
|
||||
// user is a member.
|
||||
if len(bb.Orgs) != 0 {
|
||||
resp, err := client.ListTeams(&ListTeamOpts{Page: 1, PageLen: 100, Role: "member"})
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
var member bool
|
||||
for _, team := range resp.Values {
|
||||
for _, team_ := range bb.Orgs {
|
||||
if team.Login == team_ {
|
||||
member = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !member {
|
||||
return nil, false, fmt.Errorf("User does not belong to correct org. Must belong to %v", bb.Orgs)
|
||||
}
|
||||
}
|
||||
|
||||
return &user, bb.Open, nil
|
||||
return convertUser(curr, token), nil
|
||||
}
|
||||
|
||||
// Auth authenticates the session and returns the remote user
|
||||
// login for the given token and secret
|
||||
func (bb *Bitbucket) Auth(token, secret string) (string, error) {
|
||||
token_ := oauth2.Token{AccessToken: token, RefreshToken: secret}
|
||||
client := NewClientToken(bb.Client, bb.Secret, &token_)
|
||||
|
||||
func (c *config) Auth(token, secret string) (string, error) {
|
||||
client := internal.NewClientToken(
|
||||
c.Client,
|
||||
c.Secret,
|
||||
&oauth2.Token{
|
||||
AccessToken: token,
|
||||
RefreshToken: secret,
|
||||
},
|
||||
)
|
||||
user, err := client.FindCurrent()
|
||||
if err != nil {
|
||||
return "", err
|
||||
|
@ -135,13 +86,10 @@ func (bb *Bitbucket) Auth(token, secret string) (string, error) {
|
|||
return user.Login, nil
|
||||
}
|
||||
|
||||
// Refresh refreshes an oauth token and expiration for the given
|
||||
// user. It returns true if the token was refreshed, false if the
|
||||
// token was not refreshed, and error if it failed to refersh.
|
||||
func (bb *Bitbucket) Refresh(user *model.User) (bool, error) {
|
||||
func (c *config) Refresh(user *model.User) (bool, error) {
|
||||
config := &oauth2.Config{
|
||||
ClientID: bb.Client,
|
||||
ClientSecret: bb.Secret,
|
||||
ClientID: c.Client,
|
||||
ClientSecret: c.Secret,
|
||||
Endpoint: bitbucket.Endpoint,
|
||||
}
|
||||
|
||||
|
@ -165,28 +113,35 @@ func (bb *Bitbucket) Refresh(user *model.User) (bool, error) {
|
|||
return true, nil
|
||||
}
|
||||
|
||||
// Repo fetches the named repository from the remote system.
|
||||
func (bb *Bitbucket) Repo(u *model.User, owner, name string) (*model.Repo, error) {
|
||||
token := oauth2.Token{AccessToken: u.Token, RefreshToken: u.Secret}
|
||||
client := NewClientToken(bb.Client, bb.Secret, &token)
|
||||
func (c *config) Teams(u *model.User) ([]*model.Team, error) {
|
||||
opts := &internal.ListTeamOpts{
|
||||
PageLen: 100,
|
||||
Role: "member",
|
||||
}
|
||||
resp, err := c.newClient(u).ListTeams(opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return convertTeamList(resp.Values), nil
|
||||
}
|
||||
|
||||
repo, err := client.FindRepo(owner, name)
|
||||
func (c *config) Repo(u *model.User, owner, name string) (*model.Repo, error) {
|
||||
repo, err := c.newClient(u).FindRepo(owner, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return convertRepo(repo), nil
|
||||
}
|
||||
|
||||
// Repos fetches a list of repos from the remote system.
|
||||
func (bb *Bitbucket) Repos(u *model.User) ([]*model.RepoLite, error) {
|
||||
token := oauth2.Token{AccessToken: u.Token, RefreshToken: u.Secret}
|
||||
client := NewClientToken(bb.Client, bb.Secret, &token)
|
||||
func (c *config) Repos(u *model.User) ([]*model.RepoLite, error) {
|
||||
client := c.newClient(u)
|
||||
|
||||
var repos []*model.RepoLite
|
||||
|
||||
// gets a list of all accounts to query, including the
|
||||
// user's account and all team accounts.
|
||||
logins := []string{u.Login}
|
||||
resp, err := client.ListTeams(&ListTeamOpts{PageLen: 100, Role: "member"})
|
||||
resp, err := client.ListTeams(&internal.ListTeamOpts{PageLen: 100, Role: "member"})
|
||||
if err != nil {
|
||||
return repos, err
|
||||
}
|
||||
|
@ -208,11 +163,8 @@ func (bb *Bitbucket) Repos(u *model.User) ([]*model.RepoLite, error) {
|
|||
return repos, nil
|
||||
}
|
||||
|
||||
// Perm fetches the named repository permissions from
|
||||
// the remote system for the specified user.
|
||||
func (bb *Bitbucket) Perm(u *model.User, owner, name string) (*model.Perm, error) {
|
||||
token := oauth2.Token{AccessToken: u.Token, RefreshToken: u.Secret}
|
||||
client := NewClientToken(bb.Client, bb.Secret, &token)
|
||||
func (c *config) Perm(u *model.User, owner, name string) (*model.Perm, error) {
|
||||
client := c.newClient(u)
|
||||
|
||||
perms := new(model.Perm)
|
||||
_, err := client.FindRepo(owner, name)
|
||||
|
@ -220,69 +172,39 @@ func (bb *Bitbucket) Perm(u *model.User, owner, name string) (*model.Perm, error
|
|||
return perms, err
|
||||
}
|
||||
|
||||
// if we've gotten this far we know that the user at
|
||||
// least has read access to the repository.
|
||||
// if we've gotten this far we know that the user at least has read access
|
||||
// to the repository.
|
||||
perms.Pull = true
|
||||
|
||||
// if the user has access to the repository hooks we
|
||||
// can deduce that the user has push and admin access.
|
||||
_, err = client.ListHooks(owner, name, &ListOpts{})
|
||||
// if the user has access to the repository hooks we can deduce that the user
|
||||
// has push and admin access.
|
||||
_, err = client.ListHooks(owner, name, &internal.ListOpts{})
|
||||
if err == nil {
|
||||
perms.Push = true
|
||||
perms.Admin = true
|
||||
}
|
||||
|
||||
return perms, nil
|
||||
}
|
||||
|
||||
// File fetches a file from the remote repository and returns in string format.
|
||||
func (bb *Bitbucket) File(u *model.User, r *model.Repo, b *model.Build, f string) ([]byte, error) {
|
||||
client := NewClientToken(
|
||||
bb.Client,
|
||||
bb.Secret,
|
||||
&oauth2.Token{
|
||||
AccessToken: u.Token,
|
||||
RefreshToken: u.Secret,
|
||||
},
|
||||
)
|
||||
|
||||
config, err := client.FindSource(r.Owner, r.Name, b.Commit, f)
|
||||
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)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return []byte(config.Data), err
|
||||
}
|
||||
|
||||
// Status sends the commit status to the remote system.
|
||||
// An example would be the GitHub pull request status.
|
||||
func (bb *Bitbucket) Status(u *model.User, r *model.Repo, b *model.Build, link string) error {
|
||||
client := NewClientToken(
|
||||
bb.Client,
|
||||
bb.Secret,
|
||||
&oauth2.Token{
|
||||
AccessToken: u.Token,
|
||||
RefreshToken: u.Secret,
|
||||
},
|
||||
)
|
||||
|
||||
status := getStatus(b.Status)
|
||||
desc := getDesc(b.Status)
|
||||
|
||||
data := BuildStatus{
|
||||
State: status,
|
||||
func (c *config) Status(u *model.User, r *model.Repo, b *model.Build, link string) error {
|
||||
status := internal.BuildStatus{
|
||||
State: getStatus(b.Status),
|
||||
Desc: getDesc(b.Status),
|
||||
Key: "Drone",
|
||||
Url: link,
|
||||
Desc: desc,
|
||||
}
|
||||
|
||||
err := client.CreateStatus(r.Owner, r.Name, b.Commit, &data)
|
||||
return err
|
||||
return c.newClient(u).CreateStatus(r.Owner, r.Name, b.Commit, &status)
|
||||
}
|
||||
|
||||
// Netrc returns a .netrc file that can be used to clone
|
||||
// private repositories from a remote system.
|
||||
func (bb *Bitbucket) Netrc(u *model.User, r *model.Repo) (*model.Netrc, error) {
|
||||
func (c *config) Netrc(u *model.User, r *model.Repo) (*model.Netrc, error) {
|
||||
return &model.Netrc{
|
||||
Machine: "bitbucket.org",
|
||||
Login: "x-token-auth",
|
||||
|
@ -290,113 +212,73 @@ func (bb *Bitbucket) Netrc(u *model.User, r *model.Repo) (*model.Netrc, error) {
|
|||
}, nil
|
||||
}
|
||||
|
||||
// Activate activates a repository by creating the post-commit hook and
|
||||
// adding the SSH deploy key, if applicable.
|
||||
func (bb *Bitbucket) Activate(u *model.User, r *model.Repo, k *model.Key, link string) error {
|
||||
client := NewClientToken(
|
||||
bb.Client,
|
||||
bb.Secret,
|
||||
&oauth2.Token{
|
||||
AccessToken: u.Token,
|
||||
RefreshToken: u.Secret,
|
||||
},
|
||||
)
|
||||
|
||||
linkurl, err := url.Parse(link)
|
||||
func (c *config) Activate(u *model.User, r *model.Repo, k *model.Key, link string) error {
|
||||
rawurl, err := url.Parse(link)
|
||||
if err != nil {
|
||||
log.Errorf("malformed hook url %s. %s", link, err)
|
||||
return err
|
||||
}
|
||||
|
||||
// see if the hook already exists. If yes be sure to
|
||||
// delete so that multiple messages aren't sent.
|
||||
hooks, _ := client.ListHooks(r.Owner, r.Name, &ListOpts{})
|
||||
// deletes any previously created hooks
|
||||
if err := c.Deactivate(u, r, link); err != nil {
|
||||
// we can live with failure here. Things happen and manually scrubbing
|
||||
// hooks is certinaly not the end of the world.
|
||||
}
|
||||
|
||||
return c.newClient(u).CreateHook(r.Owner, r.Name, &internal.Hook{
|
||||
Active: true,
|
||||
Desc: rawurl.Host,
|
||||
Events: []string{"repo:push"},
|
||||
Url: link,
|
||||
})
|
||||
}
|
||||
|
||||
func (c *config) Deactivate(u *model.User, r *model.Repo, link string) error {
|
||||
client := c.newClient(u)
|
||||
|
||||
linkurl, err := url.Parse(link)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
hooks, err := client.ListHooks(r.Owner, r.Name, &internal.ListOpts{})
|
||||
if err != nil {
|
||||
return nil // we can live with undeleted hooks
|
||||
}
|
||||
|
||||
for _, hook := range hooks.Values {
|
||||
hookurl, err := url.Parse(hook.Url)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if hookurl.Host == linkurl.Host {
|
||||
err = client.DeleteHook(r.Owner, r.Name, hook.Uuid)
|
||||
if err != nil {
|
||||
log.Errorf("unable to delete hook %s. %s", hookurl.Host, err)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
err = client.CreateHook(r.Owner, r.Name, &Hook{
|
||||
Active: true,
|
||||
Desc: linkurl.Host,
|
||||
Events: []string{"repo:push"},
|
||||
Url: link,
|
||||
})
|
||||
if err != nil {
|
||||
log.Errorf("unable to create hook %s. %s", link, err)
|
||||
}
|
||||
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 (bb *Bitbucket) Deactivate(u *model.User, r *model.Repo, link string) error {
|
||||
client := NewClientToken(
|
||||
bb.Client,
|
||||
bb.Secret,
|
||||
&oauth2.Token{
|
||||
AccessToken: u.Token,
|
||||
RefreshToken: u.Secret,
|
||||
},
|
||||
)
|
||||
|
||||
linkurl, err := url.Parse(link)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// see if the hook already exists. If yes be sure to
|
||||
// delete so that multiple messages aren't sent.
|
||||
hooks, _ := client.ListHooks(r.Owner, r.Name, &ListOpts{})
|
||||
for _, hook := range hooks.Values {
|
||||
hookurl, err := url.Parse(hook.Url)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if hookurl.Host == linkurl.Host {
|
||||
client.DeleteHook(r.Owner, r.Name, hook.Uuid)
|
||||
break
|
||||
break // we can live with undeleted hooks
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Hook parses the post-commit hook from the Request body
|
||||
// and returns the required data in a standard format.
|
||||
func (bb *Bitbucket) Hook(r *http.Request) (*model.Repo, *model.Build, error) {
|
||||
func (c *config) Hook(r *http.Request) (*model.Repo, *model.Build, error) {
|
||||
|
||||
switch r.Header.Get("X-Event-Key") {
|
||||
case "repo:push":
|
||||
return bb.pushHook(r)
|
||||
return c.pushHook(r)
|
||||
case "pullrequest:created", "pullrequest:updated":
|
||||
return bb.pullHook(r)
|
||||
return c.pullHook(r)
|
||||
}
|
||||
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
func (bb *Bitbucket) String() string {
|
||||
return "bitbucket"
|
||||
}
|
||||
|
||||
func (bb *Bitbucket) pushHook(r *http.Request) (*model.Repo, *model.Build, error) {
|
||||
func (c *config) pushHook(r *http.Request) (*model.Repo, *model.Build, error) {
|
||||
payload := []byte(r.FormValue("payload"))
|
||||
if len(payload) == 0 {
|
||||
defer r.Body.Close()
|
||||
payload, _ = ioutil.ReadAll(r.Body)
|
||||
}
|
||||
|
||||
hook := PushHook{}
|
||||
hook := internal.PushHook{}
|
||||
err := json.Unmarshal(payload, &hook)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
|
@ -423,6 +305,7 @@ func (bb *Bitbucket) pushHook(r *http.Request) (*model.Repo, *model.Build, error
|
|||
|
||||
// return the updated repository information and the
|
||||
// build information.
|
||||
// TODO(bradrydzewski) uses unit tested conversion function
|
||||
return convertRepo(&hook.Repo), &model.Build{
|
||||
Event: buildEventType,
|
||||
Commit: change.New.Target.Hash,
|
||||
|
@ -439,14 +322,14 @@ func (bb *Bitbucket) pushHook(r *http.Request) (*model.Repo, *model.Build, error
|
|||
return nil, nil, nil
|
||||
}
|
||||
|
||||
func (bb *Bitbucket) pullHook(r *http.Request) (*model.Repo, *model.Build, error) {
|
||||
func (c *config) pullHook(r *http.Request) (*model.Repo, *model.Build, error) {
|
||||
payload := []byte(r.FormValue("payload"))
|
||||
if len(payload) == 0 {
|
||||
defer r.Body.Close()
|
||||
payload, _ = ioutil.ReadAll(r.Body)
|
||||
}
|
||||
|
||||
hook := PullRequestHook{}
|
||||
hook := internal.PullRequestHook{}
|
||||
err := json.Unmarshal(payload, &hook)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
|
@ -455,12 +338,13 @@ func (bb *Bitbucket) pullHook(r *http.Request) (*model.Repo, *model.Build, error
|
|||
return nil, nil, nil
|
||||
}
|
||||
|
||||
// TODO(bradrydzewski) uses unit tested conversion function
|
||||
return convertRepo(&hook.Repo), &model.Build{
|
||||
Event: model.EventPull,
|
||||
Commit: hook.PullRequest.Dest.Commit.Hash,
|
||||
Ref: fmt.Sprintf("refs/heads/%s", hook.PullRequest.Dest.Branch.Name),
|
||||
Refspec: fmt.Sprintf("https://bitbucket.org/%s.git", hook.PullRequest.Source.Repo.FullName),
|
||||
Remote: cloneLink(hook.PullRequest.Dest.Repo),
|
||||
Remote: cloneLink(&hook.PullRequest.Dest.Repo),
|
||||
Link: hook.PullRequest.Links.Html.Href,
|
||||
Branch: hook.PullRequest.Dest.Branch.Name,
|
||||
Message: hook.PullRequest.Desc,
|
||||
|
@ -469,47 +353,3 @@ func (bb *Bitbucket) pullHook(r *http.Request) (*model.Repo, *model.Build, error
|
|||
Timestamp: hook.PullRequest.Updated.UTC().Unix(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
const (
|
||||
StatusPending = "INPROGRESS"
|
||||
StatusSuccess = "SUCCESSFUL"
|
||||
StatusFailure = "FAILED"
|
||||
)
|
||||
|
||||
const (
|
||||
DescPending = "this build is pending"
|
||||
DescSuccess = "the build was successful"
|
||||
DescFailure = "the build failed"
|
||||
DescError = "oops, something went wrong"
|
||||
)
|
||||
|
||||
// converts a Drone status to a BitBucket status.
|
||||
func getStatus(status string) string {
|
||||
switch status {
|
||||
case model.StatusPending, model.StatusRunning:
|
||||
return StatusPending
|
||||
case model.StatusSuccess:
|
||||
return StatusSuccess
|
||||
case model.StatusFailure, model.StatusError, model.StatusKilled:
|
||||
return StatusFailure
|
||||
default:
|
||||
return StatusFailure
|
||||
}
|
||||
}
|
||||
|
||||
// generates a description for the build based on
|
||||
// the Drone status
|
||||
func getDesc(status string) string {
|
||||
switch status {
|
||||
case model.StatusPending, model.StatusRunning:
|
||||
return DescPending
|
||||
case model.StatusSuccess:
|
||||
return DescSuccess
|
||||
case model.StatusFailure:
|
||||
return DescFailure
|
||||
case model.StatusError, model.StatusKilled:
|
||||
return DescError
|
||||
default:
|
||||
return DescError
|
||||
}
|
||||
}
|
||||
|
|
40
remote/bitbucket/const.go
Normal file
40
remote/bitbucket/const.go
Normal file
|
@ -0,0 +1,40 @@
|
|||
package bitbucket
|
||||
|
||||
import "github.com/drone/drone/model"
|
||||
|
||||
const (
|
||||
statusPending = "INPROGRESS"
|
||||
statusSuccess = "SUCCESSFUL"
|
||||
statusFailure = "FAILED"
|
||||
)
|
||||
|
||||
const (
|
||||
descPending = "this build is pending"
|
||||
descSuccess = "the build was successful"
|
||||
descFailure = "the build failed"
|
||||
descError = "oops, something went wrong"
|
||||
)
|
||||
|
||||
func getStatus(status string) string {
|
||||
switch status {
|
||||
case model.StatusPending, model.StatusRunning:
|
||||
return statusPending
|
||||
case model.StatusSuccess:
|
||||
return statusSuccess
|
||||
default:
|
||||
return statusFailure
|
||||
}
|
||||
}
|
||||
|
||||
func getDesc(status string) string {
|
||||
switch status {
|
||||
case model.StatusPending, model.StatusRunning:
|
||||
return descPending
|
||||
case model.StatusSuccess:
|
||||
return descSuccess
|
||||
case model.StatusFailure:
|
||||
return descFailure
|
||||
default:
|
||||
return descError
|
||||
}
|
||||
}
|
43
remote/bitbucket/const_test.go
Normal file
43
remote/bitbucket/const_test.go
Normal file
|
@ -0,0 +1,43 @@
|
|||
package bitbucket
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/drone/drone/model"
|
||||
|
||||
"github.com/franela/goblin"
|
||||
)
|
||||
|
||||
func Test_status(t *testing.T) {
|
||||
|
||||
g := goblin.Goblin(t)
|
||||
g.Describe("Bitbucket status", func() {
|
||||
g.It("should return passing", func() {
|
||||
g.Assert(getStatus(model.StatusSuccess)).Equal(statusSuccess)
|
||||
})
|
||||
g.It("should return pending", func() {
|
||||
g.Assert(getStatus(model.StatusPending)).Equal(statusPending)
|
||||
g.Assert(getStatus(model.StatusRunning)).Equal(statusPending)
|
||||
})
|
||||
g.It("should return failing", func() {
|
||||
g.Assert(getStatus(model.StatusFailure)).Equal(statusFailure)
|
||||
g.Assert(getStatus(model.StatusKilled)).Equal(statusFailure)
|
||||
g.Assert(getStatus(model.StatusError)).Equal(statusFailure)
|
||||
})
|
||||
|
||||
g.It("should return passing desc", func() {
|
||||
g.Assert(getDesc(model.StatusSuccess)).Equal(descSuccess)
|
||||
})
|
||||
g.It("should return pending desc", func() {
|
||||
g.Assert(getDesc(model.StatusPending)).Equal(descPending)
|
||||
g.Assert(getDesc(model.StatusRunning)).Equal(descPending)
|
||||
})
|
||||
g.It("should return failing desc", func() {
|
||||
g.Assert(getDesc(model.StatusFailure)).Equal(descFailure)
|
||||
})
|
||||
g.It("should return error desc", func() {
|
||||
g.Assert(getDesc(model.StatusKilled)).Equal(descError)
|
||||
g.Assert(getDesc(model.StatusError)).Equal(descError)
|
||||
})
|
||||
})
|
||||
}
|
|
@ -5,12 +5,16 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/drone/drone/model"
|
||||
"github.com/drone/drone/remote/bitbucket/internal"
|
||||
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
// convertRepo is a helper function used to convert a Bitbucket
|
||||
// repository structure to the common Drone repository structure.
|
||||
func convertRepo(from *Repo) *model.Repo {
|
||||
// convertRepo is a helper function used to convert a Bitbucket repository
|
||||
// structure to the common Drone repository structure.
|
||||
func convertRepo(from *internal.Repo) *model.Repo {
|
||||
repo := model.Repo{
|
||||
Clone: cloneLink(from),
|
||||
Owner: strings.Split(from.FullName, "/")[0],
|
||||
Name: strings.Split(from.FullName, "/")[1],
|
||||
FullName: from.FullName,
|
||||
|
@ -20,66 +24,34 @@ func convertRepo(from *Repo) *model.Repo {
|
|||
Kind: from.Scm,
|
||||
Branch: "master",
|
||||
}
|
||||
|
||||
if repo.Kind == model.RepoHg {
|
||||
repo.Branch = "default"
|
||||
}
|
||||
|
||||
// in some cases, the owner of the repository is not
|
||||
// provided, however, we do have the full name.
|
||||
if len(repo.Owner) == 0 {
|
||||
repo.Owner = strings.Split(repo.FullName, "/")[0]
|
||||
}
|
||||
|
||||
// above we manually constructed the repository clone url.
|
||||
// below we will iterate through the list of clone links and
|
||||
// attempt to instead use the clone url provided by bitbucket.
|
||||
for _, link := range from.Links.Clone {
|
||||
if link.Name == "https" {
|
||||
repo.Clone = link.Href
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// if no repository name is provided, we use the Html link.
|
||||
// this excludes the .git suffix, but will still clone the repo.
|
||||
if len(repo.Clone) == 0 {
|
||||
repo.Clone = repo.Link
|
||||
}
|
||||
|
||||
// if bitbucket tries to automatically populate the user
|
||||
// in the url we must strip it out.
|
||||
clone, err := url.Parse(repo.Clone)
|
||||
if err == nil {
|
||||
clone.User = nil
|
||||
repo.Clone = clone.String()
|
||||
}
|
||||
|
||||
return &repo
|
||||
}
|
||||
|
||||
// cloneLink is a helper function that tries to extract the
|
||||
// clone url from the repository object.
|
||||
func cloneLink(repo Repo) string {
|
||||
// cloneLink is a helper function that tries to extract the clone url from the
|
||||
// repository object.
|
||||
func cloneLink(repo *internal.Repo) string {
|
||||
var clone string
|
||||
|
||||
// above we manually constructed the repository clone url.
|
||||
// below we will iterate through the list of clone links and
|
||||
// attempt to instead use the clone url provided by bitbucket.
|
||||
// above we manually constructed the repository clone url. below we will
|
||||
// iterate through the list of clone links and attempt to instead use the
|
||||
// clone url provided by bitbucket.
|
||||
for _, link := range repo.Links.Clone {
|
||||
if link.Name == "https" {
|
||||
clone = link.Href
|
||||
}
|
||||
}
|
||||
|
||||
// if no repository name is provided, we use the Html link.
|
||||
// this excludes the .git suffix, but will still clone the repo.
|
||||
// if no repository name is provided, we use the Html link. this excludes the
|
||||
// .git suffix, but will still clone the repo.
|
||||
if len(clone) == 0 {
|
||||
clone = repo.Links.Html.Href
|
||||
}
|
||||
|
||||
// if bitbucket tries to automatically populate the user
|
||||
// in the url we must strip it out.
|
||||
// if bitbucket tries to automatically populate the user in the url we must
|
||||
// strip it out.
|
||||
cloneurl, err := url.Parse(clone)
|
||||
if err == nil {
|
||||
cloneurl.User = nil
|
||||
|
@ -89,9 +61,9 @@ func cloneLink(repo Repo) string {
|
|||
return clone
|
||||
}
|
||||
|
||||
// convertRepoLite is a helper function used to convert a Bitbucket
|
||||
// repository structure to the simplified Drone repository structure.
|
||||
func convertRepoLite(from *Repo) *model.RepoLite {
|
||||
// convertRepoLite is a helper function used to convert a Bitbucket repository
|
||||
// structure to the simplified Drone repository structure.
|
||||
func convertRepoLite(from *internal.Repo) *model.RepoLite {
|
||||
return &model.RepoLite{
|
||||
Owner: strings.Split(from.FullName, "/")[0],
|
||||
Name: strings.Split(from.FullName, "/")[1],
|
||||
|
@ -99,3 +71,34 @@ func convertRepoLite(from *Repo) *model.RepoLite {
|
|||
Avatar: from.Owner.Links.Avatar.Href,
|
||||
}
|
||||
}
|
||||
|
||||
// convertUser is a helper function used to convert a Bitbucket user account
|
||||
// structure to the Drone User structure.
|
||||
func convertUser(from *internal.Account, token *oauth2.Token) *model.User {
|
||||
return &model.User{
|
||||
Login: from.Login,
|
||||
Token: token.AccessToken,
|
||||
Secret: token.RefreshToken,
|
||||
Expiry: token.Expiry.UTC().Unix(),
|
||||
Avatar: from.Links.Avatar.Href,
|
||||
}
|
||||
}
|
||||
|
||||
// convertTeamList is a helper function used to convert a Bitbucket team list
|
||||
// structure to the Drone Team structure.
|
||||
func convertTeamList(from []*internal.Account) []*model.Team {
|
||||
var teams []*model.Team
|
||||
for _, team := range from {
|
||||
teams = append(teams, convertTeam(team))
|
||||
}
|
||||
return teams
|
||||
}
|
||||
|
||||
// convertTeam is a helper function used to convert a Bitbucket team account
|
||||
// structure to the Drone Team structure.
|
||||
func convertTeam(from *internal.Account) *model.Team {
|
||||
return &model.Team{
|
||||
Login: from.Login,
|
||||
Avatar: from.Links.Avatar.Href,
|
||||
}
|
||||
}
|
||||
|
|
102
remote/bitbucket/helper_test.go
Normal file
102
remote/bitbucket/helper_test.go
Normal file
|
@ -0,0 +1,102 @@
|
|||
package bitbucket
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/drone/drone/remote/bitbucket/internal"
|
||||
|
||||
"github.com/franela/goblin"
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
func Test_helper(t *testing.T) {
|
||||
|
||||
g := goblin.Goblin(t)
|
||||
g.Describe("Bitbucket", func() {
|
||||
|
||||
g.It("should convert repository lite", func() {
|
||||
from := &internal.Repo{}
|
||||
from.FullName = "octocat/hello-world"
|
||||
from.Owner.Links.Avatar.Href = "http://..."
|
||||
|
||||
to := convertRepoLite(from)
|
||||
g.Assert(to.Avatar).Equal(from.Owner.Links.Avatar.Href)
|
||||
g.Assert(to.FullName).Equal(from.FullName)
|
||||
g.Assert(to.Owner).Equal("octocat")
|
||||
g.Assert(to.Name).Equal("hello-world")
|
||||
})
|
||||
|
||||
g.It("should convert repository", func() {
|
||||
from := &internal.Repo{
|
||||
FullName: "octocat/hello-world",
|
||||
IsPrivate: true,
|
||||
Scm: "hg",
|
||||
}
|
||||
from.Owner.Links.Avatar.Href = "http://..."
|
||||
from.Links.Html.Href = "https://bitbucket.org/foo/bar"
|
||||
|
||||
to := convertRepo(from)
|
||||
g.Assert(to.Avatar).Equal(from.Owner.Links.Avatar.Href)
|
||||
g.Assert(to.FullName).Equal(from.FullName)
|
||||
g.Assert(to.Owner).Equal("octocat")
|
||||
g.Assert(to.Name).Equal("hello-world")
|
||||
g.Assert(to.Branch).Equal("default")
|
||||
g.Assert(to.Kind).Equal(from.Scm)
|
||||
g.Assert(to.IsPrivate).Equal(from.IsPrivate)
|
||||
g.Assert(to.Clone).Equal(from.Links.Html.Href)
|
||||
g.Assert(to.Link).Equal(from.Links.Html.Href)
|
||||
})
|
||||
|
||||
g.It("should convert team", func() {
|
||||
from := &internal.Account{Login: "octocat"}
|
||||
from.Links.Avatar.Href = "http://..."
|
||||
to := convertTeam(from)
|
||||
g.Assert(to.Avatar).Equal(from.Links.Avatar.Href)
|
||||
g.Assert(to.Login).Equal(from.Login)
|
||||
})
|
||||
|
||||
g.It("should convert team list", func() {
|
||||
from := &internal.Account{Login: "octocat"}
|
||||
from.Links.Avatar.Href = "http://..."
|
||||
to := convertTeamList([]*internal.Account{from})
|
||||
g.Assert(to[0].Avatar).Equal(from.Links.Avatar.Href)
|
||||
g.Assert(to[0].Login).Equal(from.Login)
|
||||
})
|
||||
|
||||
g.It("should convert user", func() {
|
||||
token := &oauth2.Token{
|
||||
AccessToken: "foo",
|
||||
RefreshToken: "bar",
|
||||
Expiry: time.Now(),
|
||||
}
|
||||
user := &internal.Account{Login: "octocat"}
|
||||
user.Links.Avatar.Href = "http://..."
|
||||
|
||||
result := convertUser(user, token)
|
||||
g.Assert(result.Avatar).Equal(user.Links.Avatar.Href)
|
||||
g.Assert(result.Login).Equal(user.Login)
|
||||
g.Assert(result.Token).Equal(token.AccessToken)
|
||||
g.Assert(result.Token).Equal(token.AccessToken)
|
||||
g.Assert(result.Secret).Equal(token.RefreshToken)
|
||||
g.Assert(result.Expiry).Equal(token.Expiry.UTC().Unix())
|
||||
})
|
||||
|
||||
g.It("should use clone url", func() {
|
||||
repo := &internal.Repo{}
|
||||
repo.Links.Clone = append(repo.Links.Clone, internal.Link{
|
||||
Name: "https",
|
||||
Href: "https://bitbucket.org/foo/bar.git",
|
||||
})
|
||||
link := cloneLink(repo)
|
||||
g.Assert(link).Equal(repo.Links.Clone[0].Href)
|
||||
})
|
||||
|
||||
g.It("should build clone url", func() {
|
||||
repo := &internal.Repo{}
|
||||
repo.Links.Html.Href = "https://foo:bar@bitbucket.org/foo/bar.git"
|
||||
link := cloneLink(repo)
|
||||
g.Assert(link).Equal("https://bitbucket.org/foo/bar.git")
|
||||
})
|
||||
})
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package bitbucket
|
||||
package internal
|
||||
|
||||
import (
|
||||
"bytes"
|
|
@ -1,4 +1,4 @@
|
|||
package bitbucket
|
||||
package internal
|
||||
|
||||
import (
|
||||
"net/url"
|
|
@ -14,13 +14,15 @@ package bitbucketserver
|
|||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/drone/drone/model"
|
||||
"github.com/mrjones/oauth"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/drone/drone/model"
|
||||
"github.com/drone/drone/remote"
|
||||
"github.com/mrjones/oauth"
|
||||
)
|
||||
|
||||
type BitbucketServer struct {
|
||||
|
@ -33,6 +35,19 @@ type BitbucketServer struct {
|
|||
Consumer oauth.Consumer
|
||||
}
|
||||
|
||||
func New(url, key, rsa, username, password string) remote.Remote {
|
||||
bb := &BitbucketServer{
|
||||
URL: url,
|
||||
ConsumerKey: key,
|
||||
GitUserName: username,
|
||||
GitPassword: password,
|
||||
ConsumerRSA: rsa,
|
||||
}
|
||||
bb.Consumer = *NewClient(bb.ConsumerRSA, bb.ConsumerKey, bb.URL)
|
||||
|
||||
return bb
|
||||
}
|
||||
|
||||
func Load(config string) *BitbucketServer {
|
||||
|
||||
url_, err := url.Parse(config)
|
||||
|
@ -105,7 +120,7 @@ func (bs *BitbucketServer) Login(res http.ResponseWriter, req *http.Request) (*m
|
|||
bits, err := ioutil.ReadAll(response.Body)
|
||||
userName := string(bits)
|
||||
|
||||
response1, err := client.Get(fmt.Sprintf("%s/rest/api/1.0/users/%s",bs.URL, userName))
|
||||
response1, err := client.Get(fmt.Sprintf("%s/rest/api/1.0/users/%s", bs.URL, userName))
|
||||
contents, err := ioutil.ReadAll(response1.Body)
|
||||
defer response1.Body.Close()
|
||||
var mUser User
|
||||
|
@ -134,7 +149,7 @@ func (bs *BitbucketServer) Repo(u *model.User, owner, name string) (*model.Repo,
|
|||
|
||||
client := NewClientWithToken(&bs.Consumer, u.Token)
|
||||
|
||||
url := fmt.Sprintf("%s/rest/api/1.0/projects/%s/repos/%s",bs.URL,owner,name)
|
||||
url := fmt.Sprintf("%s/rest/api/1.0/projects/%s/repos/%s", bs.URL, owner, name)
|
||||
log.Info("Trying to get " + url)
|
||||
response, err := client.Get(url)
|
||||
if err != nil {
|
||||
|
@ -165,7 +180,7 @@ func (bs *BitbucketServer) Repo(u *model.User, owner, name string) (*model.Repo,
|
|||
repo.Name = bsRepo.Slug
|
||||
repo.Owner = bsRepo.Project.Key
|
||||
repo.AllowPush = true
|
||||
repo.FullName = fmt.Sprintf("%s/%s",bsRepo.Project.Key,bsRepo.Slug)
|
||||
repo.FullName = fmt.Sprintf("%s/%s", bsRepo.Project.Key, bsRepo.Slug)
|
||||
repo.Branch = "master"
|
||||
repo.Kind = model.RepoGit
|
||||
|
||||
|
@ -178,7 +193,7 @@ func (bs *BitbucketServer) Repos(u *model.User) ([]*model.RepoLite, error) {
|
|||
|
||||
client := NewClientWithToken(&bs.Consumer, u.Token)
|
||||
|
||||
response, err := client.Get(fmt.Sprintf("%s/rest/api/1.0/repos?limit=10000",bs.URL))
|
||||
response, err := client.Get(fmt.Sprintf("%s/rest/api/1.0/repos?limit=10000", bs.URL))
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
|
@ -296,7 +311,7 @@ func (bs *BitbucketServer) Hook(r *http.Request) (*model.Repo, *model.Build, err
|
|||
repo.AllowDeploy = false
|
||||
repo.AllowPull = false
|
||||
repo.AllowPush = true
|
||||
repo.FullName = fmt.Sprintf("%s/%s",hookPost.Repository.Project.Key,hookPost.Repository.Slug)
|
||||
repo.FullName = fmt.Sprintf("%s/%s", hookPost.Repository.Project.Key, hookPost.Repository.Slug)
|
||||
repo.Branch = "master"
|
||||
repo.Kind = model.RepoGit
|
||||
|
||||
|
@ -307,17 +322,17 @@ func (bs *BitbucketServer) String() string {
|
|||
}
|
||||
|
||||
type HookDetail struct {
|
||||
Key string `"json:key"`
|
||||
Name string `"json:name"`
|
||||
Type string `"json:type"`
|
||||
Description string `"json:description"`
|
||||
Version string `"json:version"`
|
||||
ConfigFormKey string `"json:configFormKey"`
|
||||
Key string `json:"key"`
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
Description string `json:"description"`
|
||||
Version string `json:"version"`
|
||||
ConfigFormKey string `json:"configFormKey"`
|
||||
}
|
||||
|
||||
type Hook struct {
|
||||
Enabled bool `"json:enabled"`
|
||||
Details *HookDetail `"json:details"`
|
||||
Enabled bool `json:"enabled"`
|
||||
Details *HookDetail `json:"details"`
|
||||
}
|
||||
|
||||
// Enable hook for named repository
|
||||
|
|
85
remote/cache.go
Normal file
85
remote/cache.go
Normal file
|
@ -0,0 +1,85 @@
|
|||
package remote
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/drone/drone/model"
|
||||
)
|
||||
|
||||
// WithCache returns a the parent Remote with a front-end Cache. Remote items
|
||||
// are cached for duration d.
|
||||
func WithCache(r Remote, d time.Duration) Remote {
|
||||
return r
|
||||
}
|
||||
|
||||
// Cacher implements purge functionality so that we can evict stale data and
|
||||
// force a refresh. The indended use case is when the repository list is out
|
||||
// of date and requires manual refresh.
|
||||
type Cacher interface {
|
||||
Purge(*model.User)
|
||||
}
|
||||
|
||||
// Because the cache is so closely tied to the remote we should just include
|
||||
// them in the same package together. The below code are stubs for merging
|
||||
// the Cache with the Remote package.
|
||||
|
||||
type cache struct {
|
||||
Remote
|
||||
}
|
||||
|
||||
func (c *cache) Repos(u *model.User) ([]*model.RepoLite, error) {
|
||||
// key := fmt.Sprintf("repos:%s",
|
||||
// user.Login,
|
||||
// )
|
||||
// // if we fetch from the cache we can return immediately
|
||||
// val, err := Get(c, key)
|
||||
// if err == nil {
|
||||
// return val.([]*model.RepoLite), nil
|
||||
// }
|
||||
// // else we try to grab from the remote system and
|
||||
// // populate our cache.
|
||||
// repos, err := remote.Repos(c, user)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
//
|
||||
// Set(c, key, repos)
|
||||
// return repos, nil
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (c *cache) Perm(u *model.User, owner, repo string) (*model.Perm, error) {
|
||||
// key := fmt.Sprintf("perms:%s:%s/%s",
|
||||
// user.Login,
|
||||
// owner,
|
||||
// name,
|
||||
// )
|
||||
// // if we fetch from the cache we can return immediately
|
||||
// val, err := Get(c, key)
|
||||
// if err == nil {
|
||||
// return val.(*model.Perm), nil
|
||||
// }
|
||||
// // else we try to grab from the remote system and
|
||||
// // populate our cache.
|
||||
// perm, err := remote.Perm(c, user, owner, name)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
// Set(c, key, perm)
|
||||
// return perm, nil
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (c *cache) Purge(*model.User) {
|
||||
return
|
||||
}
|
||||
|
||||
func (c *cache) Refresh(u *model.User) (bool, error) {
|
||||
if r, ok := c.Remote.(Refresher); ok {
|
||||
return r.Refresh(u)
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
var _ Remote = &cache{}
|
||||
var _ Refresher = &cache{}
|
|
@ -11,22 +11,18 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/drone/drone/model"
|
||||
"github.com/drone/drone/remote"
|
||||
"github.com/drone/drone/shared/httputil"
|
||||
"github.com/drone/drone/shared/oauth2"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/google/go-github/github"
|
||||
)
|
||||
|
||||
const (
|
||||
DefaultURL = "https://github.com"
|
||||
DefaultAPI = "https://api.github.com"
|
||||
DefaultScope = "repo,repo:status,user:email"
|
||||
DefaultMergeRef = "merge"
|
||||
DefaultURL = "https://github.com" // Default GitHub URL
|
||||
DefaultAPI = "https://api.github.com" // Default GitHub API URL
|
||||
)
|
||||
|
||||
var githubDeployRegex = regexp.MustCompile(".+/deployments/(\\d+)")
|
||||
|
||||
type Github struct {
|
||||
URL string
|
||||
API string
|
||||
|
@ -34,58 +30,35 @@ type Github struct {
|
|||
Secret string
|
||||
Scope string
|
||||
MergeRef string
|
||||
Orgs []string
|
||||
Open bool
|
||||
PrivateMode bool
|
||||
SkipVerify bool
|
||||
GitSSH bool
|
||||
}
|
||||
|
||||
func Load(config string) *Github {
|
||||
|
||||
// parse the remote DSN configuration string
|
||||
url_, err := url.Parse(config)
|
||||
if err != nil {
|
||||
log.Fatalln("unable to parse remote dsn. %s", err)
|
||||
func New(url, client, secret string, scope []string, private, skipverify, mergeref bool) (remote.Remote, error) {
|
||||
remote := &Github{
|
||||
URL: strings.TrimSuffix(url, "/"),
|
||||
Client: client,
|
||||
Secret: secret,
|
||||
Scope: strings.Join(scope, ","),
|
||||
PrivateMode: private,
|
||||
SkipVerify: skipverify,
|
||||
MergeRef: "head",
|
||||
}
|
||||
params := url_.Query()
|
||||
url_.Path = ""
|
||||
url_.RawQuery = ""
|
||||
|
||||
// create the Githbub remote using parameters from
|
||||
// the parsed DSN configuration string.
|
||||
github := Github{}
|
||||
github.URL = url_.String()
|
||||
github.Client = params.Get("client_id")
|
||||
github.Secret = params.Get("client_secret")
|
||||
github.Scope = params.Get("scope")
|
||||
github.Orgs = params["orgs"]
|
||||
github.PrivateMode, _ = strconv.ParseBool(params.Get("private_mode"))
|
||||
github.SkipVerify, _ = strconv.ParseBool(params.Get("skip_verify"))
|
||||
github.Open, _ = strconv.ParseBool(params.Get("open"))
|
||||
github.GitSSH, _ = strconv.ParseBool(params.Get("ssh"))
|
||||
github.MergeRef = params.Get("merge_ref")
|
||||
|
||||
if github.URL == DefaultURL {
|
||||
github.API = DefaultAPI
|
||||
if remote.URL == DefaultURL {
|
||||
remote.API = DefaultAPI
|
||||
} else {
|
||||
github.API = github.URL + "/api/v3/"
|
||||
remote.API = remote.URL + "/api/v3/"
|
||||
}
|
||||
if mergeref {
|
||||
remote.MergeRef = "merge"
|
||||
}
|
||||
|
||||
if github.Scope == "" {
|
||||
github.Scope = DefaultScope
|
||||
}
|
||||
|
||||
if github.MergeRef == "" {
|
||||
github.MergeRef = DefaultMergeRef
|
||||
}
|
||||
|
||||
return &github
|
||||
return remote, nil
|
||||
}
|
||||
|
||||
// Login authenticates the session and returns the
|
||||
// remote user details.
|
||||
func (g *Github) Login(res http.ResponseWriter, req *http.Request) (*model.User, bool, error) {
|
||||
// Login authenticates the session and returns the remote user details.
|
||||
func (g *Github) Login(res http.ResponseWriter, req *http.Request) (*model.User, error) {
|
||||
|
||||
var config = &oauth2.Config{
|
||||
ClientId: g.Client,
|
||||
|
@ -101,7 +74,7 @@ func (g *Github) Login(res http.ResponseWriter, req *http.Request) (*model.User,
|
|||
if len(code) == 0 {
|
||||
var random = GetRandom()
|
||||
http.Redirect(res, req, config.AuthCodeURL(random), http.StatusSeeOther)
|
||||
return nil, false, nil
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var trans = &oauth2.Transport{
|
||||
|
@ -117,23 +90,13 @@ func (g *Github) Login(res http.ResponseWriter, req *http.Request) (*model.User,
|
|||
}
|
||||
var token, err = trans.Exchange(code)
|
||||
if err != nil {
|
||||
return nil, false, fmt.Errorf("Error exchanging token. %s", err)
|
||||
return nil, fmt.Errorf("Error exchanging token. %s", err)
|
||||
}
|
||||
|
||||
var client = NewClient(g.API, token.AccessToken, g.SkipVerify)
|
||||
var useremail, errr = GetUserEmail(client)
|
||||
if errr != nil {
|
||||
return nil, false, fmt.Errorf("Error retrieving user or verified email. %s", errr)
|
||||
}
|
||||
|
||||
if len(g.Orgs) > 0 {
|
||||
allowedOrg, err := UserBelongsToOrg(client, g.Orgs)
|
||||
if err != nil {
|
||||
return nil, false, fmt.Errorf("Could not check org membership. %s", err)
|
||||
}
|
||||
if !allowedOrg {
|
||||
return nil, false, fmt.Errorf("User does not belong to correct org. Must belong to %v", g.Orgs)
|
||||
}
|
||||
return nil, fmt.Errorf("Error retrieving user or verified email. %s", errr)
|
||||
}
|
||||
|
||||
user := model.User{}
|
||||
|
@ -141,7 +104,7 @@ func (g *Github) Login(res http.ResponseWriter, req *http.Request) (*model.User,
|
|||
user.Email = *useremail.Email
|
||||
user.Token = token.AccessToken
|
||||
user.Avatar = *useremail.AvatarURL
|
||||
return &user, g.Open, nil
|
||||
return &user, nil
|
||||
}
|
||||
|
||||
// Auth authenticates the session and returns the remote user
|
||||
|
@ -155,37 +118,52 @@ func (g *Github) Auth(token, secret string) (string, error) {
|
|||
return *user.Login, nil
|
||||
}
|
||||
|
||||
// Repo fetches the named repository from the remote system.
|
||||
func (g *Github) Repo(u *model.User, owner, name string) (*model.Repo, error) {
|
||||
func (g *Github) Teams(u *model.User) ([]*model.Team, error) {
|
||||
client := NewClient(g.API, u.Token, g.SkipVerify)
|
||||
repo_, err := GetRepo(client, owner, name)
|
||||
orgs, err := GetOrgs(client)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
repo := &model.Repo{}
|
||||
repo.Owner = owner
|
||||
repo.Name = name
|
||||
repo.FullName = *repo_.FullName
|
||||
repo.Link = *repo_.HTMLURL
|
||||
repo.IsPrivate = *repo_.Private
|
||||
repo.Clone = *repo_.CloneURL
|
||||
repo.Branch = "master"
|
||||
repo.Avatar = *repo_.Owner.AvatarURL
|
||||
repo.Kind = model.RepoGit
|
||||
var teams []*model.Team
|
||||
for _, org := range orgs {
|
||||
teams = append(teams, &model.Team{
|
||||
Login: *org.Login,
|
||||
Avatar: *org.AvatarURL,
|
||||
})
|
||||
}
|
||||
return teams, nil
|
||||
}
|
||||
|
||||
if repo_.DefaultBranch != nil {
|
||||
repo.Branch = *repo_.DefaultBranch
|
||||
// Repo fetches the named repository from the remote system.
|
||||
func (g *Github) Repo(u *model.User, owner, name string) (*model.Repo, error) {
|
||||
client := NewClient(g.API, u.Token, g.SkipVerify)
|
||||
r, err := GetRepo(client, owner, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
repo := &model.Repo{
|
||||
Owner: owner,
|
||||
Name: name,
|
||||
FullName: *r.FullName,
|
||||
Link: *r.HTMLURL,
|
||||
IsPrivate: *r.Private,
|
||||
Clone: *r.CloneURL,
|
||||
Avatar: *r.Owner.AvatarURL,
|
||||
Kind: model.RepoGit,
|
||||
}
|
||||
|
||||
if r.DefaultBranch != nil {
|
||||
repo.Branch = *r.DefaultBranch
|
||||
} else {
|
||||
repo.Branch = "master"
|
||||
}
|
||||
|
||||
if g.PrivateMode {
|
||||
repo.IsPrivate = true
|
||||
}
|
||||
|
||||
if g.GitSSH && repo.IsPrivate {
|
||||
repo.Clone = *repo_.SSHURL
|
||||
}
|
||||
|
||||
return repo, err
|
||||
}
|
||||
|
||||
|
@ -257,8 +235,10 @@ func repoStatus(client *github.Client, r *model.Repo, b *model.Build, link strin
|
|||
return err
|
||||
}
|
||||
|
||||
var reDeploy = regexp.MustCompile(".+/deployments/(\\d+)")
|
||||
|
||||
func deploymentStatus(client *github.Client, r *model.Repo, b *model.Build, link string) error {
|
||||
matches := githubDeployRegex.FindStringSubmatch(b.Link)
|
||||
matches := reDeploy.FindStringSubmatch(b.Link)
|
||||
// if the deployment was not triggered from github, don't send a deployment status
|
||||
if len(matches) != 2 {
|
||||
return nil
|
||||
|
@ -294,21 +274,7 @@ func (g *Github) Netrc(u *model.User, r *model.Repo) (*model.Netrc, error) {
|
|||
// adding the SSH deploy key, if applicable.
|
||||
func (g *Github) Activate(u *model.User, r *model.Repo, k *model.Key, link string) error {
|
||||
client := NewClient(g.API, u.Token, g.SkipVerify)
|
||||
title, err := GetKeyTitle(link)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// if the CloneURL is using the SSHURL then we know that
|
||||
// we need to add an SSH key to GitHub.
|
||||
if r.IsPrivate || g.PrivateMode {
|
||||
_, err = CreateUpdateKey(client, r.Owner, r.Name, title, k.Public)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
_, err = CreateUpdateHook(client, r.Owner, r.Name, link)
|
||||
_, err := CreateUpdateHook(client, r.Owner, r.Name, link)
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -316,18 +282,6 @@ func (g *Github) Activate(u *model.User, r *model.Repo, k *model.Key, link strin
|
|||
// which are equal to link and removing the SSH deploy key.
|
||||
func (g *Github) Deactivate(u *model.User, r *model.Repo, link string) error {
|
||||
client := NewClient(g.API, u.Token, g.SkipVerify)
|
||||
title, err := GetKeyTitle(link)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// remove the deploy-key if it is installed remote.
|
||||
if r.IsPrivate || g.PrivateMode {
|
||||
if err := DeleteKey(client, r.Owner, r.Name, title); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return DeleteHook(client, r.Owner, r.Name, link)
|
||||
}
|
||||
|
||||
|
|
|
@ -46,31 +46,32 @@ func TestHook(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestLoad(t *testing.T) {
|
||||
conf := "https://github.com?client_id=client&client_secret=secret&scope=scope1,scope2"
|
||||
|
||||
g := Load(conf)
|
||||
if g.URL != "https://github.com" {
|
||||
t.Errorf("g.URL = %q; want https://github.com", g.URL)
|
||||
}
|
||||
if g.Client != "client" {
|
||||
t.Errorf("g.Client = %q; want client", g.Client)
|
||||
}
|
||||
if g.Secret != "secret" {
|
||||
t.Errorf("g.Secret = %q; want secret", g.Secret)
|
||||
}
|
||||
if g.Scope != "scope1,scope2" {
|
||||
t.Errorf("g.Scope = %q; want scope1,scope2", g.Scope)
|
||||
}
|
||||
if g.API != DefaultAPI {
|
||||
t.Errorf("g.API = %q; want %q", g.API, DefaultAPI)
|
||||
}
|
||||
if g.MergeRef != DefaultMergeRef {
|
||||
t.Errorf("g.MergeRef = %q; want %q", g.MergeRef, DefaultMergeRef)
|
||||
}
|
||||
|
||||
g = Load("")
|
||||
if g.Scope != DefaultScope {
|
||||
t.Errorf("g.Scope = %q; want %q", g.Scope, DefaultScope)
|
||||
}
|
||||
}
|
||||
//
|
||||
// func TestLoad(t *testing.T) {
|
||||
// conf := "https://github.com?client_id=client&client_secret=secret&scope=scope1,scope2"
|
||||
//
|
||||
// g := Load(conf)
|
||||
// if g.URL != "https://github.com" {
|
||||
// t.Errorf("g.URL = %q; want https://github.com", g.URL)
|
||||
// }
|
||||
// if g.Client != "client" {
|
||||
// t.Errorf("g.Client = %q; want client", g.Client)
|
||||
// }
|
||||
// if g.Secret != "secret" {
|
||||
// t.Errorf("g.Secret = %q; want secret", g.Secret)
|
||||
// }
|
||||
// if g.Scope != "scope1,scope2" {
|
||||
// t.Errorf("g.Scope = %q; want scope1,scope2", g.Scope)
|
||||
// }
|
||||
// if g.API != DefaultAPI {
|
||||
// t.Errorf("g.API = %q; want %q", g.API, DefaultAPI)
|
||||
// }
|
||||
// if g.MergeRef != DefaultMergeRef {
|
||||
// t.Errorf("g.MergeRef = %q; want %q", g.MergeRef, DefaultMergeRef)
|
||||
// }
|
||||
//
|
||||
// g = Load("")
|
||||
// if g.Scope != DefaultScope {
|
||||
// t.Errorf("g.Scope = %q; want %q", g.Scope, DefaultScope)
|
||||
// }
|
||||
// }
|
||||
|
|
|
@ -72,53 +72,6 @@ func GetRepo(client *github.Client, owner, repo string) (*github.Repository, err
|
|||
return r, err
|
||||
}
|
||||
|
||||
// GetAllRepos is a helper function that returns an aggregated list
|
||||
// of all user and organization repositories.
|
||||
func GetAllRepos(client *github.Client) ([]github.Repository, error) {
|
||||
orgs, err := GetOrgs(client)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
repos, err := GetUserRepos(client)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, org := range orgs {
|
||||
list, err := GetOrgRepos(client, *org.Login)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
repos = append(repos, list...)
|
||||
}
|
||||
|
||||
return repos, nil
|
||||
}
|
||||
|
||||
// GetSubscriptions is a helper function that returns an aggregated list
|
||||
// of all user and organization repositories.
|
||||
// func GetSubscriptions(client *github.Client) ([]github.Repository, error) {
|
||||
// var repos []github.Repository
|
||||
// var opts = github.ListOptions{}
|
||||
// opts.PerPage = 100
|
||||
// opts.Page = 1
|
||||
|
||||
// // loop through user repository list
|
||||
// for opts.Page > 0 {
|
||||
// list, resp, err := client.Activity.ListWatched(""), &opts)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
// repos = append(repos, list...)
|
||||
|
||||
// // increment the next page to retrieve
|
||||
// opts.Page = resp.NextPage
|
||||
// }
|
||||
|
||||
// return repos, nil
|
||||
// }
|
||||
|
||||
// GetUserRepos is a helper function that returns a list of
|
||||
// all user repositories. Paginated results are aggregated into
|
||||
// a single list.
|
||||
|
@ -143,30 +96,6 @@ func GetUserRepos(client *github.Client) ([]github.Repository, error) {
|
|||
return repos, nil
|
||||
}
|
||||
|
||||
// GetOrgRepos is a helper function that returns a list of
|
||||
// all org repositories. Paginated results are aggregated into
|
||||
// a single list.
|
||||
func GetOrgRepos(client *github.Client, org string) ([]github.Repository, error) {
|
||||
var repos []github.Repository
|
||||
var opts = github.RepositoryListByOrgOptions{}
|
||||
opts.PerPage = 100
|
||||
opts.Page = 1
|
||||
|
||||
// loop through user repository list
|
||||
for opts.Page > 0 {
|
||||
list, resp, err := client.Repositories.ListByOrg(org, &opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
repos = append(repos, list...)
|
||||
|
||||
// increment the next page to retrieve
|
||||
opts.Page = resp.NextPage
|
||||
}
|
||||
|
||||
return repos, nil
|
||||
}
|
||||
|
||||
// GetOrgs is a helper function that returns a list of
|
||||
// all orgs that a user belongs to.
|
||||
func GetOrgs(client *github.Client) ([]github.Organization, error) {
|
||||
|
@ -250,70 +179,6 @@ func CreateUpdateHook(client *github.Client, owner, name, url string) (*github.H
|
|||
return CreateHook(client, owner, name, url)
|
||||
}
|
||||
|
||||
// GetKey is a heper function that retrieves a public Key by
|
||||
// title. To do this, it will retrieve a list of all keys
|
||||
// and iterate through the list.
|
||||
func GetKey(client *github.Client, owner, name, title string) (*github.Key, error) {
|
||||
keys, _, err := client.Repositories.ListKeys(owner, name, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, key := range keys {
|
||||
if *key.Title == title {
|
||||
return &key, nil
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// DeleteKey is a helper function that deletes a deploy key
|
||||
// for the specified repository.
|
||||
func DeleteKey(client *github.Client, owner, name, title string) error {
|
||||
var k, err = GetKey(client, owner, name, title)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if k == nil {
|
||||
return nil
|
||||
}
|
||||
_, err = client.Repositories.DeleteKey(owner, name, *k.ID)
|
||||
return err
|
||||
}
|
||||
|
||||
// CreateKey is a helper function that creates a deploy key
|
||||
// for the specified repository.
|
||||
func CreateKey(client *github.Client, owner, name, title, key string) (*github.Key, error) {
|
||||
var k = new(github.Key)
|
||||
k.Title = github.String(title)
|
||||
k.Key = github.String(key)
|
||||
created, _, err := client.Repositories.CreateKey(owner, name, k)
|
||||
return created, err
|
||||
}
|
||||
|
||||
// CreateUpdateKey is a helper function that creates a deployment key
|
||||
// for the specified repository if it does not already exist, otherwise
|
||||
// it updates the existing key
|
||||
func CreateUpdateKey(client *github.Client, owner, name, title, key string) (*github.Key, error) {
|
||||
var k, _ = GetKey(client, owner, name, title)
|
||||
if k != nil {
|
||||
k.Title = github.String(title)
|
||||
k.Key = github.String(key)
|
||||
client.Repositories.DeleteKey(owner, name, *k.ID)
|
||||
}
|
||||
|
||||
return CreateKey(client, owner, name, title, key)
|
||||
}
|
||||
|
||||
// GetFile is a heper function that retrieves a file from
|
||||
// GitHub and returns its contents in byte array format.
|
||||
func GetFile(client *github.Client, owner, name, path, ref string) ([]byte, error) {
|
||||
|
@ -344,25 +209,3 @@ func GetPayload(req *http.Request) []byte {
|
|||
}
|
||||
return []byte(payload)
|
||||
}
|
||||
|
||||
// UserBelongsToOrg returns true if the currently authenticated user is a
|
||||
// member of any of the organizations provided.
|
||||
func UserBelongsToOrg(client *github.Client, permittedOrgs []string) (bool, error) {
|
||||
userOrgs, err := GetOrgs(client)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
userOrgSet := make(map[string]struct{}, len(userOrgs))
|
||||
for _, org := range userOrgs {
|
||||
userOrgSet[*org.Login] = struct{}{}
|
||||
}
|
||||
|
||||
for _, org := range permittedOrgs {
|
||||
if _, ok := userOrgSet[org]; ok {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/drone/drone/model"
|
||||
"github.com/drone/drone/remote"
|
||||
"github.com/drone/drone/shared/httputil"
|
||||
"github.com/drone/drone/shared/oauth2"
|
||||
"github.com/drone/drone/shared/token"
|
||||
|
@ -34,6 +35,16 @@ type Gitlab struct {
|
|||
Search bool
|
||||
}
|
||||
|
||||
func New(url, client, secret string, private, skipverify bool) remote.Remote {
|
||||
return &Gitlab{
|
||||
URL: url,
|
||||
Client: client,
|
||||
Secret: secret,
|
||||
PrivateMode: private,
|
||||
SkipVerify: skipverify,
|
||||
}
|
||||
}
|
||||
|
||||
func Load(config string) *Gitlab {
|
||||
url_, err := url.Parse(config)
|
||||
if err != nil {
|
||||
|
|
|
@ -6,44 +6,33 @@ import (
|
|||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
|
||||
"github.com/drone/drone/model"
|
||||
"github.com/drone/drone/remote"
|
||||
"github.com/gogits/go-gogs-client"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
)
|
||||
|
||||
type Gogs struct {
|
||||
// Remote defines a remote implementation that integrates with Gogs, an open
|
||||
// source Git service written in Go. See https://gogs.io/
|
||||
type Remote struct {
|
||||
URL string
|
||||
Open bool
|
||||
PrivateMode bool
|
||||
SkipVerify bool
|
||||
}
|
||||
|
||||
func Load(config string) *Gogs {
|
||||
// parse the remote DSN configuration string
|
||||
url_, err := url.Parse(config)
|
||||
if err != nil {
|
||||
log.Fatalln("unable to parse remote dsn. %s", err)
|
||||
// New returns a Remote implementation that integrates with Gogs, an open
|
||||
// source Git service written in Go. See https://gogs.io/
|
||||
func New(url string, private, skipverify bool) remote.Remote {
|
||||
return &Remote{
|
||||
URL: url,
|
||||
PrivateMode: private,
|
||||
SkipVerify: skipverify,
|
||||
}
|
||||
params := url_.Query()
|
||||
url_.RawQuery = ""
|
||||
|
||||
// create the Githbub remote using parameters from
|
||||
// the parsed DSN configuration string.
|
||||
gogs := Gogs{}
|
||||
gogs.URL = url_.String()
|
||||
gogs.PrivateMode, _ = strconv.ParseBool(params.Get("private_mode"))
|
||||
gogs.SkipVerify, _ = strconv.ParseBool(params.Get("skip_verify"))
|
||||
gogs.Open, _ = strconv.ParseBool(params.Get("open"))
|
||||
|
||||
return &gogs
|
||||
}
|
||||
|
||||
// Login authenticates the session and returns the
|
||||
// remote user details.
|
||||
func (g *Gogs) Login(res http.ResponseWriter, req *http.Request) (*model.User, bool, error) {
|
||||
// Login authenticates the session and returns the authenticated user.
|
||||
func (g *Remote) Login(res http.ResponseWriter, req *http.Request) (*model.User, bool, error) {
|
||||
var (
|
||||
username = req.FormValue("username")
|
||||
password = req.FormValue("password")
|
||||
|
@ -91,17 +80,17 @@ func (g *Gogs) Login(res http.ResponseWriter, req *http.Request) (*model.User, b
|
|||
user.Login = userInfo.UserName
|
||||
user.Email = userInfo.Email
|
||||
user.Avatar = expandAvatar(g.URL, userInfo.AvatarUrl)
|
||||
return &user, g.Open, nil
|
||||
return &user, false, nil
|
||||
}
|
||||
|
||||
// Auth authenticates the session and returns the remote user
|
||||
// login for the given token and secret
|
||||
func (g *Gogs) Auth(token, secret string) (string, error) {
|
||||
func (g *Remote) Auth(token, secret string) (string, error) {
|
||||
return "", fmt.Errorf("Method not supported")
|
||||
}
|
||||
|
||||
// Repo fetches the named repository from the remote system.
|
||||
func (g *Gogs) Repo(u *model.User, owner, name string) (*model.Repo, error) {
|
||||
func (g *Remote) Repo(u *model.User, owner, name string) (*model.Repo, error) {
|
||||
client := NewGogsClient(g.URL, u.Token, g.SkipVerify)
|
||||
repos_, err := client.ListMyRepos()
|
||||
if err != nil {
|
||||
|
@ -119,7 +108,7 @@ func (g *Gogs) Repo(u *model.User, owner, name string) (*model.Repo, error) {
|
|||
}
|
||||
|
||||
// Repos fetches a list of repos from the remote system.
|
||||
func (g *Gogs) Repos(u *model.User) ([]*model.RepoLite, error) {
|
||||
func (g *Remote) Repos(u *model.User) ([]*model.RepoLite, error) {
|
||||
repos := []*model.RepoLite{}
|
||||
|
||||
client := NewGogsClient(g.URL, u.Token, g.SkipVerify)
|
||||
|
@ -137,7 +126,7 @@ func (g *Gogs) Repos(u *model.User) ([]*model.RepoLite, error) {
|
|||
|
||||
// Perm fetches the named repository permissions from
|
||||
// the remote system for the specified user.
|
||||
func (g *Gogs) Perm(u *model.User, owner, name string) (*model.Perm, error) {
|
||||
func (g *Remote) Perm(u *model.User, owner, name string) (*model.Perm, error) {
|
||||
client := NewGogsClient(g.URL, u.Token, g.SkipVerify)
|
||||
repos_, err := client.ListMyRepos()
|
||||
if err != nil {
|
||||
|
@ -156,7 +145,7 @@ func (g *Gogs) Perm(u *model.User, owner, name string) (*model.Perm, error) {
|
|||
}
|
||||
|
||||
// File fetches a file from the remote repository and returns in string format.
|
||||
func (g *Gogs) File(u *model.User, r *model.Repo, b *model.Build, f string) ([]byte, error) {
|
||||
func (g *Remote) File(u *model.User, r *model.Repo, b *model.Build, f string) ([]byte, error) {
|
||||
client := NewGogsClient(g.URL, u.Token, g.SkipVerify)
|
||||
cfg, err := client.GetFile(r.Owner, r.Name, b.Commit, f)
|
||||
return cfg, err
|
||||
|
@ -164,13 +153,13 @@ func (g *Gogs) File(u *model.User, r *model.Repo, b *model.Build, f string) ([]b
|
|||
|
||||
// Status sends the commit status to the remote system.
|
||||
// An example would be the GitHub pull request status.
|
||||
func (g *Gogs) Status(u *model.User, r *model.Repo, b *model.Build, link string) error {
|
||||
func (g *Remote) Status(u *model.User, r *model.Repo, b *model.Build, link string) error {
|
||||
return fmt.Errorf("Not Implemented")
|
||||
}
|
||||
|
||||
// Netrc returns a .netrc file that can be used to clone
|
||||
// private repositories from a remote system.
|
||||
func (g *Gogs) Netrc(u *model.User, r *model.Repo) (*model.Netrc, error) {
|
||||
func (g *Remote) Netrc(u *model.User, r *model.Repo) (*model.Netrc, error) {
|
||||
url_, err := url.Parse(g.URL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -188,7 +177,7 @@ func (g *Gogs) Netrc(u *model.User, r *model.Repo) (*model.Netrc, error) {
|
|||
|
||||
// Activate activates a repository by creating the post-commit hook and
|
||||
// adding the SSH deploy key, if applicable.
|
||||
func (g *Gogs) Activate(u *model.User, r *model.Repo, k *model.Key, link string) error {
|
||||
func (g *Remote) Activate(u *model.User, r *model.Repo, k *model.Key, link string) error {
|
||||
config := map[string]string{
|
||||
"url": link,
|
||||
"secret": r.Hash,
|
||||
|
@ -207,13 +196,13 @@ func (g *Gogs) Activate(u *model.User, r *model.Repo, k *model.Key, link string)
|
|||
|
||||
// Deactivate removes a repository by removing all the post-commit hooks
|
||||
// which are equal to link and removing the SSH deploy key.
|
||||
func (g *Gogs) Deactivate(u *model.User, r *model.Repo, link string) error {
|
||||
func (g *Remote) Deactivate(u *model.User, r *model.Repo, link string) error {
|
||||
return fmt.Errorf("Not Implemented")
|
||||
}
|
||||
|
||||
// Hook parses the post-commit hook from the Request body
|
||||
// and returns the required data in a standard format.
|
||||
func (g *Gogs) Hook(r *http.Request) (*model.Repo, *model.Build, error) {
|
||||
func (g *Remote) Hook(r *http.Request) (*model.Repo, *model.Build, error) {
|
||||
var (
|
||||
err error
|
||||
repo *model.Repo
|
||||
|
@ -246,6 +235,6 @@ func NewGogsClient(url, token string, skipVerify bool) *gogs.Client {
|
|||
return c
|
||||
}
|
||||
|
||||
func (g *Gogs) String() string {
|
||||
func (g *Remote) String() string {
|
||||
return "gogs"
|
||||
}
|
||||
|
|
|
@ -1,42 +1,32 @@
|
|||
package mock
|
||||
|
||||
import "github.com/stretchr/testify/mock"
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
import "net/http"
|
||||
import "github.com/drone/drone/model"
|
||||
"github.com/drone/drone/model"
|
||||
"github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
// This is an autogenerated mock type for the Remote type
|
||||
type Remote struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
func (_m *Remote) Login(w http.ResponseWriter, r *http.Request) (*model.User, bool, error) {
|
||||
ret := _m.Called(w, r)
|
||||
// Activate provides a mock function with given fields: u, r, k, link
|
||||
func (_m *Remote) Activate(u *model.User, r *model.Repo, k *model.Key, link string) error {
|
||||
ret := _m.Called(u, r, k, link)
|
||||
|
||||
var r0 *model.User
|
||||
if rf, ok := ret.Get(0).(func(http.ResponseWriter, *http.Request) *model.User); ok {
|
||||
r0 = rf(w, r)
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(*model.User, *model.Repo, *model.Key, string) error); ok {
|
||||
r0 = rf(u, r, k, link)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*model.User)
|
||||
}
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
var r1 bool
|
||||
if rf, ok := ret.Get(1).(func(http.ResponseWriter, *http.Request) bool); ok {
|
||||
r1 = rf(w, r)
|
||||
} else {
|
||||
r1 = ret.Get(1).(bool)
|
||||
}
|
||||
|
||||
var r2 error
|
||||
if rf, ok := ret.Get(2).(func(http.ResponseWriter, *http.Request) error); ok {
|
||||
r2 = rf(w, r)
|
||||
} else {
|
||||
r2 = ret.Error(2)
|
||||
}
|
||||
|
||||
return r0, r1, r2
|
||||
return r0
|
||||
}
|
||||
|
||||
// Auth provides a mock function with given fields: token, secret
|
||||
func (_m *Remote) Auth(token string, secret string) (string, error) {
|
||||
ret := _m.Called(token, secret)
|
||||
|
||||
|
@ -56,69 +46,22 @@ func (_m *Remote) Auth(token string, secret string) (string, error) {
|
|||
|
||||
return r0, r1
|
||||
}
|
||||
func (_m *Remote) Repo(u *model.User, owner string, repo string) (*model.Repo, error) {
|
||||
ret := _m.Called(u, owner, repo)
|
||||
|
||||
var r0 *model.Repo
|
||||
if rf, ok := ret.Get(0).(func(*model.User, string, string) *model.Repo); ok {
|
||||
r0 = rf(u, owner, repo)
|
||||
// Deactivate provides a mock function with given fields: u, r, link
|
||||
func (_m *Remote) Deactivate(u *model.User, r *model.Repo, link string) error {
|
||||
ret := _m.Called(u, r, link)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(*model.User, *model.Repo, string) error); ok {
|
||||
r0 = rf(u, r, link)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*model.Repo)
|
||||
}
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(*model.User, string, string) error); ok {
|
||||
r1 = rf(u, owner, repo)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
return r0
|
||||
}
|
||||
func (_m *Remote) Repos(u *model.User) ([]*model.RepoLite, error) {
|
||||
ret := _m.Called(u)
|
||||
|
||||
var r0 []*model.RepoLite
|
||||
if rf, ok := ret.Get(0).(func(*model.User) []*model.RepoLite); ok {
|
||||
r0 = rf(u)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]*model.RepoLite)
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
func (_m *Remote) Perm(u *model.User, owner string, repo string) (*model.Perm, error) {
|
||||
ret := _m.Called(u, owner, repo)
|
||||
|
||||
var r0 *model.Perm
|
||||
if rf, ok := ret.Get(0).(func(*model.User, string, string) *model.Perm); ok {
|
||||
r0 = rf(u, owner, repo)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*model.Perm)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(*model.User, string, string) error); ok {
|
||||
r1 = rf(u, owner, repo)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
// File provides a mock function with given fields: u, r, b, f
|
||||
func (_m *Remote) File(u *model.User, r *model.Repo, b *model.Build, f string) ([]byte, error) {
|
||||
ret := _m.Called(u, r, b, f)
|
||||
|
||||
|
@ -140,63 +83,8 @@ func (_m *Remote) File(u *model.User, r *model.Repo, b *model.Build, f string) (
|
|||
|
||||
return r0, r1
|
||||
}
|
||||
func (_m *Remote) Status(u *model.User, r *model.Repo, b *model.Build, link string) error {
|
||||
ret := _m.Called(u, r, b, link)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(*model.User, *model.Repo, *model.Build, string) error); ok {
|
||||
r0 = rf(u, r, b, link)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
func (_m *Remote) Netrc(u *model.User, r *model.Repo) (*model.Netrc, error) {
|
||||
ret := _m.Called(u, r)
|
||||
|
||||
var r0 *model.Netrc
|
||||
if rf, ok := ret.Get(0).(func(*model.User, *model.Repo) *model.Netrc); ok {
|
||||
r0 = rf(u, r)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*model.Netrc)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(*model.User, *model.Repo) error); ok {
|
||||
r1 = rf(u, r)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
func (_m *Remote) Activate(u *model.User, r *model.Repo, k *model.Key, link string) error {
|
||||
ret := _m.Called(u, r, k, link)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(*model.User, *model.Repo, *model.Key, string) error); ok {
|
||||
r0 = rf(u, r, k, link)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
func (_m *Remote) Deactivate(u *model.User, r *model.Repo, link string) error {
|
||||
ret := _m.Called(u, r, link)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(*model.User, *model.Repo, string) error); ok {
|
||||
r0 = rf(u, r, link)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
// Hook provides a mock function with given fields: r
|
||||
func (_m *Remote) Hook(r *http.Request) (*model.Repo, *model.Build, error) {
|
||||
ret := _m.Called(r)
|
||||
|
||||
|
@ -227,3 +115,155 @@ func (_m *Remote) Hook(r *http.Request) (*model.Repo, *model.Build, error) {
|
|||
|
||||
return r0, r1, r2
|
||||
}
|
||||
|
||||
// Login provides a mock function with given fields: w, r
|
||||
func (_m *Remote) Login(w http.ResponseWriter, r *http.Request) (*model.User, error) {
|
||||
ret := _m.Called(w, r)
|
||||
|
||||
var r0 *model.User
|
||||
if rf, ok := ret.Get(0).(func(http.ResponseWriter, *http.Request) *model.User); ok {
|
||||
r0 = rf(w, r)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*model.User)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(http.ResponseWriter, *http.Request) error); ok {
|
||||
r1 = rf(w, r)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// Netrc provides a mock function with given fields: u, r
|
||||
func (_m *Remote) Netrc(u *model.User, r *model.Repo) (*model.Netrc, error) {
|
||||
ret := _m.Called(u, r)
|
||||
|
||||
var r0 *model.Netrc
|
||||
if rf, ok := ret.Get(0).(func(*model.User, *model.Repo) *model.Netrc); ok {
|
||||
r0 = rf(u, r)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*model.Netrc)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(*model.User, *model.Repo) error); ok {
|
||||
r1 = rf(u, r)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// Perm provides a mock function with given fields: u, owner, repo
|
||||
func (_m *Remote) Perm(u *model.User, owner string, repo string) (*model.Perm, error) {
|
||||
ret := _m.Called(u, owner, repo)
|
||||
|
||||
var r0 *model.Perm
|
||||
if rf, ok := ret.Get(0).(func(*model.User, string, string) *model.Perm); ok {
|
||||
r0 = rf(u, owner, repo)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*model.Perm)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(*model.User, string, string) error); ok {
|
||||
r1 = rf(u, owner, repo)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// Repo provides a mock function with given fields: u, owner, repo
|
||||
func (_m *Remote) Repo(u *model.User, owner string, repo string) (*model.Repo, error) {
|
||||
ret := _m.Called(u, owner, repo)
|
||||
|
||||
var r0 *model.Repo
|
||||
if rf, ok := ret.Get(0).(func(*model.User, string, string) *model.Repo); ok {
|
||||
r0 = rf(u, owner, repo)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*model.Repo)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(*model.User, string, string) error); ok {
|
||||
r1 = rf(u, owner, repo)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// Repos provides a mock function with given fields: u
|
||||
func (_m *Remote) Repos(u *model.User) ([]*model.RepoLite, error) {
|
||||
ret := _m.Called(u)
|
||||
|
||||
var r0 []*model.RepoLite
|
||||
if rf, ok := ret.Get(0).(func(*model.User) []*model.RepoLite); ok {
|
||||
r0 = rf(u)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]*model.RepoLite)
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// Status provides a mock function with given fields: u, r, b, link
|
||||
func (_m *Remote) Status(u *model.User, r *model.Repo, b *model.Build, link string) error {
|
||||
ret := _m.Called(u, r, b, link)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(*model.User, *model.Repo, *model.Build, string) error); ok {
|
||||
r0 = rf(u, r, b, link)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
|
|
@ -13,12 +13,15 @@ import (
|
|||
type Remote interface {
|
||||
// Login authenticates the session and returns the
|
||||
// remote user details.
|
||||
Login(w http.ResponseWriter, r *http.Request) (*model.User, bool, error)
|
||||
Login(w http.ResponseWriter, r *http.Request) (*model.User, error)
|
||||
|
||||
// Auth authenticates the session and returns the remote user
|
||||
// login for the given token and secret
|
||||
Auth(token, secret string) (string, error)
|
||||
|
||||
// Teams fetches a list of team memberships from the remote system.
|
||||
Teams(u *model.User) ([]*model.Team, error)
|
||||
|
||||
// Repo fetches the named repository from the remote system.
|
||||
Repo(u *model.User, owner, repo string) (*model.Repo, error)
|
||||
|
||||
|
@ -49,21 +52,21 @@ type Remote interface {
|
|||
// which are equal to link and removing the SSH deploy key.
|
||||
Deactivate(u *model.User, r *model.Repo, link string) error
|
||||
|
||||
// Hook parses the post-commit hook from the Request body
|
||||
// and returns the required data in a standard format.
|
||||
// Hook parses the post-commit hook from the Request body and returns the
|
||||
// required data in a standard format.
|
||||
Hook(r *http.Request) (*model.Repo, *model.Build, error)
|
||||
}
|
||||
|
||||
// Refresher refreshes an oauth token and expiration for the given user. It
|
||||
// returns true if the token was refreshed, false if the token was not refreshed,
|
||||
// and error if it failed to refersh.
|
||||
type Refresher interface {
|
||||
// Refresh refreshes an oauth token and expiration for the given
|
||||
// user. It returns true if the token was refreshed, false if the
|
||||
// token was not refreshed, and error if it failed to refersh.
|
||||
Refresh(*model.User) (bool, error)
|
||||
}
|
||||
|
||||
// Login authenticates the session and returns the
|
||||
// remote user details.
|
||||
func Login(c context.Context, w http.ResponseWriter, r *http.Request) (*model.User, bool, error) {
|
||||
func Login(c context.Context, w http.ResponseWriter, r *http.Request) (*model.User, error) {
|
||||
return FromContext(c).Login(w, r)
|
||||
}
|
||||
|
||||
|
@ -73,6 +76,11 @@ func Auth(c context.Context, token, secret string) (string, error) {
|
|||
return FromContext(c).Auth(token, secret)
|
||||
}
|
||||
|
||||
// Teams fetches a list of team memberships from the remote system.
|
||||
func Teams(c context.Context, u *model.User) ([]*model.Team, error) {
|
||||
return FromContext(c).Teams(u)
|
||||
}
|
||||
|
||||
// Repo fetches the named repository from the remote system.
|
||||
func Repo(c context.Context, u *model.User, owner, repo string) (*model.Repo, error) {
|
||||
return FromContext(c).Repo(u, owner, repo)
|
||||
|
|
|
@ -9,7 +9,7 @@ import (
|
|||
)
|
||||
|
||||
var (
|
||||
secret = envflag.String("AGENT_SECRET", "", "")
|
||||
secret = envflag.String("DRONE_AGENT_SECRET", "", "")
|
||||
noauth = envflag.Bool("AGENT_NO_AUTH", false, "")
|
||||
)
|
||||
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"github.com/drone/drone/bus"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func Bus() gin.HandlerFunc {
|
||||
bus_ := bus.New()
|
||||
return func(c *gin.Context) {
|
||||
bus.ToContext(c, bus_)
|
||||
c.Next()
|
||||
}
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/drone/drone/cache"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/ianschenck/envflag"
|
||||
)
|
||||
|
||||
var ttl = envflag.Duration("CACHE_TTL", time.Minute*15, "")
|
||||
|
||||
// Cache is a middleware function that initializes the Cache and attaches to
|
||||
// the context of every http.Request.
|
||||
func Cache() gin.HandlerFunc {
|
||||
cc := cache.NewTTL(*ttl)
|
||||
return func(c *gin.Context) {
|
||||
cache.ToContext(c, cc)
|
||||
c.Next()
|
||||
}
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"github.com/drone/drone/queue"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func Queue() gin.HandlerFunc {
|
||||
queue_ := queue.New()
|
||||
return func(c *gin.Context) {
|
||||
queue.ToContext(c, queue_)
|
||||
c.Next()
|
||||
}
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"github.com/drone/drone/remote"
|
||||
"github.com/drone/drone/remote/bitbucket"
|
||||
"github.com/drone/drone/remote/github"
|
||||
"github.com/drone/drone/remote/gitlab"
|
||||
"github.com/drone/drone/remote/gogs"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/ianschenck/envflag"
|
||||
"github.com/drone/drone/remote/bitbucketserver"
|
||||
)
|
||||
|
||||
var (
|
||||
driver = envflag.String("REMOTE_DRIVER", "", "")
|
||||
config = envflag.String("REMOTE_CONFIG", "", "")
|
||||
)
|
||||
|
||||
// Remote is a middleware function that initializes the Remote and attaches to
|
||||
// the context of every http.Request.
|
||||
func Remote() gin.HandlerFunc {
|
||||
|
||||
logrus.Infof("using remote driver %s", *driver)
|
||||
logrus.Infof("using remote config %s", *config)
|
||||
|
||||
var remote_ remote.Remote
|
||||
switch *driver {
|
||||
case "github":
|
||||
remote_ = github.Load(*config)
|
||||
case "bitbucket":
|
||||
remote_ = bitbucket.Load(*config)
|
||||
case "gogs":
|
||||
remote_ = gogs.Load(*config)
|
||||
case "gitlab":
|
||||
remote_ = gitlab.Load(*config)
|
||||
case "bitbucketserver":
|
||||
remote_ = bitbucketserver.Load(*config)
|
||||
default:
|
||||
logrus.Fatalln("remote configuration not found")
|
||||
}
|
||||
|
||||
return func(c *gin.Context) {
|
||||
remote.ToContext(c, remote_)
|
||||
c.Next()
|
||||
}
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"github.com/drone/drone/store"
|
||||
"github.com/drone/drone/store/datastore"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/ianschenck/envflag"
|
||||
)
|
||||
|
||||
var (
|
||||
database = envflag.String("DATABASE_DRIVER", "sqlite3", "")
|
||||
datasource = envflag.String("DATABASE_CONFIG", "drone.sqlite", "")
|
||||
)
|
||||
|
||||
// Store is a middleware function that initializes the Datastore and attaches to
|
||||
// the context of every http.Request.
|
||||
func Store() gin.HandlerFunc {
|
||||
db := datastore.New(*database, *datasource)
|
||||
|
||||
logrus.Infof("using database driver %s", *database)
|
||||
logrus.Infof("using database config %s", *datasource)
|
||||
|
||||
return func(c *gin.Context) {
|
||||
store.ToContext(c, db)
|
||||
c.Next()
|
||||
}
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"github.com/drone/drone/stream"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func Stream() gin.HandlerFunc {
|
||||
stream_ := stream.New()
|
||||
return func(c *gin.Context) {
|
||||
stream.ToContext(c, stream_)
|
||||
c.Next()
|
||||
}
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"github.com/drone/drone/version"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// Version is a middleware function that appends the Drone
|
||||
// version information to the HTTP response. This is intended
|
||||
// for debugging and troubleshooting.
|
||||
func Version(c *gin.Context) {
|
||||
c.Header("X-DRONE-VERSION", version.Version)
|
||||
c.Next()
|
||||
}
|
397
router/router.go
397
router/router.go
|
@ -1,200 +1,201 @@
|
|||
package router
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"github.com/drone/drone/api"
|
||||
"github.com/drone/drone/router/middleware"
|
||||
"github.com/drone/drone/router/middleware/header"
|
||||
"github.com/drone/drone/router/middleware/session"
|
||||
"github.com/drone/drone/router/middleware/token"
|
||||
"github.com/drone/drone/static"
|
||||
"github.com/drone/drone/template"
|
||||
"github.com/drone/drone/web"
|
||||
)
|
||||
|
||||
func Load(middlewares ...gin.HandlerFunc) http.Handler {
|
||||
e := gin.New()
|
||||
e.Use(gin.Recovery())
|
||||
|
||||
e.SetHTMLTemplate(template.Load())
|
||||
e.StaticFS("/static", static.FileSystem())
|
||||
|
||||
e.Use(header.NoCache)
|
||||
e.Use(header.Options)
|
||||
e.Use(header.Secure)
|
||||
e.Use(middlewares...)
|
||||
e.Use(session.SetUser())
|
||||
e.Use(token.Refresh)
|
||||
|
||||
e.GET("/", web.ShowIndex)
|
||||
e.GET("/repos", web.ShowAllRepos)
|
||||
e.GET("/login", web.ShowLogin)
|
||||
e.GET("/login/form", web.ShowLoginForm)
|
||||
e.GET("/logout", web.GetLogout)
|
||||
|
||||
settings := e.Group("/settings")
|
||||
{
|
||||
settings.Use(session.MustUser())
|
||||
settings.GET("/profile", web.ShowUser)
|
||||
}
|
||||
repo := e.Group("/repos/:owner/:name")
|
||||
{
|
||||
repo.Use(session.SetRepo())
|
||||
repo.Use(session.SetPerm())
|
||||
repo.Use(session.MustPull)
|
||||
|
||||
repo.GET("", web.ShowRepo)
|
||||
repo.GET("/builds/:number", web.ShowBuild)
|
||||
repo.GET("/builds/:number/:job", web.ShowBuild)
|
||||
|
||||
repo_settings := repo.Group("/settings")
|
||||
{
|
||||
repo_settings.GET("", session.MustPush, web.ShowRepoConf)
|
||||
repo_settings.GET("/encrypt", session.MustPush, web.ShowRepoEncrypt)
|
||||
repo_settings.GET("/badges", web.ShowRepoBadges)
|
||||
}
|
||||
}
|
||||
|
||||
user := e.Group("/api/user")
|
||||
{
|
||||
user.Use(session.MustUser())
|
||||
user.GET("", api.GetSelf)
|
||||
user.GET("/feed", api.GetFeed)
|
||||
user.GET("/repos", api.GetRepos)
|
||||
user.GET("/repos/remote", api.GetRemoteRepos)
|
||||
user.POST("/token", api.PostToken)
|
||||
user.DELETE("/token", api.DeleteToken)
|
||||
}
|
||||
|
||||
users := e.Group("/api/users")
|
||||
{
|
||||
users.Use(session.MustAdmin())
|
||||
users.GET("", api.GetUsers)
|
||||
users.POST("", api.PostUser)
|
||||
users.GET("/:login", api.GetUser)
|
||||
users.PATCH("/:login", api.PatchUser)
|
||||
users.DELETE("/:login", api.DeleteUser)
|
||||
}
|
||||
|
||||
repos := e.Group("/api/repos/:owner/:name")
|
||||
{
|
||||
repos.POST("", api.PostRepo)
|
||||
|
||||
repo := repos.Group("")
|
||||
{
|
||||
repo.Use(session.SetRepo())
|
||||
repo.Use(session.SetPerm())
|
||||
repo.Use(session.MustPull)
|
||||
|
||||
repo.GET("", api.GetRepo)
|
||||
repo.GET("/key", api.GetRepoKey)
|
||||
repo.POST("/key", api.PostRepoKey)
|
||||
repo.GET("/builds", api.GetBuilds)
|
||||
repo.GET("/builds/:number", api.GetBuild)
|
||||
repo.GET("/logs/:number/:job", api.GetBuildLogs)
|
||||
repo.POST("/sign", session.MustPush, api.Sign)
|
||||
|
||||
repo.POST("/secrets", session.MustPush, api.PostSecret)
|
||||
repo.DELETE("/secrets/:secret", session.MustPush, api.DeleteSecret)
|
||||
|
||||
// requires authenticated user
|
||||
repo.POST("/encrypt", session.MustUser(), api.PostSecure)
|
||||
|
||||
// requires push permissions
|
||||
repo.PATCH("", session.MustPush, api.PatchRepo)
|
||||
repo.DELETE("", session.MustPush, api.DeleteRepo)
|
||||
|
||||
repo.POST("/builds/:number", session.MustPush, api.PostBuild)
|
||||
repo.DELETE("/builds/:number/:job", session.MustPush, api.DeleteBuild)
|
||||
}
|
||||
}
|
||||
|
||||
badges := e.Group("/api/badges/:owner/:name")
|
||||
{
|
||||
badges.GET("/status.svg", web.GetBadge)
|
||||
badges.GET("/cc.xml", web.GetCC)
|
||||
}
|
||||
|
||||
e.POST("/hook", web.PostHook)
|
||||
e.POST("/api/hook", web.PostHook)
|
||||
|
||||
stream := e.Group("/api/stream")
|
||||
{
|
||||
stream.Use(session.SetRepo())
|
||||
stream.Use(session.SetPerm())
|
||||
stream.Use(session.MustPull)
|
||||
|
||||
stream.GET("/:owner/:name", web.GetRepoEvents)
|
||||
stream.GET("/:owner/:name/:build/:number", web.GetStream)
|
||||
}
|
||||
|
||||
bots := e.Group("/bots")
|
||||
{
|
||||
bots.Use(session.MustUser())
|
||||
bots.POST("/slack", web.Slack)
|
||||
bots.POST("/slack/:command", web.Slack)
|
||||
}
|
||||
|
||||
auth := e.Group("/authorize")
|
||||
{
|
||||
auth.GET("", web.GetLogin)
|
||||
auth.POST("", web.GetLogin)
|
||||
auth.POST("/token", web.GetLoginToken)
|
||||
}
|
||||
|
||||
queue := e.Group("/api/queue")
|
||||
{
|
||||
queue.Use(middleware.AgentMust())
|
||||
queue.POST("/pull", api.Pull)
|
||||
queue.POST("/pull/:os/:arch", api.Pull)
|
||||
queue.POST("/wait/:id", api.Wait)
|
||||
queue.POST("/stream/:id", api.Stream)
|
||||
queue.POST("/status/:id", api.Update)
|
||||
}
|
||||
|
||||
gitlab := e.Group("/gitlab/:owner/:name")
|
||||
{
|
||||
gitlab.Use(session.SetRepo())
|
||||
gitlab.GET("/commits/:sha", web.GetCommit)
|
||||
gitlab.GET("/pulls/:number", web.GetPullRequest)
|
||||
|
||||
redirects := gitlab.Group("/redirect")
|
||||
{
|
||||
redirects.GET("/commits/:sha", web.RedirectSha)
|
||||
redirects.GET("/pulls/:number", web.RedirectPullRequest)
|
||||
}
|
||||
}
|
||||
|
||||
return normalize(e)
|
||||
}
|
||||
|
||||
// normalize is a helper function to work around the following
|
||||
// issue with gin. https://github.com/gin-gonic/gin/issues/388
|
||||
func normalize(h http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
parts := strings.Split(r.URL.Path, "/")[1:]
|
||||
switch parts[0] {
|
||||
case "settings", "bots", "repos", "api", "login", "logout", "", "authorize", "hook", "static", "gitlab":
|
||||
// no-op
|
||||
default:
|
||||
|
||||
if len(parts) > 2 && parts[2] != "settings" {
|
||||
parts = append(parts[:2], append([]string{"builds"}, parts[2:]...)...)
|
||||
}
|
||||
|
||||
// prefix the URL with /repo so that it
|
||||
// can be effectively routed.
|
||||
parts = append([]string{"", "repos"}, parts...)
|
||||
|
||||
// reconstruct the path
|
||||
r.URL.Path = strings.Join(parts, "/")
|
||||
}
|
||||
|
||||
h.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
//
|
||||
// import (
|
||||
// "net/http"
|
||||
// "strings"
|
||||
//
|
||||
// "github.com/gin-gonic/gin"
|
||||
//
|
||||
// "github.com/drone/drone/api"
|
||||
// "github.com/drone/drone/router/middleware"
|
||||
// "github.com/drone/drone/router/middleware/header"
|
||||
// "github.com/drone/drone/router/middleware/session"
|
||||
// "github.com/drone/drone/router/middleware/token"
|
||||
// "github.com/drone/drone/static"
|
||||
// "github.com/drone/drone/template"
|
||||
// "github.com/drone/drone/web"
|
||||
// )
|
||||
//
|
||||
// func Load(middlewares ...gin.HandlerFunc) http.Handler {
|
||||
// e := gin.New()
|
||||
// e.Use(gin.Recovery())
|
||||
//
|
||||
// e.SetHTMLTemplate(template.Load())
|
||||
// e.StaticFS("/static", static.FileSystem())
|
||||
//
|
||||
// e.Use(header.NoCache)
|
||||
// e.Use(header.Options)
|
||||
// e.Use(header.Secure)
|
||||
// e.Use(middlewares...)
|
||||
// e.Use(session.SetUser())
|
||||
// e.Use(token.Refresh)
|
||||
//
|
||||
// e.GET("/", web.ShowIndex)
|
||||
// e.GET("/repos", web.ShowAllRepos)
|
||||
// e.GET("/login", web.ShowLogin)
|
||||
// e.GET("/login/form", web.ShowLoginForm)
|
||||
// e.GET("/logout", web.GetLogout)
|
||||
//
|
||||
// settings := e.Group("/settings")
|
||||
// {
|
||||
// settings.Use(session.MustUser())
|
||||
// settings.GET("/profile", web.ShowUser)
|
||||
// }
|
||||
// repo := e.Group("/repos/:owner/:name")
|
||||
// {
|
||||
// repo.Use(session.SetRepo())
|
||||
// repo.Use(session.SetPerm())
|
||||
// repo.Use(session.MustPull)
|
||||
//
|
||||
// repo.GET("", web.ShowRepo)
|
||||
// repo.GET("/builds/:number", web.ShowBuild)
|
||||
// repo.GET("/builds/:number/:job", web.ShowBuild)
|
||||
//
|
||||
// repo_settings := repo.Group("/settings")
|
||||
// {
|
||||
// repo_settings.GET("", session.MustPush, web.ShowRepoConf)
|
||||
// repo_settings.GET("/encrypt", session.MustPush, web.ShowRepoEncrypt)
|
||||
// repo_settings.GET("/badges", web.ShowRepoBadges)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// user := e.Group("/api/user")
|
||||
// {
|
||||
// user.Use(session.MustUser())
|
||||
// user.GET("", api.GetSelf)
|
||||
// user.GET("/feed", api.GetFeed)
|
||||
// user.GET("/repos", api.GetRepos)
|
||||
// user.GET("/repos/remote", api.GetRemoteRepos)
|
||||
// user.POST("/token", api.PostToken)
|
||||
// user.DELETE("/token", api.DeleteToken)
|
||||
// }
|
||||
//
|
||||
// users := e.Group("/api/users")
|
||||
// {
|
||||
// users.Use(session.MustAdmin())
|
||||
// users.GET("", api.GetUsers)
|
||||
// users.POST("", api.PostUser)
|
||||
// users.GET("/:login", api.GetUser)
|
||||
// users.PATCH("/:login", api.PatchUser)
|
||||
// users.DELETE("/:login", api.DeleteUser)
|
||||
// }
|
||||
//
|
||||
// repos := e.Group("/api/repos/:owner/:name")
|
||||
// {
|
||||
// repos.POST("", api.PostRepo)
|
||||
//
|
||||
// repo := repos.Group("")
|
||||
// {
|
||||
// repo.Use(session.SetRepo())
|
||||
// repo.Use(session.SetPerm())
|
||||
// repo.Use(session.MustPull)
|
||||
//
|
||||
// repo.GET("", api.GetRepo)
|
||||
// repo.GET("/key", api.GetRepoKey)
|
||||
// repo.POST("/key", api.PostRepoKey)
|
||||
// repo.GET("/builds", api.GetBuilds)
|
||||
// repo.GET("/builds/:number", api.GetBuild)
|
||||
// repo.GET("/logs/:number/:job", api.GetBuildLogs)
|
||||
// repo.POST("/sign", session.MustPush, api.Sign)
|
||||
//
|
||||
// repo.POST("/secrets", session.MustPush, api.PostSecret)
|
||||
// repo.DELETE("/secrets/:secret", session.MustPush, api.DeleteSecret)
|
||||
//
|
||||
// // requires authenticated user
|
||||
// repo.POST("/encrypt", session.MustUser(), api.PostSecure)
|
||||
//
|
||||
// // requires push permissions
|
||||
// repo.PATCH("", session.MustPush, api.PatchRepo)
|
||||
// repo.DELETE("", session.MustPush, api.DeleteRepo)
|
||||
//
|
||||
// repo.POST("/builds/:number", session.MustPush, api.PostBuild)
|
||||
// repo.DELETE("/builds/:number/:job", session.MustPush, api.DeleteBuild)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// badges := e.Group("/api/badges/:owner/:name")
|
||||
// {
|
||||
// badges.GET("/status.svg", web.GetBadge)
|
||||
// badges.GET("/cc.xml", web.GetCC)
|
||||
// }
|
||||
//
|
||||
// e.POST("/hook", web.PostHook)
|
||||
// e.POST("/api/hook", web.PostHook)
|
||||
//
|
||||
// stream := e.Group("/api/stream")
|
||||
// {
|
||||
// stream.Use(session.SetRepo())
|
||||
// stream.Use(session.SetPerm())
|
||||
// stream.Use(session.MustPull)
|
||||
//
|
||||
// stream.GET("/:owner/:name", web.GetRepoEvents)
|
||||
// stream.GET("/:owner/:name/:build/:number", web.GetStream)
|
||||
// }
|
||||
//
|
||||
// bots := e.Group("/bots")
|
||||
// {
|
||||
// bots.Use(session.MustUser())
|
||||
// bots.POST("/slack", web.Slack)
|
||||
// bots.POST("/slack/:command", web.Slack)
|
||||
// }
|
||||
//
|
||||
// auth := e.Group("/authorize")
|
||||
// {
|
||||
// auth.GET("", web.GetLogin)
|
||||
// auth.POST("", web.GetLogin)
|
||||
// auth.POST("/token", web.GetLoginToken)
|
||||
// }
|
||||
//
|
||||
// queue := e.Group("/api/queue")
|
||||
// {
|
||||
// queue.Use(middleware.AgentMust())
|
||||
// queue.POST("/pull", api.Pull)
|
||||
// queue.POST("/pull/:os/:arch", api.Pull)
|
||||
// queue.POST("/wait/:id", api.Wait)
|
||||
// queue.POST("/stream/:id", api.Stream)
|
||||
// queue.POST("/status/:id", api.Update)
|
||||
// }
|
||||
//
|
||||
// gitlab := e.Group("/gitlab/:owner/:name")
|
||||
// {
|
||||
// gitlab.Use(session.SetRepo())
|
||||
// gitlab.GET("/commits/:sha", web.GetCommit)
|
||||
// gitlab.GET("/pulls/:number", web.GetPullRequest)
|
||||
//
|
||||
// redirects := gitlab.Group("/redirect")
|
||||
// {
|
||||
// redirects.GET("/commits/:sha", web.RedirectSha)
|
||||
// redirects.GET("/pulls/:number", web.RedirectPullRequest)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// return normalize(e)
|
||||
// }
|
||||
//
|
||||
// // normalize is a helper function to work around the following
|
||||
// // issue with gin. https://github.com/gin-gonic/gin/issues/388
|
||||
// func normalize(h http.Handler) http.Handler {
|
||||
// return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
//
|
||||
// parts := strings.Split(r.URL.Path, "/")[1:]
|
||||
// switch parts[0] {
|
||||
// case "settings", "bots", "repos", "api", "login", "logout", "", "authorize", "hook", "static", "gitlab":
|
||||
// // no-op
|
||||
// default:
|
||||
//
|
||||
// if len(parts) > 2 && parts[2] != "settings" {
|
||||
// parts = append(parts[:2], append([]string{"builds"}, parts[2:]...)...)
|
||||
// }
|
||||
//
|
||||
// // prefix the URL with /repo so that it
|
||||
// // can be effectively routed.
|
||||
// parts = append([]string{"", "repos"}, parts...)
|
||||
//
|
||||
// // reconstruct the path
|
||||
// r.URL.Path = strings.Join(parts, "/")
|
||||
// }
|
||||
//
|
||||
// h.ServeHTTP(w, r)
|
||||
// })
|
||||
// }
|
||||
|
|
79
server/handler.go
Normal file
79
server/handler.go
Normal file
|
@ -0,0 +1,79 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"github.com/drone/drone/bus"
|
||||
"github.com/drone/drone/cache"
|
||||
"github.com/drone/drone/queue"
|
||||
"github.com/drone/drone/remote"
|
||||
"github.com/drone/drone/store"
|
||||
"github.com/drone/drone/stream"
|
||||
"github.com/drone/drone/version"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// HandlerCache returns a HandlerFunc that passes a Cache to the Context.
|
||||
func HandlerCache(v cache.Cache) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
cache.ToContext(c, v)
|
||||
}
|
||||
}
|
||||
|
||||
// HandlerBus returns a HandlerFunc that passes a Bus to the Context.
|
||||
func HandlerBus(v bus.Bus) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
bus.ToContext(c, v)
|
||||
}
|
||||
}
|
||||
|
||||
// HandlerStore returns a HandlerFunc that passes a Store to the Context.
|
||||
func HandlerStore(v store.Store) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
store.ToContext(c, v)
|
||||
}
|
||||
}
|
||||
|
||||
// HandlerQueue returns a HandlerFunc that passes a Queue to the Context.
|
||||
func HandlerQueue(v queue.Queue) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
queue.ToContext(c, v)
|
||||
}
|
||||
}
|
||||
|
||||
// HandlerStream returns a HandlerFunc that passes a Stream to the Context.
|
||||
func HandlerStream(v stream.Stream) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
stream.ToContext(c, v)
|
||||
}
|
||||
}
|
||||
|
||||
// HandlerRemote returns a HandlerFunc that passes a Remote to the Context.
|
||||
func HandlerRemote(v remote.Remote) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
remote.ToContext(c, v)
|
||||
}
|
||||
}
|
||||
|
||||
// HandlerConfig returns a HandlerFunc that passes server Config to the Context.
|
||||
func HandlerConfig(v *Config) gin.HandlerFunc {
|
||||
const k = "config"
|
||||
return func(c *gin.Context) {
|
||||
c.Set(k, v)
|
||||
}
|
||||
}
|
||||
|
||||
// HandlerVersion returns a HandlerFunc that writes the Version information to
|
||||
// the http.Response as a the X-Drone-Version header.
|
||||
func HandlerVersion() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
c.Header("X-Drone-Version", version.Version)
|
||||
}
|
||||
}
|
||||
|
||||
// HandlerAgent returns a HandlerFunc that passes an Agent token to the Context.
|
||||
func HandlerAgent(v string) gin.HandlerFunc {
|
||||
const k = "agent"
|
||||
return func(c *gin.Context) {
|
||||
c.Set(k, v)
|
||||
}
|
||||
}
|
1
server/handler_test.go
Normal file
1
server/handler_test.go
Normal file
|
@ -0,0 +1 @@
|
|||
package server
|
243
server/server.go
Normal file
243
server/server.go
Normal file
|
@ -0,0 +1,243 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/drone/drone/api"
|
||||
"github.com/drone/drone/bus"
|
||||
"github.com/drone/drone/cache"
|
||||
"github.com/drone/drone/queue"
|
||||
"github.com/drone/drone/remote"
|
||||
"github.com/drone/drone/router/middleware"
|
||||
"github.com/drone/drone/router/middleware/header"
|
||||
"github.com/drone/drone/router/middleware/session"
|
||||
"github.com/drone/drone/router/middleware/token"
|
||||
"github.com/drone/drone/static"
|
||||
"github.com/drone/drone/store"
|
||||
"github.com/drone/drone/stream"
|
||||
"github.com/drone/drone/template"
|
||||
"github.com/drone/drone/web"
|
||||
|
||||
"github.com/gin-gonic/contrib/ginrus"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// Config defines system configuration parameters.
|
||||
type Config struct {
|
||||
Open bool // Enables open registration
|
||||
Yaml string // Customize the Yaml configuration file name
|
||||
Secret string // Secret token used to authenticate agents
|
||||
Admins []string // Administrative users
|
||||
Orgs []string // Organization whitelist
|
||||
}
|
||||
|
||||
// Server defines the server configuration.
|
||||
type Server struct {
|
||||
Bus bus.Bus
|
||||
Cache cache.Cache
|
||||
Queue queue.Queue
|
||||
Remote remote.Remote
|
||||
Stream stream.Stream
|
||||
Store store.Store
|
||||
Config *Config
|
||||
}
|
||||
|
||||
// Handler returns an http.Handler for servering Drone requests.
|
||||
func (s *Server) Handler() http.Handler {
|
||||
|
||||
e := gin.New()
|
||||
e.Use(gin.Recovery())
|
||||
|
||||
e.SetHTMLTemplate(template.Load())
|
||||
e.StaticFS("/static", static.FileSystem())
|
||||
|
||||
e.Use(header.NoCache)
|
||||
e.Use(header.Options)
|
||||
e.Use(header.Secure)
|
||||
e.Use(
|
||||
ginrus.Ginrus(logrus.StandardLogger(), time.RFC3339, true),
|
||||
HandlerVersion(),
|
||||
HandlerQueue(s.Queue),
|
||||
HandlerStream(s.Stream),
|
||||
HandlerBus(s.Bus),
|
||||
HandlerCache(s.Cache),
|
||||
HandlerStore(s.Store),
|
||||
HandlerRemote(s.Remote),
|
||||
HandlerConfig(s.Config),
|
||||
)
|
||||
e.Use(session.SetUser())
|
||||
e.Use(token.Refresh)
|
||||
|
||||
e.GET("/", web.ShowIndex)
|
||||
e.GET("/repos", web.ShowAllRepos)
|
||||
e.GET("/login", web.ShowLogin)
|
||||
e.GET("/login/form", web.ShowLoginForm)
|
||||
e.GET("/logout", web.GetLogout)
|
||||
|
||||
// TODO below will Go away with React UI
|
||||
settings := e.Group("/settings")
|
||||
{
|
||||
settings.Use(session.MustUser())
|
||||
settings.GET("/profile", web.ShowUser)
|
||||
}
|
||||
repo := e.Group("/repos/:owner/:name")
|
||||
{
|
||||
repo.Use(session.SetRepo())
|
||||
repo.Use(session.SetPerm())
|
||||
repo.Use(session.MustPull)
|
||||
|
||||
repo.GET("", web.ShowRepo)
|
||||
repo.GET("/builds/:number", web.ShowBuild)
|
||||
repo.GET("/builds/:number/:job", web.ShowBuild)
|
||||
|
||||
repo_settings := repo.Group("/settings")
|
||||
{
|
||||
repo_settings.GET("", session.MustPush, web.ShowRepoConf)
|
||||
repo_settings.GET("/encrypt", session.MustPush, web.ShowRepoEncrypt)
|
||||
repo_settings.GET("/badges", web.ShowRepoBadges)
|
||||
}
|
||||
}
|
||||
// TODO above will Go away with React UI
|
||||
|
||||
user := e.Group("/api/user")
|
||||
{
|
||||
user.Use(session.MustUser())
|
||||
user.GET("", api.GetSelf)
|
||||
user.GET("/feed", api.GetFeed)
|
||||
user.GET("/repos", api.GetRepos)
|
||||
user.GET("/repos/remote", api.GetRemoteRepos)
|
||||
user.POST("/token", api.PostToken)
|
||||
user.DELETE("/token", api.DeleteToken)
|
||||
}
|
||||
|
||||
users := e.Group("/api/users")
|
||||
{
|
||||
users.Use(session.MustAdmin())
|
||||
users.GET("", api.GetUsers)
|
||||
users.POST("", api.PostUser)
|
||||
users.GET("/:login", api.GetUser)
|
||||
users.PATCH("/:login", api.PatchUser)
|
||||
users.DELETE("/:login", api.DeleteUser)
|
||||
}
|
||||
|
||||
repos := e.Group("/api/repos/:owner/:name")
|
||||
{
|
||||
repos.POST("", api.PostRepo)
|
||||
|
||||
repo := repos.Group("")
|
||||
{
|
||||
repo.Use(session.SetRepo())
|
||||
repo.Use(session.SetPerm())
|
||||
repo.Use(session.MustPull)
|
||||
|
||||
repo.GET("", api.GetRepo)
|
||||
repo.GET("/key", api.GetRepoKey)
|
||||
repo.POST("/key", api.PostRepoKey)
|
||||
repo.GET("/builds", api.GetBuilds)
|
||||
repo.GET("/builds/:number", api.GetBuild)
|
||||
repo.GET("/logs/:number/:job", api.GetBuildLogs)
|
||||
repo.POST("/sign", session.MustPush, api.Sign)
|
||||
|
||||
repo.POST("/secrets", session.MustPush, api.PostSecret)
|
||||
repo.DELETE("/secrets/:secret", session.MustPush, api.DeleteSecret)
|
||||
|
||||
// requires push permissions
|
||||
repo.PATCH("", session.MustPush, api.PatchRepo)
|
||||
repo.DELETE("", session.MustPush, api.DeleteRepo)
|
||||
|
||||
repo.POST("/builds/:number", session.MustPush, api.PostBuild)
|
||||
repo.DELETE("/builds/:number/:job", session.MustPush, api.DeleteBuild)
|
||||
}
|
||||
}
|
||||
|
||||
badges := e.Group("/api/badges/:owner/:name")
|
||||
{
|
||||
badges.GET("/status.svg", web.GetBadge)
|
||||
badges.GET("/cc.xml", web.GetCC)
|
||||
}
|
||||
|
||||
e.POST("/hook", web.PostHook)
|
||||
e.POST("/api/hook", web.PostHook)
|
||||
|
||||
stream := e.Group("/api/stream")
|
||||
{
|
||||
stream.Use(session.SetRepo())
|
||||
stream.Use(session.SetPerm())
|
||||
stream.Use(session.MustPull)
|
||||
|
||||
stream.GET("/:owner/:name", web.GetRepoEvents)
|
||||
stream.GET("/:owner/:name/:build/:number", web.GetStream)
|
||||
}
|
||||
|
||||
auth := e.Group("/authorize")
|
||||
{
|
||||
auth.GET("", web.GetLogin)
|
||||
auth.POST("", web.GetLogin)
|
||||
auth.POST("/token", web.GetLoginToken)
|
||||
}
|
||||
|
||||
queue := e.Group("/api/queue")
|
||||
{
|
||||
queue.Use(middleware.AgentMust())
|
||||
queue.POST("/pull", api.Pull)
|
||||
queue.POST("/pull/:os/:arch", api.Pull)
|
||||
queue.POST("/wait/:id", api.Wait)
|
||||
queue.POST("/stream/:id", api.Stream)
|
||||
queue.POST("/status/:id", api.Update)
|
||||
}
|
||||
|
||||
// DELETE THESE
|
||||
// gitlab := e.Group("/gitlab/:owner/:name")
|
||||
// {
|
||||
// gitlab.Use(session.SetRepo())
|
||||
// gitlab.GET("/commits/:sha", web.GetCommit)
|
||||
// gitlab.GET("/pulls/:number", web.GetPullRequest)
|
||||
//
|
||||
// redirects := gitlab.Group("/redirect")
|
||||
// {
|
||||
// redirects.GET("/commits/:sha", web.RedirectSha)
|
||||
// redirects.GET("/pulls/:number", web.RedirectPullRequest)
|
||||
// }
|
||||
// }
|
||||
|
||||
// bots := e.Group("/bots")
|
||||
// {
|
||||
// bots.Use(session.MustUser())
|
||||
// bots.POST("/slack", web.Slack)
|
||||
// bots.POST("/slack/:command", web.Slack)
|
||||
// }
|
||||
|
||||
return normalize(e)
|
||||
}
|
||||
|
||||
// THIS HACK JOB IS GOING AWAY SOON.
|
||||
//
|
||||
// normalize is a helper function to work around the following
|
||||
// issue with gin. https://github.com/gin-gonic/gin/issues/388
|
||||
func normalize(h http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
parts := strings.Split(r.URL.Path, "/")[1:]
|
||||
switch parts[0] {
|
||||
case "settings", "bots", "repos", "api", "login", "logout", "", "authorize", "hook", "static", "gitlab":
|
||||
// no-op
|
||||
default:
|
||||
|
||||
if len(parts) > 2 && parts[2] != "settings" {
|
||||
parts = append(parts[:2], append([]string{"builds"}, parts[2:]...)...)
|
||||
}
|
||||
|
||||
// prefix the URL with /repo so that it
|
||||
// can be effectively routed.
|
||||
parts = append([]string{"", "repos"}, parts...)
|
||||
|
||||
// reconstruct the path
|
||||
r.URL.Path = strings.Join(parts, "/")
|
||||
}
|
||||
|
||||
h.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
Loading…
Reference in a new issue