forked from golang/dep
/
check.go
240 lines (208 loc) · 7.18 KB
/
check.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
// 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 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:
// NoVerify applies only to these three cases.
if noverify[path] {
continue
}
fallthrough
case verify.NotInTree, verify.NotInLock:
fail = true
if !vendorfail {
vendorfail = true
logger.Println("# vendor is out of sync:")
}
}
}
sort.Strings(ordered)
for _, pr := range ordered {
var nvSuffix string
if noverify[pr] {
nvSuffix = " (CHECK IGNORED: marked noverify in Gopkg.toml)"
}
status := statuses[pr]
switch status {
case verify.NotInTree:
logger.Printf("%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() {
logger.Printf("%s: unused project\n", pr)
} else {
logger.Printf("%s: orphaned file\n", pr)
}
case verify.DigestMismatchInLock:
logger.Printf("%s: hash of vendored tree not equal to digest in Gopkg.lock%s\n", pr, nvSuffix)
case verify.EmptyDigestInLock:
logger.Printf("%s: no digest in Gopkg.lock to compare against hash of vendored tree%s\n", pr, nvSuffix)
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.
logger.Printf("%s: hash algorithm mismatch, want version %v%s\n", pr, verify.HashVersion, nvSuffix)
}
}
}
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())
}