Merge pull request #1149 from donny-dont/features/git-checkout-specified

Adding support for setting the workspace directly in the config
This commit is contained in:
Brad Rydzewski 2015-08-19 10:38:24 -07:00
commit aeee201796
7 changed files with 134 additions and 43 deletions

View file

@ -5,9 +5,7 @@ import (
"encoding/json" "encoding/json"
"flag" "flag"
"fmt" "fmt"
"net/url"
"os" "os"
"path/filepath"
"strings" "strings"
log "github.com/drone/drone/Godeps/_workspace/src/github.com/Sirupsen/logrus" log "github.com/drone/drone/Godeps/_workspace/src/github.com/Sirupsen/logrus"
@ -37,7 +35,6 @@ func main() {
os.Exit(1) os.Exit(1)
return return
} }
createClone(ctx)
// creates the Docker client, connecting to the // creates the Docker client, connecting to the
// linked Docker daemon // linked Docker daemon
@ -68,6 +65,7 @@ func main() {
os.Exit(1) os.Exit(1)
return return
} }
createClone(ctx)
var execs []execFunc var execs []execFunc
if *clone { if *clone {
@ -130,13 +128,15 @@ func createClone(c *Context) error {
// to the clone object for merge requests from bitbucket, gitlab, et al // to the clone object for merge requests from bitbucket, gitlab, et al
// if len(c.Commit.PullRequest) != 0 { // if len(c.Commit.PullRequest) != 0 {
// } // }
pathv, ok := c.Conf.Clone.Config["path"]
url_, err := url.Parse(c.Repo.Link) if ok {
if err != nil { path, ok := pathv.(string)
return err if ok {
c.Clone.Dir = path
return nil
}
} }
c.Clone.Dir = filepath.Join("/drone/src", url_.Host, c.Repo.FullName) return fmt.Errorf("Workspace path not found")
return nil
} }
func parseContext() (*Context, error) { func parseContext() (*Context, error) {

View file

@ -1,8 +1,6 @@
package main package main
import ( import (
"path/filepath"
"github.com/drone/drone/Godeps/_workspace/src/github.com/samalba/dockerclient" "github.com/drone/drone/Godeps/_workspace/src/github.com/samalba/dockerclient"
common "github.com/drone/drone/pkg/types" common "github.com/drone/drone/pkg/types"
"github.com/drone/drone/pkg/yaml" "github.com/drone/drone/pkg/yaml"
@ -64,17 +62,6 @@ func setup(c *Context) error {
c.Conf.Build.Environment = append(c.Conf.Build.Environment, env) c.Conf.Build.Environment = append(c.Conf.Build.Environment, env)
} }
// attempt to extract the clone path. i'm not a huge fan of
// this, by the way, but for now we'll keep it.
// TODO consider moving this to a transform?
pathv, ok := c.Conf.Clone.Config["path"]
if ok {
path, ok := pathv.(string)
if ok {
c.Clone.Dir = filepath.Join("/drone/src", path)
}
}
return nil return nil
} }

View file

@ -2,14 +2,14 @@ package builtin
import ( import (
"bytes" "bytes"
"crypto/tls"
"crypto/x509"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io" "io"
"io/ioutil" "io/ioutil"
"os" "os"
"time" "time"
"crypto/tls"
"crypto/x509"
"github.com/drone/drone/Godeps/_workspace/src/github.com/samalba/dockerclient" "github.com/drone/drone/Godeps/_workspace/src/github.com/samalba/dockerclient"
"github.com/drone/drone/pkg/docker" "github.com/drone/drone/pkg/docker"

View file

@ -13,7 +13,7 @@ import (
// fails it should return an error message. // fails it should return an error message.
type lintRule func(*common.Config) error type lintRule func(*common.Config) error
var lintRules = [...]lintRule{ var lintRules = []lintRule{
expectBuild, expectBuild,
expectImage, expectImage,
expectCommand, expectCommand,
@ -22,6 +22,7 @@ var lintRules = [...]lintRule{
expectTrustedPublish, expectTrustedPublish,
expectTrustedDeploy, expectTrustedDeploy,
expectTrustedNotify, expectTrustedNotify,
expectCloneInWorkspace,
expectCacheInWorkspace, expectCacheInWorkspace,
} }
@ -106,6 +107,33 @@ func expectTrustedNotify(c *common.Config) error {
return nil return nil
} }
// lint rule that fails if the clone directory is not contained
// in the root workspace.
func expectCloneInWorkspace(c *common.Config) error {
pathv, ok := c.Clone.Config["path"]
var path string
if ok {
path, _ = pathv.(string)
}
if len(path) == 0 {
// This should only happen if the transformer was not run
return fmt.Errorf("No workspace specified")
}
relative, relOk := filepath.Rel("/drone/src", path)
if relOk != nil {
return fmt.Errorf("Path is not relative to root")
}
cleaned := filepath.Clean(relative)
if strings.Index(cleaned, "../") != -1 {
return fmt.Errorf("Cannot clone above the root")
}
return nil
}
// lint rule that fails if the cache directories are not contained // lint rule that fails if the cache directories are not contained
// in the workspace. // in the workspace.
func expectCacheInWorkspace(c *common.Config) error { func expectCacheInWorkspace(c *common.Config) error {

View file

@ -79,6 +79,9 @@ func Test_Linter(t *testing.T) {
c.Build.Image = "golang" c.Build.Image = "golang"
c.Build.Config = map[string]interface{}{} c.Build.Config = map[string]interface{}{}
c.Build.Config["commands"] = []string{"go build", "go test"} c.Build.Config["commands"] = []string{"go build", "go test"}
c.Clone = &common.Step{}
c.Clone.Config = map[string]interface{}{}
c.Clone.Config["path"] = "/drone/src/foo/bar"
c.Publish = map[string]*common.Step{} c.Publish = map[string]*common.Step{}
c.Publish["docker"] = &common.Step{Image: "docker"} c.Publish["docker"] = &common.Step{Image: "docker"}
c.Deploy = map[string]*common.Step{} c.Deploy = map[string]*common.Step{}
@ -88,19 +91,41 @@ func Test_Linter(t *testing.T) {
g.Assert(Lint(c) == nil).IsTrue() g.Assert(Lint(c) == nil).IsTrue()
}) })
g.It("Should pass with path inside workspace", func() { g.It("Should pass with clone path inside workspace", func() {
c := &common.Config{
Clone: &common.Step{
Config: map[string]interface{}{
"path": "/drone/src/foo/bar",
},
},
}
g.Assert(expectCloneInWorkspace(c) == nil).IsTrue()
})
g.It("Should fail with clone path outside workspace", func() {
c := &common.Config{
Clone: &common.Step{
Config: map[string]interface{}{
"path": "/foo/bar",
},
},
}
g.Assert(expectCloneInWorkspace(c) != nil).IsTrue()
})
g.It("Should pass with cache path inside workspace", func() {
c := &common.Config{ c := &common.Config{
Build: &common.Step{ Build: &common.Step{
Cache: []string{".git","/.git","/.git/../.git/../.git"}, Cache: []string{".git", "/.git", "/.git/../.git/../.git"},
}, },
} }
g.Assert(expectCacheInWorkspace(c) == nil).IsTrue() g.Assert(expectCacheInWorkspace(c) == nil).IsTrue()
}) })
g.It("Should fail with path outside workspace", func() { g.It("Should fail with cache path outside workspace", func() {
c := &common.Config{ c := &common.Config{
Build: &common.Step{ Build: &common.Step{
Cache: []string{".git","/.git","../../.git"}, Cache: []string{".git", "/.git", "../../.git"},
}, },
} }
g.Assert(expectCacheInWorkspace(c) != nil).IsTrue() g.Assert(expectCacheInWorkspace(c) != nil).IsTrue()
@ -109,16 +134,16 @@ func Test_Linter(t *testing.T) {
g.It("Should fail when caching workspace directory", func() { g.It("Should fail when caching workspace directory", func() {
c := &common.Config{ c := &common.Config{
Build: &common.Step{ Build: &common.Step{
Cache: []string{".git",".git/../"}, Cache: []string{".git", ".git/../"},
}, },
} }
g.Assert(expectCacheInWorkspace(c) != nil).IsTrue() g.Assert(expectCacheInWorkspace(c) != nil).IsTrue()
}) })
g.It("Should fail when : is in the path", func() { g.It("Should fail when : is in the cache path", func() {
c := &common.Config{ c := &common.Config{
Build: &common.Step{ Build: &common.Step{
Cache: []string{".git",".git:/../"}, Cache: []string{".git", ".git:/../"},
}, },
} }
g.Assert(expectCacheInWorkspace(c) != nil).IsTrue() g.Assert(expectCacheInWorkspace(c) != nil).IsTrue()

View file

@ -9,6 +9,12 @@ import (
common "github.com/drone/drone/pkg/types" common "github.com/drone/drone/pkg/types"
) )
// buildRoot is the root build directory.
//
// If this changes then the matching value in lint.go needs
// to be modified as well.
const buildRoot = "/drone/src"
// transformRule applies a check or transformation rule // transformRule applies a check or transformation rule
// to the build configuration. // to the build configuration.
type transformRule func(*common.Config) type transformRule func(*common.Config)
@ -65,7 +71,7 @@ func RemovePrivileged(c *common.Config) {
// Repo executes all transformers that rely on repository // Repo executes all transformers that rely on repository
// information. // information.
func Repo(c *common.Config, r *common.Repo) { func Repo(c *common.Config, r *common.Repo) {
transformWorkspace(c, r) transformWorkspace(c, r)
transformCache(c, r) transformCache(c, r)
} }
@ -203,7 +209,17 @@ func rmNetwork(c *common.Config) {
// directory to the configuration based on the repository // directory to the configuration based on the repository
// information. // information.
func transformWorkspace(c *common.Config, r *common.Repo) { func transformWorkspace(c *common.Config, r *common.Repo) {
//c.Clone.Dir = workspaceRoot(r) pathv, ok := c.Clone.Config["path"]
var path string
if ok {
path, _ = pathv.(string)
}
if len(path) == 0 {
path = repoPath(r)
}
c.Clone.Config["path"] = filepath.Join(buildRoot, path)
} }
// transformCache is a transformer that adds volumes // transformCache is a transformer that adds volumes
@ -211,21 +227,20 @@ func transformWorkspace(c *common.Config, r *common.Repo) {
func transformCache(c *common.Config, r *common.Repo) { func transformCache(c *common.Config, r *common.Repo) {
cacheCount := len(c.Build.Cache) cacheCount := len(c.Build.Cache)
if cacheCount == 0 { if cacheCount == 0 {
return return
} }
volumes := make([]string, cacheCount) volumes := make([]string, cacheCount)
cache := cacheRoot(r) cache := cacheRoot(r)
workspace := workspaceRoot(r) workspace := c.Clone.Config["path"].(string)
for i, dir := range c.Build.Cache { for i, dir := range c.Build.Cache {
cacheDir := filepath.Join(cache, dir) cacheDir := filepath.Join(cache, dir)
workspaceDir := filepath.Join(workspace, dir) workspaceDir := filepath.Join(workspace, dir)
volumes[i] = fmt.Sprintf("%s:%s", cacheDir, workspaceDir) volumes[i] = fmt.Sprintf("%s:%s", cacheDir, workspaceDir)
fmt.Printf("Volume %s", volumes[i])
} }
c.Setup.Volumes = append(c.Setup.Volumes, volumes...) c.Setup.Volumes = append(c.Setup.Volumes, volumes...)
@ -272,7 +287,7 @@ func imageNameDefault(name, defaultName string) string {
// workspaceRoot is a helper function that determines the // workspaceRoot is a helper function that determines the
// default workspace the build runs in. // default workspace the build runs in.
func workspaceRoot(r *common.Repo) string { func workspaceRoot(r *common.Repo) string {
return filepath.Join("/drone/src", repoPath(r)) return filepath.Join(buildRoot, repoPath(r))
} }
// cacheRoot is a helper function that deteremines the // cacheRoot is a helper function that deteremines the

View file

@ -1,6 +1,7 @@
package transform package transform
import ( import (
"path/filepath"
"testing" "testing"
"github.com/drone/drone/Godeps/_workspace/src/github.com/franela/goblin" "github.com/drone/drone/Godeps/_workspace/src/github.com/franela/goblin"
@ -146,21 +147,24 @@ func Test_Transform(t *testing.T) {
g.It("Should have cached volumes", func() { g.It("Should have cached volumes", func() {
c := &common.Config{ c := &common.Config{
Setup: &common.Step{}, Setup: &common.Step{},
Clone: &common.Step{}, Clone: &common.Step{
Config: map[string]interface{}{},
},
Build: &common.Step{ Build: &common.Step{
Cache: []string{".git","foo","bar"}, Cache: []string{".git", "foo", "bar"},
}, },
Notify: map[string]*common.Step{}, Notify: map[string]*common.Step{},
Deploy: map[string]*common.Step{}, Deploy: map[string]*common.Step{},
Publish: map[string]*common.Step{}, Publish: map[string]*common.Step{},
} }
r := &common.Repo{ r := &common.Repo{
Link: "https://github.com/drone/drone", Link: "https://github.com/drone/drone",
FullName: "drone/drone", FullName: "drone/drone",
} }
transformWorkspace(c, r)
transformCache(c, r) transformCache(c, r)
cacheCount := len(c.Build.Cache) cacheCount := len(c.Build.Cache)
test := func(s *common.Step) { test := func(s *common.Step) {
g.Assert(len(s.Volumes)).Equal(cacheCount) g.Assert(len(s.Volumes)).Equal(cacheCount)
@ -172,7 +176,7 @@ func Test_Transform(t *testing.T) {
} }
} }
test(c.Setup) test(c.Setup)
test(c.Clone) test(c.Clone)
test(c.Build) test(c.Build)
testRange(c.Publish) testRange(c.Publish)
@ -180,5 +184,37 @@ func Test_Transform(t *testing.T) {
testRange(c.Notify) testRange(c.Notify)
testRange(c.Compose) testRange(c.Compose)
}) })
g.It("Should have default workspace directory", func() {
c := &common.Config{
Clone: &common.Step{
Config: map[string]interface{}{},
},
}
r := &common.Repo{
Link: "https://github.com/drone/drone",
FullName: "drone/drone",
}
transformWorkspace(c, r)
g.Assert(c.Clone.Config["path"]).Equal(workspaceRoot(r))
})
g.It("Should use path for working directory", func() {
c := &common.Config{
Clone: &common.Step{
Config: map[string]interface{}{
"path": "foo/bar",
},
},
}
r := &common.Repo{
Link: "https://github.com/drone/drone",
FullName: "drone/drone",
}
transformWorkspace(c, r)
g.Assert(c.Clone.Config["path"]).Equal(filepath.Join(buildRoot, "foo/bar"))
})
}) })
} }