dep: Introduce noverify field to Gopkg.toml

This field allows bypassing of vendor verification on a
project-by-project basis. When set, `dep check` will ignore any hash
verification problems with the directory, and `dep ensure` will not
rewrite the dependency unless it is absent from vendor entirely, pruning
rules change, or solving changes one of the other, higher-order
properties.
This commit is contained in:
sam boyer 2018-07-23 10:07:18 -04:00
Родитель 5cd267faad
Коммит b1e2532ac0
4 изменённых файлов: 102 добавлений и 58 удалений

Просмотреть файл

@ -132,18 +132,32 @@ func (cmd *checkCommand) Run(ctx *dep.Ctx, args []string) error {
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)
if status != verify.NoMismatch {
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)
@ -158,19 +172,26 @@ func (cmd *checkCommand) Run(ctx *dep.Ctx, args []string) error {
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 didn't match digest in Gopkg.lock\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.
logger.Printf("%s: hash algorithm mismatch, want version %v\n", pr, verify.HashVersion)
case verify.DigestMismatchInLock, verify.HashVersionMismatch, verify.EmptyDigestInLock:
// NoVerify applies only to these three cases.
if !noverify[pr] {
switch status {
case verify.DigestMismatchInLock:
logger.Printf("%s: hash of vendored tree not equal to digest in Gopkg.lock\n", pr)
case verify.EmptyDigestInLock:
logger.Printf("%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.
logger.Printf("%s: hash algorithm mismatch, want version %v\n", pr, verify.HashVersion)
}
}
}
}
}
@ -185,12 +206,12 @@ func sprintLockUnsat(lsat verify.LockSatisfaction) string {
var buf bytes.Buffer
sort.Strings(lsat.MissingImports)
for _, missing := range lsat.MissingImports {
fmt.Fprintf(&buf, "%s: missing from input-imports\n", missing)
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 input-imports, but not imported\n", excess)
fmt.Fprintf(&buf, "%s: in Gopkg.lock's input-imports, but neither imported nor required\n", excess)
}
var ordered []string

Просмотреть файл

@ -285,11 +285,7 @@ func (cmd *ensureCommand) runDefault(ctx *dep.Ctx, args []string, p *dep.Project
lock = dep.LockFromSolution(solution, p.Manifest.PruneOptions)
}
status, err := p.VerifyVendor()
if err != nil {
return errors.Wrap(err, "error while verifying vendor directory")
}
dw, err := dep.NewDeltaWriter(p.Lock, lock, status, p.Manifest.PruneOptions, filepath.Join(p.AbsRoot, "vendor"), cmd.vendorBehavior())
dw, err := dep.NewDeltaWriter(p, lock, cmd.vendorBehavior())
if err != nil {
return err
}
@ -365,11 +361,7 @@ func (cmd *ensureCommand) runUpdate(ctx *dep.Ctx, args []string, p *dep.Project,
return handleAllTheFailuresOfTheWorld(err)
}
status, err := p.VerifyVendor()
if err != nil {
return errors.Wrap(err, "error while verifying vendor directory")
}
dw, err := dep.NewDeltaWriter(p.Lock, dep.LockFromSolution(solution, p.Manifest.PruneOptions), status, p.Manifest.PruneOptions, filepath.Join(p.AbsRoot, "vendor"), cmd.vendorBehavior())
dw, err := dep.NewDeltaWriter(p, dep.LockFromSolution(solution, p.Manifest.PruneOptions), cmd.vendorBehavior())
if err != nil {
return err
}
@ -411,11 +403,6 @@ func (cmd *ensureCommand) runAdd(ctx *dep.Ctx, args []string, p *dep.Project, sm
}
}
//exrmap, err := p.GetDirectDependencyNames(sm)
//if err != nil {
//return err
//}
// Note: these flags are only partially used by the latter parts of the
// algorithm; rather, it relies on inference. However, they remain in their
// entirety as future needs may make further use of them, being a handy,
@ -643,11 +630,7 @@ func (cmd *ensureCommand) runAdd(ctx *dep.Ctx, args []string, p *dep.Project, sm
}
sort.Strings(reqlist)
status, err := p.VerifyVendor()
if err != nil {
return errors.Wrap(err, "error while verifying vendor directory")
}
dw, err := dep.NewDeltaWriter(p.Lock, dep.LockFromSolution(solution, p.Manifest.PruneOptions), status, p.Manifest.PruneOptions, filepath.Join(p.AbsRoot, "vendor"), cmd.vendorBehavior())
dw, err := dep.NewDeltaWriter(p, dep.LockFromSolution(solution, p.Manifest.PruneOptions), cmd.vendorBehavior())
if err != nil {
return err
}

