repo_description: Go dependency management tool experiment (deprecated)
repo_topics: dependency-manager golang package-manager toolchain
code: // 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.
  
  package main
  
  import (
  	"bytes"
  	"flag"
  	"fmt"
  	"io/ioutil"
  	"log"
  	"os"
  	"path/filepath"
  	"sort"
  	"strings"
  
  	"github.com/golang/dep"
  	"github.com/golang/dep/gps"
  	"github.com/golang/dep/gps/verify"
  	"github.com/pkg/errors"
  )
  
  const checkShortHelp = `Check if imports, Gopkg.toml, and Gopkg.lock are in sync`
  const checkLongHelp = `
  Check determines if your project is in a good state. If problems are found, it
  prints a description of each issue, then exits 1. Passing -q suppresses output.
  
  Flags control which specific checks will be run. By default, dep check verifies
  that Gopkg.lock is in sync with Gopkg.toml and the imports in your project's .go
  files, and that the vendor directory is in sync with Gopkg.lock. These checks
  can be disabled with -skip-lock and -skip-vendor, respectively.
  
  (See https://golang.github.io/dep/docs/ensure-mechanics.html#staying-in-sync for
  more information on what it means to be "in sync.")
  
  If your workflow necessitates that you modify the contents of vendor, you can
  force check to ignore hash mismatches on a per-project basis by naming
  project roots in Gopkg.toml's "noverify" list.
  `
  
  type checkCommand struct {
  	quiet                bool
  	skiplock, skipvendor bool
  }
  
  func (cmd *checkCommand) Name() string { return "check" }
  func (cmd *checkCommand) Args() string {
  	return "[-q] [-skip-lock] [-skip-vendor]"
  }
  func (cmd *checkCommand) ShortHelp() string { return checkShortHelp }
  func (cmd *checkCommand) LongHelp() string  { return checkLongHelp }
  func (cmd *checkCommand) Hidden() bool      { return false }
  
  func (cmd *checkCommand) Register(fs *flag.FlagSet) {
  	fs.BoolVar(&cmd.skiplock, "skip-lock", false, "Skip checking that imports and Gopkg.toml are in sync with Gopkg.lock")
  	fs.BoolVar(&cmd.skipvendor, "skip-vendor", false, "Skip checking that vendor is in sync with Gopkg.lock")
  	fs.BoolVar(&cmd.quiet, "q", false, "Suppress non-error output")
  }
  
  func (cmd *checkCommand) Run(ctx *dep.Ctx, args []string) error {
  	logger := ctx.Out
  	if cmd.quiet {
  		logger = log.New(ioutil.Discard, "", 0)
  	}
  
  	p, err := ctx.LoadProject()
  	if err != nil {
  		return err
  	}
  
  	sm, err := ctx.SourceManager()
  	if err != nil {
  		return err
  	}
  
  	sm.UseDefaultSignalHandling()
  	defer sm.Release()
  
  	var fail bool
  	if !cmd.skiplock {
  		if p.Lock == nil {
  			return errors.New("Gopkg.lock does not exist, cannot check it against imports and Gopkg.toml")
  		}
  
  		lsat := verify.LockSatisfiesInputs(p.Lock, p.Manifest, p.RootPackageTree)
  		delta := verify.DiffLocks(p.Lock, p.ChangedLock)
  		sat, changed := lsat.Satisfied(), delta.Changed(verify.PruneOptsChanged|verify.HashVersionChanged)
  
  		if changed || !sat {
  			fail = true
  			logger.Println("# Gopkg.lock is out of sync:")
  			if !sat {
  				logger.Printf("%s\n", sprintLockUnsat(lsat))
  			}
  			if changed {
  				// Sort, for deterministic output.
  				var ordered []string
  				for pr := range delta.ProjectDeltas {
  					ordered = append(ordered, string(pr))
  				}
  				sort.Strings(ordered)
  
  				for _, pr := range ordered {
  					lpd := delta.ProjectDeltas[gps.ProjectRoot(pr)]
  					// Only two possible changes right now are prune opts
  					// changing or a missing hash digest (for old Gopkg.lock
  					// files)
  					if lpd.PruneOptsChanged() {
  						// Override what's on the lockdiff with the extra info we have;
  						// this lets us excise PruneNestedVendorDirs and get the real
  						// value from the input param in place.
  						old := lpd.PruneOptsBefore & ^gps.PruneNestedVendorDirs
  						new := lpd.PruneOptsAfter & ^gps.PruneNestedVendorDirs
  						logger.Printf("%s: prune options changed (%s -> %s)\n", pr, old, new)
  					}
  					if lpd.HashVersionWasZero() {
  						logger.Printf("%s: no hash digest in lock\n", pr)
  					}
  				}
  			}
  		}
  	}
  
  	if !cmd.skipvendor {
  		if p.Lock == nil {
  			return errors.New("Gopkg.lock does not exist, cannot check vendor against it")
  		}
  
  		statuses, err := p.VerifyVendor()
  		if err != nil {
  			return errors.Wrap(err, "error while verifying vendor")
  		}
  
  		if fail {
  			logger.Println()
  		}
  
  		noverify := make(map[string]bool)
  		for _, skip := range p.Manifest.NoVerify {
  			noverify[skip] = true
  		}
  
  		var vendorfail, hasnoverify bool
  		// One full pass through, to see if we need to print the header, and to
  		// create an array of names to sort for deterministic output.
  		var ordered []string
  		for path, status := range statuses {
  			ordered = append(ordered, path)
  
  			switch status {
  			case verify.DigestMismatchInLock, verify.HashVersionMismatch, verify.EmptyDigestInLock, verify.NotInLock:
  				if noverify[path] {
  					hasnoverify = true
  					continue
  				}
  				fallthrough
  			case verify.NotInTree:
  				// NoVerify cannot be used to make dep check ignore the absence
  				// of a project entirely.
  				if noverify[path] {
  					delete(noverify, path)
  				}
  
  				fail = true
  				if !vendorfail {
  					vendorfail = true
  				}
  			}
  		}
  		sort.Strings(ordered)
  
  		var vfbuf, novbuf bytes.Buffer
  		var bufptr *bytes.Buffer
  
  		fmt.Fprintf(&vfbuf, "# vendor is out of sync:\n")
  		fmt.Fprintf(&novbuf, "# out of sync, but ignored, due to noverify in Gopkg.toml:\n")
  
  		for _, pr := range ordered {
  			if noverify[pr] {
  				bufptr = &novbuf
  			} else {
  				bufptr = &vfbuf
  			}
  
  			status := statuses[pr]
  			switch status {
  			case verify.NotInTree:
  				fmt.Fprintf(bufptr, "%s: missing from vendor\n", pr)
  			case verify.NotInLock:
  				fi, err := os.Stat(filepath.Join(p.AbsRoot, "vendor", pr))
  				if err != nil {
  					return errors.Wrap(err, "could not stat file that VerifyVendor claimed existed")
  				}
  				if fi.IsDir() {
  					fmt.Fprintf(bufptr, "%s: unused project\n", pr)
  				} else {
  					fmt.Fprintf(bufptr, "%s: orphaned file\n", pr)
  				}
  			case verify.DigestMismatchInLock:
  				fmt.Fprintf(bufptr, "%s: hash of vendored tree not equal to digest in Gopkg.lock\n", pr)
  			case verify.EmptyDigestInLock:
  				fmt.Fprintf(bufptr, "%s: no digest in Gopkg.lock to compare against hash of vendored tree\n", pr)
  			case verify.HashVersionMismatch:
  				// This will double-print if the hash version is zero, but
  				// that's a rare case that really only occurs before the first
  				// run with a version of dep >=0.5.0, so it's fine.
  				fmt.Fprintf(bufptr, "%s: hash algorithm mismatch, want version %v\n", pr, verify.HashVersion)
  			}
  		}
  
  		if vendorfail {
  			logger.Print(vfbuf.String())
  			if hasnoverify {
  				logger.Println()
  			}
  		}
  		if hasnoverify {
  			logger.Print(novbuf.String())
  		}
  	}
  
  	if fail {
  		return silentfail{}
  	}
  	return nil
  }
  
  func sprintLockUnsat(lsat verify.LockSatisfaction) string {
  	var buf bytes.Buffer
  	sort.Strings(lsat.MissingImports)
  	for _, missing := range lsat.MissingImports {
  		fmt.Fprintf(&buf, "%s: imported or required, but missing from Gopkg.lock's input-imports\n", missing)
  	}
  
  	sort.Strings(lsat.ExcessImports)
  	for _, excess := range lsat.ExcessImports {
  		fmt.Fprintf(&buf, "%s: in Gopkg.lock's input-imports, but neither imported nor required\n", excess)
  	}
  
  	var ordered []string
  	for pr := range lsat.UnmetOverrides {
  		ordered = append(ordered, string(pr))
  	}
  	sort.Strings(ordered)
  	for _, pr := range ordered {
  		unmatched := lsat.UnmetOverrides[gps.ProjectRoot(pr)]
  		fmt.Fprintf(&buf, "%s@%s: not allowed by override %s\n", pr, unmatched.V, unmatched.C)
  	}
  
  	ordered = ordered[:0]
  	for pr := range lsat.UnmetConstraints {
  		ordered = append(ordered, string(pr))
  	}
  	sort.Strings(ordered)
  	for _, pr := range ordered {
  		unmatched := lsat.UnmetConstraints[gps.ProjectRoot(pr)]
  		fmt.Fprintf(&buf, "%s@%s: not allowed by constraint %s\n", pr, unmatched.V, unmatched.C)
  	}
  	return strings.TrimSpace(buf.String())
  }
  