diff --git a/main.go b/main.go index 6dca5e2fb3..51048dacc3 100644 --- a/main.go +++ b/main.go @@ -5,6 +5,7 @@ package main import ( + "errors" "flag" "fmt" "os" @@ -151,34 +152,33 @@ func findProjectRootFromWD() (string, error) { return findProjectRoot(path) } -func findProjectRoot(from string) (string, error) { - var f func(string) (string, error) - f = func(dir string) (string, error) { - - fullpath := filepath.Join(dir, manifestName) +var ( + errProjectNotFound = errors.New("no project could be found") + vcsDirs = []string{".git"} // TODO: add others +) - if _, err := os.Stat(fullpath); err == nil { - return dir, nil - } else if !os.IsNotExist(err) { - // Some err other than non-existence - return that out - return "", err +// findProjectRoot walks up the file system tree from the given directory to +// the file system root looking for a known VCS metadata directory (.git, .hg, +// etc). If none found, it returns errProjectNotFound. +func findProjectRoot(dir string) (string, error) { + for { + for _, vcsDir := range vcsDirs { + vcsMeta := filepath.Join(dir, vcsDir) + if _, err := os.Stat(vcsMeta); err == nil { + return dir, nil + } else if !os.IsNotExist(err) { + // Something went wrong trying to read the directory, + // the user should be told of this. + return "", err + } } - - base := filepath.Dir(dir) - if base == dir { - return "", fmt.Errorf("cannot resolve parent of %s", base) + parent := filepath.Dir(dir) + if parent == dir { + // We've hit the root without finding anything. + return "", errProjectNotFound } - - return f(base) + dir = parent } - - path, err := f(from) - if err != nil { - return "", fmt.Errorf("error while searching for manifest: %s", err) - } else if path == "" { - return "", fmt.Errorf("could not find manifest in any parent of %s", from) - } - return path, nil } type project struct { @@ -227,12 +227,15 @@ func loadProject(path string) (*project, error) { return nil, fmt.Errorf("could not determine project root - not on GOPATH") } - mp := filepath.Join(path, manifestName) + mp := filepath.Join(p.absroot, manifestName) mf, err := os.Open(mp) if err != nil { - // Should be impossible at this point for the manifest file not to - // exist, so this is some other kind of err - return nil, fmt.Errorf("could not open %s: %s", mp, err) + if os.IsNotExist(err) { + // TODO: list possible solutions? (dep init, cd $project) + return nil, fmt.Errorf("no %v found in project root %v", manifestName, p.absroot) + } + // Unable to read the manifest file. + return nil, err } defer mf.Close() diff --git a/main_test.go b/main_test.go index d5fdde315c..1aa86b3b12 100644 --- a/main_test.go +++ b/main_test.go @@ -1,6 +1,7 @@ package main import ( + "io/ioutil" "os" "path/filepath" "testing" @@ -14,6 +15,16 @@ func TestFindRoot(t *testing.T) { expect := filepath.Join(wd, "_testdata", "rootfind") + // Drop a .git file in the project root. + // We would commit a file named .git, + // but that would confuse git. Silly git. + gitFile := filepath.Join(expect, ".git") + err = ioutil.WriteFile(gitFile, []byte("not really a .git directory"), 0644) + if err != nil { + t.Fatal(err) + } + defer os.Remove(gitFile) + got1, err := findProjectRoot(expect) if err != nil { t.Errorf("Unexpected error while finding root: %s", err) diff --git a/manifest_test.go b/manifest_test.go index df8a5400c0..bde23e75e4 100644 --- a/manifest_test.go +++ b/manifest_test.go @@ -12,7 +12,7 @@ import ( "github.com/sdboyer/gps" ) -func TestReadManifest(t *testing.T) { +func TestreadManifest(t *testing.T) { const je = `{ "dependencies": { "github.com/sdboyer/gps": { @@ -55,20 +55,20 @@ func TestReadManifest(t *testing.T) { ] }` - _, err := ReadManifest(strings.NewReader(je)) + _, err := readManifest(strings.NewReader(je)) if err == nil { t.Error("Reading manifest with invalid props should have caused error, but did not") } else if !strings.Contains(err.Error(), "multiple constraints") { t.Errorf("Unexpected error %q; expected multiple constraint error", err) } - m2, err := ReadManifest(strings.NewReader(jg)) + m2, err := readManifest(strings.NewReader(jg)) if err != nil { t.Fatalf("Should have read Manifest correctly, but got err %q", err) } c, _ := gps.NewSemverConstraint("^v0.12.0") - em := Manifest{ + em := manifest{ Dependencies: map[gps.ProjectRoot]gps.ProjectProperties{ gps.ProjectRoot("github.com/sdboyer/gps"): { Constraint: c,