github.com/golang/dep@v0.5.4/cmd/dep/check.go (about) 1 // Copyright 2018 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 "flag" 10 "fmt" 11 "io/ioutil" 12 "log" 13 "os" 14 "path/filepath" 15 "sort" 16 "strings" 17 18 "github.com/golang/dep" 19 "github.com/golang/dep/gps" 20 "github.com/golang/dep/gps/verify" 21 "github.com/pkg/errors" 22 ) 23 24 const checkShortHelp = `Check if imports, Gopkg.toml, and Gopkg.lock are in sync` 25 const checkLongHelp = ` 26 Check determines if your project is in a good state. If problems are found, it 27 prints a description of each issue, then exits 1. Passing -q suppresses output. 28 29 Flags control which specific checks will be run. By default, dep check verifies 30 that Gopkg.lock is in sync with Gopkg.toml and the imports in your project's .go 31 files, and that the vendor directory is in sync with Gopkg.lock. These checks 32 can be disabled with -skip-lock and -skip-vendor, respectively. 33 34 (See https://golang.github.io/dep/docs/ensure-mechanics.html#staying-in-sync for 35 more information on what it means to be "in sync.") 36 37 If your workflow necessitates that you modify the contents of vendor, you can 38 force check to ignore hash mismatches on a per-project basis by naming 39 project roots in Gopkg.toml's "noverify" list. 40 ` 41 42 type checkCommand struct { 43 quiet bool 44 skiplock, skipvendor bool 45 } 46 47 func (cmd *checkCommand) Name() string { return "check" } 48 func (cmd *checkCommand) Args() string { 49 return "[-q] [-skip-lock] [-skip-vendor]" 50 } 51 func (cmd *checkCommand) ShortHelp() string { return checkShortHelp } 52 func (cmd *checkCommand) LongHelp() string { return checkLongHelp } 53 func (cmd *checkCommand) Hidden() bool { return false } 54 55 func (cmd *checkCommand) Register(fs *flag.FlagSet) { 56 fs.BoolVar(&cmd.skiplock, "skip-lock", false, "Skip checking that imports and Gopkg.toml are in sync with Gopkg.lock") 57 fs.BoolVar(&cmd.skipvendor, "skip-vendor", false, "Skip checking that vendor is in sync with Gopkg.lock") 58 fs.BoolVar(&cmd.quiet, "q", false, "Suppress non-error output") 59 } 60 61 func (cmd *checkCommand) Run(ctx *dep.Ctx, args []string) error { 62 logger := ctx.Out 63 if cmd.quiet { 64 logger = log.New(ioutil.Discard, "", 0) 65 } 66 67 p, err := ctx.LoadProject() 68 if err != nil { 69 return err 70 } 71 72 sm, err := ctx.SourceManager() 73 if err != nil { 74 return err 75 } 76 77 sm.UseDefaultSignalHandling() 78 defer sm.Release() 79 80 var fail bool 81 if !cmd.skiplock { 82 if p.Lock == nil { 83 return errors.New("Gopkg.lock does not exist, cannot check it against imports and Gopkg.toml") 84 } 85 86 lsat := verify.LockSatisfiesInputs(p.Lock, p.Manifest, p.RootPackageTree) 87 delta := verify.DiffLocks(p.Lock, p.ChangedLock) 88 sat, changed := lsat.Satisfied(), delta.Changed(verify.PruneOptsChanged|verify.HashVersionChanged) 89 90 if changed || !sat { 91 fail = true 92 logger.Println("# Gopkg.lock is out of sync:") 93 if !sat { 94 logger.Printf("%s\n", sprintLockUnsat(lsat)) 95 } 96 if changed { 97 // Sort, for deterministic output. 98 var ordered []string 99 for pr := range delta.ProjectDeltas { 100 ordered = append(ordered, string(pr)) 101 } 102 sort.Strings(ordered) 103 104 for _, pr := range ordered { 105 lpd := delta.ProjectDeltas[gps.ProjectRoot(pr)] 106 // Only two possible changes right now are prune opts 107 // changing or a missing hash digest (for old Gopkg.lock 108 // files) 109 if lpd.PruneOptsChanged() { 110 // Override what's on the lockdiff with the extra info we have; 111 // this lets us excise PruneNestedVendorDirs and get the real 112 // value from the input param in place. 113 old := lpd.PruneOptsBefore & ^gps.PruneNestedVendorDirs 114 new := lpd.PruneOptsAfter & ^gps.PruneNestedVendorDirs 115 logger.Printf("%s: prune options changed (%s -> %s)\n", pr, old, new) 116 } 117 if lpd.HashVersionWasZero() { 118 logger.Printf("%s: no hash digest in lock\n", pr) 119 } 120 } 121 } 122 } 123 } 124 125 if !cmd.skipvendor { 126 if p.Lock == nil { 127 return errors.New("Gopkg.lock does not exist, cannot check vendor against it") 128 } 129 130 statuses, err := p.VerifyVendor() 131 if err != nil { 132 return errors.Wrap(err, "error while verifying vendor") 133 } 134 135 if fail { 136 logger.Println() 137 } 138 139 noverify := make(map[string]bool) 140 for _, skip := range p.Manifest.NoVerify { 141 noverify[skip] = true 142 } 143 144 var vendorfail, hasnoverify bool 145 // One full pass through, to see if we need to print the header, and to 146 // create an array of names to sort for deterministic output. 147 var ordered []string 148 for path, status := range statuses { 149 ordered = append(ordered, path) 150 151 switch status { 152 case verify.DigestMismatchInLock, verify.HashVersionMismatch, verify.EmptyDigestInLock, verify.NotInLock: 153 if noverify[path] { 154 hasnoverify = true 155 continue 156 } 157 fallthrough 158 case verify.NotInTree: 159 // NoVerify cannot be used to make dep check ignore the absence 160 // of a project entirely. 161 if noverify[path] { 162 delete(noverify, path) 163 } 164 165 fail = true 166 if !vendorfail { 167 vendorfail = true 168 } 169 } 170 } 171 sort.Strings(ordered) 172 173 var vfbuf, novbuf bytes.Buffer 174 var bufptr *bytes.Buffer 175 176 fmt.Fprintf(&vfbuf, "# vendor is out of sync:\n") 177 fmt.Fprintf(&novbuf, "# out of sync, but ignored, due to noverify in Gopkg.toml:\n") 178 179 for _, pr := range ordered { 180 if noverify[pr] { 181 bufptr = &novbuf 182 } else { 183 bufptr = &vfbuf 184 } 185 186 status := statuses[pr] 187 switch status { 188 case verify.NotInTree: 189 fmt.Fprintf(bufptr, "%s: missing from vendor\n", pr) 190 case verify.NotInLock: 191 fi, err := os.Stat(filepath.Join(p.AbsRoot, "vendor", pr)) 192 if err != nil { 193 return errors.Wrap(err, "could not stat file that VerifyVendor claimed existed") 194 } 195 if fi.IsDir() { 196 fmt.Fprintf(bufptr, "%s: unused project\n", pr) 197 } else { 198 fmt.Fprintf(bufptr, "%s: orphaned file\n", pr) 199 } 200 case verify.DigestMismatchInLock: 201 fmt.Fprintf(bufptr, "%s: hash of vendored tree not equal to digest in Gopkg.lock\n", pr) 202 case verify.EmptyDigestInLock: 203 fmt.Fprintf(bufptr, "%s: no digest in Gopkg.lock to compare against hash of vendored tree\n", pr) 204 case verify.HashVersionMismatch: 205 // This will double-print if the hash version is zero, but 206 // that's a rare case that really only occurs before the first 207 // run with a version of dep >=0.5.0, so it's fine. 208 fmt.Fprintf(bufptr, "%s: hash algorithm mismatch, want version %v\n", pr, verify.HashVersion) 209 } 210 } 211 212 if vendorfail { 213 logger.Print(vfbuf.String()) 214 if hasnoverify { 215 logger.Println() 216 } 217 } 218 if hasnoverify { 219 logger.Print(novbuf.String()) 220 } 221 } 222 223 if fail { 224 return silentfail{} 225 } 226 return nil 227 } 228 229 func sprintLockUnsat(lsat verify.LockSatisfaction) string { 230 var buf bytes.Buffer 231 sort.Strings(lsat.MissingImports) 232 for _, missing := range lsat.MissingImports { 233 fmt.Fprintf(&buf, "%s: imported or required, but missing from Gopkg.lock's input-imports\n", missing) 234 } 235 236 sort.Strings(lsat.ExcessImports) 237 for _, excess := range lsat.ExcessImports { 238 fmt.Fprintf(&buf, "%s: in Gopkg.lock's input-imports, but neither imported nor required\n", excess) 239 } 240 241 var ordered []string 242 for pr := range lsat.UnmetOverrides { 243 ordered = append(ordered, string(pr)) 244 } 245 sort.Strings(ordered) 246 for _, pr := range ordered { 247 unmatched := lsat.UnmetOverrides[gps.ProjectRoot(pr)] 248 fmt.Fprintf(&buf, "%s@%s: not allowed by override %s\n", pr, unmatched.V, unmatched.C) 249 } 250 251 ordered = ordered[:0] 252 for pr := range lsat.UnmetConstraints { 253 ordered = append(ordered, string(pr)) 254 } 255 sort.Strings(ordered) 256 for _, pr := range ordered { 257 unmatched := lsat.UnmetConstraints[gps.ProjectRoot(pr)] 258 fmt.Fprintf(&buf, "%s@%s: not allowed by constraint %s\n", pr, unmatched.V, unmatched.C) 259 } 260 return strings.TrimSpace(buf.String()) 261 }