From 80ba89ac219875d56baee35539beeabd174d5be3 Mon Sep 17 00:00:00 2001 From: Christian Tinnefeld Date: Sun, 20 May 2018 19:30:37 -0700 Subject: [PATCH 1/4] Implement missing flag for dep status Comply with command spec by making dep status -missing list the dependencies that are missing from the lock file but are used in the project. If there are no dependencies missing it returns "No missing dependencies found." If there are errors preventing the execution of dep status, the error msg will be displayed instead. --- cmd/dep/status.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/cmd/dep/status.go b/cmd/dep/status.go index 42b6dd32ea..336dc57462 100644 --- a/cmd/dep/status.go +++ b/cmd/dep/status.go @@ -333,8 +333,6 @@ func (cmd *statusCommand) Run(ctx *dep.Ctx, args []string) error { var buf bytes.Buffer var out outputter switch { - case cmd.missing: - return errors.Errorf("not implemented") case cmd.json: out = &jsonOutput{ w: &buf, @@ -399,6 +397,11 @@ func (cmd *statusCommand) Run(ctx *dep.Ctx, args []string) error { return err } + if cmd.missing && !hasMissingPkgs { + buf.Reset() + buf.Write([]byte("No missing dependencies found.\n")) + } + // Print the status output ctx.Out.Print(buf.String()) From 7ea232e3000769831f9629d653625c39e34a63fb Mon Sep 17 00:00:00 2001 From: Christian Tinnefeld Date: Mon, 28 May 2018 12:39:02 -0700 Subject: [PATCH 2/4] Implement missing flag for dep status Comply with command spec by making dep status -missing list the dependencies that are missing from the lock file but are used in the project. Issue #1382. --- cmd/dep/status.go | 45 +++++++++-- cmd/dep/status_test.go | 100 ++++++++++++++++++++++++ cmd/dep/testdata/status/missing/main.go | 13 +++ 3 files changed, 153 insertions(+), 5 deletions(-) create mode 100644 cmd/dep/testdata/status/missing/main.go diff --git a/cmd/dep/status.go b/cmd/dep/status.go index 336dc57462..daf1be1700 100644 --- a/cmd/dep/status.go +++ b/cmd/dep/status.go @@ -372,6 +372,11 @@ func (cmd *statusCommand) Run(ctx *dep.Ctx, args []string) error { return err } + if cmd.missing { + err := cmd.runStatusMissing(ctx, p) + return err + } + hasMissingPkgs, errCount, err := runStatusAll(ctx, out, p, sm) if err != nil { switch err { @@ -397,11 +402,6 @@ func (cmd *statusCommand) Run(ctx *dep.Ctx, args []string) error { return err } - if cmd.missing && !hasMissingPkgs { - buf.Reset() - buf.Write([]byte("No missing dependencies found.\n")) - } - // Print the status output ctx.Out.Print(buf.String()) @@ -490,6 +490,41 @@ func (os OldStatus) marshalJSON() *rawOldStatus { } } +// runStatusMissing analyses the project for missing dependencies in lock file. +func (cmd *statusCommand) runStatusMissing(ctx *dep.Ctx, p *dep.Project) error { + + ptree, err := p.ParseRootPackageTree() + if err != nil { + return err + } + rm, _ := ptree.ToReachMap(true, true, false, p.Manifest.IgnoredPackages()) + external := rm.FlattenFn(paths.IsStandardImportPath) + + var missingDeps []string + +missingOuter: + for _, d := range external { + for _, lp := range p.Lock.Projects() { + if string(lp.Ident().ProjectRoot) == d { + continue missingOuter + } + } + + missingDeps = append(missingDeps, d) + } + + if missingDeps != nil { + ctx.Err.Printf("Missing dependencies (not present in lock):\n") + for _, d := range missingDeps { + ctx.Err.Printf(" %v\n", d) + } + } else { + ctx.Err.Println("No missing dependencies found.") + } + + return nil +} + func (cmd *statusCommand) runOld(ctx *dep.Ctx, out oldOutputter, p *dep.Project, sm gps.SourceManager) error { // While the network churns on ListVersions() requests, statically analyze // code from the current project. diff --git a/cmd/dep/status_test.go b/cmd/dep/status_test.go index 509a6749d3..4e8227cb59 100644 --- a/cmd/dep/status_test.go +++ b/cmd/dep/status_test.go @@ -5,6 +5,7 @@ package main import ( + "bufio" "bytes" "io/ioutil" "log" @@ -555,3 +556,102 @@ func TestValidateFlags(t *testing.T) { }) } } + +func TestStatusMissing(t *testing.T) { + + cases := []struct { + name string + lock dep.Lock + wantStatus string + wantErr bool + }{ + { + name: "no missing dependencies", + lock: dep.Lock{ + P: []gps.LockedProject{ + gps.NewLockedProject( + gps.ProjectIdentifier{ProjectRoot: gps.ProjectRoot("github.com/boltdb/bolt")}, + gps.NewVersion("v1.0.0"), + []string{"."}, + ), + gps.NewLockedProject( + gps.ProjectIdentifier{ProjectRoot: gps.ProjectRoot("github.com/sdboyer/dep-test")}, + gps.NewVersion("v1.0.0"), + []string{"."}, + ), + gps.NewLockedProject( + gps.ProjectIdentifier{ProjectRoot: gps.ProjectRoot("github.com/sdboyer/deptestdos")}, + gps.NewVersion("v1.0.0"), + []string{"."}, + ), + }, + }, + wantStatus: "No missing dependencies found.\n", + }, + { + name: "two missing dependencies", + lock: dep.Lock{ + P: []gps.LockedProject{ + gps.NewLockedProject( + gps.ProjectIdentifier{ProjectRoot: gps.ProjectRoot("github.com/sdboyer/deptestdos")}, + gps.NewVersion("v1.0.0"), + []string{"."}, + ), + }, + }, + wantStatus: "Missing dependencies (not present in lock):\n" + + " github.com/boltdb/bolt\n" + + " github.com/sdboyer/dep-test\n", + }, + } + + h := test.NewHelper(t) + defer h.Cleanup() + + testdir := filepath.Join("src", "status_missing_test") + h.TempDir(testdir) + h.TempCopy(filepath.Join(testdir, "main.go"), filepath.Join("status", "missing", "main.go")) + testProjPath := h.Path(testdir) + + var buf bytes.Buffer + bufferWriter := bufio.NewWriter(&buf) + bufferLogger := log.New(bufferWriter, "", 0) + discardLogger := log.New(ioutil.Discard, "", 0) + + ctx := &dep.Ctx{ + GOPATH: testProjPath, + Out: discardLogger, + Err: bufferLogger, + } + + sm, err := ctx.SourceManager() + h.Must(err) + defer sm.Release() + + p := new(dep.Project) + p.SetRoot(testProjPath) + + cmd := statusCommand{ + missing: true, + } + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + buf.Reset() + p.Manifest = &dep.Manifest{} // needed for empty Ignored packages string + p.Lock = &c.lock + + err = cmd.runStatusMissing(ctx, p) + bufferWriter.Flush() + actualStatus := buf.String() + + if err != nil && !c.wantErr { + t.Fatalf("unexpected errors while collecting constraints: %v", err) + } + if actualStatus != c.wantStatus { + t.Fatalf("unexpected missign status: \n\t(GOT): %v\n\t(WNT): %v", actualStatus, c.wantStatus) + } + }) + } + +} diff --git a/cmd/dep/testdata/status/missing/main.go b/cmd/dep/testdata/status/missing/main.go new file mode 100644 index 0000000000..74a17cf531 --- /dev/null +++ b/cmd/dep/testdata/status/missing/main.go @@ -0,0 +1,13 @@ +// Copyright 2017 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/boltdb/bolt" + _ "github.com/sdboyer/dep-test" + _ "github.com/sdboyer/deptestdos" +) + +type FooBar int From 391e768af268da865414c724ed7a764b09259c9a Mon Sep 17 00:00:00 2001 From: Christian Tinnefeld Date: Mon, 28 May 2018 12:39:02 -0700 Subject: [PATCH 3/4] Implement missing flag for dep status Comply with command spec by making dep status -missing list the dependencies that are missing from the lock file but are used in the project. Issue #1382. --- cmd/dep/status.go | 80 ++++++++++++++++++---- cmd/dep/status_test.go | 88 ++++++++++++++++++------- cmd/dep/testdata/status/missing/main.go | 3 +- 3 files changed, 132 insertions(+), 39 deletions(-) diff --git a/cmd/dep/status.go b/cmd/dep/status.go index daf1be1700..f9b02ff4ac 100644 --- a/cmd/dep/status.go +++ b/cmd/dep/status.go @@ -119,6 +119,9 @@ type outputter interface { MissingHeader() error MissingLine(*MissingStatus) error MissingFooter() error + StatusMissingHeader() error + StatusMissingLine(gps.ProjectRoot) error + StatusMissingFooter() error } // Only a subset of the outputters should be able to output old statuses. @@ -170,6 +173,20 @@ func (out *tableOutput) MissingFooter() error { return out.w.Flush() } +func (out *tableOutput) StatusMissingHeader() error { + _, err := fmt.Fprintln(out.w, "Missing dependencies (not present in lock):") + return err +} + +func (out *tableOutput) StatusMissingLine(missingDep gps.ProjectRoot) error { + _, err := fmt.Fprintf(out.w, " %v\n", missingDep) + return err +} + +func (out *tableOutput) StatusMissingFooter() error { + return out.w.Flush() +} + func (out *tableOutput) OldHeader() error { _, err := fmt.Fprintf(out.w, "PROJECT\tCONSTRAINT\tREVISION\tLATEST\n") return err @@ -191,10 +208,11 @@ func (out *tableOutput) OldFooter() error { } type jsonOutput struct { - w io.Writer - basic []*rawStatus - missing []*MissingStatus - old []*rawOldStatus + w io.Writer + basic []*rawStatus + missing []*MissingStatus + statusMissing []struct{ ProjectRoot gps.ProjectRoot } + old []*rawOldStatus } func (out *jsonOutput) BasicHeader() error { @@ -225,6 +243,21 @@ func (out *jsonOutput) MissingFooter() error { return json.NewEncoder(out.w).Encode(out.missing) } +func (out *jsonOutput) StatusMissingHeader() error { + out.statusMissing = []struct{ ProjectRoot gps.ProjectRoot }{} + return nil +} + +func (out *jsonOutput) StatusMissingLine(missingDep gps.ProjectRoot) error { + out.statusMissing = append(out.statusMissing, + struct{ ProjectRoot gps.ProjectRoot }{missingDep}) + return nil +} + +func (out *jsonOutput) StatusMissingFooter() error { + return json.NewEncoder(out.w).Encode(out.statusMissing) +} + func (out *jsonOutput) OldHeader() error { out.old = []*rawOldStatus{} return nil @@ -269,9 +302,12 @@ func (out *dotOutput) BasicLine(bs *BasicStatus) error { return nil } -func (out *dotOutput) MissingHeader() error { return nil } -func (out *dotOutput) MissingLine(ms *MissingStatus) error { return nil } -func (out *dotOutput) MissingFooter() error { return nil } +func (out *dotOutput) MissingHeader() error { return nil } +func (out *dotOutput) MissingLine(ms *MissingStatus) error { return nil } +func (out *dotOutput) MissingFooter() error { return nil } +func (out *dotOutput) StatusMissingHeader() error { return nil } +func (out *dotOutput) StatusMissingLine(missingDep gps.ProjectRoot) error { return nil } +func (out *dotOutput) StatusMissingFooter() error { return nil } type templateOutput struct { w io.Writer @@ -303,6 +339,12 @@ func (out *templateOutput) MissingFooter() error { return nil } func (out *templateOutput) MissingLine(ms *MissingStatus) error { return out.tmpl.Execute(out.w, ms) } +func (out *templateOutput) StatusMissingHeader() error { return nil } +func (out *templateOutput) StatusMissingLine(missingDep gps.ProjectRoot) error { + t := struct{ ProjectRoot gps.ProjectRoot }{missingDep} + return out.tmpl.Execute(out.w, t) +} +func (out *templateOutput) StatusMissingFooter() error { return nil } func (cmd *statusCommand) Run(ctx *dep.Ctx, args []string) error { if cmd.examples { @@ -373,7 +415,10 @@ func (cmd *statusCommand) Run(ctx *dep.Ctx, args []string) error { } if cmd.missing { - err := cmd.runStatusMissing(ctx, p) + err := cmd.runStatusMissing(ctx, out, p) + if len(buf.String()) > 0 { + ctx.Out.Print(buf.String()) + } return err } @@ -491,7 +536,7 @@ func (os OldStatus) marshalJSON() *rawOldStatus { } // runStatusMissing analyses the project for missing dependencies in lock file. -func (cmd *statusCommand) runStatusMissing(ctx *dep.Ctx, p *dep.Project) error { +func (cmd *statusCommand) runStatusMissing(ctx *dep.Ctx, out outputter, p *dep.Project) error { ptree, err := p.ParseRootPackageTree() if err != nil { @@ -500,7 +545,7 @@ func (cmd *statusCommand) runStatusMissing(ctx *dep.Ctx, p *dep.Project) error { rm, _ := ptree.ToReachMap(true, true, false, p.Manifest.IgnoredPackages()) external := rm.FlattenFn(paths.IsStandardImportPath) - var missingDeps []string + var missingDeps []gps.ProjectRoot missingOuter: for _, d := range external { @@ -510,16 +555,23 @@ missingOuter: } } - missingDeps = append(missingDeps, d) + missingDeps = append(missingDeps, gps.ProjectRoot(d)) } if missingDeps != nil { - ctx.Err.Printf("Missing dependencies (not present in lock):\n") + if err = out.StatusMissingHeader(); err != nil { + return err + } for _, d := range missingDeps { - ctx.Err.Printf(" %v\n", d) + if err = out.StatusMissingLine(d); err != nil { + return err + } + } + if err = out.StatusMissingFooter(); err != nil { + return err } } else { - ctx.Err.Println("No missing dependencies found.") + ctx.Err.Printf("No missing dependencies found.") } return nil diff --git a/cmd/dep/status_test.go b/cmd/dep/status_test.go index 4e8227cb59..a19d5cb9fa 100644 --- a/cmd/dep/status_test.go +++ b/cmd/dep/status_test.go @@ -560,10 +560,13 @@ func TestValidateFlags(t *testing.T) { func TestStatusMissing(t *testing.T) { cases := []struct { - name string - lock dep.Lock - wantStatus string - wantErr bool + name string + lock dep.Lock + cmds []statusCommand + wantTableStatus string + wantJSONStatus string + wantTemplateStatus string + wantErr bool }{ { name: "no missing dependencies", @@ -586,7 +589,10 @@ func TestStatusMissing(t *testing.T) { ), }, }, - wantStatus: "No missing dependencies found.\n", + cmds: []statusCommand{ + statusCommand{missing: true}, //default for table output + }, + wantTableStatus: "No missing dependencies found.\n", }, { name: "two missing dependencies", @@ -599,9 +605,16 @@ func TestStatusMissing(t *testing.T) { ), }, }, - wantStatus: "Missing dependencies (not present in lock):\n" + + cmds: []statusCommand{ + statusCommand{missing: true}, //default for table output + statusCommand{missing: true, json: true}, + statusCommand{missing: true, template: "Missing:{{.ProjectRoot}} "}, + }, + wantTableStatus: "Missing dependencies (not present in lock):\n" + " github.com/boltdb/bolt\n" + " github.com/sdboyer/dep-test\n", + wantJSONStatus: `[{"ProjectRoot":"github.com/boltdb/bolt"},{"ProjectRoot":"github.com/sdboyer/dep-test"}]` + "\n", + wantTemplateStatus: "Missing:github.com/boltdb/bolt Missing:github.com/sdboyer/dep-test \n", }, } @@ -613,14 +626,13 @@ func TestStatusMissing(t *testing.T) { h.TempCopy(filepath.Join(testdir, "main.go"), filepath.Join("status", "missing", "main.go")) testProjPath := h.Path(testdir) - var buf bytes.Buffer - bufferWriter := bufio.NewWriter(&buf) + var bufW bytes.Buffer + bufferWriter := bufio.NewWriter(&bufW) bufferLogger := log.New(bufferWriter, "", 0) - discardLogger := log.New(ioutil.Discard, "", 0) ctx := &dep.Ctx{ GOPATH: testProjPath, - Out: discardLogger, + Out: bufferLogger, Err: bufferLogger, } @@ -631,25 +643,53 @@ func TestStatusMissing(t *testing.T) { p := new(dep.Project) p.SetRoot(testProjPath) - cmd := statusCommand{ - missing: true, - } + var bufO bytes.Buffer + var out outputter for _, c := range cases { t.Run(c.name, func(t *testing.T) { - buf.Reset() - p.Manifest = &dep.Manifest{} // needed for empty Ignored packages string - p.Lock = &c.lock - err = cmd.runStatusMissing(ctx, p) - bufferWriter.Flush() - actualStatus := buf.String() + for _, statusCmd := range c.cmds { + bufO.Reset() + bufW.Reset() + + var wantStatus string + switch { + case statusCmd.json: + out = &jsonOutput{ + w: &bufO, + } + wantStatus = c.wantJSONStatus + case statusCmd.template != "": + tmpl, _ := template.New("status").Parse(statusCmd.template) + out = &templateOutput{ + w: &bufO, + tmpl: tmpl, + } + wantStatus = c.wantTemplateStatus + default: + out = &tableOutput{ + w: tabwriter.NewWriter(&bufO, 0, 4, 2, ' ', 0), + } + wantStatus = c.wantTableStatus + } - if err != nil && !c.wantErr { - t.Fatalf("unexpected errors while collecting constraints: %v", err) - } - if actualStatus != c.wantStatus { - t.Fatalf("unexpected missign status: \n\t(GOT): %v\n\t(WNT): %v", actualStatus, c.wantStatus) + p.Manifest = &dep.Manifest{} // needed for empty Ignored packages string + p.Lock = &c.lock + + err = statusCmd.runStatusMissing(ctx, out, p) + if len(bufO.String()) > 0 { + ctx.Out.Print(bufO.String()) + } + bufferWriter.Flush() + actualStatus := bufW.String() + + if err != nil && !c.wantErr { + t.Fatalf("unexpected errors while collecting constraints: %v", err) + } + if actualStatus != wantStatus { + t.Fatalf("unexpected missing status: \n\t(GOT): %v\n\t(WNT): %v", actualStatus, wantStatus) + } } }) } diff --git a/cmd/dep/testdata/status/missing/main.go b/cmd/dep/testdata/status/missing/main.go index 74a17cf531..e9e7027e79 100644 --- a/cmd/dep/testdata/status/missing/main.go +++ b/cmd/dep/testdata/status/missing/main.go @@ -1,4 +1,4 @@ -// Copyright 2017 The Go Authors. All rights reserved. +// Copyright 2018 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. @@ -10,4 +10,5 @@ import ( _ "github.com/sdboyer/deptestdos" ) +// FooBar is a dummy type type FooBar int From e75176474c3d3228ca14b1e850f0f7ca90366c27 Mon Sep 17 00:00:00 2001 From: Christian Tinnefeld Date: Sun, 8 Jul 2018 19:38:54 -0700 Subject: [PATCH 4/4] Implement missing flag for dep status Minor code cleanup to comply with linting. Issue #1382. --- cmd/dep/status.go | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/cmd/dep/status.go b/cmd/dep/status.go index 52d5b22c86..d87f8ffdb2 100644 --- a/cmd/dep/status.go +++ b/cmd/dep/status.go @@ -271,7 +271,7 @@ type jsonOutput struct { basic []*rawStatus detail []rawDetailProject missing []*MissingStatus - statusMissing []struct{ ProjectRoot gps.ProjectRoot } + statusMissing []*StatusMissing old []*rawOldStatus } @@ -323,13 +323,12 @@ func (out *jsonOutput) MissingFooter() error { } func (out *jsonOutput) StatusMissingHeader() error { - out.statusMissing = []struct{ ProjectRoot gps.ProjectRoot }{} + out.statusMissing = []*StatusMissing{} return nil } func (out *jsonOutput) StatusMissingLine(missingDep gps.ProjectRoot) error { - out.statusMissing = append(out.statusMissing, - struct{ ProjectRoot gps.ProjectRoot }{missingDep}) + out.statusMissing = append(out.statusMissing, &StatusMissing{missingDep}) return nil } @@ -464,7 +463,7 @@ func (out *templateOutput) MissingLine(ms *MissingStatus) error { } func (out *templateOutput) StatusMissingHeader() error { return nil } func (out *templateOutput) StatusMissingLine(missingDep gps.ProjectRoot) error { - t := struct{ ProjectRoot gps.ProjectRoot }{missingDep} + t := StatusMissing{missingDep} return out.tmpl.Execute(out.w, t) } func (out *templateOutput) StatusMissingFooter() error { return nil } @@ -969,11 +968,18 @@ func (ds *DetailStatus) marshalJSON() *rawDetailProject { } // MissingStatus contains information about all the missing packages in a project. +// Deprecated: Use StatusMissing instead, to-be-removed together with refactoring +// of runStatusAll type MissingStatus struct { ProjectRoot string MissingPackages []string } +// StatusMissing contains the root of missing packages in a project. +type StatusMissing struct { + ProjectRoot gps.ProjectRoot +} + func (cmd *statusCommand) runStatusAll(ctx *dep.Ctx, out outputter, p *dep.Project, sm gps.SourceManager) (hasMissingPkgs bool, errCount int, err error) { // While the network churns on ListVersions() requests, statically analyze // code from the current project.