Merge remote-tracking branch 'origin/master'
This commit is contained in:
commit
9fcec0025d
26 changed files with 731 additions and 27 deletions
3
.gitmodules
vendored
3
.gitmodules
vendored
|
@ -1,3 +1,6 @@
|
||||||
[submodule "vendor/src/github.com/GeertJohan/go.rice"]
|
[submodule "vendor/src/github.com/GeertJohan/go.rice"]
|
||||||
path = vendor/src/github.com/GeertJohan/go.rice
|
path = vendor/src/github.com/GeertJohan/go.rice
|
||||||
url = https://github.com/GeertJohan/go.rice.git
|
url = https://github.com/GeertJohan/go.rice.git
|
||||||
|
[submodule "vendor/src/github.com/GeertJohan/rsrc"]
|
||||||
|
path = vendor/src/github.com/GeertJohan/rsrc
|
||||||
|
url = https://github.com/GeertJohan/rsrc.git
|
||||||
|
|
|
@ -10,13 +10,15 @@ RUN wget https://go.googlecode.com/files/go1.2.src.tar.gz && tar zxvf go1.2.src.
|
||||||
ENV PATH $PATH:/go/bin:/gocode/bin
|
ENV PATH $PATH:/go/bin:/gocode/bin
|
||||||
ENV GOPATH /gocode
|
ENV GOPATH /gocode
|
||||||
|
|
||||||
|
RUN go get github.com/tools/godep
|
||||||
|
|
||||||
RUN mkdir -p /gocode/src/github.com/drone
|
RUN mkdir -p /gocode/src/github.com/drone
|
||||||
|
|
||||||
ADD . /gocode/src/github.com/drone/drone
|
ADD . /gocode/src/github.com/drone/drone
|
||||||
|
|
||||||
WORKDIR /gocode/src/github.com/drone/drone
|
WORKDIR /gocode/src/github.com/drone/drone
|
||||||
|
|
||||||
RUN make deps
|
RUN godep restore
|
||||||
RUN make
|
RUN make
|
||||||
RUN make install
|
RUN make install
|
||||||
|
|
||||||
|
@ -24,4 +26,4 @@ EXPOSE 80
|
||||||
|
|
||||||
ENTRYPOINT ["/usr/local/bin/droned"]
|
ENTRYPOINT ["/usr/local/bin/droned"]
|
||||||
|
|
||||||
CMD ["--port=:80", "--path=/var/lib/drone/drone.sqlite"]
|
CMD ["--port=:80", "--datasource=/var/lib/drone/drone.sqlite"]
|
||||||
|
|
4
Godeps/Godeps.json
generated
4
Godeps/Godeps.json
generated
|
@ -174,6 +174,10 @@
|
||||||
"Comment": "1.5.0-168-g2abea07",
|
"Comment": "1.5.0-168-g2abea07",
|
||||||
"Rev": "2abea075ec076abf0572d29bdb28ae7da64cfb7a"
|
"Rev": "2abea075ec076abf0572d29bdb28ae7da64cfb7a"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/stvp/flowdock",
|
||||||
|
"Rev": "50362abeabebf40b0f56a326d62f17e61a59302b"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "launchpad.net/goyaml",
|
"ImportPath": "launchpad.net/goyaml",
|
||||||
"Comment": "51",
|
"Comment": "51",
|
||||||
|
|
53
Godeps/_workspace/src/github.com/stvp/flowdock/README.md
generated
vendored
Normal file
53
Godeps/_workspace/src/github.com/stvp/flowdock/README.md
generated
vendored
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
flowdock
|
||||||
|
========
|
||||||
|
|
||||||
|
A Flowdock client for Golang. See the [Flowdock API docs][api_docs] for more
|
||||||
|
information on the individual attributes.
|
||||||
|
|
||||||
|
[Go API Documentation][godocs]
|
||||||
|
|
||||||
|
Examples
|
||||||
|
--------
|
||||||
|
|
||||||
|
You can set global defaults and use `flowdock.Inbox()` directly:
|
||||||
|
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
"github.com/stvp/flowdock"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
flowdock.Token = "732da505d0284d5b1d909b1a32426345"
|
||||||
|
flowdock.Source = "My Cool App"
|
||||||
|
flowdock.FromAddress = "cool@dudes.com"
|
||||||
|
// See API docs for more variables
|
||||||
|
|
||||||
|
go flowdock.Inbox("My subject", "My content goes here.")
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Or you can create a `flowdock.Client` to use different Flowdock message
|
||||||
|
settings in the same app:
|
||||||
|
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
"github.com/stvp/flowdock"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
client := flowdock.Client{
|
||||||
|
Token: "732da505d0284d5b1d909b1a32426345",
|
||||||
|
Source: "App A",
|
||||||
|
FromAddress: "email@stovepipestudios.com",
|
||||||
|
FromName: "Client A",
|
||||||
|
ReplyTo: "app_a@stovepipestudios.com",
|
||||||
|
Tags: []string{"app_a"},
|
||||||
|
}
|
||||||
|
|
||||||
|
go client.Inbox("Subject", "Content")
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
[api_docs]: https://www.flowdock.com/api/team-inbox
|
||||||
|
[godocs]: http://godoc.org/github.com/stvp/flowdock
|
||||||
|
|
105
Godeps/_workspace/src/github.com/stvp/flowdock/flowdock.go
generated
vendored
Normal file
105
Godeps/_workspace/src/github.com/stvp/flowdock/flowdock.go
generated
vendored
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
package flowdock
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
ENDPOINT = "https://api.flowdock.com/v1/messages/team_inbox/"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// Required default client settings
|
||||||
|
Token = ""
|
||||||
|
Source = ""
|
||||||
|
FromAddress = ""
|
||||||
|
|
||||||
|
// Optional default client settings
|
||||||
|
FromName = ""
|
||||||
|
ReplyTo = ""
|
||||||
|
Project = ""
|
||||||
|
Link = ""
|
||||||
|
Tags = []string{}
|
||||||
|
)
|
||||||
|
|
||||||
|
type Client struct {
|
||||||
|
// Required
|
||||||
|
Token string
|
||||||
|
Source string
|
||||||
|
FromAddress string
|
||||||
|
Subject string
|
||||||
|
Content string
|
||||||
|
|
||||||
|
// Optional
|
||||||
|
FromName string
|
||||||
|
ReplyTo string
|
||||||
|
Project string
|
||||||
|
Link string
|
||||||
|
Tags []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) Inbox(subject, content string) error {
|
||||||
|
return send(c.Token, c.Source, c.FromAddress, subject, content, c.FromName, c.ReplyTo, c.Project, c.Link, c.Tags)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Inbox(subject, content string) error {
|
||||||
|
return send(Token, Source, FromAddress, subject, content, FromName, ReplyTo, Project, Link, Tags)
|
||||||
|
}
|
||||||
|
|
||||||
|
func send(token, source, fromAddress, subject, content, fromName, replyTo, project, link string, tags []string) error {
|
||||||
|
// Required validation
|
||||||
|
if len(token) == 0 {
|
||||||
|
return fmt.Errorf(`"Token" is required`)
|
||||||
|
}
|
||||||
|
if len(source) == 0 {
|
||||||
|
return fmt.Errorf(`"Source" is required`)
|
||||||
|
}
|
||||||
|
if len(fromAddress) == 0 {
|
||||||
|
return fmt.Errorf(`"FromAddress" is required`)
|
||||||
|
}
|
||||||
|
if len(subject) == 0 {
|
||||||
|
return fmt.Errorf(`"Subject" is required`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build payload
|
||||||
|
payload := map[string]interface{}{
|
||||||
|
"source": source,
|
||||||
|
"from_address": fromAddress,
|
||||||
|
"subject": subject,
|
||||||
|
"content": content,
|
||||||
|
}
|
||||||
|
if len(fromName) > 0 {
|
||||||
|
payload["from_name"] = fromName
|
||||||
|
}
|
||||||
|
if len(replyTo) > 0 {
|
||||||
|
payload["reply_to"] = replyTo
|
||||||
|
}
|
||||||
|
if len(project) > 0 {
|
||||||
|
payload["project"] = project
|
||||||
|
}
|
||||||
|
if len(link) > 0 {
|
||||||
|
payload["link"] = link
|
||||||
|
}
|
||||||
|
if len(tags) > 0 {
|
||||||
|
payload["tags"] = tags
|
||||||
|
}
|
||||||
|
jsonPayload, err := json.Marshal(payload)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send to Flowdock
|
||||||
|
resp, err := http.Post(ENDPOINT+token, "application/json", bytes.NewReader(jsonPayload))
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode == 200 {
|
||||||
|
return nil
|
||||||
|
} else {
|
||||||
|
bodyBytes, _ := ioutil.ReadAll(resp.Body)
|
||||||
|
return fmt.Errorf("Unexpected response from Flowdock: %s %s", resp.Status, string(bodyBytes))
|
||||||
|
}
|
||||||
|
}
|
3
Makefile
3
Makefile
|
@ -94,4 +94,5 @@ godep:
|
||||||
go get github.com/tools/godep
|
go get github.com/tools/godep
|
||||||
|
|
||||||
rice:
|
rice:
|
||||||
go install github.com/GeertJohan/go.rice/rice
|
go get github.com/GeertJohan/go.rice/rice
|
||||||
|
go build github.com/GeertJohan/go.rice/rice
|
||||||
|
|
|
@ -227,6 +227,7 @@ func setupHandlers() {
|
||||||
|
|
||||||
// handlers for repository, commits and build details
|
// handlers for repository, commits and build details
|
||||||
m.Get("/:host/:owner/:name/commit/:commit/build/:label/out.txt", handler.RepoHandler(handler.BuildOut))
|
m.Get("/:host/:owner/:name/commit/:commit/build/:label/out.txt", handler.RepoHandler(handler.BuildOut))
|
||||||
|
m.Get("/:host/:owner/:name/commit/:commit/build/:label/status.json", handler.PublicHandler(handler.BuildStatus))
|
||||||
m.Post("/:host/:owner/:name/commit/:commit/build/:label/rebuild", handler.RepoAdminHandler(rebuild.CommitRebuild))
|
m.Post("/:host/:owner/:name/commit/:commit/build/:label/rebuild", handler.RepoAdminHandler(rebuild.CommitRebuild))
|
||||||
m.Get("/:host/:owner/:name/commit/:commit/build/:label", handler.RepoHandler(handler.CommitShow))
|
m.Get("/:host/:owner/:name/commit/:commit/build/:label", handler.RepoHandler(handler.CommitShow))
|
||||||
m.Post("/:host/:owner/:name/commit/:commit/rebuild", handler.RepoAdminHandler(rebuild.CommitRebuild))
|
m.Post("/:host/:owner/:name/commit/:commit/rebuild", handler.RepoAdminHandler(rebuild.CommitRebuild))
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
#
|
#
|
||||||
# -datasource="drone.sqlite":
|
# -datasource="drone.sqlite":
|
||||||
# -driver="sqlite3":
|
# -driver="sqlite3":
|
||||||
# -path="":
|
|
||||||
# -port=":8080":
|
# -port=":8080":
|
||||||
# -workers="4":
|
# -workers="4":
|
||||||
#
|
#
|
||||||
|
|
|
@ -244,6 +244,8 @@ func (b *Builder) setup() error {
|
||||||
if err := b.dockerClient.Images.Pull(b.Build.Image); err != nil {
|
if err := b.dockerClient.Images.Pull(b.Build.Image); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
} else if err != nil {
|
||||||
|
log.Errf("failed to inspect image %s", b.Build.Image)
|
||||||
}
|
}
|
||||||
|
|
||||||
// create the Docker image
|
// create the Docker image
|
||||||
|
@ -439,7 +441,7 @@ func (b *Builder) writeDockerfile(dir string) error {
|
||||||
switch {
|
switch {
|
||||||
case strings.HasPrefix(b.Build.Image, "bradrydzewski/"),
|
case strings.HasPrefix(b.Build.Image, "bradrydzewski/"),
|
||||||
strings.HasPrefix(b.Build.Image, "drone/"):
|
strings.HasPrefix(b.Build.Image, "drone/"):
|
||||||
// the default user for all official Drone imnage
|
// the default user for all official Drone images
|
||||||
// is the "ubuntu" user, since all build images
|
// is the "ubuntu" user, since all build images
|
||||||
// inherit from the ubuntu cloud ISO
|
// inherit from the ubuntu cloud ISO
|
||||||
dockerfile.WriteUser("ubuntu")
|
dockerfile.WriteUser("ubuntu")
|
||||||
|
|
|
@ -13,10 +13,7 @@ type Git struct {
|
||||||
Depth *int `yaml:"depth,omitempty"`
|
Depth *int `yaml:"depth,omitempty"`
|
||||||
|
|
||||||
// The name of a directory to clone into.
|
// The name of a directory to clone into.
|
||||||
// TODO this still needs to be implemented. this field is
|
Path *string `yaml:"path,omitempty"`
|
||||||
// critical for forked Go projects, that need to clone
|
|
||||||
// to a specific repository.
|
|
||||||
Path string `yaml:"path,omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GitDepth returns GitDefaultDepth
|
// GitDepth returns GitDefaultDepth
|
||||||
|
@ -29,3 +26,14 @@ func GitDepth(g *Git) int {
|
||||||
}
|
}
|
||||||
return *g.Depth
|
return *g.Depth
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GitPath returns the given default path
|
||||||
|
// when Git.Path is empty.
|
||||||
|
// GitPath returns Git.Path
|
||||||
|
// when it is not empty.
|
||||||
|
func GitPath(g *Git, defaultPath string) string {
|
||||||
|
if g == nil || g.Path == nil {
|
||||||
|
return defaultPath
|
||||||
|
}
|
||||||
|
return *g.Path
|
||||||
|
}
|
||||||
|
|
|
@ -7,6 +7,10 @@ import (
|
||||||
. "github.com/drone/drone/pkg/model"
|
. "github.com/drone/drone/pkg/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type BuildResult struct {
|
||||||
|
Status string
|
||||||
|
}
|
||||||
|
|
||||||
// Returns the combined stdout / stderr for an individual Build.
|
// Returns the combined stdout / stderr for an individual Build.
|
||||||
func BuildOut(w http.ResponseWriter, r *http.Request, u *User, repo *Repo) error {
|
func BuildOut(w http.ResponseWriter, r *http.Request, u *User, repo *Repo) error {
|
||||||
branch := r.FormValue("branch")
|
branch := r.FormValue("branch")
|
||||||
|
@ -32,6 +36,33 @@ func BuildOut(w http.ResponseWriter, r *http.Request, u *User, repo *Repo) error
|
||||||
return RenderText(w, build.Stdout, http.StatusOK)
|
return RenderText(w, build.Stdout, http.StatusOK)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns the combined stdout / stderr for an individual Build.
|
||||||
|
func BuildStatus(w http.ResponseWriter, r *http.Request, repo *Repo) error {
|
||||||
|
branch := r.FormValue("branch")
|
||||||
|
if branch == "" {
|
||||||
|
branch = "master"
|
||||||
|
}
|
||||||
|
|
||||||
|
hash := r.FormValue(":commit")
|
||||||
|
labl := r.FormValue(":label")
|
||||||
|
|
||||||
|
// get the commit from the database
|
||||||
|
commit, err := database.GetCommitBranchHash(branch, hash, repo.ID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the build from the database
|
||||||
|
build, err := database.GetBuildSlug(labl, commit.ID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
build_result := BuildResult{build.Status}
|
||||||
|
|
||||||
|
return RenderJson(w, build_result)
|
||||||
|
}
|
||||||
|
|
||||||
// Returns the gzipped stdout / stderr for an individual Build
|
// Returns the gzipped stdout / stderr for an individual Build
|
||||||
func BuildOutGzip(w http.ResponseWriter, r *http.Request, u *User) error {
|
func BuildOutGzip(w http.ResponseWriter, r *http.Request, u *User) error {
|
||||||
// TODO
|
// TODO
|
||||||
|
|
|
@ -64,10 +64,33 @@ func (h AdminHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PublicHandler wraps the default http.HandlerFunc to include
|
||||||
|
// requested Repository in the method signature, in addition
|
||||||
|
// to handling an error as the return value.
|
||||||
|
type PublicHandler func(w http.ResponseWriter, r *http.Request, repo *Repo) error
|
||||||
|
|
||||||
|
func (h PublicHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
|
// repository name from the URL parameters
|
||||||
|
hostParam := r.FormValue(":host")
|
||||||
|
userParam := r.FormValue(":owner")
|
||||||
|
nameParam := r.FormValue(":name")
|
||||||
|
repoName := fmt.Sprintf("%s/%s/%s", hostParam, userParam, nameParam)
|
||||||
|
|
||||||
|
repo, err := database.GetRepoSlug(repoName)
|
||||||
|
if err != nil || repo == nil {
|
||||||
|
RenderNotFound(w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
h(w, r, repo)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// RepoHandler wraps the default http.HandlerFunc to include
|
// RepoHandler wraps the default http.HandlerFunc to include
|
||||||
// the currently authenticated User and requested Repository
|
// the currently authenticated User and requested Repository
|
||||||
// in the method signature, in addition to handling an error
|
// in the method signature, in addition to handling an error
|
||||||
// as the return value.
|
// as the return value.
|
||||||
|
|
||||||
type RepoHandler func(w http.ResponseWriter, r *http.Request, user *User, repo *Repo) error
|
type RepoHandler func(w http.ResponseWriter, r *http.Request, user *User, repo *Repo) error
|
||||||
|
|
||||||
func (h RepoHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
func (h RepoHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
|
@ -326,6 +326,7 @@ func RepoUpdate(w http.ResponseWriter, r *http.Request, u *User, repo *Repo) err
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
repo.URL = r.FormValue("URL")
|
||||||
repo.Disabled = len(r.FormValue("Disabled")) == 0
|
repo.Disabled = len(r.FormValue("Disabled")) == 0
|
||||||
repo.DisabledPullRequest = len(r.FormValue("DisabledPullRequest")) == 0
|
repo.DisabledPullRequest = len(r.FormValue("DisabledPullRequest")) == 0
|
||||||
repo.Private = len(r.FormValue("Private")) > 0
|
repo.Private = len(r.FormValue("Private")) > 0
|
||||||
|
|
|
@ -35,7 +35,7 @@ func (g *Git) Write(f *buildfile.Buildfile) {
|
||||||
// that need to be deployed to git remote.
|
// that need to be deployed to git remote.
|
||||||
f.WriteCmd(fmt.Sprintf("git add -A"))
|
f.WriteCmd(fmt.Sprintf("git add -A"))
|
||||||
f.WriteCmd(fmt.Sprintf("git commit -m 'add build artifacts'"))
|
f.WriteCmd(fmt.Sprintf("git commit -m 'add build artifacts'"))
|
||||||
f.WriteCmd(fmt.Sprintf("git push deploy $COMMIT:%s --force", destinationBranch))
|
f.WriteCmd(fmt.Sprintf("git push deploy HEAD:%s --force", destinationBranch))
|
||||||
case false:
|
case false:
|
||||||
// otherwise we just do a standard git push
|
// otherwise we just do a standard git push
|
||||||
f.WriteCmd(fmt.Sprintf("git push deploy $COMMIT:%s", destinationBranch))
|
f.WriteCmd(fmt.Sprintf("git push deploy $COMMIT:%s", destinationBranch))
|
||||||
|
|
|
@ -30,7 +30,7 @@ func (h *Heroku) Write(f *buildfile.Buildfile) {
|
||||||
// that need to be deployed to Heroku.
|
// that need to be deployed to Heroku.
|
||||||
f.WriteCmd(fmt.Sprintf("git add -A"))
|
f.WriteCmd(fmt.Sprintf("git add -A"))
|
||||||
f.WriteCmd(fmt.Sprintf("git commit -m 'adding build artifacts'"))
|
f.WriteCmd(fmt.Sprintf("git commit -m 'adding build artifacts'"))
|
||||||
f.WriteCmd(fmt.Sprintf("git push heroku $COMMIT:master --force"))
|
f.WriteCmd(fmt.Sprintf("git push heroku HEAD:master --force"))
|
||||||
case false:
|
case false:
|
||||||
// otherwise we just do a standard git push
|
// otherwise we just do a standard git push
|
||||||
f.WriteCmd(fmt.Sprintf("git push heroku $COMMIT:master"))
|
f.WriteCmd(fmt.Sprintf("git push heroku $COMMIT:master"))
|
||||||
|
|
|
@ -30,7 +30,7 @@ func (h *Tsuru) Write(f *buildfile.Buildfile) {
|
||||||
// that need to be deployed to Tsuru.
|
// that need to be deployed to Tsuru.
|
||||||
f.WriteCmd(fmt.Sprintf("git add -A"))
|
f.WriteCmd(fmt.Sprintf("git add -A"))
|
||||||
f.WriteCmd(fmt.Sprintf("git commit -m 'adding build artifacts'"))
|
f.WriteCmd(fmt.Sprintf("git commit -m 'adding build artifacts'"))
|
||||||
f.WriteCmd(fmt.Sprintf("git push tsuru $COMMIT:master --force"))
|
f.WriteCmd(fmt.Sprintf("git push tsuru HEAD:master --force"))
|
||||||
case false:
|
case false:
|
||||||
// otherwise we just do a standard git push
|
// otherwise we just do a standard git push
|
||||||
f.WriteCmd(fmt.Sprintf("git push tsuru $COMMIT:master"))
|
f.WriteCmd(fmt.Sprintf("git push tsuru $COMMIT:master"))
|
||||||
|
|
90
pkg/plugin/notify/flowdock.go
Normal file
90
pkg/plugin/notify/flowdock.go
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
package notify
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"net/url"
|
||||||
|
"github.com/stvp/flowdock"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
flowdockStartedSubject = "Building %s (%s)"
|
||||||
|
flowdockSuccessSubject = "Build: %s (%s) is SUCCESS"
|
||||||
|
flowdockFailureSubject = "Build: %s (%s) is FAILED"
|
||||||
|
flowdockMessage = "<h2>%s </h2>\nBuild: %s <br/>\nResult: %s <br/>\nAuthor: %s <br/>Commit: <span class=\"commit-message\">%s</span> <br/>\nRepository Url: %s"
|
||||||
|
flowdockBuildOkEmail = "build+ok@flowdock.com"
|
||||||
|
flowdockBuildFailEmail = "build+fail@flowdock.com";
|
||||||
|
)
|
||||||
|
|
||||||
|
type Flowdock struct {
|
||||||
|
Token string `yaml:"token,omitempty"`
|
||||||
|
Source string `yaml:"source,omitempty"`
|
||||||
|
Tags string `yaml:"tags,omitempty"`
|
||||||
|
Started bool `yaml:"on_started,omitempty"`
|
||||||
|
Success bool `yaml:"on_success,omitempty"`
|
||||||
|
Failure bool `yaml:"on_failure,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Flowdock) Send(context *Context) error {
|
||||||
|
switch {
|
||||||
|
case context.Commit.Status == "Started" && f.Started:
|
||||||
|
return f.sendStarted(context)
|
||||||
|
case context.Commit.Status == "Success" && f.Success:
|
||||||
|
return f.sendSuccess(context)
|
||||||
|
case context.Commit.Status == "Failure" && f.Failure:
|
||||||
|
return f.sendFailure(context)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Flowdock) getBuildUrl(context *Context) string {
|
||||||
|
branchQuery := url.Values{}
|
||||||
|
if context.Commit.Branch != "" {
|
||||||
|
branchQuery.Set("branch", context.Commit.Branch)
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("%s/%s/commit/%s?%s", context.Host, context.Repo.Slug, context.Commit.Hash, branchQuery.Encode())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Flowdock) getRepoUrl(context *Context) string {
|
||||||
|
return fmt.Sprintf("%s/%s", context.Host, context.Repo.Slug)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Flowdock) getMessage(context *Context) string {
|
||||||
|
buildUrl := fmt.Sprintf("<a href=\"%s\"><span class=\"commit-sha\">%s</span></a>", f.getBuildUrl(context), context.Commit.HashShort())
|
||||||
|
return fmt.Sprintf(flowdockMessage, context.Repo.Name, buildUrl, context.Commit.Status, context.Commit.Author, context.Commit.Message, f.getRepoUrl(context))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Flowdock) sendStarted(context *Context) error {
|
||||||
|
fromAddress := context.Commit.Author
|
||||||
|
subject := fmt.Sprintf(flowdockStartedSubject, context.Repo.Name, context.Commit.Branch)
|
||||||
|
msg := f.getMessage(context)
|
||||||
|
tags := strings.Split(f.Tags, ",")
|
||||||
|
return f.send(fromAddress, subject, msg, tags)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Flowdock) sendFailure(context *Context) error {
|
||||||
|
fromAddress := flowdockBuildFailEmail
|
||||||
|
tags := strings.Split(f.Tags, ",")
|
||||||
|
subject := fmt.Sprintf(flowdockFailureSubject, context.Repo.Name, context.Commit.Branch)
|
||||||
|
msg := f.getMessage(context)
|
||||||
|
return f.send(fromAddress, subject, msg, tags)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Flowdock) sendSuccess(context *Context) error {
|
||||||
|
fromAddress := flowdockBuildOkEmail
|
||||||
|
tags := strings.Split(f.Tags, ",")
|
||||||
|
subject := fmt.Sprintf(flowdockSuccessSubject, context.Repo.Name, context.Commit.Branch)
|
||||||
|
msg := f.getMessage(context)
|
||||||
|
return f.send(fromAddress, subject, msg, tags)
|
||||||
|
}
|
||||||
|
|
||||||
|
// helper function to send Flowdock requests
|
||||||
|
func (f *Flowdock) send(fromAddress, subject, message string, tags []string) error {
|
||||||
|
|
||||||
|
c := flowdock.Client{Token: f.Token, Source: f.Source, FromName: "drone.io", FromAddress: fromAddress, Tags: tags}
|
||||||
|
|
||||||
|
go c.Inbox(subject, message)
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -28,11 +28,12 @@ type Sender interface {
|
||||||
// for notifying a user, or group of users,
|
// for notifying a user, or group of users,
|
||||||
// when their Build has completed.
|
// when their Build has completed.
|
||||||
type Notification struct {
|
type Notification struct {
|
||||||
Email *Email `yaml:"email,omitempty"`
|
Email *Email `yaml:"email,omitempty"`
|
||||||
Webhook *Webhook `yaml:"webhook,omitempty"`
|
Webhook *Webhook `yaml:"webhook,omitempty"`
|
||||||
Hipchat *Hipchat `yaml:"hipchat,omitempty"`
|
Hipchat *Hipchat `yaml:"hipchat,omitempty"`
|
||||||
Irc *IRC `yaml:"irc,omitempty"`
|
Irc *IRC `yaml:"irc,omitempty"`
|
||||||
Slack *Slack `yaml:"slack,omitempty"`
|
Slack *Slack `yaml:"slack,omitempty"`
|
||||||
|
Flowdock *Flowdock `yaml:"flowdock,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *Notification) Send(context *Context) error {
|
func (n *Notification) Send(context *Context) error {
|
||||||
|
@ -61,5 +62,10 @@ func (n *Notification) Send(context *Context) error {
|
||||||
n.Slack.Send(context)
|
n.Slack.Send(context)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// send flowdock notifications
|
||||||
|
if n.Flowdock != nil {
|
||||||
|
n.Flowdock.Send(context)
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,9 +8,9 @@ import (
|
||||||
|
|
||||||
const (
|
const (
|
||||||
slackEndpoint = "https://%s.slack.com/services/hooks/incoming-webhook?token=%s"
|
slackEndpoint = "https://%s.slack.com/services/hooks/incoming-webhook?token=%s"
|
||||||
slackStartedMessage = "*Building* %s, commit <%s|%s>, author %s"
|
slackStartedMessage = "*Building* %s <%s|%s>, by %s:\n> %s"
|
||||||
slackSuccessMessage = "*Success* %s, commit <%s|%s>, author %s"
|
slackSuccessMessage = "*Success* %s <%s|%s>, by %s:\n> %s"
|
||||||
slackFailureMessage = "*Failed* %s, commit <%s|%s>, author %s"
|
slackFailureMessage = "*Failed* %s <%s|%s>, by %s:\n> %s"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Slack struct {
|
type Slack struct {
|
||||||
|
@ -47,7 +47,13 @@ func getBuildUrl(context *Context) string {
|
||||||
|
|
||||||
func getMessage(context *Context, message string) string {
|
func getMessage(context *Context, message string) string {
|
||||||
url := getBuildUrl(context)
|
url := getBuildUrl(context)
|
||||||
return fmt.Sprintf(message, context.Repo.Name, url, context.Commit.HashShort(), context.Commit.Author)
|
return fmt.Sprintf(
|
||||||
|
message,
|
||||||
|
context.Repo.Name,
|
||||||
|
url,
|
||||||
|
context.Commit.HashShort(),
|
||||||
|
context.Commit.Author,
|
||||||
|
context.Commit.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Slack) sendStarted(context *Context) error {
|
func (s *Slack) sendStarted(context *Context) error {
|
||||||
|
|
118
pkg/plugin/publish/docker.go
Normal file
118
pkg/plugin/publish/docker.go
Normal file
|
@ -0,0 +1,118 @@
|
||||||
|
package publish
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/drone/drone/pkg/build/buildfile"
|
||||||
|
"github.com/drone/drone/pkg/build/repo"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Docker struct {
|
||||||
|
// The path to the dockerfile to create the image from. If the path is empty or no
|
||||||
|
// path is specified then the docker file will be built from the base directory.
|
||||||
|
Dockerfile string `yaml:"docker_file"`
|
||||||
|
|
||||||
|
// Connection information for the docker server that will build the image
|
||||||
|
DockerServer string `yaml:"docker_server"`
|
||||||
|
DockerServerPort int `yaml:"docker_port"`
|
||||||
|
// The Docker client version to download. This must match the docker version on the server
|
||||||
|
DockerVersion string `yaml:"docker_version"`
|
||||||
|
|
||||||
|
// Optional Arguments to allow finer-grained control of registry
|
||||||
|
// endpoints
|
||||||
|
RegistryLoginUrl string `yaml:"registry_login_url"`
|
||||||
|
ImageName string `yaml:"image_name"`
|
||||||
|
RegistryLogin bool `yaml:"registry_login"`
|
||||||
|
|
||||||
|
// Authentication credentials for index.docker.io
|
||||||
|
Username string `yaml:"username"`
|
||||||
|
Password string `yaml:"password"`
|
||||||
|
Email string `yaml:"email"`
|
||||||
|
|
||||||
|
// Keep the build on the Docker host after pushing?
|
||||||
|
KeepBuild bool `yaml:"keep_build"`
|
||||||
|
// Do we want to override "latest" automatically with this build?
|
||||||
|
PushLatest bool `yaml:"push_latest"`
|
||||||
|
CustomTag string `yaml:"custom_tag"`
|
||||||
|
Branch string `yaml:"branch"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write adds commands to the buildfile to do the following:
|
||||||
|
// 1. Install the docker client in the Drone container.
|
||||||
|
// 2. Build a docker image based on the dockerfile defined in the config.
|
||||||
|
// 3. Push that docker image to index.docker.io.
|
||||||
|
// 4. Delete the docker image on the server it was build on so we conserve disk space.
|
||||||
|
func (d *Docker) Write(f *buildfile.Buildfile, r *repo.Repo) {
|
||||||
|
if len(d.DockerServer) == 0 || d.DockerServerPort == 0 || len(d.DockerVersion) == 0 ||
|
||||||
|
len(d.ImageName) == 0 {
|
||||||
|
f.WriteCmdSilent(`echo -e "Docker Plugin: Missing argument(s)"\n\n`)
|
||||||
|
if len(d.DockerServer) == 0 { f.WriteCmdSilent(`echo -e "\tdocker_server not defined in yaml`) }
|
||||||
|
if d.DockerServerPort == 0 { f.WriteCmdSilent(`echo -e "\tdocker_port not defined in yaml`) }
|
||||||
|
if len(d.DockerVersion) == 0 { f.WriteCmdSilent(`echo -e "\tdocker_version not defined in yaml`) }
|
||||||
|
if len(d.ImageName) == 0 { f.WriteCmdSilent(`echo -e "\timage_name not defined in yaml`) }
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
f.WriteCmd("sudo apt-get update")
|
||||||
|
|
||||||
|
// Ensure correct apt-get has the https method-driver as per (http://askubuntu.com/questions/165676/)
|
||||||
|
f.WriteCmd("sudo apt-get install apt-transport-https")
|
||||||
|
|
||||||
|
// Install Docker on the container
|
||||||
|
f.WriteCmd("sudo sh -c \"echo deb https://get.docker.io/ubuntu docker main\\ > " +
|
||||||
|
"/etc/apt/sources.list.d/docker.list\"")
|
||||||
|
f.WriteCmd("sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys " +
|
||||||
|
"36A1D7869245C8950F966E92D8576A8BA88D21E9")
|
||||||
|
f.WriteCmd("sudo apt-get update")
|
||||||
|
f.WriteCmd("sudo apt-get --yes install lxc-docker-" + d.DockerVersion)
|
||||||
|
|
||||||
|
// Format our Build Server Endpoint
|
||||||
|
dockerServerUrl := d.DockerServer + ":" + strconv.Itoa(d.DockerServerPort)
|
||||||
|
|
||||||
|
dockerPath := "."
|
||||||
|
if len(d.Dockerfile) != 0 {
|
||||||
|
dockerPath = fmt.Sprintf("- < %s", d.Dockerfile)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run the command commands to build and deploy the image.
|
||||||
|
// Are we setting a custom tag, or do we use the git hash?
|
||||||
|
imageTag := ""
|
||||||
|
if len(d.CustomTag) > 0 {
|
||||||
|
imageTag = d.CustomTag
|
||||||
|
} else {
|
||||||
|
imageTag = "$(git rev-parse --short HEAD)"
|
||||||
|
}
|
||||||
|
f.WriteCmd(fmt.Sprintf("docker -H %s build -t %s:%s %s", dockerServerUrl, d.ImageName, imageTag, dockerPath))
|
||||||
|
|
||||||
|
// Login?
|
||||||
|
if d.RegistryLogin == true {
|
||||||
|
// Are we logging in to a custom Registry?
|
||||||
|
if len(d.RegistryLoginUrl) > 0 {
|
||||||
|
f.WriteCmdSilent(fmt.Sprintf("docker -H %s login -u %s -p %s -e %s %s",
|
||||||
|
dockerServerUrl, d.Username, d.Password, d.Email, d.RegistryLoginUrl))
|
||||||
|
} else {
|
||||||
|
// Assume index.docker.io
|
||||||
|
f.WriteCmdSilent(fmt.Sprintf("docker -H %s login -u %s -p %s -e %s",
|
||||||
|
dockerServerUrl, d.Username, d.Password, d.Email))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Are we overriding the "latest" tag?
|
||||||
|
if d.PushLatest {
|
||||||
|
f.WriteCmd(fmt.Sprintf("docker -H %s tag %s:%s %s:latest",
|
||||||
|
dockerServerUrl, d.ImageName, imageTag, d.ImageName))
|
||||||
|
}
|
||||||
|
|
||||||
|
f.WriteCmd(fmt.Sprintf("docker -H %s push %s", dockerServerUrl, d.ImageName))
|
||||||
|
|
||||||
|
// Delete the image from the docker server we built on.
|
||||||
|
if ! d.KeepBuild {
|
||||||
|
f.WriteCmd(fmt.Sprintf("docker -H %s rmi %s:%s",
|
||||||
|
dockerServerUrl, d.ImageName, imageTag))
|
||||||
|
if d.PushLatest {
|
||||||
|
f.WriteCmd(fmt.Sprintf("docker -H %s rmi %s:latest",
|
||||||
|
dockerServerUrl, d.ImageName))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
240
pkg/plugin/publish/docker_test.go
Normal file
240
pkg/plugin/publish/docker_test.go
Normal file
|
@ -0,0 +1,240 @@
|
||||||
|
package publish
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"gopkg.in/v1/yaml"
|
||||||
|
"github.com/drone/drone/pkg/build/buildfile"
|
||||||
|
"github.com/drone/drone/pkg/build/repo"
|
||||||
|
)
|
||||||
|
|
||||||
|
type PublishToDrone struct {
|
||||||
|
Publish *Publish `yaml:"publish,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func setUpWithDrone(input string) (string, error) {
|
||||||
|
var buildStruct PublishToDrone
|
||||||
|
err := yaml.Unmarshal([]byte(input), &buildStruct)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
bf := buildfile.New()
|
||||||
|
buildStruct.Publish.Write(bf, &repo.Repo{Name: "name"})
|
||||||
|
return bf.String(), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Private Registry Test (no auth)
|
||||||
|
var privateRegistryNoAuthYaml = `
|
||||||
|
publish:
|
||||||
|
docker:
|
||||||
|
dockerfile: file_path
|
||||||
|
docker_server: server
|
||||||
|
docker_port: 1000
|
||||||
|
docker_version: 1.0
|
||||||
|
registry_login: false
|
||||||
|
image_name: registry/image
|
||||||
|
`
|
||||||
|
func TestPrivateRegistryNoAuth(t *testing.T) {
|
||||||
|
response, err := setUpWithDrone(privateRegistryNoAuthYaml)
|
||||||
|
t.Log(privateRegistryNoAuthYaml)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Can't unmarshal script: %s\n\n", err.Error())
|
||||||
|
}
|
||||||
|
if !strings.Contains(response, "docker -H server:1000 build -t registry/image:$(git rev-parse --short HEAD)") {
|
||||||
|
t.Fatalf("Response: " + response + " doesn't contain registry in image-names: expected registry/image\n\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Private Registry Test (with auth)
|
||||||
|
var privateRegistryAuthYaml = `
|
||||||
|
publish:
|
||||||
|
docker:
|
||||||
|
dockerfile: file_path
|
||||||
|
docker_server: server
|
||||||
|
docker_port: 1000
|
||||||
|
docker_version: 1.0
|
||||||
|
registry_login_url: https://registry:8000/v1/
|
||||||
|
registry_login: true
|
||||||
|
username: username
|
||||||
|
password: password
|
||||||
|
email: email@example.com
|
||||||
|
image_name: registry/image
|
||||||
|
`
|
||||||
|
func TestPrivateRegistryAuth(t *testing.T) {
|
||||||
|
response, err := setUpWithDrone(privateRegistryAuthYaml)
|
||||||
|
t.Log(privateRegistryAuthYaml)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Can't unmarshal script: %s\n\n", err.Error())
|
||||||
|
}
|
||||||
|
if !strings.Contains(response, "docker -H server:1000 login -u username -p password -e email@example.com https://registry:8000/v1/") {
|
||||||
|
t.Log("\n\n\n\ndocker -H server:1000 login -u username -p xxxxxxxx -e email@example.com https://registry:8000/v1/\n\n\n\n")
|
||||||
|
t.Fatalf("Response: " + response + " doesn't contain private registry login\n\n")
|
||||||
|
}
|
||||||
|
if !strings.Contains(response, "docker -H server:1000 build -t registry/image:$(git rev-parse --short HEAD) .") {
|
||||||
|
t.Log("docker -H server:1000 build -t registry/image:$(git rev-parse --short HEAD) .")
|
||||||
|
t.Fatalf("Response: " + response + " doesn't contain registry in image-names\n\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Override "latest" Test
|
||||||
|
var overrideLatestTagYaml = `
|
||||||
|
publish:
|
||||||
|
docker:
|
||||||
|
docker_server: server
|
||||||
|
docker_port: 1000
|
||||||
|
docker_version: 1.0
|
||||||
|
username: username
|
||||||
|
password: password
|
||||||
|
email: email@example.com
|
||||||
|
image_name: username/image
|
||||||
|
push_latest: true
|
||||||
|
`
|
||||||
|
func TestOverrideLatestTag(t *testing.T) {
|
||||||
|
response, err := setUpWithDrone(overrideLatestTagYaml)
|
||||||
|
t.Log(overrideLatestTagYaml)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Can't unmarshal script: %s\n\n", err.Error())
|
||||||
|
}
|
||||||
|
if !strings.Contains(response, "docker -H server:1000 build -t username/image:$(git rev-parse --short HEAD) .") {
|
||||||
|
t.Fatalf("Response: " + response + " doesn't contain the git-ref tagged image\n\n")
|
||||||
|
}
|
||||||
|
if !strings.Contains(response, "docker -H server:1000 tag username/image:$(git rev-parse --short HEAD) username/image:latest") {
|
||||||
|
t.Fatalf("Response: " + response + " doesn't contain 'latest' tag command\n\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep builds Test
|
||||||
|
var keepBuildsYaml = `
|
||||||
|
publish:
|
||||||
|
docker:
|
||||||
|
docker_server: server
|
||||||
|
docker_port: 1000
|
||||||
|
docker_version: 1.0
|
||||||
|
keep_build: true
|
||||||
|
username: username
|
||||||
|
password: password
|
||||||
|
email: email@example.com
|
||||||
|
image_name: image
|
||||||
|
`
|
||||||
|
func TestKeepBuilds(t *testing.T) {
|
||||||
|
response, err := setUpWithDrone(keepBuildsYaml)
|
||||||
|
t.Log(keepBuildsYaml)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Can't unmarshal script: %s\n\n", err.Error())
|
||||||
|
}
|
||||||
|
if strings.Contains(response, "docker -H server:1000 rmi") {
|
||||||
|
t.Fatalf("Response: " + response + " incorrectly instructs the docker server to remove the builds when it shouldn't\n\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Custom Tag test
|
||||||
|
var customTagYaml = `
|
||||||
|
publish:
|
||||||
|
docker:
|
||||||
|
docker_server: server
|
||||||
|
docker_port: 1000
|
||||||
|
docker_version: 1.0
|
||||||
|
custom_tag: release-0.1
|
||||||
|
username: username
|
||||||
|
password: password
|
||||||
|
email: email@example.com
|
||||||
|
image_name: username/image
|
||||||
|
`
|
||||||
|
func TestCustomTag(t *testing.T) {
|
||||||
|
response, err := setUpWithDrone(customTagYaml)
|
||||||
|
t.Log(customTagYaml)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Can't unmarshal script: %s\n", err.Error())
|
||||||
|
}
|
||||||
|
if strings.Contains(response, "$(git rev-parse --short HEAD)") {
|
||||||
|
t.Fatalf("Response: " + response + " is tagging images from git-refs when it should use a custom tag\n\n")
|
||||||
|
}
|
||||||
|
if !strings.Contains(response, "docker -H server:1000 build -t username/image:release-0.1") {
|
||||||
|
t.Fatalf("Response: " + response + " isn't tagging images using our custom tag\n\n")
|
||||||
|
}
|
||||||
|
if !strings.Contains(response, "docker -H server:1000 push username/image"){
|
||||||
|
t.Fatalf("Response: " + response + " doesn't push the custom tagged image\n\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var missingFieldsYaml = `
|
||||||
|
publish:
|
||||||
|
docker:
|
||||||
|
dockerfile: file
|
||||||
|
`
|
||||||
|
|
||||||
|
func TestMissingFields(t *testing.T) {
|
||||||
|
response, err := setUpWithDrone(missingFieldsYaml)
|
||||||
|
t.Log(missingFieldsYaml)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Can't unmarshal script: %s\n\n", err.Error())
|
||||||
|
}
|
||||||
|
if !strings.Contains(response, "Missing argument(s)") {
|
||||||
|
t.Fatalf("Response: " + response + " didn't contain missing arguments warning\n\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var validYaml = `
|
||||||
|
publish:
|
||||||
|
docker:
|
||||||
|
docker_file: file_path
|
||||||
|
docker_server: server
|
||||||
|
docker_port: 1000
|
||||||
|
docker_version: 1.0
|
||||||
|
username: user
|
||||||
|
password: password
|
||||||
|
email: email
|
||||||
|
image_name: user/image
|
||||||
|
push_latest: true
|
||||||
|
registry_login: true
|
||||||
|
`
|
||||||
|
|
||||||
|
func TestValidYaml(t *testing.T) {
|
||||||
|
response, err := setUpWithDrone(validYaml)
|
||||||
|
t.Log(validYaml)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Can't unmarshal script: %s\n\n", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.Contains(response, "docker -H server:1000 tag user/image:$(git rev-parse --short HEAD) user/image:latest") {
|
||||||
|
t.Fatalf("Response: " + response + " doesn't contain tag command for latest\n\n")
|
||||||
|
}
|
||||||
|
if !strings.Contains(response, "docker -H server:1000 build -t user/image:$(git rev-parse --short HEAD) - <") {
|
||||||
|
t.Fatalf("Response: " + response + "doesn't contain build command for commit hash\n\n")
|
||||||
|
}
|
||||||
|
if !strings.Contains(response, "docker -H server:1000 login -u user -p password -e email") {
|
||||||
|
t.Fatalf("Response: " + response + " doesn't contain login command\n\n")
|
||||||
|
}
|
||||||
|
if !strings.Contains(response, "docker -H server:1000 push user/image") {
|
||||||
|
t.Fatalf("Response: " + response + " doesn't contain push command\n\n")
|
||||||
|
}
|
||||||
|
if !strings.Contains(response, "docker -H server:1000 rmi user/image:" +
|
||||||
|
"$(git rev-parse --short HEAD)") {
|
||||||
|
t.Fatalf("Response: " + response + " doesn't contain remove image command\n\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var withoutDockerFileYaml = `
|
||||||
|
publish:
|
||||||
|
docker:
|
||||||
|
docker_server: server
|
||||||
|
docker_port: 1000
|
||||||
|
docker_version: 1.0
|
||||||
|
image_name: user/image
|
||||||
|
username: user
|
||||||
|
password: password
|
||||||
|
email: email
|
||||||
|
`
|
||||||
|
|
||||||
|
func TestWithoutDockerFile(t *testing.T) {
|
||||||
|
response, err := setUpWithDrone(withoutDockerFileYaml)
|
||||||
|
t.Log(withoutDockerFileYaml)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Can't unmarshal script: %s\n\n", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.Contains(response, "docker -H server:1000 build -t user/image:$(git rev-parse --short HEAD) .") {
|
||||||
|
t.Fatalf("Response: " + response + " doesn't contain build command\n\n")
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,10 +9,11 @@ import (
|
||||||
// for publishing build artifacts when
|
// for publishing build artifacts when
|
||||||
// a Build has succeeded
|
// a Build has succeeded
|
||||||
type Publish struct {
|
type Publish struct {
|
||||||
S3 *S3 `yaml:"s3,omitempty"`
|
S3 *S3 `yaml:"s3,omitempty"`
|
||||||
Swift *Swift `yaml:"swift,omitempty"`
|
Swift *Swift `yaml:"swift,omitempty"`
|
||||||
PyPI *PyPI `yaml:"pypi,omitempty"`
|
PyPI *PyPI `yaml:"pypi,omitempty"`
|
||||||
NPM *NPM `yaml:"npm,omitempty"`
|
NPM *NPM `yaml:"npm,omitempty"`
|
||||||
|
Docker *Docker `yaml:"docker,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Publish) Write(f *buildfile.Buildfile, r *repo.Repo) {
|
func (p *Publish) Write(f *buildfile.Buildfile, r *repo.Repo) {
|
||||||
|
@ -35,4 +36,9 @@ func (p *Publish) Write(f *buildfile.Buildfile, r *repo.Repo) {
|
||||||
if p.NPM != nil && (len(p.NPM.Branch) == 0 || (len(p.NPM.Branch) > 0 && r.Branch == p.NPM.Branch)) {
|
if p.NPM != nil && (len(p.NPM.Branch) == 0 || (len(p.NPM.Branch) > 0 && r.Branch == p.NPM.Branch)) {
|
||||||
p.NPM.Write(f)
|
p.NPM.Write(f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Docker
|
||||||
|
if p.Docker != nil && (len(p.Docker.Branch) == 0 || (len(p.Docker.Branch) > 0 && r.Branch == p.Docker.Branch)) {
|
||||||
|
p.Docker.Write(f, r)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -184,7 +184,7 @@ func (w *worker) runBuild(task *BuildTask, buildscript *script.Build, buf io.Wri
|
||||||
Branch: task.Commit.Branch,
|
Branch: task.Commit.Branch,
|
||||||
Commit: task.Commit.Hash,
|
Commit: task.Commit.Hash,
|
||||||
PR: task.Commit.PullRequest,
|
PR: task.Commit.PullRequest,
|
||||||
Dir: filepath.Join("/var/cache/drone/src", task.Repo.Slug),
|
Dir: filepath.Join("/var/cache/drone/src", git.GitPath(buildscript.Git, task.Repo.Slug)),
|
||||||
Depth: git.GitDepth(buildscript.Git),
|
Depth: git.GitDepth(buildscript.Git),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -39,7 +39,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="alert">GitHub OAuth Consumer Key and Secret</div>
|
<div class="alert">GitHub OAuth Consumer Key and Secret</div>
|
||||||
<label>GitHub Client ID and Secret:</label>
|
<label>GitHub Client ID and Secret: (<a href="https://github.com/settings/applications/new"> Register a new OAuth application</a>)</label>
|
||||||
<div>
|
<div>
|
||||||
<input class="form-control form-control-large" type="text" name="GitHubKey" value="{{.Settings.GitHubKey}}" />
|
<input class="form-control form-control-large" type="text" name="GitHubKey" value="{{.Settings.GitHubKey}}" />
|
||||||
<input class="form-control form-control-large" type="password" name="GitHubSecret" value="{{.Settings.GitHubSecret}}" />
|
<input class="form-control form-control-large" type="password" name="GitHubSecret" value="{{.Settings.GitHubSecret}}" />
|
||||||
|
|
|
@ -31,6 +31,10 @@
|
||||||
<div class="col-xs-9" role="main">
|
<div class="col-xs-9" role="main">
|
||||||
<div class="alert">Manage your repository settings.</div>
|
<div class="alert">Manage your repository settings.</div>
|
||||||
<form method="POST" action="/{{.Repo.Slug}}" role="form">
|
<form method="POST" action="/{{.Repo.Slug}}" role="form">
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Repository URL</label>
|
||||||
|
<input class="form-control form-control-xlarge" type="text" name="URL" value="{{.Repo.URL}}" />
|
||||||
|
</div>
|
||||||
<div class="checkbox form-group">
|
<div class="checkbox form-group">
|
||||||
<label>
|
<label>
|
||||||
<input class="" type="checkbox" name="Disabled" {{ if not .Repo.Disabled }}checked="True" {{ end }}/>
|
<input class="" type="checkbox" name="Disabled" {{ if not .Repo.Disabled }}checked="True" {{ end }}/>
|
||||||
|
|
1
vendor/src/github.com/GeertJohan/rsrc
vendored
Submodule
1
vendor/src/github.com/GeertJohan/rsrc
vendored
Submodule
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 370d163b6fcb4dc5016a8dc40cbd9861828b2e9e
|
Loading…
Reference in a new issue