Skip to content
This repository has been archived by the owner on Sep 9, 2020. It is now read-only.

Preliminary status command #8

Merged
merged 11 commits into from
Oct 25, 2016
22 changes: 22 additions & 0 deletions analyzer.go
Original file line number Diff line number Diff line change
@@ -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
}
128 changes: 77 additions & 51 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ import (
"fmt"
"os"
"path/filepath"
"strings"

"github.com/sdboyer/gps"
)

const ManifestName = "manifest.json"
Expand Down Expand Up @@ -116,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",
Expand Down Expand Up @@ -228,3 +180,77 @@ func findProjectRoot(from string) (string, error) {
}
return path, nil
}

type project struct {
root string
pr gps.ProjectRoot

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is a root (string) and a projectRoot ? What makes them different?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, i should name the vars better so that it's clearer.

root is a string that describes the absolute path to the root of the repository. a ProjectRoot, istype ProjectRoot string, but is specifically intended to be an import path that corresponds to the root of a project/repository.

I'm not entirely happy with doing things this way, but as the linked docs note, I did it because being clear about when a string is expected to have the specific properties of being a root project import path, vs just an fs path, vs any import path, are so muddled in a lot of this domain that i wanted a stronger signaling mechanism to implementors about when we needed this type of thing in particular.

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
}

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 {
// 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
}
24 changes: 24 additions & 0 deletions sm.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// 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"
)

func getSourceManager() (*gps.SourceMgr, error) {
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"))
}
Loading