Merge remote-tracking branch 'origin/master'

This commit is contained in:
Brad Rydzewski 2014-09-23 23:36:39 -07:00
commit 9fcec0025d
26 changed files with 731 additions and 27 deletions

3
.gitmodules vendored
View file

@ -1,3 +1,6 @@
[submodule "vendor/src/github.com/GeertJohan/go.rice"]
path = vendor/src/github.com/GeertJohan/go.rice
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

View file

@ -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 GOPATH /gocode
RUN go get github.com/tools/godep
RUN mkdir -p /gocode/src/github.com/drone
ADD . /gocode/src/github.com/drone/drone
WORKDIR /gocode/src/github.com/drone/drone
RUN make deps
RUN godep restore
RUN make
RUN make install
@ -24,4 +26,4 @@ EXPOSE 80
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
View file

@ -174,6 +174,10 @@
"Comment": "1.5.0-168-g2abea07",
"Rev": "2abea075ec076abf0572d29bdb28ae7da64cfb7a"
},
{
"ImportPath": "github.com/stvp/flowdock",
"Rev": "50362abeabebf40b0f56a326d62f17e61a59302b"
},
{
"ImportPath": "launchpad.net/goyaml",
"Comment": "51",

View 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

View 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))
}
}

View file

@ -94,4 +94,5 @@ godep:
go get github.com/tools/godep
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

View file

