Merge pull request #1570 from bradrydzewski/master

stub handler for slash commands
This commit is contained in:
Brad Rydzewski 2016-04-12 16:06:22 -07:00
commit 7129cbf184
27 changed files with 309 additions and 416 deletions

View file

@ -18,7 +18,6 @@ import (
"github.com/gin-gonic/gin"
"github.com/drone/drone/model"
"github.com/drone/drone/router/middleware/context"
"github.com/drone/drone/router/middleware/session"
)
@ -130,7 +129,7 @@ func GetBuildLogs(c *gin.Context) {
}
func DeleteBuild(c *gin.Context) {
engine_ := context.Engine(c)
engine_ := engine.FromContext(c)
repo := session.Repo(c)
// parse the build number and job sequence number from
@ -281,7 +280,7 @@ func PostBuild(c *gin.Context) {
// on status change notifications
last, _ := store.GetBuildLastBefore(c, repo, build.Branch, build.ID)
engine_ := context.Engine(c)
engine_ := engine.FromContext(c)
go engine_.Schedule(c.Copy(), &engine.Task{
User: user,
Repo: repo,

View file

@ -6,8 +6,8 @@ import (
"github.com/gin-gonic/gin"
"github.com/drone/drone/engine"
"github.com/drone/drone/model"
"github.com/drone/drone/router/middleware/context"
"github.com/drone/drone/store"
)
@ -25,7 +25,7 @@ func GetNode(c *gin.Context) {
}
func PostNode(c *gin.Context) {
engine := context.Engine(c)
engine := engine.FromContext(c)
in := struct {
Addr string `json:"address"`
@ -63,7 +63,7 @@ func PostNode(c *gin.Context) {
}
func DeleteNode(c *gin.Context) {
engine := context.Engine(c)
engine := engine.FromContext(c)
id, _ := strconv.Atoi(c.Param("node"))
node, err := store.GetNode(c, int64(id))

View file

@ -4,14 +4,8 @@ import (
"net/http"
"time"
"github.com/drone/drone/engine"
"github.com/drone/drone/remote"
"github.com/drone/drone/router"
"github.com/drone/drone/router/middleware/cache"
"github.com/drone/drone/router/middleware/context"
"github.com/drone/drone/router/middleware/header"
"github.com/drone/drone/shared/envconfig"
"github.com/drone/drone/store/datastore"
"github.com/drone/drone/router/middleware"
"github.com/Sirupsen/logrus"
"github.com/gin-gonic/contrib/ginrus"
@ -37,26 +31,14 @@ func main() {
logrus.SetLevel(logrus.WarnLevel)
}
// Load the configuration from env file
env := envconfig.Load(".env")
// Setup the database driver
store_ := datastore.Load(env)
// setup the remote driver
remote_ := remote.Load(env)
// setup the runner
engine_ := engine.Load(env, store_)
// setup the server and start the listener
handler := router.Load(
ginrus.Ginrus(logrus.StandardLogger(), time.RFC3339, true),
header.Version,
cache.Default(),
context.SetStore(store_),
context.SetRemote(remote_),
context.SetEngine(engine_),
middleware.Version,
middleware.Cache(),
middleware.Store(),
middleware.Remote(),
middleware.Engine(),
)
if *cert != "" {

23
engine/context.go Normal file
View file

@ -0,0 +1,23 @@
package engine
import (
"golang.org/x/net/context"
)
const key = "engine"
// Setter defines a context that enables setting values.
type Setter interface {
Set(string, interface{})
}
// FromContext returns the Engine associated with this context.
func FromContext(c context.Context) Engine {
return c.Value(key).(Engine)
}
// ToContext adds the Engine to this context if it supports
// the Setter interface.
func ToContext(c Setter, engine Engine) {
c.Set(key, engine)
}

View file

@ -8,6 +8,7 @@ import (
"fmt"
"io"
"io/ioutil"
"os"
"runtime"
"time"
@ -15,7 +16,6 @@ import (
"github.com/docker/docker/pkg/stdcopy"
"github.com/drone/drone/model"
"github.com/drone/drone/shared/docker"
"github.com/drone/drone/shared/envconfig"
"github.com/drone/drone/store"
"github.com/samalba/dockerclient"
"golang.org/x/net/context"
@ -60,7 +60,7 @@ type engine struct {
// Load creates a new build engine, loaded with registered nodes from the
// database. The registered nodes are added to the pool of nodes to immediately
// start accepting workloads.
func Load(env envconfig.Env, s store.Store) Engine {
func Load(s store.Store) Engine {
engine := &engine{}
engine.bus = newEventbus()
engine.pool = newPool()
@ -70,7 +70,7 @@ func Load(env envconfig.Env, s store.Store) Engine {
// throughout the build environment.
var proxyVars = []string{"HTTP_PROXY", "http_proxy", "HTTPS_PROXY", "https_proxy", "NO_PROXY", "no_proxy"}
for _, proxyVar := range proxyVars {
proxyVal := env.Get(proxyVar)
proxyVal := os.Getenv(proxyVar)
if len(proxyVal) != 0 {
engine.envs = append(engine.envs, proxyVar+"="+proxyVal)
}

View file

@ -9,7 +9,6 @@ import (
"strconv"
"github.com/drone/drone/model"
"github.com/drone/drone/shared/envconfig"
"github.com/drone/drone/shared/httputil"
log "github.com/Sirupsen/logrus"
@ -24,8 +23,7 @@ type Bitbucket struct {
Open bool
}
func Load(env envconfig.Env) *Bitbucket {
config := env.String("REMOTE_CONFIG", "")
func Load(config string) *Bitbucket {
// parse the remote DSN configuration string
url_, err := url.Parse(config)

View file

@ -11,7 +11,6 @@ import (
"strings"
"github.com/drone/drone/model"
"github.com/drone/drone/shared/envconfig"
"github.com/drone/drone/shared/httputil"
"github.com/drone/drone/shared/oauth2"
@ -42,8 +41,7 @@ type Github struct {
GitSSH bool
}
func Load(env envconfig.Env) *Github {
config := env.String("REMOTE_CONFIG", "")
func Load(config string) *Github {
// parse the remote DSN configuration string
url_, err := url.Parse(config)

View file

@ -6,7 +6,6 @@ import (
"net/http"
"testing"
"github.com/drone/drone/shared/envconfig"
"github.com/franela/goblin"
)
@ -48,12 +47,11 @@ func TestHook(t *testing.T) {
}
func TestLoad(t *testing.T) {
env := envconfig.Env{
"REMOTE_CONFIG": "https://github.com?client_id=client&client_secret=secret&scope=scope1,scope2",
}
g := Load(env)
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")
t.Errorf("g.URL = %q; want https://github.com", g.URL)
}
if g.Client != "client" {
t.Errorf("g.Client = %q; want client", g.Client)
@ -71,7 +69,7 @@ func TestLoad(t *testing.T) {
t.Errorf("g.MergeRef = %q; want %q", g.MergeRef, DefaultMergeRef)
}
g = Load(envconfig.Env{})
g = Load("")
if g.Scope != DefaultScope {
t.Errorf("g.Scope = %q; want %q", g.Scope, DefaultScope)
}

View file

@ -10,7 +10,6 @@ import (
"strings"
"github.com/drone/drone/model"
"github.com/drone/drone/shared/envconfig"
"github.com/drone/drone/shared/httputil"
"github.com/drone/drone/shared/oauth2"
"github.com/drone/drone/shared/token"
@ -35,9 +34,7 @@ type Gitlab struct {
Search bool
}
func Load(env envconfig.Env) *Gitlab {
config := env.String("REMOTE_CONFIG", "")
func Load(config string) *Gitlab {
url_, err := url.Parse(config)
if err != nil {
panic(err)

View file

@ -15,8 +15,7 @@ func Test_Gitlab(t *testing.T) {
var server = testdata.NewServer()
defer server.Close()
env := map[string]string{}
env["REMOTE_CONFIG"] = server.URL + "?client_id=test&client_secret=test"
env := server.URL + "?client_id=test&client_secret=test"
gitlab := Load(env)

View file

@ -9,7 +9,6 @@ import (
"strconv"
"github.com/drone/drone/model"
"github.com/drone/drone/shared/envconfig"
"github.com/gogits/go-gogs-client"
log "github.com/Sirupsen/logrus"
@ -22,9 +21,7 @@ type Gogs struct {
SkipVerify bool
}
func Load(env envconfig.Env) *Gogs {
config := env.String("REMOTE_CONFIG", "")
func Load(config string) *Gogs {
// parse the remote DSN configuration string
url_, err := url.Parse(config)
if err != nil {

View file

@ -6,36 +6,10 @@ import (
"net/http"
"github.com/drone/drone/model"
"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/drone/drone/shared/envconfig"
"github.com/Sirupsen/logrus"
"golang.org/x/net/context"
)
func Load(env envconfig.Env) Remote {
driver := env.Get("REMOTE_DRIVER")
switch driver {
case "bitbucket":
return bitbucket.Load(env)
case "github":
return github.Load(env)
case "gitlab":
return gitlab.Load(env)
case "gogs":
return gogs.Load(env)
default:
logrus.Fatalf("unknown remote driver %s", driver)
}
return nil
}
type Remote interface {
// Login authenticates the session and returns the
// remote user details.

View file

@ -0,0 +1,22 @@
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()
}
}

View file

@ -1,14 +0,0 @@
package cache
import (
"github.com/drone/drone/cache"
"github.com/gin-gonic/gin"
)
func Default() gin.HandlerFunc {
cc := cache.Default()
return func(c *gin.Context) {
cache.ToContext(c, cc)
c.Next()
}
}

View file

@ -1,37 +0,0 @@
package context
import (
"github.com/drone/drone/engine"
"github.com/drone/drone/remote"
"github.com/drone/drone/store"
"github.com/gin-gonic/gin"
)
func SetStore(s store.Store) gin.HandlerFunc {
return func(c *gin.Context) {
store.ToContext(c, s)
c.Next()
}
}
func SetRemote(remote remote.Remote) gin.HandlerFunc {
return func(c *gin.Context) {
c.Set("remote", remote)
c.Next()
}
}
func Remote(c *gin.Context) remote.Remote {
return c.MustGet("remote").(remote.Remote)
}
func SetEngine(engine engine.Engine) gin.HandlerFunc {
return func(c *gin.Context) {
c.Set("engine", engine)
c.Next()
}
}
func Engine(c *gin.Context) engine.Engine {
return c.MustGet("engine").(engine.Engine)
}

View file

@ -0,0 +1,28 @@
package middleware
import (
"sync"
"github.com/drone/drone/engine"
"github.com/drone/drone/store"
"github.com/gin-gonic/gin"
)
// Engine is a middleware function that initializes the Engine and attaches to
// the context of every http.Request.
func Engine() gin.HandlerFunc {
var once sync.Once
var engine_ engine.Engine
return func(c *gin.Context) {
once.Do(func() {
store_ := store.FromContext(c)
engine_ = engine.Load(store_)
})
engine.ToContext(c, engine_)
c.Next()
}
}

View file

@ -1,95 +0,0 @@
package location
import (
"net/http"
"strings"
"github.com/gin-gonic/gin"
)
// Resolve is a middleware function that resolves the hostname
// and scheme for the http.Request and adds to the context.
func Resolve(c *gin.Context) {
c.Set("host", resolveHost(c.Request))
c.Set("scheme", resolveScheme(c.Request))
c.Next()
}
// parseHeader parses non unique headers value
// from a http.Request and return a slice of the values
// queried from the header
func parseHeader(r *http.Request, header string, token string) (val []string) {
for _, v := range r.Header[header] {
options := strings.Split(v, ";")
for _, o := range options {
keyvalue := strings.Split(o, "=")
var key, value string
if len(keyvalue) > 1 {
key, value = strings.TrimSpace(keyvalue[0]), strings.TrimSpace(keyvalue[1])
}
key = strings.ToLower(key)
if key == token {
val = append(val, value)
}
}
}
return
}
// resolveScheme is a helper function that evaluates the http.Request
// and returns the scheme, HTTP or HTTPS. It is able to detect,
// using the X-Forwarded-Proto, if the original request was HTTPS
// and routed through a reverse proxy with SSL termination.
func resolveScheme(r *http.Request) string {
switch {
case r.URL.Scheme == "https":
return "https"
case r.TLS != nil:
return "https"
case strings.HasPrefix(r.Proto, "HTTPS"):
return "https"
case r.Header.Get("X-Forwarded-Proto") == "https":
return "https"
case len(r.Header.Get("Forwarded")) != 0 && len(parseHeader(r, "Forwarded", "proto")) != 0 && parseHeader(r, "Forwarded", "proto")[0] == "https":
return "https"
default:
return "http"
}
}
// resolveHost is a helper function that evaluates the http.Request
// and returns the hostname. It is able to detect, using the
// X-Forarded-For header, the original hostname when routed
// through a reverse proxy.
func resolveHost(r *http.Request) string {
switch {
case len(r.Host) != 0:
return r.Host
case len(r.URL.Host) != 0:
return r.URL.Host
case len(r.Header.Get("X-Forwarded-For")) != 0:
return r.Header.Get("X-Forwarded-For")
case len(r.Header.Get("Forwarded")) != 0 && len(parseHeader(r, "Forwarded", "for")) != 0:
return parseHeader(r, "Forwarded", "for")[0]
case len(r.Header.Get("X-Host")) != 0:
return r.Header.Get("X-Host")
case len(r.Header.Get("Forwarded")) != 0 && len(parseHeader(r, "Forwarded", "host")) != 0:
return parseHeader(r, "Forwarded", "host")[0]
case len(r.Header.Get("XFF")) != 0:
return r.Header.Get("XFF")
case len(r.Header.Get("X-Real-IP")) != 0:
return r.Header.Get("X-Real-IP")
default:
return "localhost:8000"
}
}
// Hostname returns the hostname associated with
// the current context.
func Hostname(c *gin.Context) (host string) {
v, ok := c.Get("host")
if ok {
host = v.(string)
}
return
}

View file

@ -1,48 +0,0 @@
package location
import (
"github.com/franela/goblin"
"net/http"
"reflect"
"testing"
)
var mockHeader []string
var mockRequest *http.Request
var wronglyFormedHeader []string
var wronglyFormedRequest *http.Request
func init() {
mockHeader = []string{"For= 110.0.2.2", "for = \"[::1]\"; Host=example.com; foR=10.2.3.4; pRoto =https ; By = 127.0.0.1"}
mockRequest = &http.Request{Header: map[string][]string{"Forwarded": mockHeader}}
wronglyFormedHeader = []string{"Fro= 110.0.2.2", "for = \"[:1]\"% Host=example:.com| foR=10.278.3.4% poto =https | Bi % 127.0.0.1", ""}
wronglyFormedRequest = &http.Request{Header: map[string][]string{"Forwarded": wronglyFormedHeader}}
}
func TestParseForwardedHeadersProto(t *testing.T) {
g := goblin.Goblin(t)
g.Describe("Parse proto Forwarded Headers", func() {
g.It("Should parse a normal proto Forwarded header", func() {
parsedHeader := parseHeader(mockRequest, "Forwarded", "proto")
g.Assert("https" == parsedHeader[0]).IsTrue()
})
g.It("Should parse a normal for Forwarded header", func() {
parsedHeader := parseHeader(mockRequest, "Forwarded", "for")
g.Assert(reflect.DeepEqual([]string{"110.0.2.2", "\"[::1]\"", "10.2.3.4"}, parsedHeader)).IsTrue()
})
g.It("Should parse a normal host Forwarded header", func() {
parsedHeader := parseHeader(mockRequest, "Forwarded", "host")
g.Assert("example.com" == parsedHeader[0]).IsTrue()
})
g.It("Should parse a normal by Forwarded header", func() {
parsedHeader := parseHeader(mockRequest, "Forwarded", "by")
g.Assert("127.0.0.1" == parsedHeader[0]).IsTrue()
})
g.It("Should not crash if a wrongly formed Forwarder header is sent", func() {
parsedHeader := parseHeader(wronglyFormedRequest, "Forwarded", "by")
g.Assert(len(parsedHeader) == 0).IsTrue()
})
})
}

View file

@ -0,0 +1,45 @@
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"
)
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)
default:
logrus.Fatalln("remote configuraiton not found")
}
return func(c *gin.Context) {
remote.ToContext(c, remote_)
c.Next()
}
}

View file

@ -0,0 +1,29 @@
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()
}
}

View file

@ -0,0 +1,14 @@
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()
}

View file

@ -8,7 +8,6 @@ import (
"github.com/drone/drone/api"
"github.com/drone/drone/router/middleware/header"
"github.com/drone/drone/router/middleware/location"
"github.com/drone/drone/router/middleware/session"
"github.com/drone/drone/router/middleware/token"
"github.com/drone/drone/static"
@ -23,7 +22,6 @@ func Load(middleware ...gin.HandlerFunc) http.Handler {
e.SetHTMLTemplate(template.Load())
e.StaticFS("/static", static.FileSystem())
e.Use(location.Resolve)
e.Use(header.NoCache)
e.Use(header.Options)
e.Use(header.Secure)
@ -142,6 +140,13 @@ func Load(middleware ...gin.HandlerFunc) http.Handler {
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)
@ -172,7 +177,7 @@ func normalize(h http.Handler) http.Handler {
parts := strings.Split(r.URL.Path, "/")[1:]
switch parts[0] {
case "settings", "repos", "api", "login", "logout", "", "authorize", "hook", "static", "gitlab":
case "settings", "bots", "repos", "api", "login", "logout", "", "authorize", "hook", "static", "gitlab":
// no-op
default:

View file

@ -1,117 +0,0 @@
package envconfig
import (
"bufio"
"errors"
"os"
"strconv"
"strings"
)
type Env map[string]string
// Get returns the value of the environment variable named by the key.
func (env Env) Get(key string) string {
return env[key]
}
// String returns the string value of the environment variable named by the
// key. If the variable is not present, the default value is returned.
func (env Env) String(key, value string) string {
got, ok := env[key]
if ok {
value = got
}
return value
}
// Bool returns the boolean value of the environment variable named by the key.
// If the variable is not present, the default value is returned.
func (env Env) Bool(name string, value bool) bool {
got, ok := env[name]
if ok {
value, _ = strconv.ParseBool(got)
}
return value
}
// Int returns the integer value of the environment variable named by the key.
// If the variable is not present, the default value is returned.
func (env Env) Int(name string, value int) int {
got, ok := env[name]
if ok {
value, _ = strconv.Atoi(got)
}
return value
}
// Load reads the environment file and reads variables in "key=value" format.
// Then it read the system environment variables. It returns the combined
// results in a key value map.
func Load(filepath string) Env {
var envs = map[string]string{}
// load the environment file
f, err := os.Open(filepath)
if err == nil {
defer f.Close()
r := bufio.NewReader(f)
for {
line, _, err := r.ReadLine()
if err != nil {
break
}
key, val, err := parseln(string(line))
if err != nil {
continue
}
os.Setenv(key, val)
}
}
// load the environment variables
for _, env := range os.Environ() {
key, val, err := parseln(env)
if err != nil {
continue
}
envs[key] = val
}
return Env(envs)
}
// helper function to parse a "key=value" environment variable string.
func parseln(line string) (key string, val string, err error) {
line = removeComments(line)
if len(line) == 0 {
return
}
splits := strings.SplitN(line, "=", 2)
if len(splits) < 2 {
err = errors.New("missing delimiter '='")
return
}
key = strings.Trim(splits[0], " ")
val = strings.Trim(splits[1], ` "'`)
return
}
// helper function to trim comments and whitespace from a string.
func removeComments(s string) (_ string) {
if len(s) == 0 || string(s[0]) == "#" {
return
} else {
index := strings.Index(s, " #")
if index > -1 {
s = strings.TrimSpace(s[0:index])
}
}
return s
}

View file

@ -5,7 +5,6 @@ import (
"os"
"time"
"github.com/drone/drone/shared/envconfig"
"github.com/drone/drone/store"
"github.com/drone/drone/store/datastore/ddl"
_ "github.com/go-sql-driver/mysql"
@ -23,20 +22,6 @@ type datastore struct {
*sql.DB
}
// Load opens a new database connection with the specified driver
// and connection string specified in the environment variables.
func Load(env envconfig.Env) store.Store {
var (
driver = env.String("DATABASE_DRIVER", "sqlite3")
config = env.String("DATABASE_CONFIG", "drone.sqlite")
)
logrus.Infof("using database driver %s", driver)
logrus.Infof("using database config %s", config)
return New(driver, config)
}
// New creates a database connection for the given driver and datasource
// and returns a new Store.
func New(driver, config string) store.Store {

View file

@ -14,7 +14,6 @@ import (
"github.com/drone/drone/engine/parser"
"github.com/drone/drone/model"
"github.com/drone/drone/remote"
"github.com/drone/drone/router/middleware/context"
"github.com/drone/drone/shared/httputil"
"github.com/drone/drone/shared/token"
"github.com/drone/drone/store"
@ -205,7 +204,7 @@ func PostHook(c *gin.Context) {
// on status change notifications
last, _ := store.GetBuildLastBefore(c, repo, build.Branch, build.ID)
engine_ := context.Engine(c)
engine_ := engine.FromContext(c)
go engine_.Schedule(c.Copy(), &engine.Task{
User: user,
Repo: repo,

113
web/slack.go Normal file
View file

@ -0,0 +1,113 @@
package web
import (
"strings"
"github.com/drone/drone/store"
"github.com/gin-gonic/gin"
)
const (
slashDeploy = "deploy"
slashRestart = "restart"
slashStatus = "status"
)
// Slack is handler function that handles Slack slash commands.
func Slack(c *gin.Context) {
command := c.Param("command")
text := c.PostForm("text")
args := strings.Split(text, " ")
if command == "" {
command = args[0]
args = args[1:]
}
switch command {
case slashStatus:
slackStatus(c, args)
case slashRestart:
slackRestart(c, args)
case slashDeploy:
slackDeploy(c, args)
default:
c.String(200, "sorry, I didn't understand [%s]", text)
}
}
func slackDeploy(c *gin.Context, args []string) {
if len(args) != 3 {
c.String(200, "Invalid command. Please provide [repo] [build number] [environment]")
return
}
var (
repo = args[0]
num = args[1]
env = args[2]
)
owner, name, _ := parseRepoBranch(repo)
c.String(200, "deploying build %s/%s#%s to %s", owner, name, num, env)
}
func slackRestart(c *gin.Context, args []string) {
var (
repo = args[0]
num = args[1]
)
owner, name, _ := parseRepoBranch(repo)
c.String(200, "restarting build %s/%s#%s", owner, name, num)
}
func slackStatus(c *gin.Context, args []string) {
var (
owner string
name string
branch string
)
if len(args) > 0 {
owner, name, branch = parseRepoBranch(args[0])
}
repo, err := store.GetRepoOwnerName(c, owner, name)
if err != nil {
c.String(200, "cannot find repository %s/%s", owner, name)
return
}
if branch == "" {
branch = repo.Branch
}
build, err := store.GetBuildLast(c, repo, branch)
if err != nil {
c.String(200, "cannot find status for %s/%s@%s", owner, name, branch)
return
}
c.String(200, "%s@%s build number %d finished with status %s",
repo.FullName,
build.Branch,
build.Number,
build.Status,
)
}
func parseRepoBranch(repo string) (owner, name, branch string) {
parts := strings.Split(repo, "@")
if len(parts) == 2 {
branch = parts[1]
repo = parts[0]
}
parts = strings.Split(repo, "/")
if len(parts) == 2 {
owner = parts[0]
name = parts[1]
}
return owner, name, branch
}

View file

@ -8,7 +8,6 @@ import (
"github.com/docker/docker/pkg/stdcopy"
"github.com/drone/drone/engine"
"github.com/drone/drone/router/middleware/context"
"github.com/drone/drone/router/middleware/session"
"github.com/drone/drone/store"
@ -20,7 +19,7 @@ import (
// GetRepoEvents will upgrade the connection to a Websocket and will stream
// event updates to the browser.
func GetRepoEvents(c *gin.Context) {
engine_ := context.Engine(c)
engine_ := engine.FromContext(c)
repo := session.Repo(c)
c.Writer.Header().Set("Content-Type", "text/event-stream")
@ -55,7 +54,7 @@ func GetRepoEvents(c *gin.Context) {
func GetStream(c *gin.Context) {
engine_ := context.Engine(c)
engine_ := engine.FromContext(c)
repo := session.Repo(c)
buildn, _ := strconv.Atoi(c.Param("build"))
jobn, _ := strconv.Atoi(c.Param("number"))