github.com/golang/dep@v0.5.4/cmd/dep/status.go (about)

     1  // Copyright 2016 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package main
     6  
     7  import (
     8  	"bytes"
     9  	"context"
    10  	"encoding/json"
    11  	"flag"
    12  	"fmt"
    13  	"io"
    14  	"io/ioutil"
    15  	"log"
    16  	"os"
    17  	"sort"
    18  	"strings"
    19  	"sync"
    20  	"text/tabwriter"
    21  	"text/template"
    22  
    23  	"github.com/golang/dep"
    24  	"github.com/golang/dep/gps"
    25  	"github.com/golang/dep/gps/paths"
    26  	"github.com/golang/dep/gps/verify"
    27  	"github.com/pkg/errors"
    28  )
    29  
    30  const availableTemplateVariables = "ProjectRoot, Constraint, Version, Revision, Latest, and PackageCount."
    31  const availableDefaultTemplateVariables = `.Projects[]{
    32  	    .ProjectRoot,.Source,.Constraint,.PackageCount,.Packages[],
    33  		.PruneOpts,.Digest,.Locked{.Branch,.Revision,.Version},
    34  		.Latest{.Revision,.Version}
    35  	},
    36  	.Metadata{
    37  	    .AnalyzerName,.AnalyzerVersion,.InputImports,.SolverName,
    38  	    .SolverVersion
    39  	}`
    40  
    41  const statusShortHelp = `Report the status of the project's dependencies`
    42  const statusLongHelp = `
    43  With no arguments, print the status of each dependency of the project.
    44  
    45    PROJECT     Import path
    46    CONSTRAINT  Version constraint, from the manifest
    47    VERSION     Version chosen, from the lock
    48    REVISION    VCS revision of the chosen version
    49    LATEST      Latest VCS revision available
    50    PKGS USED   Number of packages from this project that are actually used
    51  
    52  You may use the -f flag to create a custom format for the output of the
    53  dep status command. The available fields you can utilize are as follows:
    54  ` + availableTemplateVariables + `
    55  
    56  Status returns exit code zero if all dependencies are in a "good state".
    57  `
    58  
    59  const statusExamples = `
    60  dep status
    61  
    62  	Displays a table of the various dependencies in the project along with
    63  	their properties such as the constraints they are bound by and the
    64  	revision they are at.
    65  
    66  dep status -detail
    67  
    68  	Displays a detailed table of the dependencies in the project including
    69  	the value of any source rules used and full list of packages used from
    70  	each project (instead of simply a count). Text wrapping may make this
    71  	output hard to read.
    72  
    73  dep status -f='{{if eq .Constraint "master"}}{{.ProjectRoot}} {{end}}'
    74  
    75  	Displays the list of package names constrained on the master branch.
    76  	The -f flag allows you to use Go templates along with it's various
    77  	constructs for formating output data. Available flags are as follows:
    78  	` + availableTemplateVariables + `
    79  
    80  dep status -detail -f='{{range $i, $p := .Projects}}{{if ne .Source "" -}}
    81  	    {{- if $i}},{{end}}{{$p.ProjectRoot}}:{{$p.Source}}{{end}}{{end}}'
    82  
    83  	Displays the package name and source for each package with a source
    84  	rule defined, with a comma between each name-source pair.
    85  
    86  	When used with -detail, the -f flag applies the supplied Go templates
    87  	to the full output document, instead of to packages one at a time.
    88  	Available flags are as follows: ` + availableDefaultTemplateVariables + `
    89  
    90  dep status -json
    91  
    92  	Displays the dependency information in JSON format as a list of
    93  	project objects. Each project object contains keys which correspond
    94  	to the table column names from the standard 'dep status' command.
    95  
    96  Linux:   dep status -dot | dot -T png | display
    97  MacOS:   dep status -dot | dot -T png | open -f -a /Applications/Preview.app
    98  Windows: dep status -dot | dot -T png -o status.png; start status.png
    99  
   100  	Generates a visual representation of the dependency tree using GraphViz.
   101  	(Note: in order for this example to work you must first have graphviz
   102  	installed on your system)
   103  
   104  `
   105  
   106  const (
   107  	shortRev uint8 = iota
   108  	longRev
   109  )
   110  
   111  var (
   112  	errFailedUpdate        = errors.New("failed to fetch updates")
   113  	errFailedListPkg       = errors.New("failed to list packages")
   114  	errMultipleFailures    = errors.New("multiple sources of failure")
   115  	errInputDigestMismatch = errors.New("input-digest mismatch")
   116  )
   117  
   118  func (cmd *statusCommand) Name() string      { return "status" }
   119  func (cmd *statusCommand) Args() string      { return "[package...]" }
   120  func (cmd *statusCommand) ShortHelp() string { return statusShortHelp }
   121  func (cmd *statusCommand) LongHelp() string  { return statusLongHelp }
   122  func (cmd *statusCommand) Hidden() bool      { return false }
   123  
   124  func (cmd *statusCommand) Register(fs *flag.FlagSet) {
   125  	fs.BoolVar(&cmd.examples, "examples", false, "print detailed usage examples")
   126  	fs.BoolVar(&cmd.json, "json", false, "output in JSON format")
   127  	fs.StringVar(&cmd.template, "f", "", "output in text/template format")
   128  	fs.BoolVar(&cmd.lock, "lock", false, "output in the lock file format (assumes -detail)")
   129  	fs.BoolVar(&cmd.dot, "dot", false, "output the dependency graph in GraphViz format")
   130  	fs.BoolVar(&cmd.old, "old", false, "only show out-of-date dependencies")
   131  	fs.BoolVar(&cmd.missing, "missing", false, "only show missing dependencies")
   132  	fs.StringVar(&cmd.outFilePath, "out", "", "path to a file to which to write the output. Blank value will be ignored")
   133  	fs.BoolVar(&cmd.detail, "detail", false, "include more detail in the chosen format")
   134  }
   135  
   136  type statusCommand struct {
   137  	examples    bool
   138  	json        bool
   139  	template    string
   140  	lock        bool
   141  	output      string
   142  	dot         bool
   143  	old         bool
   144  	missing     bool
   145  	outFilePath string
   146  	detail      bool
   147  }
   148  
   149  type outputter interface {
   150  	BasicHeader() error
   151  	BasicLine(*BasicStatus) error
   152  	BasicFooter() error
   153  	DetailHeader(*dep.SolveMeta) error
   154  	DetailLine(*DetailStatus) error
   155  	DetailFooter(*dep.SolveMeta) error
   156  	MissingHeader() error
   157  	MissingLine(*MissingStatus) error
   158  	MissingFooter() error
   159  }
   160  
   161  // Only a subset of the outputters should be able to output old statuses.
   162  type oldOutputter interface {
   163  	OldHeader() error
   164  	OldLine(*OldStatus) error
   165  	OldFooter() error
   166  }
   167  
   168  type tableOutput struct{ w *tabwriter.Writer }
   169  
   170  func (out *tableOutput) BasicHeader() error {
   171  	_, err := fmt.Fprintf(out.w, "PROJECT\tCONSTRAINT\tVERSION\tREVISION\tLATEST\tPKGS USED\n")
   172  	return err
   173  }
   174  
   175  func (out *tableOutput) BasicFooter() error {
   176  	return out.w.Flush()
   177  }
   178  
   179  func (out *tableOutput) BasicLine(bs *BasicStatus) error {
   180  	_, err := fmt.Fprintf(out.w,
   181  		"%s\t%s\t%s\t%s\t%s\t%d\t\n",
   182  		bs.ProjectRoot,
   183  		bs.getConsolidatedConstraint(),
   184  		formatVersion(bs.Version),
   185  		formatVersion(bs.Revision),
   186  		bs.getConsolidatedLatest(shortRev),
   187  		bs.PackageCount,
   188  	)
   189  	return err
   190  }
   191  
   192  func (out *tableOutput) DetailHeader(metadata *dep.SolveMeta) error {
   193  	_, err := fmt.Fprintf(out.w, "PROJECT\tSOURCE\tCONSTRAINT\tVERSION\tREVISION\tLATEST\tPKGS USED\n")
   194  	return err
   195  }
   196  
   197  func (out *tableOutput) DetailFooter(metadata *dep.SolveMeta) error {
   198  	return out.BasicFooter()
   199  }
   200  
   201  func (out *tableOutput) DetailLine(ds *DetailStatus) error {
   202  	_, err := fmt.Fprintf(out.w,
   203  		"%s\t%s\t%s\t%s\t%s\t%s\t[%s]\t\n",
   204  		ds.ProjectRoot,
   205  		ds.Source,
   206  		ds.getConsolidatedConstraint(),
   207  		formatVersion(ds.Version),
   208  		formatVersion(ds.Revision),
   209  		ds.getConsolidatedLatest(shortRev),
   210  		strings.Join(ds.Packages, ", "),
   211  	)
   212  	return err
   213  }
   214  
   215  func (out *tableOutput) MissingHeader() error {
   216  	_, err := fmt.Fprintln(out.w, "PROJECT\tMISSING PACKAGES")
   217  	return err
   218  }
   219  
   220  func (out *tableOutput) MissingLine(ms *MissingStatus) error {
   221  	_, err := fmt.Fprintf(out.w,
   222  		"%s\t%s\t\n",
   223  		ms.ProjectRoot,
   224  		ms.MissingPackages,
   225  	)
   226  	return err
   227  }
   228  
   229  func (out *tableOutput) MissingFooter() error {
   230  	return out.w.Flush()
   231  }
   232  
   233  func (out *tableOutput) OldHeader() error {
   234  	_, err := fmt.Fprintf(out.w, "PROJECT\tCONSTRAINT\tREVISION\tLATEST\n")
   235  	return err
   236  }
   237  
   238  func (out *tableOutput) OldLine(os *OldStatus) error {
   239  	_, err := fmt.Fprintf(out.w,
   240  		"%s\t%s\t%s\t%s\t\n",
   241  		os.ProjectRoot,
   242  		os.getConsolidatedConstraint(),
   243  		formatVersion(os.Revision),
   244  		os.getConsolidatedLatest(shortRev),
   245  	)
   246  	return err
   247  }
   248  
   249  func (out *tableOutput) OldFooter() error {
   250  	return out.w.Flush()
   251  }
   252  
   253  type jsonOutput struct {
   254  	w       io.Writer
   255  	basic   []*rawStatus
   256  	detail  []rawDetailProject
   257  	missing []*MissingStatus
   258  	old     []*rawOldStatus
   259  }
   260  
   261  func (out *jsonOutput) BasicHeader() error {
   262  	out.basic = []*rawStatus{}
   263  	return nil
   264  }
   265  
   266  func (out *jsonOutput) BasicFooter() error {
   267  	return json.NewEncoder(out.w).Encode(out.basic)
   268  }
   269  
   270  func (out *jsonOutput) BasicLine(bs *BasicStatus) error {
   271  	out.basic = append(out.basic, bs.marshalJSON())
   272  	return nil
   273  }
   274  
   275  func (out *jsonOutput) DetailHeader(metadata *dep.SolveMeta) error {
   276  	out.detail = []rawDetailProject{}
   277  	return nil
   278  }
   279  
   280  func (out *jsonOutput) DetailFooter(metadata *dep.SolveMeta) error {
   281  	doc := rawDetail{
   282  		Projects: out.detail,
   283  		Metadata: newRawMetadata(metadata),
   284  	}
   285  
   286  	return json.NewEncoder(out.w).Encode(doc)
   287  }
   288  
   289  func (out *jsonOutput) DetailLine(ds *DetailStatus) error {
   290  	out.detail = append(out.detail, *ds.marshalJSON())
   291  	return nil
   292  }
   293  
   294  func (out *jsonOutput) MissingHeader() error {
   295  	out.missing = []*MissingStatus{}
   296  	return nil
   297  }
   298  
   299  func (out *jsonOutput) MissingLine(ms *MissingStatus) error {
   300  	out.missing = append(out.missing, ms)
   301  	return nil
   302  }
   303  
   304  func (out *jsonOutput) MissingFooter() error {
   305  	return json.NewEncoder(out.w).Encode(out.missing)
   306  }
   307  
   308  func (out *jsonOutput) OldHeader() error {
   309  	out.old = []*rawOldStatus{}
   310  	return nil
   311  }
   312  
   313  func (out *jsonOutput) OldLine(os *OldStatus) error {
   314  	out.old = append(out.old, os.marshalJSON())
   315  	return nil
   316  }
   317  
   318  func (out *jsonOutput) OldFooter() error {
   319  	return json.NewEncoder(out.w).Encode(out.old)
   320  }
   321  
   322  type dotOutput struct {
   323  	w io.Writer
   324  	o string
   325  	g *graphviz
   326  	p *dep.Project
   327  }
   328  
   329  func (out *dotOutput) BasicHeader() error {
   330  	out.g = new(graphviz).New()
   331  
   332  	ptree := out.p.RootPackageTree
   333  	// TODO(sdboyer) should be true, true, false, out.p.Manifest.IgnoredPackages()
   334  	prm, _ := ptree.ToReachMap(true, false, false, nil)
   335  
   336  	out.g.createNode(string(out.p.ImportRoot), "", prm.FlattenFn(paths.IsStandardImportPath))
   337  
   338  	return nil
   339  }
   340  
   341  func (out *dotOutput) BasicFooter() error {
   342  	gvo := out.g.output("")
   343  	_, err := fmt.Fprint(out.w, gvo.String())
   344  	return err
   345  }
   346  
   347  func (out *dotOutput) BasicLine(bs *BasicStatus) error {
   348  	out.g.createNode(bs.ProjectRoot, bs.getConsolidatedVersion(), bs.Children)
   349  	return nil
   350  }
   351  
   352  func (out *dotOutput) DetailHeader(metadata *dep.SolveMeta) error {
   353  	return out.BasicHeader()
   354  }
   355  
   356  func (out *dotOutput) DetailFooter(metadata *dep.SolveMeta) error {
   357  	return out.BasicFooter()
   358  }
   359  
   360  func (out *dotOutput) DetailLine(ds *DetailStatus) error {
   361  	return out.BasicLine(&ds.BasicStatus)
   362  }
   363  
   364  func (out *dotOutput) MissingHeader() error                { return nil }
   365  func (out *dotOutput) MissingLine(ms *MissingStatus) error { return nil }
   366  func (out *dotOutput) MissingFooter() error                { return nil }
   367  
   368  type templateOutput struct {
   369  	w      io.Writer
   370  	tmpl   *template.Template
   371  	detail []rawDetailProject
   372  }
   373  
   374  func (out *templateOutput) BasicHeader() error { return nil }
   375  func (out *templateOutput) BasicFooter() error { return nil }
   376  func (out *templateOutput) BasicLine(bs *BasicStatus) error {
   377  	data := rawStatus{
   378  		ProjectRoot:  bs.ProjectRoot,
   379  		Constraint:   bs.getConsolidatedConstraint(),
   380  		Version:      bs.getConsolidatedVersion(),
   381  		Revision:     bs.Revision.String(),
   382  		Latest:       bs.getConsolidatedLatest(shortRev),
   383  		PackageCount: bs.PackageCount,
   384  	}
   385  	return out.tmpl.Execute(out.w, data)
   386  }
   387  
   388  func (out *templateOutput) DetailHeader(metadata *dep.SolveMeta) error {
   389  	out.detail = []rawDetailProject{}
   390  
   391  	return nil
   392  }
   393  
   394  func (out *templateOutput) DetailFooter(metadata *dep.SolveMeta) error {
   395  	raw := rawDetail{
   396  		Projects: out.detail,
   397  		Metadata: newRawMetadata(metadata),
   398  	}
   399  
   400  	return out.tmpl.Execute(out.w, raw)
   401  }
   402  
   403  func (out *templateOutput) DetailLine(ds *DetailStatus) error {
   404  	data := rawDetailProject{
   405  		ProjectRoot:  ds.ProjectRoot,
   406  		Constraint:   ds.getConsolidatedConstraint(),
   407  		Locked:       formatDetailVersion(ds.Version, ds.Revision),
   408  		Latest:       formatDetailLatestVersion(ds.Latest, ds.hasError),
   409  		PruneOpts:    ds.getPruneOpts(),
   410  		Digest:       ds.Digest.String(),
   411  		PackageCount: ds.PackageCount,
   412  		Source:       ds.Source,
   413  		Packages:     ds.Packages,
   414  	}
   415  
   416  	out.detail = append(out.detail, data)
   417  
   418  	return nil
   419  }
   420  
   421  func (out *templateOutput) OldHeader() error { return nil }
   422  func (out *templateOutput) OldFooter() error { return nil }
   423  func (out *templateOutput) OldLine(os *OldStatus) error {
   424  	return out.tmpl.Execute(out.w, os)
   425  }
   426  
   427  func (out *templateOutput) MissingHeader() error { return nil }
   428  func (out *templateOutput) MissingFooter() error { return nil }
   429  func (out *templateOutput) MissingLine(ms *MissingStatus) error {
   430  	return out.tmpl.Execute(out.w, ms)
   431  }
   432  
   433  func (cmd *statusCommand) Run(ctx *dep.Ctx, args []string) error {
   434  	if cmd.examples {
   435  		ctx.Err.Println(strings.TrimSpace(statusExamples))
   436  		return nil
   437  	}
   438  
   439  	if err := cmd.validateFlags(); err != nil {
   440  		return err
   441  	}
   442  
   443  	p, err := ctx.LoadProject()
   444  	if err != nil {
   445  		return err
   446  	}
   447  
   448  	sm, err := ctx.SourceManager()
   449  	if err != nil {
   450  		return err
   451  	}
   452  	sm.UseDefaultSignalHandling()
   453  	defer sm.Release()
   454  
   455  	if err := dep.ValidateProjectRoots(ctx, p.Manifest, sm); err != nil {
   456  		return err
   457  	}
   458  
   459  	var buf bytes.Buffer
   460  	var out outputter
   461  	switch {
   462  	case cmd.missing:
   463  		return errors.Errorf("not implemented")
   464  	case cmd.json:
   465  		out = &jsonOutput{
   466  			w: &buf,
   467  		}
   468  	case cmd.dot:
   469  		out = &dotOutput{
   470  			p: p,
   471  			o: cmd.output,
   472  			w: &buf,
   473  		}
   474  	case cmd.template != "":
   475  		tmpl, err := parseStatusTemplate(cmd.template)
   476  		if err != nil {
   477  			return err
   478  		}
   479  		out = &templateOutput{
   480  			w:    &buf,
   481  			tmpl: tmpl,
   482  		}
   483  	case cmd.lock:
   484  		tmpl, err := parseStatusTemplate(statusLockTemplate)
   485  		if err != nil {
   486  			return err
   487  		}
   488  		out = &templateOutput{
   489  			w:    &buf,
   490  			tmpl: tmpl,
   491  		}
   492  	default:
   493  		out = &tableOutput{
   494  			w: tabwriter.NewWriter(&buf, 0, 4, 2, ' ', 0),
   495  		}
   496  	}
   497  
   498  	// Check if the lock file exists.
   499  	if p.Lock == nil {
   500  		return errors.Errorf("no Gopkg.lock found. Run `dep ensure` to generate lock file")
   501  	}
   502  
   503  	if cmd.old {
   504  		if _, ok := out.(oldOutputter); !ok {
   505  			return errors.Errorf("invalid output format used")
   506  		}
   507  		err = cmd.runOld(ctx, out.(oldOutputter), p, sm)
   508  		ctx.Out.Print(buf.String())
   509  		return err
   510  	}
   511  
   512  	_, errCount, runerr := cmd.runStatusAll(ctx, out, p, sm)
   513  	if runerr != nil {
   514  		switch runerr {
   515  		case errFailedUpdate:
   516  			// Print the help when in non-verbose mode
   517  			if !ctx.Verbose {
   518  				ctx.Out.Printf("The status of %d projects are unknown due to errors. Rerun with `-v` flag to see details.\n", errCount)
   519  			}
   520  		case errInputDigestMismatch:
   521  			ctx.Err.Printf("Gopkg.lock is out of sync with imports and/or Gopkg.toml. Run `dep check` for details.\n")
   522  		default:
   523  			return runerr
   524  		}
   525  	}
   526  
   527  	if cmd.outFilePath == "" {
   528  		// Print the status output
   529  		ctx.Out.Print(buf.String())
   530  	} else {
   531  		file, err := os.Create(cmd.outFilePath)
   532  		if err != nil {
   533  			return fmt.Errorf("error creating output file: %v", err)
   534  		}
   535  
   536  		defer file.Close()
   537  		if _, err := io.Copy(file, bytes.NewReader(buf.Bytes())); err != nil {
   538  			return fmt.Errorf("error writing output file: %v", err)
   539  		}
   540  	}
   541  
   542  	return runerr
   543  }
   544  
   545  func (cmd *statusCommand) validateFlags() error {
   546  	// Operating mode flags.
   547  	var opModes []string
   548  
   549  	if cmd.old {
   550  		opModes = append(opModes, "-old")
   551  	}
   552  
   553  	if cmd.missing {
   554  		opModes = append(opModes, "-missing")
   555  	}
   556  
   557  	if cmd.detail {
   558  		opModes = append(opModes, "-detail")
   559  	}
   560  
   561  	// Check if any other flags are passed with -dot.
   562  	if cmd.dot {
   563  		if cmd.template != "" {
   564  			return errors.New("cannot pass template string with -dot")
   565  		}
   566  
   567  		if cmd.json {
   568  			return errors.New("cannot pass multiple output format flags")
   569  		}
   570  
   571  		if len(opModes) > 0 {
   572  			return errors.New("-dot generates dependency graph; cannot pass other flags")
   573  		}
   574  	}
   575  
   576  	if cmd.lock {
   577  		if cmd.template != "" {
   578  			return errors.New("cannot pass template string with -lock")
   579  		}
   580  
   581  		if !cmd.detail {
   582  			cmd.detail = true
   583  		}
   584  	}
   585  
   586  	if len(opModes) > 1 {
   587  		// List the flags because which flags are for operation mode might not
   588  		// be apparent to the users.
   589  		return errors.Wrapf(errors.New("cannot pass multiple operating mode flags"), "%v", opModes)
   590  	}
   591  
   592  	return nil
   593  }
   594  
   595  // OldStatus contains information about all the out of date packages in a project.
   596  type OldStatus struct {
   597  	ProjectRoot string
   598  	Constraint  gps.Constraint
   599  	Revision    gps.Revision
   600  	Latest      gps.Version
   601  }
   602  
   603  type rawOldStatus struct {
   604  	ProjectRoot, Constraint, Revision, Latest string
   605  }
   606  
   607  func (os OldStatus) getConsolidatedConstraint() string {
   608  	var constraint string
   609  	if os.Constraint != nil {
   610  		if v, ok := os.Constraint.(gps.Version); ok {
   611  			constraint = formatVersion(v)
   612  		} else {
   613  			constraint = os.Constraint.String()
   614  		}
   615  	}
   616  	return constraint
   617  }
   618  
   619  func (os OldStatus) getConsolidatedLatest(revSize uint8) string {
   620  	latest := ""
   621  	if os.Latest != nil {
   622  		switch revSize {
   623  		case shortRev:
   624  			latest = formatVersion(os.Latest)
   625  		case longRev:
   626  			latest = os.Latest.String()
   627  		}
   628  	}
   629  	return latest
   630  }
   631  
   632  func (os OldStatus) marshalJSON() *rawOldStatus {
   633  	return &rawOldStatus{
   634  		ProjectRoot: os.ProjectRoot,
   635  		Constraint:  os.getConsolidatedConstraint(),
   636  		Revision:    string(os.Revision),
   637  		Latest:      os.getConsolidatedLatest(longRev),
   638  	}
   639  }
   640  
   641  func (cmd *statusCommand) runOld(ctx *dep.Ctx, out oldOutputter, p *dep.Project, sm gps.SourceManager) error {
   642  	// While the network churns on ListVersions() requests, statically analyze
   643  	// code from the current project.
   644  	ptree := p.RootPackageTree
   645  
   646  	// Set up a solver in order to check the InputHash.
   647  	params := gps.SolveParameters{
   648  		ProjectAnalyzer: dep.Analyzer{},
   649  		RootDir:         p.AbsRoot,
   650  		RootPackageTree: ptree,
   651  		Manifest:        p.Manifest,
   652  		// Locks aren't a part of the input hash check, so we can omit it.
   653  	}
   654  
   655  	logger := ctx.Err
   656  	if ctx.Verbose {
   657  		params.TraceLogger = ctx.Err
   658  	} else {
   659  		logger = log.New(ioutil.Discard, "", 0)
   660  	}
   661  
   662  	// Check update for all the projects.
   663  	params.ChangeAll = true
   664  
   665  	solver, err := gps.Prepare(params, sm)
   666  	if err != nil {
   667  		return errors.Wrap(err, "fastpath solver prepare")
   668  	}
   669  
   670  	logger.Println("Solving dependency graph to determine which dependencies can be updated.")
   671  	solution, err := solver.Solve(context.TODO())
   672  	if err != nil {
   673  		return errors.Wrap(err, "runOld")
   674  	}
   675  
   676  	var oldStatuses []OldStatus
   677  	solutionProjects := solution.Projects()
   678  
   679  	for _, proj := range p.Lock.Projects() {
   680  		for _, sProj := range solutionProjects {
   681  			// Look for the same project in solution and lock.
   682  			if sProj.Ident().ProjectRoot != proj.Ident().ProjectRoot {
   683  				continue
   684  			}
   685  
   686  			// If revisions are not the same then it is old and we should display it.
   687  			latestRev, _, _ := gps.VersionComponentStrings(sProj.Version())
   688  			atRev, _, _ := gps.VersionComponentStrings(proj.Version())
   689  			if atRev == latestRev {
   690  				continue
   691  			}
   692  
   693  			var constraint gps.Constraint
   694  			// Getting Constraint.
   695  			if pp, has := p.Manifest.Ovr[proj.Ident().ProjectRoot]; has && pp.Constraint != nil {
   696  				// manifest has override for project.
   697  				constraint = pp.Constraint
   698  			} else if pp, has := p.Manifest.Constraints[proj.Ident().ProjectRoot]; has && pp.Constraint != nil {
   699  				// manifest has normal constraint.
   700  				constraint = pp.Constraint
   701  			} else {
   702  				// No constraint exists. No need to worry about displaying it.
   703  				continue
   704  			}
   705  
   706  			// Generate the old status data and append it.
   707  			os := OldStatus{
   708  				ProjectRoot: proj.Ident().String(),
   709  				Revision:    gps.Revision(atRev),
   710  				Latest:      gps.Revision(latestRev),
   711  				Constraint:  constraint,
   712  			}
   713  			oldStatuses = append(oldStatuses, os)
   714  		}
   715  	}
   716  
   717  	out.OldHeader()
   718  	for _, ostat := range oldStatuses {
   719  		out.OldLine(&ostat)
   720  	}
   721  	out.OldFooter()
   722  
   723  	return nil
   724  }
   725  
   726  type rawStatus struct {
   727  	ProjectRoot  string
   728  	Constraint   string
   729  	Version      string
   730  	Revision     string
   731  	Latest       string
   732  	PackageCount int
   733  }
   734  
   735  // rawDetail is is additional information used for the status when the
   736  // -detail flag is specified
   737  type rawDetail struct {
   738  	Projects []rawDetailProject
   739  	Metadata rawDetailMetadata
   740  }
   741  
   742  type rawDetailVersion struct {
   743  	Revision string `json:"Revision,omitempty"`
   744  	Version  string `json:"Version,omitempty"`
   745  	Branch   string `json:"Branch,omitempty"`
   746  }
   747  
   748  type rawDetailProject struct {
   749  	ProjectRoot  string
   750  	Packages     []string
   751  	Locked       rawDetailVersion
   752  	Latest       rawDetailVersion
   753  	PruneOpts    string
   754  	Digest       string
   755  	Source       string `json:"Source,omitempty"`
   756  	Constraint   string
   757  	PackageCount int
   758  }
   759  
   760  type rawDetailMetadata struct {
   761  	AnalyzerName    string
   762  	AnalyzerVersion int
   763  	InputsDigest    string // deprecated
   764  	InputImports    []string
   765  	SolverName      string
   766  	SolverVersion   int
   767  }
   768  
   769  func newRawMetadata(metadata *dep.SolveMeta) rawDetailMetadata {
   770  	if metadata == nil {
   771  		return rawDetailMetadata{}
   772  	}
   773  
   774  	return rawDetailMetadata{
   775  		AnalyzerName:    metadata.AnalyzerName,
   776  		AnalyzerVersion: metadata.AnalyzerVersion,
   777  		InputImports:    metadata.InputImports,
   778  		SolverName:      metadata.SolverName,
   779  		SolverVersion:   metadata.SolverVersion,
   780  	}
   781  }
   782  
   783  // BasicStatus contains all the information reported about a single dependency
   784  // in the summary/list status output mode.
   785  type BasicStatus struct {
   786  	ProjectRoot  string
   787  	Children     []string
   788  	Constraint   gps.Constraint
   789  	Version      gps.UnpairedVersion
   790  	Revision     gps.Revision
   791  	Latest       gps.Version
   792  	PackageCount int
   793  	hasOverride  bool
   794  	hasError     bool
   795  }
   796  
   797  // DetailStatus contains all information reported about a single dependency
   798  // in the detailed status output mode. The included information matches the
   799  // information included about a a project in a lock file.
   800  type DetailStatus struct {
   801  	BasicStatus
   802  	Packages  []string
   803  	Source    string
   804  	PruneOpts gps.PruneOptions
   805  	Digest    verify.VersionedDigest
   806  }
   807  
   808  func (bs *BasicStatus) getConsolidatedConstraint() string {
   809  	var constraint string
   810  	if bs.Constraint != nil {
   811  		if v, ok := bs.Constraint.(gps.Version); ok {
   812  			constraint = formatVersion(v)
   813  		} else {
   814  			constraint = bs.Constraint.String()
   815  		}
   816  	}
   817  
   818  	if bs.hasOverride {
   819  		constraint += " (override)"
   820  	}
   821  
   822  	return constraint
   823  }
   824  
   825  func (bs *BasicStatus) getConsolidatedVersion() string {
   826  	version := formatVersion(bs.Revision)
   827  	if bs.Version != nil {
   828  		version = formatVersion(bs.Version)
   829  	}
   830  	return version
   831  }
   832  
   833  func (bs *BasicStatus) getConsolidatedLatest(revSize uint8) string {
   834  	latest := ""
   835  	if bs.Latest != nil {
   836  		switch revSize {
   837  		case shortRev:
   838  			latest = formatVersion(bs.Latest)
   839  		case longRev:
   840  			latest = bs.Latest.String()
   841  		}
   842  	}
   843  
   844  	if bs.hasError {
   845  		latest += "unknown"
   846  	}
   847  
   848  	return latest
   849  }
   850  
   851  func (ds *DetailStatus) getPruneOpts() string {
   852  	return (ds.PruneOpts & ^gps.PruneNestedVendorDirs).String()
   853  }
   854  
   855  func (bs *BasicStatus) marshalJSON() *rawStatus {
   856  	return &rawStatus{
   857  		ProjectRoot:  bs.ProjectRoot,
   858  		Constraint:   bs.getConsolidatedConstraint(),
   859  		Version:      formatVersion(bs.Version),
   860  		Revision:     string(bs.Revision),
   861  		Latest:       bs.getConsolidatedLatest(longRev),
   862  		PackageCount: bs.PackageCount,
   863  	}
   864  }
   865  
   866  func (ds *DetailStatus) marshalJSON() *rawDetailProject {
   867  	rawStatus := ds.BasicStatus.marshalJSON()
   868  
   869  	return &rawDetailProject{
   870  		ProjectRoot:  rawStatus.ProjectRoot,
   871  		Constraint:   rawStatus.Constraint,
   872  		Locked:       formatDetailVersion(ds.Version, ds.Revision),
   873  		Latest:       formatDetailLatestVersion(ds.Latest, ds.hasError),
   874  		PruneOpts:    ds.getPruneOpts(),
   875  		Digest:       ds.Digest.String(),
   876  		Source:       ds.Source,
   877  		Packages:     ds.Packages,
   878  		PackageCount: ds.PackageCount,
   879  	}
   880  }
   881  
   882  // MissingStatus contains information about all the missing packages in a project.
   883  type MissingStatus struct {
   884  	ProjectRoot     string
   885  	MissingPackages []string
   886  }
   887  
   888  func (cmd *statusCommand) runStatusAll(ctx *dep.Ctx, out outputter, p *dep.Project, sm gps.SourceManager) (hasMissingPkgs bool, errCount int, err error) {
   889  	// While the network churns on ListVersions() requests, statically analyze
   890  	// code from the current project.
   891  	ptree := p.RootPackageTree
   892  
   893  	// Set up a solver in order to check the InputHash.
   894  	params := gps.SolveParameters{
   895  		ProjectAnalyzer: dep.Analyzer{},
   896  		RootDir:         p.AbsRoot,
   897  		RootPackageTree: ptree,
   898  		Manifest:        p.Manifest,
   899  		// Locks aren't a part of the input hash check, so we can omit it.
   900  	}
   901  
   902  	logger := ctx.Err
   903  	if ctx.Verbose {
   904  		params.TraceLogger = ctx.Err
   905  	} else {
   906  		logger = log.New(ioutil.Discard, "", 0)
   907  	}
   908  
   909  	if err := ctx.ValidateParams(sm, params); err != nil {
   910  		return false, 0, err
   911  	}
   912  
   913  	// Errors while collecting constraints should not fail the whole status run.
   914  	// It should count the error and tell the user about incomplete results.
   915  	cm, ccerrs := collectConstraints(ctx, p, sm)
   916  	if len(ccerrs) > 0 {
   917  		errCount += len(ccerrs)
   918  	}
   919  
   920  	// Get the project list and sort it so that the printed output users see is
   921  	// deterministically ordered. (This may be superfluous if the lock is always
   922  	// written in alpha order, but it doesn't hurt to double down.)
   923  	slp := p.Lock.Projects()
   924  	sort.Slice(slp, func(i, j int) bool {
   925  		return slp[i].Ident().Less(slp[j].Ident())
   926  	})
   927  	slcp := p.ChangedLock.Projects()
   928  	sort.Slice(slcp, func(i, j int) bool {
   929  		return slcp[i].Ident().Less(slcp[j].Ident())
   930  	})
   931  
   932  	lsat := verify.LockSatisfiesInputs(p.Lock, p.Manifest, params.RootPackageTree)
   933  	if lsat.Satisfied() {
   934  		// If the lock satisfies the inputs, we're guaranteed (barring manual
   935  		// meddling, about which we can do nothing) that the lock is a
   936  		// transitively complete picture of all deps. That eliminates the need
   937  		// for some checks.
   938  
   939  		logger.Println("Checking upstream projects:")
   940  
   941  		// DetailStatus channel to collect all the DetailStatus.
   942  		dsCh := make(chan *DetailStatus, len(slp))
   943  
   944  		// Error channels to collect different errors.
   945  		errListPkgCh := make(chan error, len(slp))
   946  		errListVerCh := make(chan error, len(slp))
   947  
   948  		var wg sync.WaitGroup
   949  
   950  		for i, proj := range slp {
   951  			wg.Add(1)
   952  			logger.Printf("(%d/%d) %s\n", i+1, len(slp), proj.Ident().ProjectRoot)
   953  
   954  			go func(proj verify.VerifiableProject) {
   955  				bs := BasicStatus{
   956  					ProjectRoot:  string(proj.Ident().ProjectRoot),
   957  					PackageCount: len(proj.Packages()),
   958  				}
   959  
   960  				// Get children only for specific outputers
   961  				// in order to avoid slower status process.
   962  				switch out.(type) {
   963  				case *dotOutput:
   964  					ptr, err := sm.ListPackages(proj.Ident(), proj.Version())
   965  
   966  					if err != nil {
   967  						bs.hasError = true
   968  						errListPkgCh <- err
   969  					}
   970  
   971  					prm, _ := ptr.ToReachMap(true, true, false, p.Manifest.IgnoredPackages())
   972  					bs.Children = prm.FlattenFn(paths.IsStandardImportPath)
   973  				}
   974  
   975  				// Split apart the version from the lock into its constituent parts.
   976  				switch tv := proj.Version().(type) {
   977  				case gps.UnpairedVersion:
   978  					bs.Version = tv
   979  				case gps.Revision:
   980  					bs.Revision = tv
   981  				case gps.PairedVersion:
   982  					bs.Version = tv.Unpair()
   983  					bs.Revision = tv.Revision()
   984  				}
   985  
   986  				// Check if the manifest has an override for this project. If so,
   987  				// set that as the constraint.
   988  				if pp, has := p.Manifest.Ovr[proj.Ident().ProjectRoot]; has && pp.Constraint != nil {
   989  					bs.hasOverride = true
   990  					bs.Constraint = pp.Constraint
   991  				} else if pp, has := p.Manifest.Constraints[proj.Ident().ProjectRoot]; has && pp.Constraint != nil {
   992  					// If the manifest has a constraint then set that as the constraint.
   993  					bs.Constraint = pp.Constraint
   994  				} else {
   995  					bs.Constraint = gps.Any()
   996  					for _, c := range cm[bs.ProjectRoot] {
   997  						bs.Constraint = c.Constraint.Intersect(bs.Constraint)
   998  					}
   999  				}
  1000  
  1001  				// Only if we have a non-rev and non-plain version do/can we display
  1002  				// anything wrt the version's updateability.
  1003  				if bs.Version != nil && bs.Version.Type() != gps.IsVersion {
  1004  					c, has := p.Manifest.Constraints[proj.Ident().ProjectRoot]
  1005  					if !has {
  1006  						// Get constraint for locked project
  1007  						for _, lockedP := range p.Lock.P {
  1008  							if lockedP.Ident().ProjectRoot == proj.Ident().ProjectRoot {
  1009  								// Use the unpaired version as the constraint for checking updates.
  1010  								c.Constraint = bs.Version
  1011  							}
  1012  						}
  1013  					}
  1014  					// TODO: This constraint is only the constraint imposed by the
  1015  					// current project, not by any transitive deps. As a result,
  1016  					// transitive project deps will always show "any" here.
  1017  					bs.Constraint = c.Constraint
  1018  
  1019  					vl, err := sm.ListVersions(proj.Ident())
  1020  					if err == nil {
  1021  						gps.SortPairedForUpgrade(vl)
  1022  
  1023  						for _, v := range vl {
  1024  							// Because we've sorted the version list for
  1025  							// upgrade, the first version we encounter that
  1026  							// matches our constraint will be what we want.
  1027  							if c.Constraint.Matches(v) {
  1028  								// Latest should be of the same type as the Version.
  1029  								if bs.Version.Type() == gps.IsSemver {
  1030  									bs.Latest = v
  1031  								} else {
  1032  									bs.Latest = v.Revision()
  1033  								}
  1034  								break
  1035  							}
  1036  						}
  1037  					} else {
  1038  						// Failed to fetch version list (could happen due to
  1039  						// network issue).
  1040  						bs.hasError = true
  1041  						errListVerCh <- err
  1042  					}
  1043  				}
  1044  
  1045  				ds := DetailStatus{
  1046  					BasicStatus: bs,
  1047  				}
  1048  
  1049  				if cmd.detail {
  1050  					ds.Source = proj.Ident().Source
  1051  					ds.Packages = proj.Packages()
  1052  					ds.PruneOpts = proj.PruneOpts
  1053  					ds.Digest = proj.Digest
  1054  				}
  1055  
  1056  				dsCh <- &ds
  1057  
  1058  				wg.Done()
  1059  			}(proj.(verify.VerifiableProject))
  1060  		}
  1061  
  1062  		wg.Wait()
  1063  		close(dsCh)
  1064  		close(errListPkgCh)
  1065  		close(errListVerCh)
  1066  
  1067  		// Newline after printing the status progress output.
  1068  		logger.Println()
  1069  
  1070  		// List Packages errors. This would happen only for dot output.
  1071  		if len(errListPkgCh) > 0 {
  1072  			err = errFailedListPkg
  1073  			if ctx.Verbose {
  1074  				for err := range errListPkgCh {
  1075  					ctx.Err.Println(err.Error())
  1076  				}
  1077  				ctx.Err.Println()
  1078  			}
  1079  		}
  1080  
  1081  		// List Version errors.
  1082  		if len(errListVerCh) > 0 {
  1083  			if err == nil {
  1084  				err = errFailedUpdate
  1085  			} else {
  1086  				err = errMultipleFailures
  1087  			}
  1088  
  1089  			// Count ListVersions error because we get partial results when
  1090  			// this happens.
  1091  			errCount += len(errListVerCh)
  1092  			if ctx.Verbose {
  1093  				for err := range errListVerCh {
  1094  					ctx.Err.Println(err.Error())
  1095  				}
  1096  				ctx.Err.Println()
  1097  			}
  1098  		}
  1099  
  1100  		if cmd.detail {
  1101  			// A map of ProjectRoot and *DetailStatus. This is used in maintain the
  1102  			// order of DetailStatus in output by collecting all the DetailStatus and
  1103  			// then using them in order.
  1104  			dsMap := make(map[string]*DetailStatus)
  1105  			for ds := range dsCh {
  1106  				dsMap[ds.ProjectRoot] = ds
  1107  			}
  1108  
  1109  			if err := detailOutputAll(out, slp, dsMap, &p.Lock.SolveMeta); err != nil {
  1110  				return false, 0, err
  1111  			}
  1112  		} else {
  1113  			// A map of ProjectRoot and *BasicStatus. This is used in maintain the
  1114  			// order of BasicStatus in output by collecting all the BasicStatus and
  1115  			// then using them in order.
  1116  			bsMap := make(map[string]*BasicStatus)
  1117  			for bs := range dsCh {
  1118  				bsMap[bs.ProjectRoot] = &bs.BasicStatus
  1119  			}
  1120  
  1121  			if err := basicOutputAll(out, slp, bsMap); err != nil {
  1122  				return false, 0, err
  1123  			}
  1124  		}
  1125  
  1126  		return false, errCount, err
  1127  	}
  1128  
  1129  	rm, _ := ptree.ToReachMap(true, true, false, p.Manifest.IgnoredPackages())
  1130  
  1131  	external := rm.FlattenFn(paths.IsStandardImportPath)
  1132  	roots := make(map[gps.ProjectRoot][]string, len(external))
  1133  
  1134  	type fail struct {
  1135  		ex  string
  1136  		err error
  1137  	}
  1138  	var errs []fail
  1139  	for _, e := range external {
  1140  		root, err := sm.DeduceProjectRoot(e)
  1141  		if err != nil {
  1142  			errs = append(errs, fail{
  1143  				ex:  e,
  1144  				err: err,
  1145  			})
  1146  			continue
  1147  		}
  1148  
  1149  		roots[root] = append(roots[root], e)
  1150  	}
  1151  
  1152  	if len(errs) != 0 {
  1153  		// TODO this is just a fix quick so staticcheck doesn't complain.
  1154  		// Visually reconciling failure to deduce project roots with the rest of
  1155  		// the mismatch output is a larger problem.
  1156  		ctx.Err.Printf("Failed to deduce project roots for import paths:\n")
  1157  		for _, fail := range errs {
  1158  			ctx.Err.Printf("\t%s: %s\n", fail.ex, fail.err.Error())
  1159  		}
  1160  
  1161  		return false, 0, errors.New("address issues with undeducible import paths to get more status information")
  1162  	}
  1163  
  1164  	if err = out.MissingHeader(); err != nil {
  1165  		return false, 0, err
  1166  	}
  1167  
  1168  outer:
  1169  	for root, pkgs := range roots {
  1170  		// TODO also handle the case where the project is present, but there
  1171  		// are items missing from just the package list
  1172  		for _, lp := range slp {
  1173  			if lp.Ident().ProjectRoot == root {
  1174  				continue outer
  1175  			}
  1176  		}
  1177  
  1178  		hasMissingPkgs = true
  1179  		err := out.MissingLine(&MissingStatus{ProjectRoot: string(root), MissingPackages: pkgs})
  1180  		if err != nil {
  1181  			return false, 0, err
  1182  		}
  1183  	}
  1184  	if err = out.MissingFooter(); err != nil {
  1185  		return false, 0, err
  1186  	}
  1187  
  1188  	// We are here because of an input-digest mismatch. Return error.
  1189  	return hasMissingPkgs, 0, errInputDigestMismatch
  1190  }
  1191  
  1192  // basicOutputAll takes an outputter, a project list, and a map of ProjectRoot to *BasicStatus and
  1193  // uses the outputter to output basic header, body lines (in the order of the project list), and
  1194  // footer based on the project information.
  1195  func basicOutputAll(out outputter, slp []gps.LockedProject, bsMap map[string]*BasicStatus) (err error) {
  1196  	if err := out.BasicHeader(); err != nil {
  1197  		return err
  1198  	}
  1199  
  1200  	// Use the collected BasicStatus in outputter.
  1201  	for _, proj := range slp {
  1202  		if err := out.BasicLine(bsMap[string(proj.Ident().ProjectRoot)]); err != nil {
  1203  			return err
  1204  		}
  1205  	}
  1206  
  1207  	return out.BasicFooter()
  1208  }
  1209  
  1210  // detailOutputAll takes an outputter, a project list, and a map of ProjectRoot to *DetailStatus and
  1211  // uses the outputter to output detailed header, body lines (in the order of the project list), and
  1212  // footer based on the project information.
  1213  func detailOutputAll(out outputter, slp []gps.LockedProject, dsMap map[string]*DetailStatus, metadata *dep.SolveMeta) (err error) {
  1214  	if err := out.DetailHeader(metadata); err != nil {
  1215  		return err
  1216  	}
  1217  
  1218  	// Use the collected BasicStatus in outputter.
  1219  	for _, proj := range slp {
  1220  		if err := out.DetailLine(dsMap[string(proj.Ident().ProjectRoot)]); err != nil {
  1221  			return err
  1222  		}
  1223  	}
  1224  
  1225  	return out.DetailFooter(metadata)
  1226  }
  1227  
  1228  func formatVersion(v gps.Version) string {
  1229  	if v == nil {
  1230  		return ""
  1231  	}
  1232  	switch v.Type() {
  1233  	case gps.IsBranch:
  1234  		return "branch " + v.String()
  1235  	case gps.IsRevision:
  1236  		r := v.String()
  1237  		if len(r) > 7 {
  1238  			r = r[:7]
  1239  		}
  1240  		return r
  1241  	}
  1242  	return v.String()
  1243  }
  1244  
  1245  func formatDetailVersion(v gps.Version, r gps.Revision) rawDetailVersion {
  1246  	if v == nil {
  1247  		return rawDetailVersion{
  1248  			Revision: r.String(),
  1249  		}
  1250  	}
  1251  	switch v.Type() {
  1252  	case gps.IsBranch:
  1253  		return rawDetailVersion{
  1254  			Branch:   v.String(),
  1255  			Revision: r.String(),
  1256  		}
  1257  	case gps.IsRevision:
  1258  		return rawDetailVersion{
  1259  			Revision: v.String(),
  1260  		}
  1261  	}
  1262  
  1263  	return rawDetailVersion{
  1264  		Version:  v.String(),
  1265  		Revision: r.String(),
  1266  	}
  1267  }
  1268  
  1269  func formatDetailLatestVersion(v gps.Version, hasError bool) rawDetailVersion {
  1270  	if hasError {
  1271  		return rawDetailVersion{
  1272  			Revision: "unknown",
  1273  		}
  1274  	}
  1275  
  1276  	return formatDetailVersion(v, "")
  1277  }
  1278  
  1279  // projectConstraint stores ProjectRoot and Constraint for that project.
  1280  type projectConstraint struct {
  1281  	Project    gps.ProjectRoot
  1282  	Constraint gps.Constraint
  1283  }
  1284  
  1285  // constraintsCollection is a map of ProjectRoot(dependency) and a collection of
  1286  // projectConstraint for the dependencies. This can be used to find constraints
  1287  // on a dependency and the projects that apply those constraints.
  1288  type constraintsCollection map[string][]projectConstraint
  1289  
  1290  // collectConstraints collects constraints declared by all the dependencies and
  1291  // constraints from the root project. It returns constraintsCollection and
  1292  // a slice of errors encountered while collecting the constraints, if any.
  1293  func collectConstraints(ctx *dep.Ctx, p *dep.Project, sm gps.SourceManager) (constraintsCollection, []error) {
  1294  	logger := ctx.Err
  1295  	if !ctx.Verbose {
  1296  		logger = log.New(ioutil.Discard, "", 0)
  1297  	}
  1298  
  1299  	logger.Println("Collecting project constraints:")
  1300  
  1301  	var mutex sync.Mutex
  1302  	constraintCollection := make(constraintsCollection)
  1303  
  1304  	// Collect the complete set of direct project dependencies, incorporating
  1305  	// requireds and ignores appropriately.
  1306  	directDeps, err := p.GetDirectDependencyNames(sm)
  1307  	if err != nil {
  1308  		// Return empty collection, not nil, if we fail here.
  1309  		return constraintCollection, []error{errors.Wrap(err, "failed to get direct dependencies")}
  1310  	}
  1311  
  1312  	// Create a root analyzer.
  1313  	rootAnalyzer := newRootAnalyzer(true, ctx, directDeps, sm)
  1314  
  1315  	lp := p.Lock.Projects()
  1316  
  1317  	// Channel for receiving all the errors.
  1318  	errCh := make(chan error, len(lp))
  1319  
  1320  	var wg sync.WaitGroup
  1321  
  1322  	// Iterate through the locked projects and collect constraints of all the projects.
  1323  	for i, proj := range lp {
  1324  		wg.Add(1)
  1325  		logger.Printf("(%d/%d) %s\n", i+1, len(lp), proj.Ident().ProjectRoot)
  1326  
  1327  		go func(proj gps.LockedProject) {
  1328  			defer wg.Done()
  1329  
  1330  			manifest, _, err := sm.GetManifestAndLock(proj.Ident(), proj.Version(), rootAnalyzer)
  1331  			if err != nil {
  1332  				errCh <- errors.Wrap(err, "error getting manifest and lock")
  1333  				return
  1334  			}
  1335  
  1336  			// Get project constraints.
  1337  			pc := manifest.DependencyConstraints()
  1338  
  1339  			// Obtain a lock for constraintCollection.
  1340  			mutex.Lock()
  1341  			defer mutex.Unlock()
  1342  			// Iterate through the project constraints to get individual dependency
  1343  			// project and constraint values.
  1344  			for pr, pp := range pc {
  1345  				// Check if the project constraint is imported in the root project
  1346  				if _, ok := directDeps[pr]; !ok {
  1347  					continue
  1348  				}
  1349  
  1350  				tempCC := append(
  1351  					constraintCollection[string(pr)],
  1352  					projectConstraint{proj.Ident().ProjectRoot, pp.Constraint},
  1353  				)
  1354  
  1355  				// Sort the inner projectConstraint slice by Project string.
  1356  				// Required for consistent returned value.
  1357  				sort.Sort(byProject(tempCC))
  1358  				constraintCollection[string(pr)] = tempCC
  1359  			}
  1360  		}(proj)
  1361  	}
  1362  
  1363  	wg.Wait()
  1364  	close(errCh)
  1365  
  1366  	var errs []error
  1367  	if len(errCh) > 0 {
  1368  		for e := range errCh {
  1369  			errs = append(errs, e)
  1370  			logger.Println(e.Error())
  1371  		}
  1372  	}
  1373  
  1374  	// Incorporate constraints set in the manifest of the root project.
  1375  	if p.Manifest != nil {
  1376  
  1377  		// Iterate through constraints in the manifest, append if it is a
  1378  		// direct dependency
  1379  		for pr, pp := range p.Manifest.Constraints {
  1380  			if _, ok := directDeps[pr]; !ok {
  1381  				continue
  1382  			}
  1383  
  1384  			// Mark constraints coming from the manifest as "root"
  1385  			tempCC := append(
  1386  				constraintCollection[string(pr)],
  1387  				projectConstraint{"root", pp.Constraint},
  1388  			)
  1389  
  1390  			// Sort the inner projectConstraint slice by Project string.
  1391  			// Required for consistent returned value.
  1392  			sort.Sort(byProject(tempCC))
  1393  			constraintCollection[string(pr)] = tempCC
  1394  		}
  1395  	}
  1396  
  1397  	return constraintCollection, errs
  1398  }
  1399  
  1400  type byProject []projectConstraint
  1401  
  1402  func (p byProject) Len() int           { return len(p) }
  1403  func (p byProject) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }
  1404  func (p byProject) Less(i, j int) bool { return p[i].Project < p[j].Project }
  1405  
  1406  func parseStatusTemplate(format string) (*template.Template, error) {
  1407  	tmpl, err := template.New("status").Funcs(template.FuncMap{
  1408  		"dec": func(i int) int {
  1409  			return i - 1
  1410  		},
  1411  		"tomlStrSplit": tomlStrSplit,
  1412  		"tomlStrSplit2": func(strlist []string, level int) string {
  1413  			// Hardcode to two spaces.
  1414  			inbracket, inp := strings.Repeat("  ", level), strings.Repeat("  ", level+1)
  1415  			switch len(strlist) {
  1416  			case 0:
  1417  				return "[]"
  1418  			case 1:
  1419  				return fmt.Sprintf("[\"%s\"]", strlist[0])
  1420  			default:
  1421  				var buf bytes.Buffer
  1422  
  1423  				fmt.Fprintf(&buf, "[\n")
  1424  				for _, str := range strlist {
  1425  					fmt.Fprintf(&buf, "%s\"%s\",\n", inp, str)
  1426  				}
  1427  				fmt.Fprintf(&buf, "%s]", inbracket)
  1428  
  1429  				return buf.String()
  1430  			}
  1431  		},
  1432  	}).Parse(format)
  1433  
  1434  	return tmpl, err
  1435  }
  1436  
  1437  func tomlStrSplit(strlist []string) string {
  1438  	switch len(strlist) {
  1439  	case 0:
  1440  		return "[]"
  1441  	case 1:
  1442  		return fmt.Sprintf("[\"%s\"]", strlist[0])
  1443  	default:
  1444  		var buf bytes.Buffer
  1445  
  1446  		// Hardcode to two spaces.
  1447  		fmt.Fprintf(&buf, "[\n")
  1448  		for _, str := range strlist {
  1449  			fmt.Fprintf(&buf, "    \"%s\",\n", str)
  1450  		}
  1451  		fmt.Fprintf(&buf, "  ]")
  1452  
  1453  		return buf.String()
  1454  	}
  1455  }
  1456  
  1457  const statusLockTemplate = `# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
  1458  
  1459  
  1460  {{range $p := .Projects}}[[projects]]
  1461    {{- if $p.Locked.Branch}}
  1462    branch = "{{$p.Locked.Branch}}"
  1463    {{- end}}
  1464    digest = "{{$p.Digest}}"
  1465    name = "{{$p.ProjectRoot}}"
  1466    packages = {{(tomlStrSplit $p.Packages)}}
  1467    pruneopts = "{{$p.PruneOpts}}"
  1468    revision = "{{$p.Locked.Revision}}"
  1469    {{- if $p.Source}}
  1470    source = "{{$p.Source}}"
  1471    {{- end}}
  1472    {{- if $p.Locked.Version}}
  1473    version = "{{$p.Locked.Version}}"
  1474    {{- end}}
  1475  
  1476  {{end}}[solve-meta]
  1477    analyzer-name = "{{.Metadata.AnalyzerName}}"
  1478    analyzer-version = {{.Metadata.AnalyzerVersion}}
  1479    input-imports = {{(tomlStrSplit .Metadata.InputImports)}}
  1480    solver-name = "{{.Metadata.SolverName}}"
  1481    solver-version = {{.Metadata.SolverVersion}}
  1482  `