github.com/golang/dep@v0.5.4/internal/feedback/feedback.go (about)

     1  // Copyright 2017 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 feedback
     6  
     7  import (
     8  	"encoding/hex"
     9  	"fmt"
    10  	"log"
    11  
    12  	"github.com/golang/dep/gps"
    13  )
    14  
    15  const (
    16  	// ConsTypeConstraint represents a constraint
    17  	ConsTypeConstraint = "constraint"
    18  
    19  	// ConsTypeHint represents a constraint type hint
    20  	ConsTypeHint = "hint"
    21  
    22  	// DepTypeDirect represents a direct dependency
    23  	DepTypeDirect = "direct dep"
    24  
    25  	// DepTypeTransitive represents a transitive dependency,
    26  	// or a dependency of a dependency
    27  	DepTypeTransitive = "transitive dep"
    28  
    29  	// DepTypeImported represents a dependency imported by an external tool
    30  	DepTypeImported = "imported dep"
    31  )
    32  
    33  // ConstraintFeedback holds project constraint feedback data
    34  type ConstraintFeedback struct {
    35  	Constraint, LockedVersion, Revision, ConstraintType, DependencyType, ProjectPath string
    36  }
    37  
    38  // NewConstraintFeedback builds a feedback entry for a constraint in the manifest.
    39  func NewConstraintFeedback(pc gps.ProjectConstraint, depType string) *ConstraintFeedback {
    40  	cf := &ConstraintFeedback{
    41  		Constraint:     pc.Constraint.String(),
    42  		ProjectPath:    string(pc.Ident.ProjectRoot),
    43  		DependencyType: depType,
    44  	}
    45  
    46  	if _, ok := pc.Constraint.(gps.Revision); ok {
    47  		cf.ConstraintType = ConsTypeHint
    48  	} else {
    49  		cf.ConstraintType = ConsTypeConstraint
    50  	}
    51  
    52  	return cf
    53  }
    54  
    55  // NewLockedProjectFeedback builds a feedback entry for a project in the lock.
    56  func NewLockedProjectFeedback(lp gps.LockedProject, depType string) *ConstraintFeedback {
    57  	cf := &ConstraintFeedback{
    58  		ProjectPath:    string(lp.Ident().ProjectRoot),
    59  		DependencyType: depType,
    60  	}
    61  
    62  	switch vt := lp.Version().(type) {
    63  	case gps.PairedVersion:
    64  		cf.LockedVersion = vt.String()
    65  		cf.Revision = vt.Revision().String()
    66  	case gps.UnpairedVersion: // Logically this should never occur, but handle for completeness sake
    67  		cf.LockedVersion = vt.String()
    68  	case gps.Revision:
    69  		cf.Revision = vt.String()
    70  	}
    71  
    72  	return cf
    73  }
    74  
    75  // LogFeedback logs feedback on changes made to the manifest or lock.
    76  func (cf ConstraintFeedback) LogFeedback(logger *log.Logger) {
    77  	if cf.Constraint != "" {
    78  		logger.Printf("  %v", GetUsingFeedback(cf.Constraint, cf.ConstraintType, cf.DependencyType, cf.ProjectPath))
    79  	}
    80  	if cf.Revision != "" {
    81  		logger.Printf("  %v", GetLockingFeedback(cf.LockedVersion, cf.Revision, cf.DependencyType, cf.ProjectPath))
    82  	}
    83  }
    84  
    85  type brokenImport interface {
    86  	String() string
    87  }
    88  
    89  type modifiedImport struct {
    90  	source, branch, revision, version *StringDiff
    91  	projectPath                       string
    92  }
    93  
    94  func (mi modifiedImport) String() string {
    95  	var pv string
    96  	var pr string
    97  	pp := mi.projectPath
    98  
    99  	var cr string
   100  	var cv string
   101  	cp := ""
   102  
   103  	if mi.revision != nil {
   104  		pr = fmt.Sprintf("(%s)", trimSHA(mi.revision.Previous))
   105  		cr = fmt.Sprintf("(%s)", trimSHA(mi.revision.Current))
   106  	}
   107  
   108  	if mi.version != nil {
   109  		pv = mi.version.Previous
   110  		cv = mi.version.Current
   111  	} else if mi.branch != nil {
   112  		pv = mi.branch.Previous
   113  		cv = mi.branch.Current
   114  	}
   115  
   116  	if mi.source != nil {
   117  		pp = fmt.Sprintf("%s(%s)", mi.projectPath, mi.source.Previous)
   118  		cp = fmt.Sprintf(" for %s(%s)", mi.projectPath, mi.source.Current)
   119  	}
   120  
   121  	// Warning: Unable to preserve imported lock VERSION/BRANCH (REV) for PROJECT(SOURCE). Locking in VERSION/BRANCH (REV) for PROJECT(SOURCE)
   122  	return fmt.Sprintf("%v %s for %s. Locking in %v %s%s", pv, pr, pp, cv, cr, cp)
   123  }
   124  
   125  type removedImport struct {
   126  	source, branch, revision, version *StringDiff
   127  	projectPath                       string
   128  }
   129  
   130  func (ri removedImport) String() string {
   131  	var pr string
   132  	var pv string
   133  	pp := ri.projectPath
   134  
   135  	if ri.revision != nil {
   136  		pr = fmt.Sprintf("(%s)", trimSHA(ri.revision.Previous))
   137  	}
   138  
   139  	if ri.version != nil {
   140  		pv = ri.version.Previous
   141  	} else if ri.branch != nil {
   142  		pv = ri.branch.Previous
   143  	}
   144  
   145  	if ri.source != nil {
   146  		pp = fmt.Sprintf("%s(%s)", ri.projectPath, ri.source.Previous)
   147  	}
   148  
   149  	// Warning: Unable to preserve imported lock VERSION/BRANCH (REV) for PROJECT(SOURCE). Locking in VERSION/BRANCH (REV) for PROJECT(SOURCE)
   150  	return fmt.Sprintf("%v %s for %s. The project was removed from the lock because it is not used.", pv, pr, pp)
   151  }
   152  
   153  // BrokenImportFeedback holds information on changes to locks pre- and post- solving.
   154  type BrokenImportFeedback struct {
   155  	brokenImports []brokenImport
   156  }
   157  
   158  // NewBrokenImportFeedback builds a feedback entry that compares an initially
   159  // imported, unsolved lock to the same lock after it has been solved.
   160  func NewBrokenImportFeedback(ld *LockDiff) *BrokenImportFeedback {
   161  	bi := &BrokenImportFeedback{}
   162  	if ld == nil {
   163  		return bi
   164  	}
   165  
   166  	for _, lpd := range ld.Modify {
   167  		if lpd.Branch == nil && lpd.Revision == nil && lpd.Source == nil && lpd.Version == nil {
   168  			continue
   169  		}
   170  		bi.brokenImports = append(bi.brokenImports, modifiedImport{
   171  			projectPath: string(lpd.Name),
   172  			source:      lpd.Source,
   173  			branch:      lpd.Branch,
   174  			revision:    lpd.Revision,
   175  			version:     lpd.Version,
   176  		})
   177  	}
   178  
   179  	for _, lpd := range ld.Remove {
   180  		bi.brokenImports = append(bi.brokenImports, removedImport{
   181  			projectPath: string(lpd.Name),
   182  			source:      lpd.Source,
   183  			branch:      lpd.Branch,
   184  			revision:    lpd.Revision,
   185  			version:     lpd.Version,
   186  		})
   187  	}
   188  
   189  	return bi
   190  }
   191  
   192  // LogFeedback logs a warning for all changes between the initially imported and post- solve locks
   193  func (b BrokenImportFeedback) LogFeedback(logger *log.Logger) {
   194  	for _, bi := range b.brokenImports {
   195  		logger.Printf("Warning: Unable to preserve imported lock %v\n", bi)
   196  	}
   197  }
   198  
   199  // GetUsingFeedback returns a dependency "using" feedback message. For example:
   200  //
   201  //    Using ^1.0.0 as constraint for direct dep github.com/foo/bar
   202  //    Using 1b8edb3 as hint for direct dep github.com/bar/baz
   203  func GetUsingFeedback(version, consType, depType, projectPath string) string {
   204  	if depType == DepTypeImported {
   205  		return fmt.Sprintf("Using %s as initial %s for %s %s", version, consType, depType, projectPath)
   206  	}
   207  	return fmt.Sprintf("Using %s as %s for %s %s", version, consType, depType, projectPath)
   208  }
   209  
   210  // GetLockingFeedback returns a dependency "locking" feedback message. For
   211  // example:
   212  //
   213  //    Locking in v1.1.4 (bc29b4f) for direct dep github.com/foo/bar
   214  //    Locking in master (436f39d) for transitive dep github.com/baz/qux
   215  func GetLockingFeedback(version, revision, depType, projectPath string) string {
   216  	revision = trimSHA(revision)
   217  
   218  	if depType == DepTypeImported {
   219  		if version == "" {
   220  			version = "*"
   221  		}
   222  		return fmt.Sprintf("Trying %s (%s) as initial lock for %s %s", version, revision, depType, projectPath)
   223  	}
   224  	return fmt.Sprintf("Locking in %s (%s) for %s %s", version, revision, depType, projectPath)
   225  }
   226  
   227  // trimSHA checks if revision is a valid SHA1 digest and trims to 7 characters.
   228  func trimSHA(revision string) string {
   229  	if len(revision) == 40 {
   230  		if _, err := hex.DecodeString(revision); err == nil {
   231  			// Valid SHA1 digest
   232  			revision = revision[0:7]
   233  		}
   234  	}
   235  
   236  	return revision
   237  }