Просмотреть файл

@ -28,6 +28,7 @@ var (
errInvalidOverride = errors.Errorf("%q must be a TOML array of tables", "override")
errInvalidRequired = errors.Errorf("%q must be a TOML list of strings", "required")
errInvalidIgnored = errors.Errorf("%q must be a TOML list of strings", "ignored")
errInvalidNoVerify = errors.Errorf("%q must be a TOML list of strings", "noverify")
errInvalidPrune = errors.Errorf("%q must be a TOML table of booleans", "prune")
errInvalidPruneProject = errors.Errorf("%q must be a TOML array of tables", "prune.project")
errInvalidMetadata = errors.New("metadata should be a TOML table")
@ -51,6 +52,8 @@ type Manifest struct {
Ignored []string
Required []string
NoVerify []string
PruneOptions gps.CascadingPruneOptions
}
@ -59,6 +62,7 @@ type rawManifest struct {
Overrides []rawProject `toml:"override,omitempty"`
Ignored []string `toml:"ignored,omitempty"`
Required []string `toml:"required,omitempty"`
NoVerify []string `toml:"noverify,omitempty"`
PruneOptions rawPruneOptions `toml:"prune,omitempty"`
}
@ -85,7 +89,7 @@ const (
pruneOptionNonGo = "non-go"
)
// Constants to represents per-project prune uint8 values.
// Constants representing per-project prune uint8 values.
const (
pvnone uint8 = 0 // No per-project prune value was set in Gopkg.toml.
pvtrue uint8 = 1 // Per-project prune value was explicitly set to true.
@ -182,7 +186,7 @@ func validateManifest(s string) ([]error, error) {
return warns, errInvalidOverride
}
}
case "ignored", "required":
case "ignored", "required", "noverify":
valid := true
if rawList, ok := val.([]interface{}); ok {
// Check element type of the array. TOML doesn't let mixing of types in
@ -201,6 +205,9 @@ func validateManifest(s string) ([]error, error) {
if prop == "required" {
return warns, errInvalidRequired
}
if prop == "noverify" {
return warns, errInvalidNoVerify
}
}
case "prune":
pruneWarns, err := validatePruneOptions(val, true)
@ -368,6 +375,7 @@ func fromRawManifest(raw rawManifest, buf *bytes.Buffer) (*Manifest, error) {
m.Ovr = make(gps.ProjectConstraints, len(raw.Overrides))
m.Ignored = raw.Ignored
m.Required = raw.Required
m.NoVerify = raw.NoVerify
for i := 0; i < len(raw.Constraints); i++ {
name, prj, err := toProject(raw.Constraints[i])
@ -532,6 +540,7 @@ func (m *Manifest) toRaw() rawManifest {
Overrides: make([]rawProject, 0, len(m.Ovr)),
Ignored: m.Ignored,
Required: m.Required,
NoVerify: m.NoVerify,
}
for n, prj := range m.Constraints {

Просмотреть файл

@ -423,10 +423,11 @@ type DeltaWriter struct {
type changeType uint8
const (
solveChanged changeType = iota + 1
hashMismatch
hashMismatch changeType = iota + 1
hashVersionMismatch
hashAbsent
noVerify
solveChanged
pruneOptsChanged
missingFromTree
projectAdded
@ -437,10 +438,10 @@ const (
// directory by writing out only those projects that actually need to be written
// out - they have changed in some way, or they lack the necessary hash
// information to be verified.
func NewDeltaWriter(oldLock, newLock *Lock, status map[string]verify.VendorStatus, prune gps.CascadingPruneOptions, vendorDir string, behavior VendorBehavior) (TreeWriter, error) {
sw := &DeltaWriter{
func NewDeltaWriter(p *Project, newLock *Lock, behavior VendorBehavior) (TreeWriter, error) {
dw := &DeltaWriter{
lock: newLock,
vendorDir: vendorDir,
vendorDir: filepath.Join(p.AbsRoot, "vendor"),
changed: make(map[gps.ProjectRoot]changeType),
behavior: behavior,
}
@ -449,27 +450,35 @@ func NewDeltaWriter(oldLock, newLock *Lock, status map[string]verify.VendorStatu
return nil, errors.New("must provide a non-nil newlock")
}
_, err := os.Stat(vendorDir)
if err != nil && os.IsNotExist(err) {
// Provided dir does not exist, so there's no disk contents to compare
// against. Fall back to the old SafeWriter.
return NewSafeWriter(nil, oldLock, newLock, behavior, prune, status)
status, err := p.VerifyVendor()
if err != nil {
return nil, err
}
sw.lockDiff = verify.DiffLocks(oldLock, newLock)
_, err = os.Stat(dw.vendorDir)
if err != nil {
if os.IsNotExist(err) {
// Provided dir does not exist, so there's no disk contents to compare
// against. Fall back to the old SafeWriter.
return NewSafeWriter(nil, p.Lock, newLock, behavior, p.Manifest.PruneOptions, status)
}
return nil, err
}
for pr, lpd := range sw.lockDiff.ProjectDeltas {
dw.lockDiff = verify.DiffLocks(p.Lock, newLock)
for pr, lpd := range dw.lockDiff.ProjectDeltas {
// Hash changes aren't relevant at this point, as they could be empty
// in the new lock, and therefore a symptom of a solver change.
if lpd.Changed(anyExceptHash) {
if lpd.WasAdded() {
sw.changed[pr] = projectAdded
dw.changed[pr] = projectAdded
} else if lpd.WasRemoved() {
sw.changed[pr] = projectRemoved
dw.changed[pr] = projectRemoved
} else if lpd.PruneOptsChanged() {
sw.changed[pr] = pruneOptsChanged
dw.changed[pr] = pruneOptsChanged
} else {
sw.changed[pr] = solveChanged
dw.changed[pr] = solveChanged
}
}
}
@ -478,23 +487,44 @@ func NewDeltaWriter(oldLock, newLock *Lock, status map[string]verify.VendorStatu
pr := gps.ProjectRoot(spr)
// These cases only matter if there was no change already recorded via
// the differ.
if _, has := sw.changed[pr]; !has {
if _, has := dw.changed[pr]; !has {
switch stat {
case verify.NotInTree:
sw.changed[pr] = missingFromTree
dw.changed[pr] = missingFromTree
case verify.NotInLock:
sw.changed[pr] = projectRemoved
dw.changed[pr] = projectRemoved
case verify.DigestMismatchInLock:
sw.changed[pr] = hashMismatch
dw.changed[pr] = hashMismatch
case verify.HashVersionMismatch:
sw.changed[pr] = hashVersionMismatch
dw.changed[pr] = hashVersionMismatch
case verify.EmptyDigestInLock:
sw.changed[pr] = hashAbsent
dw.changed[pr] = hashAbsent
}
}
}
return sw, nil
// Apply noverify last, as it should only supersede changeTypes with lower
// values. It is NOT applied if no existing change is registered.
for _, spr := range p.Manifest.NoVerify {
pr := gps.ProjectRoot(spr)
// We don't validate this field elsewhere as it can be difficult to know
// at the beginning of a dep ensure command whether or not the noverify
// project actually will exist as part of the Lock by the end of the
// run. So, only apply if it's in the lockdiff, and isn't a removal.
if _, has := dw.lockDiff.ProjectDeltas[pr]; has {
if typ, has := dw.changed[pr]; has && typ < noVerify {
// Avoid writing noverify projects at all for the lower change
// types.
delete(dw.changed, pr)
// Uncomment this if we want to switch to the safer behavior,
// where we ALWAYS write noverify projects.
//dw.changed[pr] = noVerify
}
}
}
return dw, nil
}
// Write executes the planned changes.
@ -659,6 +689,8 @@ func (dw *DeltaWriter) Write(path string, sm gps.SourceManager, examples bool, l
// possible changeType.
func changeExplanation(c changeType, lpd verify.LockedProjectDelta) string {
switch c {
case noVerify:
return "verification is disabled"
case solveChanged:
if lpd.SourceChanged() {
return fmt.Sprintf("source changed (%s -> %s)", lpd.SourceBefore, lpd.SourceAfter)
@ -675,9 +707,8 @@ func changeExplanation(c changeType, lpd verify.LockedProjectDelta) string {
return fmt.Sprintf("packages changed (%v added, %v removed)", la, lr)
} else if la > 0 {
return fmt.Sprintf("packages changed (%v added)", la)
} else {
return fmt.Sprintf("packages changed (%v removed)", lr)
}
return fmt.Sprintf("packages changed (%v removed)", lr)
}
case pruneOptsChanged:
// Override what's on the lockdiff with the extra info we have;