@ -227,6 +227,7 @@ func setupHandlers() {
// 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/status.json", handler.PublicHandler(handler.BuildStatus))
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.Post("/:host/:owner/:name/commit/:commit/rebuild", handler.RepoAdminHandler(rebuild.CommitRebuild))

View file

@ -4,7 +4,6 @@
#
# -datasource="drone.sqlite":
# -driver="sqlite3":
# -path="":
# -port=":8080":
# -workers="4":
#

View file

@ -244,6 +244,8 @@ func (b *Builder) setup() error {
if err := b.dockerClient.Images.Pull(b.Build.Image); err != nil {
return err
}
} else if err != nil {
log.Errf("failed to inspect image %s", b.Build.Image)
}
// create the Docker image
@ -439,7 +441,7 @@ func (b *Builder) writeDockerfile(dir string) error {
switch {
case strings.HasPrefix(b.Build.Image, "bradrydzewski/"),
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
// inherit from the ubuntu cloud ISO
dockerfile.WriteUser("ubuntu")

View file

@ -13,10 +13,7 @@ type Git struct {
Depth *int `yaml:"depth,omitempty"`
// The name of a directory to clone into.
// TODO this still needs to be implemented. this field is
// critical for forked Go projects, that need to clone
// to a specific repository.
Path string `yaml:"path,omitempty"`
Path *string `yaml:"path,omitempty"`
}
// GitDepth returns GitDefaultDepth
@ -29,3 +26,14 @@ func GitDepth(g *Git) int {
}
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
}

View file

@ -7,6 +7,10 @@ import (
. "github.com/drone/drone/pkg/model"
)
type BuildResult struct {
Status string
}
// Returns the combined stdout / stderr for an individual Build.
func BuildOut(w http.ResponseWriter, r *http.Request, u *User, repo *Repo) error {
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)
}
// 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
func BuildOutGzip(w http.ResponseWriter, r *http.Request, u *User) error {
// TODO

View file

@ -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
// the currently authenticated User and requested Repository
// in the method signature, in addition to handling an error
// as the return value.
type RepoHandler func(w http.ResponseWriter, r *http.Request, user *User, repo *Repo) error
func (h RepoHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {

View file

@ -326,6 +326,7 @@ func RepoUpdate(w http.ResponseWriter, r *http.Request, u *User, repo *Repo) err
return err
}
default:
repo.URL = r.FormValue("URL")
repo.Disabled = len(r.FormValue("Disabled")) == 0
repo.DisabledPullRequest = len(r.FormValue("DisabledPullRequest")) == 0
repo.Private = len(r.FormValue("Private")) > 0

View file

@ -35,7 +35,7 @@ func (g *Git) Write(f *buildfile.Buildfile) {
// that need to be deployed to git remote.
f.WriteCmd(fmt.Sprintf("git add -A"))
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:
// otherwise we just do a standard git push
f.WriteCmd(fmt.Sprintf("git push deploy $COMMIT:%s", destinationBranch))

View file

@ -30,7 +30,7 @@ func (h *Heroku) Write(f *buildfile.Buildfile) {
// that need to be deployed to Heroku.
f.WriteCmd(fmt.Sprintf("git add -A"))
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:
// otherwise we just do a standard git push
f.WriteCmd(fmt.Sprintf("git push heroku $COMMIT:master"))

View file

@ -30,7 +30,7 @@ func (h *Tsuru) Write(f *buildfile.Buildfile) {
// that need to be deployed to Tsuru.
f.WriteCmd(fmt.Sprintf("git add -A"))
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:
// otherwise we just do a standard git push
f.WriteCmd(fmt.Sprintf("git push tsuru $COMMIT:master"))

View 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
}

View file

@ -28,11 +28,12 @@ type Sender interface {
// for notifying a user, or group of users,
// when their Build has completed.
type Notification struct {
Email *Email `yaml:"email,omitempty"`
Webhook *Webhook `yaml:"webhook,omitempty"`
Hipchat *Hipchat `yaml:"hipchat,omitempty"`
Irc *IRC `yaml:"irc,omitempty"`
Slack *Slack `yaml:"slack,omitempty"`
Email *Email `yaml:"email,omitempty"`
Webhook *Webhook `yaml:"webhook,omitempty"`
Hipchat *Hipchat `yaml:"hipchat,omitempty"`
Irc *IRC `yaml:"irc,omitempty"`
Slack *Slack `yaml:"slack,omitempty"`
Flowdock *Flowdock `yaml:"flowdock,omitempty"`
}
func (n *Notification) Send(context *Context) error {
@ -61,5 +62,10 @@ func (n *Notification) Send(context *Context) error {
n.Slack.Send(context)
}
// send flowdock notifications
if n.Flowdock != nil {
n.Flowdock.Send(context)
}
return nil
}

View file

@ -8,9 +8,9 @@ import (
const (
slackEndpoint = "https://%s.slack.com/services/hooks/incoming-webhook?token=%s"
slackStartedMessage = "*Building* %s, commit <%s|%s>, author %s"
slackSuccessMessage = "*Success* %s, commit <%s|%s>, author %s"
slackFailureMessage = "*Failed* %s, commit <%s|%s>, author %s"
slackStartedMessage = "*Building* %s <%s|%s>, by %s:\n> %s"
slackSuccessMessage = "*Success* %s <%s|%s>, by %s:\n> %s"
slackFailureMessage = "*Failed* %s <%s|%s>, by %s:\n> %s"
)
type Slack struct {
@ -47,7 +47,13 @@ func getBuildUrl(context *Context) string {
func getMessage(context *Context, message string) string {
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 {

View 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))
}
}
}

View 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")
}
}

View file

@ -9,10 +9,11 @@ import (
// for publishing build artifacts when
// a Build has succeeded
type Publish struct {
S3 *S3 `yaml:"s3,omitempty"`
Swift *Swift `yaml:"swift,omitempty"`
PyPI *PyPI `yaml:"pypi,omitempty"`
NPM *NPM `yaml:"npm,omitempty"`
S3 *S3 `yaml:"s3,omitempty"`
Swift *Swift `yaml:"swift,omitempty"`
PyPI *PyPI `yaml:"pypi,omitempty"`
NPM *NPM `yaml:"npm,omitempty"`
Docker *Docker `yaml:"docker,omitempty"`
}
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)) {
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)
}
}

View file

@ -184,7 +184,7 @@ func (w *worker) runBuild(task *BuildTask, buildscript *script.Build, buf io.Wri
Branch: task.Commit.Branch,
Commit: task.Commit.Hash,
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),
}

View file

@ -39,7 +39,7 @@
</div>
<div class="form-group">
<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>
<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}}" />

View file

@ -31,6 +31,10 @@
<div class="col-xs-9" role="main">
<div class="alert">Manage your repository settings.</div>
<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">
<label>
<input class="" type="checkbox" name="Disabled" {{ if not .Repo.Disabled }}checked="True" {{ end }}/>

@ -0,0 +1 @@
Subproject commit 370d163b6fcb4dc5016a8dc40cbd9861828b2e9e