From e8a55eb689ed801522e36dc2ea56e951ff4b0f93 Mon Sep 17 00:00:00 2001 From: sam boyer Date: Tue, 18 Oct 2016 11:29:29 -0400 Subject: [PATCH 01/10] Add rudimentary analyzer This is the minimum amount necessary to get a SourceManager working. --- analyzer.go | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 analyzer.go diff --git a/analyzer.go b/analyzer.go new file mode 100644 index 0000000000..326f01df0a --- /dev/null +++ b/analyzer.go @@ -0,0 +1,22 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "github.com/Masterminds/semver" + "github.com/sdboyer/gps" +) + +type analyzer struct{} + +func (a analyzer) DeriveManifestAndLock(path string, n gps.ProjectRoot) (gps.Manifest, gps.Lock, error) { + // TODO initial impl would just be looking for our own manifest and lock + return nil, nil, nil +} + +func (a analyzer) Info() (name string, version *semver.Version) { + v, _ := semver.NewVersion("v0.0.1") + return "example-analyzer", v +} From 3b747c2f1010a9ecd11e338f75a6ba17e7d48001 Mon Sep 17 00:00:00 2001 From: sam boyer Date: Tue, 18 Oct 2016 11:30:17 -0400 Subject: [PATCH 02/10] Add singleton to handle SourceManager --- sm.go | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 sm.go diff --git a/sm.go b/sm.go new file mode 100644 index 0000000000..7b5c31d83f --- /dev/null +++ b/sm.go @@ -0,0 +1,31 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "fmt" + "os" + "path/filepath" + + "github.com/sdboyer/gps" +) + +// Singletons aren't the best, but it's sufficient for experimentation +var sm *gps.SourceMgr + +func getSourceManager() (*gps.SourceMgr, error) { + if sm != nil { + return sm, nil + } + + gopath := os.Getenv("GOPATH") + if gopath == "" { + return nil, fmt.Errorf("GOPATH is not set") + } + // Use the first entry in GOPATH for the depcache + first := filepath.SplitList(gopath)[0] + + return gps.NewSourceManager(analyzer{}, filepath.Join(first, "depcache")) +} From b9bdbdad8647965194eb17e2cd092b256fb951ad Mon Sep 17 00:00:00 2001 From: sam boyer Date: Tue, 18 Oct 2016 15:10:48 -0400 Subject: [PATCH 03/10] Get rid of package-level var for SourceMgr --- sm.go | 7 ------- 1 file changed, 7 deletions(-) diff --git a/sm.go b/sm.go index 7b5c31d83f..ae4218f5e7 100644 --- a/sm.go +++ b/sm.go @@ -12,14 +12,7 @@ import ( "github.com/sdboyer/gps" ) -// Singletons aren't the best, but it's sufficient for experimentation -var sm *gps.SourceMgr - func getSourceManager() (*gps.SourceMgr, error) { - if sm != nil { - return sm, nil - } - gopath := os.Getenv("GOPATH") if gopath == "" { return nil, fmt.Errorf("GOPATH is not set") From 26976cb66bf0eaafc93e36687f0f757eb615b59f Mon Sep 17 00:00:00 2001 From: sam boyer Date: Tue, 18 Oct 2016 16:32:51 -0400 Subject: [PATCH 04/10] Tie up root, manifest and lock w/loadProject() Mostly just a helper function that pulls together the basic info we need for most commands. --- main.go | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/main.go b/main.go index 2b945006c5..db9fd716f2 100644 --- a/main.go +++ b/main.go @@ -228,3 +228,59 @@ func findProjectRoot(from string) (string, error) { } return path, nil } + +type project struct { + root string + m *Manifest + l *Lock +} + +// loadProject searches for a project root from the provided path, then loads +// the manifest and lock (if any) it finds there. +// +// If the provided path is empty, it will search from the path indicated by +// os.Getwd(). +func loadProject(path string) (p *project, err error) { + if path == "" { + p.root, err = findProjectRootFromWD() + } else { + p.root, err = findProjectRoot(path) + } + + if err != nil { + return + } + + mp := filepath.Join(path, 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) + } + defer mf.Close() + + p.m, err = ReadManifest(mf) + if err != nil { + return nil, fmt.Errorf("error while parsing %s: %s", mp, err) + } + + lp := filepath.Join(path, LockName) + lf, err := os.Open(lp) + if err != nil { + if os.IsNotExist(err) { + // It's fine for the lock not to exist + return p, nil + } + // But if a lock does exist and we can't open it, that's a problem + return nil, fmt.Errorf("could not open %s: %s", lp, err) + } + + defer lf.Close() + p.l, err = ReadLock(lf) + if err != nil { + return nil, fmt.Errorf("error while parsing %s: %s", lp, err) + } + + return p, nil +} From 69b5748591bff2c02d44308e8dddd936f73ecc05 Mon Sep 17 00:00:00 2001 From: sam boyer Date: Tue, 18 Oct 2016 18:36:08 -0400 Subject: [PATCH 05/10] Compute the ProjectRoot on the project, as well --- main.go | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/main.go b/main.go index db9fd716f2..f49c1f8788 100644 --- a/main.go +++ b/main.go @@ -9,6 +9,9 @@ import ( "fmt" "os" "path/filepath" + "strings" + + "github.com/sdboyer/gps" ) const ManifestName = "manifest.json" @@ -231,6 +234,7 @@ func findProjectRoot(from string) (string, error) { type project struct { root string + pr gps.ProjectRoot m *Manifest l *Lock } @@ -251,6 +255,23 @@ func loadProject(path string) (p *project, err error) { return } + gopath := os.Getenv("GOPATH") + var match bool + for _, gp := range filepath.SplitList(gopath) { + srcprefix := filepath.Join(gp, "src") + string(filepath.Separator) + if strings.HasPrefix(p.root, srcprefix) { + gopath = gp + match = true + // filepath.ToSlash because we're dealing with an import path now, + // not an fs path + p.pr = gps.ProjectRoot(filepath.ToSlash(strings.TrimPrefix(p.root, srcprefix))) + break + } + } + if !match { + return nil, fmt.Errorf("could not determine project root - not on GOPATH") + } + mp := filepath.Join(path, ManifestName) mf, err := os.Open(mp) if err != nil { From 1acf3cd1c56ef5f1363c993450f795986646a754 Mon Sep 17 00:00:00 2001 From: sam boyer Date: Tue, 18 Oct 2016 19:05:28 -0400 Subject: [PATCH 06/10] Skeletal initial bits of a status command --- main.go | 51 --------------------- status.go | 131 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 131 insertions(+), 51 deletions(-) create mode 100644 status.go diff --git a/main.go b/main.go index f49c1f8788..ed66e7289b 100644 --- a/main.go +++ b/main.go @@ -119,57 +119,6 @@ var initCmd = &command{ `, } -var statusCmd = &command{ - fn: noop, - name: "status", - short: `[flags] [packages] - Report the status of the current project's dependencies. - `, - long: ` - If no packages are specified, for each dependency: - - root import path - - (if present in lock) the currently selected version - - (else) that it's missing from the lock - - whether it's present in the vendor directory (or if it's in - workspace, if that's a thing?) - - the current aggregate constraints on that project (as specified by - the Manifest) - - if -u is specified, whether there are newer versions of this - dependency - - VCS state (uncommitted changes? pruned?) - - If packages are specified, or if -a is specified, - for each of those dependencies: - - (if present in lock) the currently selected version - - (else) that it's missing from the lock - - whether it's present in the vendor directory - - The set of possible versions for that project - - The upstream source URL(s) from which the project may be retrieved - - The type of upstream source (git, hg, bzr, svn, registry) - - Other versions that might work, given the current constraints - - The list of all projects that import the project within the current - depgraph - - The current constraint. If more than one project constrains it, both - the aggregate and the individual components (and which project provides - that constraint) are printed - - License information - - Package source location, if fetched from an alternate location - - Flags: - -json Output in json format - -f [template] Output in text/template format - - -old Only show out of date packages and the current version - -missing Only show missing packages. - -unused Only show unused packages. - -modified Only show modified packages. - - -dot Export dependency graph in GraphViz format - - The exit code of status is zero if all repositories are in a "good state". - `, -} - var getCmd = &command{ fn: noop, name: "get", diff --git a/status.go b/status.go new file mode 100644 index 0000000000..4e0d019db5 --- /dev/null +++ b/status.go @@ -0,0 +1,131 @@ +package main + +import ( + "bytes" + "fmt" + + "github.com/sdboyer/gps" +) + +var statusCmd = &command{ + fn: noop, + name: "status", + short: `[flags] [packages] + Report the status of the current project's dependencies. + `, + long: ` + If no packages are specified, for each dependency: + - root import path + - (if present in lock) the currently selected version + - (else) that it's missing from the lock + - whether it's present in the vendor directory (or if it's in + workspace, if that's a thing?) + - the current aggregate constraints on that project (as specified by + the Manifest) + - if -u is specified, whether there are newer versions of this + dependency + - VCS state (uncommitted changes? pruned?) + + If packages are specified, or if -a is specified, + for each of those dependencies: + - (if present in lock) the currently selected version + - (else) that it's missing from the lock + - whether it's present in the vendor directory + - The set of possible versions for that project + - The upstream source URL(s) from which the project may be retrieved + - The type of upstream source (git, hg, bzr, svn, registry) + - Other versions that might work, given the current constraints + - The list of all projects that import the project within the current + depgraph + - The current constraint. If more than one project constrains it, both + the aggregate and the individual components (and which project provides + that constraint) are printed + - License information + - Package source location, if fetched from an alternate location + + Flags: + -json Output in json format + -f [template] Output in text/template format + + -old Only show out of date packages and the current version + -missing Only show missing packages. + -unused Only show unused packages. + -modified Only show modified packages. + + -dot Export dependency graph in GraphViz format + + The exit code of status is zero if all repositories are in a "good state". + `, +} + +func runStatus(args []string) error { + p, err := loadProject("") + if err != nil { + return err + } + + sm, err := getSourceManager() + if err != nil { + return err + } + defer sm.Release() + + if len(args) == 0 { + return runStatusAll(p, sm) + } else { + return runStatusDetailed(p, sm, args) + } +} + +func runStatusAll(p *project, sm *gps.SourceMgr) error { + if p.l == nil { + // TODO if we have no lock file, do...other stuff + return nil + } + + // In the background, warm caches of version lists for all the projects in + // the lock. The SourceMgr coordinates access to this information - if the + // main goroutine asks for the version list while the background goroutine's + // request is in flight (or vice versa), both calls are folded together and + // are fulfilled from the same network response, and the same on-disk + // repository cache. + for _, proj := range p.l.Projects() { + id := proj.Ident() + go func() { + sm.ListVersions(id) + }() + } + + // Statically analyze code from the current project while the network churns + ptree, err := gps.ListPackages(p.root, string(p.pr)) + + // Set up a solver in order to check the InputHash. + params := gps.SolveParameters{ + RootDir: rd, + RootPackageTree: rt, + Manifest: conf, + // Locks aren't a part of the input hash check, so we can omit it. + } + + s, err = gps.Prepare(params, sm) + if err != nil { + return fmt.Errorf("could not set up solver for input hashing, err: %s", err) + } + digest := s.HashInputs() + if bytes.Equal(digest, p.l.Memo) { + // If these are equal, we're guaranteed that the lock is a transitively + // complete picture of all deps. That may change the output we need to + // generate + } else { + // Not equal - the lock may or may not be a complete picture, and even + // if it does have all the deps, it may not be a valid set of + // constraints. + } + + return nil +} + +func runStatusDetailed(p *project, sm *gps.SourceMgr, args []string) error { + // TODO + return fmt.Errorf("not implemented") +} From 84bb5be4f3f4a252b3ac50780eac168ef5839dbd Mon Sep 17 00:00:00 2001 From: sam boyer Date: Wed, 19 Oct 2016 00:24:20 -0400 Subject: [PATCH 07/10] Basic, untested implementation for one path This is hardly complete, particularly given the empty collectConstraints() func and the fact that our manifests and locks don't really support working with actual code yet. Nevertheless, it is at least a strong demonstration of how one goes about collecting the relevant information. --- status.go | 116 +++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 106 insertions(+), 10 deletions(-) diff --git a/status.go b/status.go index 4e0d019db5..f10d9fea99 100644 --- a/status.go +++ b/status.go @@ -1,8 +1,14 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + package main import ( "bytes" "fmt" + "os" + "text/tabwriter" "github.com/sdboyer/gps" ) @@ -24,7 +30,6 @@ var statusCmd = &command{ the Manifest) - if -u is specified, whether there are newer versions of this dependency - - VCS state (uncommitted changes? pruned?) If packages are specified, or if -a is specified, for each of those dependencies: @@ -58,6 +63,17 @@ var statusCmd = &command{ `, } +// BasicStatus contains all the information reported about a single dependency +// in the summary/list status output mode. +type BasicStatus struct { + ProjectRoot string + Constraint gps.Constraint + Version gps.UnpairedVersion + Revision gps.Revision + Latest gps.Version + PackageCount int +} + func runStatus(args []string) error { p, err := loadProject("") if err != nil { @@ -96,30 +112,105 @@ func runStatusAll(p *project, sm *gps.SourceMgr) error { }() } - // Statically analyze code from the current project while the network churns + // While the network churns on ListVersions() requests, statically analyze + // code from the current project. ptree, err := gps.ListPackages(p.root, string(p.pr)) // Set up a solver in order to check the InputHash. params := gps.SolveParameters{ - RootDir: rd, - RootPackageTree: rt, - Manifest: conf, + RootDir: p.root, + RootPackageTree: ptree, + Manifest: p.m, // Locks aren't a part of the input hash check, so we can omit it. } - s, err = gps.Prepare(params, sm) + s, err := gps.Prepare(params, sm) if err != nil { return fmt.Errorf("could not set up solver for input hashing, err: %s", err) } - digest := s.HashInputs() - if bytes.Equal(digest, p.l.Memo) { + + cm := collectConstraints(ptree, p, sm) + tw := tabwriter.NewWriter(os.Stdout, 0, 4, 1, ' ', 0) + fmt.Fprintf(tw, + "Project\tConstraint\tVersion\tRevision\tLatest\tPkgs Used\t\n", + bs.ProjectRoot, + bs.Constraint, + bs.Version(), + string(bs.Revision)[:8], + bs.Latest, + bs.PackageCount, + ) + + if bytes.Equal(s.HashInputs(), p.l.Memo) { // If these are equal, we're guaranteed that the lock is a transitively - // complete picture of all deps. That may change the output we need to - // generate + // complete picture of all deps. That eliminates the need for at least + // some checks. + for _, proj := range p.l.Projects() { + bs := BasicStatus{ + ProjectRoot: string(proj.Ident().ProjectRoot), + PackageCount: len(proj.Packages()), + } + + // Split apart the version from the lock into its constituent parts + switch tv := proj.Version().(type) { + case gps.UnpairedVersion: + bs.Version = tv + case gps.Revision: + bs.Revision = tv + case gps.PairedVersion: + bs.Version = tv.Unpair() + bs.Revision = tv.Underlying() + } + + // Check if the manifest has an override for this project. If so, + // set that as the constraint. + if pp, has := p.m.Ovr[proj.Ident().ProjectRoot]; has && pp.Constraint != nil { + // TODO note somehow that it's overridden + bs.Constraint = pp.Constraint + } else { + bs.Constraint = gps.Any() + for _, c := range cm[bs.ProjectRoot] { + bs.Constraint = c.Intersect(bs.Constraint) + } + } + + // Only if we have a non-rev and non-plain version do/can we display + // anything wrt the version's updateability. + if bs.Version != nil && bs.Version.Type() != "version" { + c, has := p.m.Dependencies[proj.Ident().ProjectRoot] + if !has { + c = gps.Any() + } + + vl := sm.ListVersions(proj.Ident()) + gps.SortForUpgrade(vl) + + for _, v := range vl { + // Because we've sorted the version list for upgrade, the + // first version we encounter that matches our constraint + // will be what we want + if c.Matches(v) { + bs.Latest = v + break + } + } + } + fmt.Fprintf(tw, + "%s\t%s\t%s\t%s\t%s\t%s\t\n", + bs.ProjectRoot, + bs.Constraint, + bs.Version(), + string(bs.Revision)[:7], + bs.Latest, + bs.PackageCount, + ) + } } else { // Not equal - the lock may or may not be a complete picture, and even // if it does have all the deps, it may not be a valid set of // constraints. + // + // TODO } return nil @@ -129,3 +220,8 @@ func runStatusDetailed(p *project, sm *gps.SourceMgr, args []string) error { // TODO return fmt.Errorf("not implemented") } + +func collectConstraints(ptree gps.PackageTree, p *project, sm *gps.SourceMgr) map[string][]gps.Constraint { + // TODO + return map[string][]gps.Constraint{} +} From acdee23191231b421a9195f895eb1fd61ede2eb1 Mon Sep 17 00:00:00 2001 From: sam boyer Date: Fri, 21 Oct 2016 08:47:53 -0400 Subject: [PATCH 08/10] Fix typos and mistakes in status impl --- status.go | 38 +++++++++++++++++--------------------- 1 file changed, 17 insertions(+), 21 deletions(-) diff --git a/status.go b/status.go index f10d9fea99..cf85301a79 100644 --- a/status.go +++ b/status.go @@ -131,15 +131,8 @@ func runStatusAll(p *project, sm *gps.SourceMgr) error { cm := collectConstraints(ptree, p, sm) tw := tabwriter.NewWriter(os.Stdout, 0, 4, 1, ' ', 0) - fmt.Fprintf(tw, - "Project\tConstraint\tVersion\tRevision\tLatest\tPkgs Used\t\n", - bs.ProjectRoot, - bs.Constraint, - bs.Version(), - string(bs.Revision)[:8], - bs.Latest, - bs.PackageCount, - ) + // Print header row + fmt.Fprintf(tw, "Project\tConstraint\tVersion\tRevision\tLatest\tPkgs Used\t\n") if bytes.Equal(s.HashInputs(), p.l.Memo) { // If these are equal, we're guaranteed that the lock is a transitively @@ -179,27 +172,30 @@ func runStatusAll(p *project, sm *gps.SourceMgr) error { if bs.Version != nil && bs.Version.Type() != "version" { c, has := p.m.Dependencies[proj.Ident().ProjectRoot] if !has { - c = gps.Any() + c.Constraint = gps.Any() } - vl := sm.ListVersions(proj.Ident()) - gps.SortForUpgrade(vl) - - for _, v := range vl { - // Because we've sorted the version list for upgrade, the - // first version we encounter that matches our constraint - // will be what we want - if c.Matches(v) { - bs.Latest = v - break + vl, err := sm.ListVersions(proj.Ident()) + if err != nil { + gps.SortForUpgrade(vl) + + for _, v := range vl { + // Because we've sorted the version list for upgrade, the + // first version we encounter that matches our constraint + // will be what we want + if c.Constraint.Matches(v) { + bs.Latest = v + break + } } } } + fmt.Fprintf(tw, "%s\t%s\t%s\t%s\t%s\t%s\t\n", bs.ProjectRoot, bs.Constraint, - bs.Version(), + bs.Version, string(bs.Revision)[:7], bs.Latest, bs.PackageCount, From b2fca3458cdab4f192ed14a36e48f97e256d3e06 Mon Sep 17 00:00:00 2001 From: sam boyer Date: Fri, 21 Oct 2016 08:58:42 -0400 Subject: [PATCH 09/10] Rename project path-related props for clarity --- main.go | 18 ++++++++++-------- status.go | 4 ++-- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/main.go b/main.go index ed66e7289b..25afa04516 100644 --- a/main.go +++ b/main.go @@ -182,10 +182,12 @@ func findProjectRoot(from string) (string, error) { } type project struct { - root string - pr gps.ProjectRoot - m *Manifest - l *Lock + // absroot is the absolute path to the root directory of the project. + absroot string + // importroot is the import path of the project's root directory. + importroot gps.ProjectRoot + m *Manifest + l *Lock } // loadProject searches for a project root from the provided path, then loads @@ -195,9 +197,9 @@ type project struct { // os.Getwd(). func loadProject(path string) (p *project, err error) { if path == "" { - p.root, err = findProjectRootFromWD() + p.absroot, err = findProjectRootFromWD() } else { - p.root, err = findProjectRoot(path) + p.absroot, err = findProjectRoot(path) } if err != nil { @@ -208,12 +210,12 @@ func loadProject(path string) (p *project, err error) { var match bool for _, gp := range filepath.SplitList(gopath) { srcprefix := filepath.Join(gp, "src") + string(filepath.Separator) - if strings.HasPrefix(p.root, srcprefix) { + if strings.HasPrefix(p.absroot, srcprefix) { gopath = gp match = true // filepath.ToSlash because we're dealing with an import path now, // not an fs path - p.pr = gps.ProjectRoot(filepath.ToSlash(strings.TrimPrefix(p.root, srcprefix))) + p.importroot = gps.ProjectRoot(filepath.ToSlash(strings.TrimPrefix(p.absroot, srcprefix))) break } } diff --git a/status.go b/status.go index cf85301a79..8fb0b1e8ca 100644 --- a/status.go +++ b/status.go @@ -114,11 +114,11 @@ func runStatusAll(p *project, sm *gps.SourceMgr) error { // While the network churns on ListVersions() requests, statically analyze // code from the current project. - ptree, err := gps.ListPackages(p.root, string(p.pr)) + ptree, err := gps.ListPackages(p.absroot, string(p.importroot)) // Set up a solver in order to check the InputHash. params := gps.SolveParameters{ - RootDir: p.root, + RootDir: p.absroot, RootPackageTree: ptree, Manifest: p.m, // Locks aren't a part of the input hash check, so we can omit it. From 4f2ec04e0892b00abfdf78bc9cceb0cb42031a2c Mon Sep 17 00:00:00 2001 From: Edward Muller Date: Fri, 21 Oct 2016 11:18:49 -0700 Subject: [PATCH 10/10] Misc cleanups make lint most happy --- lock.go | 10 +++++----- lock_test.go | 6 +++--- main.go | 34 +++++++++++++++++++--------------- main_test.go | 2 +- manifest.go | 14 +++++++------- status.go | 9 ++++----- 6 files changed, 39 insertions(+), 36 deletions(-) diff --git a/lock.go b/lock.go index 2b8fb9b652..ebafa2df8a 100644 --- a/lock.go +++ b/lock.go @@ -13,7 +13,7 @@ import ( "github.com/sdboyer/gps" ) -type Lock struct { +type lock struct { Memo []byte P []gps.LockedProject } @@ -32,7 +32,7 @@ type lockedDep struct { Packages []string `json:"packages"` } -func ReadLock(r io.Reader) (*Lock, error) { +func readLock(r io.Reader) (*lock, error) { rl := rawLock{} err := json.NewDecoder(r).Decode(&rl) if err != nil { @@ -43,7 +43,7 @@ func ReadLock(r io.Reader) (*Lock, error) { if err != nil { return nil, fmt.Errorf("invalid hash digest in lock's memo field") } - l := &Lock{ + l := &lock{ Memo: b, P: make([]gps.LockedProject, len(rl.P)), } @@ -75,10 +75,10 @@ func ReadLock(r io.Reader) (*Lock, error) { return l, nil } -func (l *Lock) InputHash() []byte { +func (l *lock) InputHash() []byte { return l.Memo } -func (l *Lock) Projects() []gps.LockedProject { +func (l *lock) Projects() []gps.LockedProject { return l.P } diff --git a/lock_test.go b/lock_test.go index ddaa68f015..0619f6af85 100644 --- a/lock_test.go +++ b/lock_test.go @@ -38,20 +38,20 @@ func TestReadLock(t *testing.T) { ] }` - _, err := ReadLock(strings.NewReader(le)) + _, err := readLock(strings.NewReader(le)) if err == nil { t.Error("Reading lock with invalid props should have caused error, but did not") } else if !strings.Contains(err.Error(), "both a branch") { t.Errorf("Unexpected error %q; expected multiple version error", err) } - l, err := ReadLock(strings.NewReader(lg)) + l, err := readLock(strings.NewReader(lg)) if err != nil { t.Fatalf("Should have read Lock correctly, but got err %q", err) } b, _ := hex.DecodeString("2252a285ab27944a4d7adcba8dbd03980f59ba652f12db39fa93b927c345593e") - l2 := &Lock{ + l2 := &lock{ Memo: b, P: []gps.LockedProject{ gps.NewLockedProject( diff --git a/main.go b/main.go index 25afa04516..6dca5e2fb3 100644 --- a/main.go +++ b/main.go @@ -14,8 +14,8 @@ import ( "github.com/sdboyer/gps" ) -const ManifestName = "manifest.json" -const LockName = "lock.json" +const manifestName = "manifest.json" +const lockName = "lock.json" func main() { flag.Parse() @@ -109,7 +109,7 @@ var initCmd = &command{ Write Manifest file in the root of the project directory. `, long: ` - Populates Manifest file with current deps of this project. + Populates Manifest file with current deps of this project. The specified version of each dependent repository is the version available in the user's workspaces (as specified by GOPATH). If the dependency is not present in any workspaces it is not be @@ -131,7 +131,7 @@ var getCmd = &command{ -x dry run -f force the given package to be updated to the specified version - + Package specs: [@] @@ -155,7 +155,7 @@ func findProjectRoot(from string) (string, error) { var f func(string) (string, error) f = func(dir string) (string, error) { - fullpath := filepath.Join(dir, ManifestName) + fullpath := filepath.Join(dir, manifestName) if _, err := os.Stat(fullpath); err == nil { return dir, nil @@ -186,8 +186,8 @@ type project struct { absroot string // importroot is the import path of the project's root directory. importroot gps.ProjectRoot - m *Manifest - l *Lock + m *manifest + l *lock } // loadProject searches for a project root from the provided path, then loads @@ -195,15 +195,19 @@ type project struct { // // If the provided path is empty, it will search from the path indicated by // os.Getwd(). -func loadProject(path string) (p *project, err error) { - if path == "" { +func loadProject(path string) (*project, error) { + var err error + p := new(project) + + switch path { + case "": p.absroot, err = findProjectRootFromWD() - } else { + default: p.absroot, err = findProjectRoot(path) } if err != nil { - return + return p, err } gopath := os.Getenv("GOPATH") @@ -223,7 +227,7 @@ func loadProject(path string) (p *project, err error) { return nil, fmt.Errorf("could not determine project root - not on GOPATH") } - mp := filepath.Join(path, ManifestName) + mp := filepath.Join(path, manifestName) mf, err := os.Open(mp) if err != nil { // Should be impossible at this point for the manifest file not to @@ -232,12 +236,12 @@ func loadProject(path string) (p *project, err error) { } defer mf.Close() - p.m, err = ReadManifest(mf) + p.m, err = readManifest(mf) if err != nil { return nil, fmt.Errorf("error while parsing %s: %s", mp, err) } - lp := filepath.Join(path, LockName) + lp := filepath.Join(path, lockName) lf, err := os.Open(lp) if err != nil { if os.IsNotExist(err) { @@ -249,7 +253,7 @@ func loadProject(path string) (p *project, err error) { } defer lf.Close() - p.l, err = ReadLock(lf) + p.l, err = readLock(lf) if err != nil { return nil, fmt.Errorf("error while parsing %s: %s", lp, err) } diff --git a/main_test.go b/main_test.go index fc65cb8a2e..d5fdde315c 100644 --- a/main_test.go +++ b/main_test.go @@ -35,7 +35,7 @@ func TestFindRoot(t *testing.T) { t.Errorf("findProjectRoot on nonexistent subdir should still work and give %s, got %s", expect, got3) } - got4, err := findProjectRoot(filepath.Join(expect, ManifestName)) + got4, err := findProjectRoot(filepath.Join(expect, manifestName)) if err == nil { t.Errorf("Should have err'd when trying subdir of file, but returned %s", got4) } diff --git a/manifest.go b/manifest.go index 044d5ea74e..830cb2d50a 100644 --- a/manifest.go +++ b/manifest.go @@ -12,7 +12,7 @@ import ( "github.com/sdboyer/gps" ) -type Manifest struct { +type manifest struct { Dependencies gps.ProjectConstraints Ovr gps.ProjectConstraints Ignores []string @@ -31,14 +31,14 @@ type possibleProps struct { NetworkName string `json:"network_name"` } -func ReadManifest(r io.Reader) (*Manifest, error) { +func readManifest(r io.Reader) (*manifest, error) { rm := rawManifest{} err := json.NewDecoder(r).Decode(&rm) if err != nil { return nil, err } - m := &Manifest{ + m := &manifest{ Dependencies: make(gps.ProjectConstraints, len(rm.Dependencies)), Ovr: make(gps.ProjectConstraints, len(rm.Overrides)), Ignores: rm.Ignores, @@ -90,20 +90,20 @@ func toProps(n string, p possibleProps) (pp gps.ProjectProperties, err error) { return pp, nil } -func (m *Manifest) DependencyConstraints() gps.ProjectConstraints { +func (m *manifest) DependencyConstraints() gps.ProjectConstraints { return m.Dependencies } -func (m *Manifest) TestDependencyConstraints() gps.ProjectConstraints { +func (m *manifest) TestDependencyConstraints() gps.ProjectConstraints { // TODO decide whether we're going to incorporate this or not return nil } -func (m *Manifest) Overrides() gps.ProjectConstraints { +func (m *manifest) Overrides() gps.ProjectConstraints { return m.Ovr } -func (m *Manifest) IgnorePackages() map[string]bool { +func (m *manifest) IgnorePackages() map[string]bool { if len(m.Ignores) == 0 { return nil } diff --git a/status.go b/status.go index 8fb0b1e8ca..fc0aec7637 100644 --- a/status.go +++ b/status.go @@ -14,7 +14,7 @@ import ( ) var statusCmd = &command{ - fn: noop, + fn: runStatus, name: "status", short: `[flags] [packages] Report the status of the current project's dependencies. @@ -47,7 +47,7 @@ var statusCmd = &command{ that constraint) are printed - License information - Package source location, if fetched from an alternate location - + Flags: -json Output in json format -f [template] Output in text/template format @@ -59,7 +59,7 @@ var statusCmd = &command{ -dot Export dependency graph in GraphViz format - The exit code of status is zero if all repositories are in a "good state". + The exit code of status is zero if all repositories are in a "good state". `, } @@ -88,9 +88,8 @@ func runStatus(args []string) error { if len(args) == 0 { return runStatusAll(p, sm) - } else { - return runStatusDetailed(p, sm, args) } + return runStatusDetailed(p, sm, args) } func runStatusAll(p *project, sm *gps.SourceMgr) error {