diff --git a/cmd/dep/ensure.go b/cmd/dep/ensure.go index bc2eabe006..dd558b9047 100644 --- a/cmd/dep/ensure.go +++ b/cmd/dep/ensure.go @@ -104,7 +104,7 @@ type ensureCommand struct { func (cmd *ensureCommand) Run(ctx *dep.Ctx, args []string) error { if cmd.examples { - ctx.Loggers.Err.Println(strings.TrimSpace(ensureExamples)) + ctx.Err.Println(strings.TrimSpace(ensureExamples)) return nil } @@ -121,8 +121,8 @@ func (cmd *ensureCommand) Run(ctx *dep.Ctx, args []string) error { defer sm.Release() params := p.MakeParams() - if ctx.Loggers.Verbose { - params.TraceLogger = ctx.Loggers.Err + if ctx.Verbose { + params.TraceLogger = ctx.Err } params.RootPackageTree, err = pkgtree.ListPackages(p.AbsRoot, string(p.ImportRoot)) if err != nil { @@ -136,7 +136,7 @@ func (cmd *ensureCommand) Run(ctx *dep.Ctx, args []string) error { if cmd.update { applyUpdateArgs(args, ¶ms) } else { - err := applyEnsureArgs(ctx.Loggers.Err, args, cmd.overrides, p, sm, ¶ms) + err := applyEnsureArgs(ctx.Err, args, cmd.overrides, p, sm, ¶ms) if err != nil { return err } @@ -169,7 +169,7 @@ func (cmd *ensureCommand) Run(ctx *dep.Ctx, args []string) error { return err } if cmd.dryRun { - return sw.PrintPreparedActions(ctx.Loggers.Out) + return sw.PrintPreparedActions(ctx.Out) } return errors.Wrap(sw.Write(p.AbsRoot, sm, false), "grouped write of manifest, lock and vendor") diff --git a/cmd/dep/glide_importer_test.go b/cmd/dep/glide_importer_test.go index 03acf6b4f4..dc8c051b75 100644 --- a/cmd/dep/glide_importer_test.go +++ b/cmd/dep/glide_importer_test.go @@ -21,16 +21,16 @@ import ( const testGlideProjectRoot = "github.com/golang/notexist" var ( - discardLogger = log.New(ioutil.Discard, "", 0) - discardLoggers = &dep.Loggers{Out: discardLogger, Err: discardLogger} + discardLogger = log.New(ioutil.Discard, "", 0) ) func newTestContext(h *test.Helper) *dep.Ctx { h.TempDir("src") pwd := h.Path(".") return &dep.Ctx{ - GOPATH: pwd, - Loggers: discardLoggers, + GOPATH: pwd, + Out: discardLogger, + Err: discardLogger, } } diff --git a/cmd/dep/gopath_scanner.go b/cmd/dep/gopath_scanner.go index a5824be924..d0b77f2940 100644 --- a/cmd/dep/gopath_scanner.go +++ b/cmd/dep/gopath_scanner.go @@ -46,7 +46,7 @@ func newGopathScanner(ctx *dep.Ctx, directDeps map[string]bool, sm gps.SourceMan func (g *gopathScanner) InitializeRootManifestAndLock(rootM *dep.Manifest, rootL *dep.Lock) error { var err error - g.ctx.Loggers.Err.Println("Searching GOPATH for projects...") + g.ctx.Err.Println("Searching GOPATH for projects...") g.pd, err = g.scanGopathForDependencies() if err != nil { return err @@ -123,7 +123,7 @@ func (g *gopathScanner) overlay(rootM *dep.Manifest, rootL *dep.Lock) { unlockedProjects = append(unlockedProjects, string(pr)) } if len(unlockedProjects) > 0 { - g.ctx.Loggers.Err.Printf("Following dependencies were not found in GOPATH. "+ + g.ctx.Err.Printf("Following dependencies were not found in GOPATH. "+ "Dep will use the most recent versions of these projects.\n %s", strings.Join(unlockedProjects, "\n ")) } @@ -211,7 +211,7 @@ func (g *gopathScanner) scanGopathForDependencies() (projectData, error) { var syncDepGroup sync.WaitGroup syncDep := func(pr gps.ProjectRoot, sm gps.SourceManager) { if err := sm.SyncSourceFor(gps.ProjectIdentifier{ProjectRoot: pr}); err != nil { - g.ctx.Loggers.Err.Printf("%+v", errors.Wrapf(err, "Unable to cache %s", pr)) + g.ctx.Err.Printf("%+v", errors.Wrapf(err, "Unable to cache %s", pr)) } syncDepGroup.Done() } diff --git a/cmd/dep/hash_in.go b/cmd/dep/hash_in.go index d2af94d969..5b3e8e9f09 100644 --- a/cmd/dep/hash_in.go +++ b/cmd/dep/hash_in.go @@ -51,6 +51,6 @@ func (hashinCommand) Run(ctx *dep.Ctx, args []string) error { if err != nil { return errors.Wrap(err, "prepare solver") } - ctx.Loggers.Out.Println(gps.HashingInputsAsString(s)) + ctx.Out.Println(gps.HashingInputsAsString(s)) return nil } diff --git a/cmd/dep/init.go b/cmd/dep/init.go index df5452be97..2cfe19017d 100644 --- a/cmd/dep/init.go +++ b/cmd/dep/init.go @@ -135,8 +135,8 @@ func (cmd *initCommand) Run(ctx *dep.Ctx, args []string) error { ProjectAnalyzer: rootAnalyzer, } - if ctx.Loggers.Verbose { - params.TraceLogger = ctx.Loggers.Err + if ctx.Verbose { + params.TraceLogger = ctx.Err } s, err := gps.Prepare(params, sm) @@ -169,7 +169,7 @@ func (cmd *initCommand) Run(ctx *dep.Ctx, args []string) error { return err } if vendorbak != "" { - ctx.Loggers.Err.Printf("Old vendor backed up to %v", vendorbak) + ctx.Err.Printf("Old vendor backed up to %v", vendorbak) } sw, err := dep.NewSafeWriter(m, nil, l, dep.VendorAlways) diff --git a/cmd/dep/main.go b/cmd/dep/main.go index dfd960abf5..4094f2e152 100644 --- a/cmd/dep/main.go +++ b/cmd/dep/main.go @@ -12,6 +12,7 @@ import ( "io" "log" "os" + "path/filepath" "strings" "text/tabwriter" @@ -46,11 +47,10 @@ func main() { // A Config specifies a full configuration for a dep execution. type Config struct { - // Args hold the command-line arguments, starting with the program name. - Args []string - Stdout, Stderr io.Writer - WorkingDir string - Env []string + WorkingDir string // Where to execute + Args []string // Command-line arguments, starting with the program name. + Env []string // Environment variables + Stdout, Stderr io.Writer // Log output } // Run executes a configuration and returns an exit code. @@ -135,23 +135,23 @@ func (c *Config) Run() (exitCode int) { } // Parse the flags the user gave us. - // flag package automaticly prints usage and error message in err != nil + // flag package automatically prints usage and error message in err != nil // or if '-h' flag provided if err := fs.Parse(c.Args[2:]); err != nil { exitCode = 1 return } - loggers := &dep.Loggers{ + // Set up the dep context. + ctx := &dep.Ctx{ Out: log.New(c.Stdout, "", 0), Err: errLogger, Verbose: *verbose, } - - // Set up the dep context. - ctx, err := dep.NewContext(c.WorkingDir, c.Env, loggers) + gopaths := filepath.SplitList(getEnv(c.Env, "GOPATH")) + err := ctx.SetPaths(c.WorkingDir, gopaths...) if err != nil { - loggers.Err.Println(err) + errLogger.Printf("%q not in any GOPATH: %s\n", c.WorkingDir, err) exitCode = 1 return } @@ -174,6 +174,18 @@ func (c *Config) Run() (exitCode int) { return } +// getEnv returns the last instance of the environment variable. +func getEnv(env []string, key string) string { + pre := key + "=" + for i := len(env) - 1; i >= 0; i-- { + v := env[i] + if strings.HasPrefix(v, pre) { + return strings.TrimPrefix(v, pre) + } + } + return "" +} + func resetUsage(logger *log.Logger, fs *flag.FlagSet, name, args, longHelp string) { var ( hasFlags bool diff --git a/cmd/dep/prune.go b/cmd/dep/prune.go index 89e61403fc..201c09e672 100644 --- a/cmd/dep/prune.go +++ b/cmd/dep/prune.go @@ -59,8 +59,8 @@ func (cmd *pruneCommand) Run(ctx *dep.Ctx, args []string) error { params := p.MakeParams() params.RootPackageTree = ptree - if ctx.Loggers.Verbose { - params.TraceLogger = ctx.Loggers.Err + if ctx.Verbose { + params.TraceLogger = ctx.Err } s, err := gps.Prepare(params, sm) @@ -77,8 +77,8 @@ func (cmd *pruneCommand) Run(ctx *dep.Ctx, args []string) error { } var pruneLogger *log.Logger - if ctx.Loggers.Verbose { - pruneLogger = ctx.Loggers.Err + if ctx.Verbose { + pruneLogger = ctx.Err } return dep.PruneProject(p, sm, pruneLogger) } diff --git a/cmd/dep/root_analyzer.go b/cmd/dep/root_analyzer.go index 9868cede4e..a92e9063da 100644 --- a/cmd/dep/root_analyzer.go +++ b/cmd/dep/root_analyzer.go @@ -77,7 +77,7 @@ func (a *rootAnalyzer) importManifestAndLock(dir string, pr gps.ProjectRoot, sup for _, i := range importers { if i.HasDepMetadata(dir) { - a.ctx.Loggers.Err.Printf("Importing configuration from %s. These are only initial constraints, and are further refined during the solve process.", i.Name()) + a.ctx.Err.Printf("Importing configuration from %s. These are only initial constraints, and are further refined during the solve process.", i.Name()) m, l, err := i.Import(dir, pr) a.removeTransitiveDependencies(m) return m, l, err diff --git a/cmd/dep/status.go b/cmd/dep/status.go index 45ca58fdb5..9313311040 100644 --- a/cmd/dep/status.go +++ b/cmd/dep/status.go @@ -215,22 +215,22 @@ func (cmd *statusCommand) Run(ctx *dep.Ctx, args []string) error { } } - digestMismatch, hasMissingPkgs, err := runStatusAll(ctx.Loggers, out, p, sm) + digestMismatch, hasMissingPkgs, err := runStatusAll(ctx, out, p, sm) if err != nil { return err } if digestMismatch { if hasMissingPkgs { - ctx.Loggers.Err.Printf("Lock inputs-digest mismatch due to the following packages missing from the lock:\n\n") - ctx.Loggers.Out.Print(buf.String()) - ctx.Loggers.Err.Printf("\nThis happens when a new import is added. Run `dep ensure` to install the missing packages.\n") + ctx.Err.Printf("Lock inputs-digest mismatch due to the following packages missing from the lock:\n\n") + ctx.Out.Print(buf.String()) + ctx.Err.Printf("\nThis happens when a new import is added. Run `dep ensure` to install the missing packages.\n") } else { - ctx.Loggers.Err.Printf("Lock inputs-digest mismatch. This happens when Gopkg.toml is modified.\n" + + ctx.Err.Printf("Lock inputs-digest mismatch. This happens when Gopkg.toml is modified.\n" + "Run `dep ensure` to regenerate the inputs-digest.") } } else { - ctx.Loggers.Out.Print(buf.String()) + ctx.Out.Print(buf.String()) } return nil @@ -253,7 +253,7 @@ type MissingStatus struct { MissingPackages []string } -func runStatusAll(loggers *dep.Loggers, out outputter, p *dep.Project, sm gps.SourceManager) (bool, bool, error) { +func runStatusAll(ctx *dep.Ctx, out outputter, p *dep.Project, sm gps.SourceManager) (bool, bool, error) { var digestMismatch, hasMissingPkgs bool if p.Lock == nil { @@ -276,8 +276,8 @@ func runStatusAll(loggers *dep.Loggers, out outputter, p *dep.Project, sm gps.So Manifest: p.Manifest, // Locks aren't a part of the input hash check, so we can omit it. } - if loggers.Verbose { - params.TraceLogger = loggers.Err + if ctx.Verbose { + params.TraceLogger = ctx.Err } s, err := gps.Prepare(params, sm) @@ -412,9 +412,9 @@ func runStatusAll(loggers *dep.Loggers, out outputter, p *dep.Project, sm gps.So // TODO this is just a fix quick so staticcheck doesn't complain. // Visually reconciling failure to deduce project roots with the rest of // the mismatch output is a larger problem. - loggers.Err.Printf("Failed to deduce project roots for import paths:\n") + ctx.Err.Printf("Failed to deduce project roots for import paths:\n") for _, fail := range errs { - loggers.Err.Printf("\t%s: %s\n", fail.ex, fail.err.Error()) + ctx.Err.Printf("\t%s: %s\n", fail.ex, fail.err.Error()) } return digestMismatch, hasMissingPkgs, errors.New("address issues with undeducible import paths to get more status information") diff --git a/context.go b/context.go index 0aa96afbd4..c34f3b74d8 100644 --- a/context.go +++ b/context.go @@ -5,6 +5,7 @@ package dep import ( + "fmt" "log" "os" "path/filepath" @@ -17,60 +18,79 @@ import ( "github.com/pkg/errors" ) -// Ctx defines the supporting context of the tool. +/* +Ctx defines the supporting context of the tool. +A properly initialized Ctx has a GOPATH containing WorkingDir, and non-nil Loggers. + + ctx := &dep.Ctx{ + WorkingDir: gopath + "/src/project/root", + GOPATH: gopath, + Out: log.New(os.Stdout, "", 0), + Err: log.New(os.Stderr, "", 0), + } + +SetPaths assists with setting consistent path fields. + + ctx := &dep.Ctx{ + Out: log.New(os.Stdout, "", 0), + Err: log.New(os.Stderr, "", 0), + } + err := ctx.SetPaths(projectRootPath, filepath.SplitList(os.Getenv("GOPATH")) + if err != nil { + // projectRootPath not in any GOPATH + } + +*/ type Ctx struct { - GOPATH string // Selected Go path - GOPATHS []string // Other Go paths - WorkingDir string - *Loggers + WorkingDir string // Where to execute. + GOPATH string // Selected Go path, containing WorkingDir. + GOPATHS []string // Other Go paths. + Out, Err *log.Logger // Required loggers. + Verbose bool // Enables more verbose logging. } -// Loggers holds standard loggers and a verbosity flag. -type Loggers struct { - Out, Err *log.Logger - // Whether verbose logging is enabled. - Verbose bool -} +/* +SetPaths sets the WorkingDir, GOPATH, and GOPATHS fields. +It selects the GOPATH containing WorkingDir, or returns an error if none is found. + + err := ctx.SetPaths(projectRootPath, filepath.SplitList(os.Getenv("GOPATH")) + if err != nil { + // project root not in any GOPATH + } + +The default GOPATH is checked when none are provided. -// NewContext creates a struct with the project's GOPATH. It assumes -// that of your "GOPATH"'s we want the one we are currently in. -func NewContext(wd string, env []string, loggers *Loggers) (*Ctx, error) { - ctx := &Ctx{WorkingDir: wd, Loggers: loggers} + err := ctx.SetPaths(projectRootPath) + if err != nil { + // project root not in default GOPATH, or none available + } - GOPATH := getEnv(env, "GOPATH") - if GOPATH == "" { - GOPATH = defaultGOPATH() +*/ +func (c *Ctx) SetPaths(workingDir string, gopaths ...string) error { + c.WorkingDir = workingDir + if len(gopaths) == 0 { + d := defaultGOPATH() + if d == "" { + return errors.New("no default GOPATH available") + } + gopaths = []string{d} } - for _, gp := range filepath.SplitList(GOPATH) { + wd := filepath.FromSlash(workingDir) + for _, gp := range gopaths { gp = filepath.FromSlash(gp) - if fs.HasFilepathPrefix(filepath.FromSlash(wd), gp) { - ctx.GOPATH = gp + if fs.HasFilepathPrefix(wd, gp) { + c.GOPATH = gp } - ctx.GOPATHS = append(ctx.GOPATHS, gp) + c.GOPATHS = append(c.GOPATHS, gp) } - if ctx.GOPATH == "" { - return nil, errors.New("project not in a GOPATH") + if c.GOPATH == "" { + return fmt.Errorf("%q not in any GOPATH", wd) } - return ctx, nil -} - -// getEnv returns the last instance of an environment variable. -func getEnv(env []string, key string) string { - for i := len(env) - 1; i >= 0; i-- { - v := env[i] - kv := strings.SplitN(v, "=", 2) - if kv[0] == key { - if len(kv) > 1 { - return kv[1] - } - return "" - } - } - return "" + return nil } // defaultGOPATH gets the default GOPATH that was added in 1.8 @@ -142,7 +162,7 @@ func (c *Ctx) LoadProject() (*Project, error) { var warns []error p.Manifest, warns, err = readManifest(mf) for _, warn := range warns { - c.Loggers.Err.Printf("dep: WARNING: %v\n", warn) + c.Err.Printf("dep: WARNING: %v\n", warn) } if err != nil { return nil, errors.Errorf("error while parsing %s: %s", mp, err) diff --git a/context_test.go b/context_test.go index 9709538031..88b117a1d3 100644 --- a/context_test.go +++ b/context_test.go @@ -19,30 +19,44 @@ import ( ) var ( - discardLogger = log.New(ioutil.Discard, "", 0) - discardLoggers = &Loggers{Out: discardLogger, Err: discardLogger} + discardLogger = log.New(ioutil.Discard, "", 0) ) -func TestNewContextNoGOPATH(t *testing.T) { +func TestCtx_SetGOPATH(t *testing.T) { h := test.NewHelper(t) defer h.Cleanup() - h.TempDir("src") - h.Cd(h.Path(".")) - wd, err := os.Getwd() - if err != nil { - t.Fatal("failed to get work directory:", err) - } + wd := h.Path(".") + + t.Run("slash", func(t *testing.T) { + var c Ctx + err := c.SetPaths(wd, filepath.ToSlash(wd)) + if err != nil { + t.Error(err) + } + }) + + t.Run("separator", func(t *testing.T) { + var c Ctx + err := c.SetPaths(wd, filepath.FromSlash(wd)) + if err != nil { + t.Error(err) + } + }) +} - c, err := NewContext(wd, os.Environ(), nil) +func TestCtx_SetGOPATH_empty(t *testing.T) { + h := test.NewHelper(t) + defer h.Cleanup() + + h.TempDir("src") + var c Ctx + + err := c.SetPaths(h.Path("."), "") if err == nil { t.Fatal("error should not have been nil") } - - if c != nil { - t.Fatalf("expected context to be nil, got: %#v", c) - } } func TestSplitAbsoluteProjectRoot(t *testing.T) { @@ -198,7 +212,12 @@ func TestLoadProject(t *testing.T) { for _, testcase := range testcases { start := testcase.start - ctx := &Ctx{GOPATH: tg.Path("."), WorkingDir: tg.Path(start), Loggers: discardLoggers} + ctx := &Ctx{ + GOPATH: tg.Path("."), + WorkingDir: tg.Path(start), + Out: discardLogger, + Err: discardLogger, + } proj, err := ctx.LoadProject() tg.Must(err) @@ -261,7 +280,12 @@ func TestLoadProjectManifestParseError(t *testing.T) { t.Fatal("failed to get working directory", err) } - ctx := &Ctx{GOPATH: tg.Path("."), WorkingDir: wd, Loggers: discardLoggers} + ctx := &Ctx{ + GOPATH: tg.Path("."), + WorkingDir: wd, + Out: discardLogger, + Err: discardLogger, + } _, err = ctx.LoadProject() if err == nil { @@ -287,7 +311,12 @@ func TestLoadProjectLockParseError(t *testing.T) { t.Fatal("failed to get working directory", err) } - ctx := &Ctx{GOPATH: tg.Path("."), WorkingDir: wd, Loggers: discardLoggers} + ctx := &Ctx{ + GOPATH: tg.Path("."), + WorkingDir: wd, + Out: discardLogger, + Err: discardLogger, + } _, err = ctx.LoadProject() if err == nil { diff --git a/doc.go b/doc.go new file mode 100644 index 0000000000..63226ce4c2 --- /dev/null +++ b/doc.go @@ -0,0 +1,6 @@ +// 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 dep is a prototype dependency management library. +package dep diff --git a/project_test.go b/project_test.go index 889282c570..2629a279d9 100644 --- a/project_test.go +++ b/project_test.go @@ -80,30 +80,6 @@ func TestProjectMakeParams(t *testing.T) { } } -func TestSlashedGOPATH(t *testing.T) { - h := test.NewHelper(t) - defer h.Cleanup() - h.TempDir("src") - - wd, err := os.Getwd() - if err != nil { - t.Fatal("failed to get work directory:", err) - } - env := os.Environ() - - h.Setenv("GOPATH", filepath.ToSlash(h.Path("."))) - _, err = NewContext(wd, env, nil) - if err != nil { - t.Fatal(err) - } - - h.Setenv("GOPATH", filepath.FromSlash(h.Path("."))) - _, err = NewContext(wd, env, nil) - if err != nil { - t.Fatal(err) - } -} - func TestBackupVendor(t *testing.T) { h := test.NewHelper(t) defer h.Cleanup() diff --git a/test_project_context_test.go b/test_project_context_test.go index 3ae86ec320..09ffda6111 100644 --- a/test_project_context_test.go +++ b/test_project_context_test.go @@ -37,7 +37,11 @@ func NewTestProjectContext(h *test.Helper, projectName string) *TestProjectConte // Set up a Source Manager var err error - pc.Context = &Ctx{GOPATH: pc.tempDir, Loggers: discardLoggers} + pc.Context = &Ctx{ + GOPATH: pc.tempDir, + Out: discardLogger, + Err: discardLogger, + } pc.SourceManager, err = pc.Context.SourceManager() h.Must(errors.Wrap(err, "Unable to create a SourceManager")) @@ -64,7 +68,7 @@ func (pc *TestProjectContext) Load() { var warns []error m, warns, err = readManifest(mf) for _, warn := range warns { - pc.Context.Loggers.Err.Printf("dep: WARNING: %v\n", warn) + pc.Context.Err.Printf("dep: WARNING: %v\n", warn) } pc.h.Must(errors.Wrapf(err, "Unable to read manifest at %s", mp)) }