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 }