Merge pull request #740 from monopole/replaceStringWithRepoSpec

Replace all repo uri strings with git.RepoSpec.
This commit is contained in:
Jeff Regan
2019-01-28 16:48:44 -08:00
committed by GitHub
4 changed files with 85 additions and 91 deletions

View File

@@ -25,23 +25,19 @@ import (
) )
// Cloner is a function that can clone a git repo. // Cloner is a function that can clone a git repo.
type Cloner func(url string) (*RepoSpec, error) type Cloner func(repoSpec *RepoSpec) error
// ClonerUsingGitExec uses a local git install, as opposed // ClonerUsingGitExec uses a local git install, as opposed
// to say, some remote API, to obtain a local clone of // to say, some remote API, to obtain a local clone of
// a remote repo. // a remote repo.
func ClonerUsingGitExec(spec string) (*RepoSpec, error) { func ClonerUsingGitExec(repoSpec *RepoSpec) error {
gitProgram, err := exec.LookPath("git") gitProgram, err := exec.LookPath("git")
if err != nil { if err != nil {
return nil, errors.Wrap(err, "no 'git' program on path") return errors.Wrap(err, "no 'git' program on path")
}
repoSpec, err := NewRepoSpecFromUrl(spec)
if err != nil {
return nil, err
} }
repoSpec.cloneDir, err = fs.NewTmpConfirmedDir() repoSpec.cloneDir, err = fs.NewTmpConfirmedDir()
if err != nil { if err != nil {
return nil, err return err
} }
cmd := exec.Command( cmd := exec.Command(
gitProgram, gitProgram,
@@ -52,17 +48,28 @@ func ClonerUsingGitExec(spec string) (*RepoSpec, error) {
cmd.Stdout = &out cmd.Stdout = &out
err = cmd.Run() err = cmd.Run()
if err != nil { if err != nil {
return nil, errors.Wrapf(err, "trouble cloning %s", spec) return errors.Wrapf(err, "trouble cloning %s", repoSpec.raw)
} }
if repoSpec.ref == "" { if repoSpec.ref == "" {
return repoSpec, nil return nil
} }
cmd = exec.Command(gitProgram, "checkout", repoSpec.ref) cmd = exec.Command(gitProgram, "checkout", repoSpec.ref)
cmd.Dir = repoSpec.cloneDir.String() cmd.Dir = repoSpec.cloneDir.String()
err = cmd.Run() err = cmd.Run()
if err != nil { if err != nil {
return nil, errors.Wrapf( return errors.Wrapf(
err, "trouble checking out href %s", repoSpec.ref) err, "trouble checking out href %s", repoSpec.ref)
} }
return repoSpec, nil return nil
}
// DoNothingCloner returns a cloner that only sets
// cloneDir field in the repoSpec. It's assumed that
// the cloneDir is associated with some fake filesystem
// used in a test.
func DoNothingCloner(dir fs.ConfirmedDir) Cloner {
return func(rs *RepoSpec) error {
rs.cloneDir = dir
return nil
}
} }

View File

@@ -114,44 +114,45 @@ func (l *fileLoader) Root() string {
} }
func newLoaderOrDie(fSys fs.FileSystem, path string) *fileLoader { func newLoaderOrDie(fSys fs.FileSystem, path string) *fileLoader {
l, err := newLoaderAtConfirmedDir( root, err := demandDirectoryRoot(fSys, path)
path, fSys, nil, git.ClonerUsingGitExec)
if err != nil { if err != nil {
log.Fatalf("unable to make loader at '%s'; %v", path, err) log.Fatalf("unable to make loader at '%s'; %v", path, err)
} }
return l return newLoaderAtConfirmedDir(
root, fSys, nil, git.ClonerUsingGitExec)
} }
// newLoaderAtConfirmedDir returns a new fileLoader with given root. // newLoaderAtConfirmedDir returns a new fileLoader with given root.
func newLoaderAtConfirmedDir( func newLoaderAtConfirmedDir(
possibleRoot string, fSys fs.FileSystem, root fs.ConfirmedDir, fSys fs.FileSystem,
referrer *fileLoader, cloner git.Cloner) (*fileLoader, error) { referrer *fileLoader, cloner git.Cloner) *fileLoader {
if possibleRoot == "" {
return nil, fmt.Errorf(
"loader root cannot be empty")
}
root, f, err := fSys.CleanedAbs(possibleRoot)
if err != nil {
return nil, fmt.Errorf(
"absolute path error in '%s' : %v", possibleRoot, err)
}
if f != "" {
return nil, fmt.Errorf(
"got file '%s', but '%s' must be a directory to be a root",
f, possibleRoot)
}
if referrer != nil {
if err := referrer.errIfArgEqualOrHigher(root); err != nil {
return nil, err
}
}
return &fileLoader{ return &fileLoader{
root: root, root: root,
referrer: referrer, referrer: referrer,
fSys: fSys, fSys: fSys,
cloner: cloner, cloner: cloner,
cleaner: func() error { return nil }, cleaner: func() error { return nil },
}, nil }
}
// Assure that the given path is in fact a directory.
func demandDirectoryRoot(
fSys fs.FileSystem, path string) (fs.ConfirmedDir, error) {
if path == "" {
return "", fmt.Errorf(
"loader root cannot be empty")
}
d, f, err := fSys.CleanedAbs(path)
if err != nil {
return "", fmt.Errorf(
"absolute path error in '%s' : %v", path, err)
}
if f != "" {
return "", fmt.Errorf(
"got file '%s', but '%s' must be a directory to be a root",
f, path)
}
return d, nil
} }
// New returns a new Loader, rooted relative to current loader, // New returns a new Loader, rooted relative to current loader,
@@ -160,26 +161,34 @@ func (l *fileLoader) New(path string) (ifc.Loader, error) {
if path == "" { if path == "" {
return nil, fmt.Errorf("new root cannot be empty") return nil, fmt.Errorf("new root cannot be empty")
} }
if git.IsRepoUrl(path) { repoSpec, err := git.NewRepoSpecFromUrl(path)
// Avoid cycles. if err == nil {
if err := l.errIfPreviouslySeenUri(path); err != nil { // Treat this as git repo clone request.
if err := l.errIfRepoCycle(repoSpec); err != nil {
return nil, err return nil, err
} }
return newLoaderAtGitClone(path, l.fSys, l.referrer, l.cloner) return newLoaderAtGitClone(repoSpec, l.fSys, l.referrer, l.cloner)
} }
if filepath.IsAbs(path) { if filepath.IsAbs(path) {
return nil, fmt.Errorf("new root '%s' cannot be absolute", path) return nil, fmt.Errorf("new root '%s' cannot be absolute", path)
} }
root, err := demandDirectoryRoot(l.fSys, l.root.Join(path))
if err != nil {
return nil, err
}
if err := l.errIfArgEqualOrHigher(root); err != nil {
return nil, err
}
return newLoaderAtConfirmedDir( return newLoaderAtConfirmedDir(
l.root.Join(path), l.fSys, l, l.cloner) root, l.fSys, l, l.cloner), nil
} }
// newLoaderAtGitClone returns a new Loader pinned to a temporary // newLoaderAtGitClone returns a new Loader pinned to a temporary
// directory holding a cloned git repo. // directory holding a cloned git repo.
func newLoaderAtGitClone( func newLoaderAtGitClone(
uri string, fSys fs.FileSystem, repoSpec *git.RepoSpec, fSys fs.FileSystem,
referrer *fileLoader, cloner git.Cloner) (ifc.Loader, error) { referrer *fileLoader, cloner git.Cloner) (ifc.Loader, error) {
repoSpec, err := cloner(uri) err := cloner(repoSpec)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -187,6 +196,10 @@ func newLoaderAtGitClone(
if err != nil { if err != nil {
return nil, err return nil, err
} }
// We don't know that the path requested in repoSpec
// is a directory until we actually clone it and look
// inside. That just happened, hence the error check
// is here.
if f != "" { if f != "" {
return nil, fmt.Errorf( return nil, fmt.Errorf(
"'%s' refers to file '%s'; expecting directory", "'%s' refers to file '%s'; expecting directory",
@@ -221,17 +234,17 @@ func (l *fileLoader) errIfArgEqualOrHigher(
// I.e. Allow a distinction between git URI with // I.e. Allow a distinction between git URI with
// path foo and tag bar and a git URI with the same // path foo and tag bar and a git URI with the same
// path but a different tag? // path but a different tag?
// TODO(monopole): Use parsed data instead of looking at Raw(). func (l *fileLoader) errIfRepoCycle(newRepoSpec *git.RepoSpec) error {
func (l *fileLoader) errIfPreviouslySeenUri(uri string) error { // TODO(monopole): Use parsed data instead of Raw().
if strings.HasPrefix(l.repoSpec.Raw(), uri) { if strings.HasPrefix(l.repoSpec.Raw(), newRepoSpec.Raw()) {
return fmt.Errorf( return fmt.Errorf(
"cycle detected: URI '%s' referenced by previous URI '%s'", "cycle detected: URI '%s' referenced by previous URI '%s'",
uri, l.repoSpec.Raw()) newRepoSpec.Raw(), l.repoSpec.Raw())
} }
if l.referrer == nil { if l.referrer == nil {
return nil return nil
} }
return l.referrer.errIfPreviouslySeenUri(uri) return l.referrer.errIfRepoCycle(newRepoSpec)
} }
// Load returns content of file at the given relative path, // Load returns content of file at the given relative path,

View File

@@ -64,25 +64,6 @@ func MakeFakeFs(td []testData) fs.FileSystem {
return fSys return fSys
} }
func TestNewLoaderAtConfirmedDir_DemandsDirectory(t *testing.T) {
fSys := MakeFakeFs(testCases)
_, err := newLoaderAtConfirmedDir("/foo", fSys, nil, nil)
if err != nil {
t.Fatalf("Unexpected error - a directory should work.")
}
_, err = newLoaderAtConfirmedDir("/foo/project", fSys, nil, nil)
if err != nil {
t.Fatalf("Unexpected error - a directory should work.")
}
_, err = newLoaderAtConfirmedDir("/foo/project/fileA.yaml", fSys, nil, nil)
if err == nil {
t.Fatalf("Expected error - a file should not work.")
}
if !strings.Contains(err.Error(), "must be a directory to be a root") {
t.Fatalf("unexpected err: %v", err)
}
}
func TestLoaderLoad(t *testing.T) { func TestLoaderLoad(t *testing.T) {
l1 := NewFileLoaderAtRoot(MakeFakeFs(testCases)) l1 := NewFileLoaderAtRoot(MakeFakeFs(testCases))
if "/" != l1.Root() { if "/" != l1.Root() {
@@ -351,23 +332,6 @@ func TestSplit(t *testing.T) {
} }
} }
// makeFakeGitCloner returns a cloner that ignores the
// URL argument and returns a path in a fake file system
// that should already hold the 'repo' contents.
func makeFakeGitCloner(t *testing.T, fSys fs.FileSystem, coRoot string) git.Cloner {
if !fSys.IsDir(coRoot) {
t.Fatalf("expecting a directory at '%s'", coRoot)
}
return func(url string) (*git.RepoSpec, error) {
_, path := splitOnNthSlash(url, 3)
if !fSys.IsDir(coRoot + "/" + path) {
t.Fatalf("expecting a directory at '%s'/'%s'",
coRoot, path)
}
return git.NewRepoSpec(url, fs.ConfirmedDir(coRoot), path), nil
}
}
func TestNewLoaderAtGitClone(t *testing.T) { func TestNewLoaderAtGitClone(t *testing.T) {
rootUrl := "github.com/someOrg/someRepo" rootUrl := "github.com/someOrg/someRepo"
pathInRepo := "foo/base" pathInRepo := "foo/base"
@@ -385,9 +349,14 @@ func TestNewLoaderAtGitClone(t *testing.T) {
[]byte(` []byte(`
whatever whatever
`)) `))
repoSpec, err := git.NewRepoSpecFromUrl(url)
if err != nil {
t.Fatalf("unexpected err: %v\n", err)
}
l, err := newLoaderAtGitClone( l, err := newLoaderAtGitClone(
url, fSys, nil, repoSpec, fSys, nil,
makeFakeGitCloner(t, fSys, coRoot)) git.DoNothingCloner(fs.ConfirmedDir(coRoot)))
if err != nil { if err != nil {
t.Fatalf("unexpected err: %v\n", err) t.Fatalf("unexpected err: %v\n", err)
} }

View File

@@ -24,11 +24,16 @@ import (
) )
// NewLoader returns a Loader. // NewLoader returns a Loader.
func NewLoader(root string, fSys fs.FileSystem) (ifc.Loader, error) { func NewLoader(path string, fSys fs.FileSystem) (ifc.Loader, error) {
if git.IsRepoUrl(root) { repoSpec, err := git.NewRepoSpecFromUrl(path)
if err == nil {
return newLoaderAtGitClone( return newLoaderAtGitClone(
root, fSys, nil, git.ClonerUsingGitExec) repoSpec, fSys, nil, git.ClonerUsingGitExec)
}
root, err := demandDirectoryRoot(fSys, path)
if err != nil {
return nil, err
} }
return newLoaderAtConfirmedDir( return newLoaderAtConfirmedDir(
root, fSys, nil, git.ClonerUsingGitExec) root, fSys, nil, git.ClonerUsingGitExec), nil
} }