From 6a1e0c866e2950f9679f092f2b32f4bb6f2f869b Mon Sep 17 00:00:00 2001 From: Jess Frazelle Date: Tue, 17 Jan 2017 16:40:07 -0800 Subject: [PATCH 1/2] ensure: add tests and behavior for when a dep is unused Signed-off-by: Jess Frazelle --- ensure.go | 18 ++++++++++++++++ ensure_test.go | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+) diff --git a/ensure.go b/ensure.go index 71fe2111f8..e2cc774d1f 100644 --- a/ensure.go +++ b/ensure.go @@ -119,6 +119,7 @@ func (cmd *ensureCommand) Run(args []string) error { defer sm.Release() var errs []error + var requestedPackages []string for _, arg := range args { // default persist to manifest pc, err := getProjectConstraint(arg, sm) @@ -126,6 +127,7 @@ func (cmd *ensureCommand) Run(args []string) error { errs = append(errs, err) continue } + requestedPackages = append(requestedPackages, string(pc.Ident.ProjectRoot)) if gps.IsAny(pc.Constraint) && pc.Ident.Source == "" { // If the input specified neither a network name nor a constraint, @@ -211,6 +213,22 @@ func (cmd *ensureCommand) Run(args []string) error { return errors.Wrap(err, "ensure Solve()") } + // generate warning if the package specifically requested was not added to + // the generated lock, this would mean it is not imported in their code + for _, pkg := range requestedPackages { + var found bool + for _, lp := range solution.Projects() { + if string(lp.Ident().ProjectRoot) == pkg { + found = true + break + } + } + + if !found { + fmt.Fprintf(os.Stdout, "WARNING: %s was requested but is not imported in your code.\n", pkg) + } + } + sw := safeWriter{ root: p.absroot, m: p.m, diff --git a/ensure_test.go b/ensure_test.go index 2fa8c3beda..d597931fd8 100644 --- a/ensure_test.go +++ b/ensure_test.go @@ -13,6 +13,62 @@ import ( "github.com/sdboyer/gps" ) +func TestEnsureUnusedDep(t *testing.T) { + needsExternalNetwork(t) + needsGit(t) + + tg := testgo(t) + defer tg.cleanup() + + tg.tempDir("src") + tg.setenv("GOPATH", tg.path(".")) + + m := `package main + +import ( + "fmt" +) + +func main() { + fmt.Println("hello world") +}` + + tg.tempFile("src/thing/thing.go", m) + tg.cd(tg.path("src/thing")) + + tg.run("init") + tg.run("ensure", "github.com/Sirupsen/logrus@0.11.0") + tg.grepStdout("WARNING: github.com/Sirupsen/logrus was requested but is not imported in your code", "") + + // manifest should not show the dependency as required + expectedManifest := `{ + "dependencies": { + "github.com/Sirupsen/logrus": { + "version": "0.11.0" + } + } +} +` + + manifest := tg.readManifest() + if manifest != expectedManifest { + t.Fatalf("expected %s, got %s", expectedManifest, manifest) + } + + // we should not have a vendor folder with logrus + tg.mustNotExist(tg.path("src/thing/vendor/github.com/Sirupsen/logrus")) + + expectedLock := `{ + "memo": "fe519839881b58f20ae6efe7a78004b320bc9ba14c88a4ea6a061e8030b7a493", + "projects": [] +} +` + lock := tg.readLock() + if lock != expectedLock { + t.Fatalf("expected %s, got %s", expectedLock, lock) + } +} + func TestEnsureOverrides(t *testing.T) { needsExternalNetwork(t) needsGit(t) From 2e8abb70d09b86f77ee78c94910c88cbd1393089 Mon Sep 17 00:00:00 2001 From: Jess Frazelle Date: Wed, 18 Jan 2017 17:23:02 -0800 Subject: [PATCH 2/2] add require command Signed-off-by: Jess Frazelle --- main.go | 1 + require.go | 112 ++++++++++++++++++++++++++++++++ require_test.go | 165 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 278 insertions(+) create mode 100644 require.go create mode 100644 require_test.go diff --git a/main.go b/main.go index bf207a88ed..a7a7d43ada 100644 --- a/main.go +++ b/main.go @@ -53,6 +53,7 @@ func main() { &statusCommand{}, &ensureCommand{}, &removeCommand{}, + &requireCommand{}, &hashinCommand{}, } diff --git a/require.go b/require.go new file mode 100644 index 0000000000..d538dfca1a --- /dev/null +++ b/require.go @@ -0,0 +1,112 @@ +// 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 ( + "bytes" + "flag" + "log" + "os" + + "github.com/pkg/errors" + "github.com/sdboyer/gps" +) + +func (cmd *requireCommand) Name() string { return "require" } +func (cmd *requireCommand) Args() string { return "" } +func (cmd *requireCommand) ShortHelp() string { + return "Require a package to be vendored even if it is not imported." +} +func (cmd *requireCommand) LongHelp() string { return "" } +func (cmd *requireCommand) Hidden() bool { return false } + +func (cmd *requireCommand) Register(fs *flag.FlagSet) { +} + +type requireCommand struct{} + +func (_ requireCommand) Run(args []string) error { + if len(args) == 0 { + return errors.New("must pass a package to require") + } + p, err := depContext.loadProject("") + if err != nil { + return err + } + + sm, err := depContext.sourceManager() + if err != nil { + return err + } + sm.UseDefaultSignalHandling() + defer sm.Release() + + pt, err := gps.ListPackages(p.absroot, string(p.importroot)) + if err != nil { + return errors.Wrap(err, "require ListPackage for project") + } + + reachMap := pt.ExternalReach(true, true, p.m.IgnoredPackages()) + external := reachMap.ListExternalImports() + em := map[string]bool{} + for _, ep := range external { + em[ep] = true + } + + curRequired := map[string]bool{} + for _, rp := range p.m.Required { + curRequired[rp] = true + } + + for _, arg := range args { + if _, err := sm.DeduceProjectRoot(arg); err != nil { + return errors.Wrapf(err, "could not deduce project root for %s", arg) + } + + if curRequired[arg] { + logf("%s is already required, skipping", arg) + continue + } + + if em[arg] { + logf("%s is imported, adding to required.", arg) + } + + // add to manifest + p.m.Required = append(p.m.Required, arg) + } + + params := p.makeParams() + params.RootPackageTree = pt + if *verbose { + params.Trace = true + params.TraceLogger = log.New(os.Stderr, "", 0) + } + + solver, err := gps.Prepare(params, sm) + if err != nil { + return errors.Wrap(err, "require Prepare") + } + + sw := safeWriter{ + root: p.absroot, + m: p.m, + sm: sm, + } + + if bytes.Equal(solver.HashInputs(), p.l.InputHash()) { + return errors.Wrap(sw.writeAllSafe(false), "writing of manifest") + } + + solution, err := solver.Solve() + if err != nil { + handleAllTheFailuresOfTheWorld(err) + return errors.Wrap(err, "require Solve()") + } + sw.l = p.l + sw.nl = solution + + return errors.Wrap(sw.writeAllSafe(false), "grouped write of manifest, lock and vendor") +} diff --git a/require_test.go b/require_test.go new file mode 100644 index 0000000000..cf7f810b8c --- /dev/null +++ b/require_test.go @@ -0,0 +1,165 @@ +package main + +import "testing" + +func TestRequire(t *testing.T) { + needsExternalNetwork(t) + needsGit(t) + + tg := testgo(t) + defer tg.cleanup() + + tg.tempDir("src") + tg.setenv("GOPATH", tg.path(".")) + + m := `package main + +import ( + "fmt" +) + +func main() { + fmt.Println("hello world") +}` + + tg.tempFile("src/thing/thing.go", m) + tg.cd(tg.path("src/thing")) + + tg.run("init") + tg.run("require", "github.com/jessfraz/weather/geocode") + tg.run("require", "github.com/jessfraz/reg/registry") + + // manifest should not show the dependency as required + expectedManifest := `{ + "required": [ + "github.com/jessfraz/weather/geocode", + "github.com/jessfraz/reg/registry" + ] +} +` + + manifest := tg.readManifest() + if manifest != expectedManifest { + t.Fatalf("expected %s, got %s", expectedManifest, manifest) + } + + tg.mustExist(tg.path("src/thing/vendor/github.com/jessfraz/weather/geocode")) + tg.mustExist(tg.path("src/thing/vendor/github.com/jessfraz/reg/registry")) + + expectedLock := `{ + "memo": "8eafbe7fd7b5a490d309f25ec3e4acd995806e4cea557452bfafeedaf0e1fb5a", + "projects": [ + { + "name": "github.com/Sirupsen/logrus", + "version": "v0.11.0", + "revision": "d26492970760ca5d33129d2d799e34be5c4782eb", + "packages": [ + "." + ] + }, + { + "name": "github.com/docker/distribution", + "version": "v2.6.0", + "revision": "325b0804fef3a66309d962357aac3c2ce3f4d329", + "packages": [ + "digest", + "manifest/schema1", + "manifest/schema2" + ] + }, + { + "name": "github.com/docker/engine-api", + "version": "v0.4.0", + "revision": "3d1601b9d2436a70b0dfc045a23f6503d19195df", + "packages": [ + "types" + ] + }, + { + "name": "github.com/docker/go-connections", + "version": "v0.2.1", + "revision": "990a1a1a70b0da4c4cb70e117971a4f0babfbf1a", + "packages": [ + "nat" + ] + }, + { + "name": "github.com/docker/go-units", + "version": "v0.3.1", + "revision": "f2d77a61e3c169b43402a0a1e84f06daf29b8190", + "packages": [ + "." + ] + }, + { + "name": "github.com/docker/libtrust", + "branch": "master", + "revision": "aabc10ec26b754e797f9028f4589c5b7bd90dc20", + "packages": [ + "." + ] + }, + { + "name": "github.com/gorilla/context", + "version": "v1.1", + "revision": "1ea25387ff6f684839d82767c1733ff4d4d15d0a", + "packages": [ + "." + ] + }, + { + "name": "github.com/gorilla/mux", + "version": "v1.3.0", + "revision": "392c28fe23e1c45ddba891b0320b3b5df220beea", + "packages": [ + "." + ] + }, + { + "name": "github.com/jessfraz/reg", + "branch": "master", + "revision": "7d3217e55266e66c19943e99847b0ab56568117b", + "packages": [ + "registry" + ] + }, + { + "name": "github.com/jessfraz/weather", + "version": "v0.9.1", + "revision": "a69f1b14ff98663e524100cdd164e5b9ecc306d9", + "packages": [ + "geocode" + ] + }, + { + "name": "github.com/peterhellberg/link", + "version": "v1.0.0", + "revision": "d1cebc7ea14a5fc0de7cb4a45acae773161642c6", + "packages": [ + "." + ] + }, + { + "name": "golang.org/x/net", + "branch": "master", + "revision": "f2499483f923065a842d38eb4c7f1927e6fc6e6d", + "packages": [ + "context" + ] + }, + { + "name": "golang.org/x/sys", + "branch": "master", + "revision": "d75a52659825e75fff6158388dddc6a5b04f9ba5", + "packages": [ + "unix" + ] + } + ] +} +` + lock := tg.readLock() + if lock != expectedLock { + t.Fatalf("expected %s, got %s", expectedLock, lock) + } +}