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
}
10 changes: 5 additions & 5 deletions lock.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (
"github.com/sdboyer/gps"
)

type Lock struct {
type lock struct {
Memo []byte
P []gps.LockedProject
}
Expand All @@ -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 {
Expand All @@ -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)),
}
Expand Down Expand Up @@ -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
}
6 changes: 3 additions & 3 deletions lock_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
144 changes: 88 additions & 56 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,13 @@ import (
"fmt"
"os"
"path/filepath"
"strings"

"github.com/sdboyer/gps"
)

const ManifestName = "manifest.json"
const LockName = "lock.json"
const manifestName = "manifest.json"
const lockName = "lock.json"

func main() {
flag.Parse()
Expand Down Expand Up @@ -106,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
Expand All @@ -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 All @@ -179,7 +131,7 @@ var getCmd = &command{
-x dry run
-f force the given package to be updated to the specified
version

Package specs:
<path>[@<version specifier>]

Expand All @@ -203,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
Expand All @@ -228,3 +180,83 @@ func findProjectRoot(from string) (string, error) {
}
return path, nil
}

type project struct {
// 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
// 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) (*project, error) {
var err error
p := new(project)

switch path {
case "":
p.absroot, err = findProjectRootFromWD()
default:
p.absroot, err = findProjectRoot(path)
}

if err != nil {
return p, err
}

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.absroot, srcprefix) {
gopath = gp
match = true
// filepath.ToSlash because we're dealing with an import path now,
// not an fs path
p.importroot = gps.ProjectRoot(filepath.ToSlash(strings.TrimPrefix(p.absroot, 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
}
2 changes: 1 addition & 1 deletion main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
14 changes: 7 additions & 7 deletions manifest.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (
"github.com/sdboyer/gps"
)

type Manifest struct {
type manifest struct {
Dependencies gps.ProjectConstraints
Ovr gps.ProjectConstraints
Ignores []string
Expand All @@ -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,
Expand Down Expand Up @@ -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
}
Expand Down
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