github.com/ilyakaznacheev/glide@v0.13.2/cfg/lock.go (about) 1 package cfg 2 3 import ( 4 "crypto/sha256" 5 "fmt" 6 "io/ioutil" 7 "sort" 8 "strings" 9 "time" 10 11 "gopkg.in/yaml.v2" 12 ) 13 14 // Lockfile represents a glide.lock file. 15 type Lockfile struct { 16 Hash string `yaml:"hash"` 17 Updated time.Time `yaml:"updated"` 18 Imports Locks `yaml:"imports"` 19 DevImports Locks `yaml:"testImports"` 20 } 21 22 // LockfileFromYaml returns an instance of Lockfile from YAML 23 func LockfileFromYaml(yml []byte) (*Lockfile, error) { 24 lock := &Lockfile{} 25 err := yaml.Unmarshal([]byte(yml), &lock) 26 return lock, err 27 } 28 29 // Marshal converts a Config instance to YAML 30 func (lf *Lockfile) Marshal() ([]byte, error) { 31 yml, err := yaml.Marshal(&lf) 32 if err != nil { 33 return []byte{}, err 34 } 35 return yml, nil 36 } 37 38 // MarshalYAML is a hook for gopkg.in/yaml.v2. 39 // It sorts import subpackages lexicographically for reproducibility. 40 func (lf *Lockfile) MarshalYAML() (interface{}, error) { 41 for _, imp := range lf.Imports { 42 sort.Strings(imp.Subpackages) 43 } 44 45 // Ensure elements on testImport don't already exist on import. 46 var newDI Locks 47 var found bool 48 for _, imp := range lf.DevImports { 49 found = false 50 for i := 0; i < len(lf.Imports); i++ { 51 if lf.Imports[i].Name == imp.Name { 52 found = true 53 if lf.Imports[i].Version != imp.Version { 54 return lf, fmt.Errorf("Generating lock YAML produced conflicting versions of %s. import (%s), testImport (%s)", imp.Name, lf.Imports[i].Version, imp.Version) 55 } 56 } 57 } 58 59 if !found { 60 newDI = append(newDI, imp) 61 } 62 } 63 lf.DevImports = newDI 64 65 for _, imp := range lf.DevImports { 66 sort.Strings(imp.Subpackages) 67 } 68 return lf, nil 69 } 70 71 // WriteFile writes a Glide lock file. 72 // 73 // This is a convenience function that marshals the YAML and then writes it to 74 // the given file. If the file exists, it will be clobbered. 75 func (lf *Lockfile) WriteFile(lockpath string) error { 76 o, err := lf.Marshal() 77 if err != nil { 78 return err 79 } 80 return ioutil.WriteFile(lockpath, o, 0666) 81 } 82 83 // Clone returns a clone of Lockfile 84 func (lf *Lockfile) Clone() *Lockfile { 85 n := &Lockfile{} 86 n.Hash = lf.Hash 87 n.Updated = lf.Updated 88 n.Imports = lf.Imports.Clone() 89 n.DevImports = lf.DevImports.Clone() 90 91 return n 92 } 93 94 // Fingerprint returns a hash of the contents minus the date. This allows for 95 // two lockfiles to be compared irrespective of their updated times. 96 func (lf *Lockfile) Fingerprint() ([32]byte, error) { 97 c := lf.Clone() 98 c.Updated = time.Time{} // Set the time to be the nil equivalent 99 sort.Sort(c.Imports) 100 sort.Sort(c.DevImports) 101 yml, err := c.Marshal() 102 if err != nil { 103 return [32]byte{}, err 104 } 105 106 return sha256.Sum256(yml), nil 107 } 108 109 // ReadLockFile loads the contents of a glide.lock file. 110 func ReadLockFile(lockpath string) (*Lockfile, error) { 111 yml, err := ioutil.ReadFile(lockpath) 112 if err != nil { 113 return nil, err 114 } 115 lock, err := LockfileFromYaml(yml) 116 if err != nil { 117 return nil, err 118 } 119 return lock, nil 120 } 121 122 // Locks is a slice of locked dependencies. 123 type Locks []*Lock 124 125 // Clone returns a Clone of Locks. 126 func (l Locks) Clone() Locks { 127 n := make(Locks, 0, len(l)) 128 for _, v := range l { 129 n = append(n, v.Clone()) 130 } 131 return n 132 } 133 134 // Len returns the length of the Locks. This is needed for sorting with 135 // the sort package. 136 func (l Locks) Len() int { 137 return len(l) 138 } 139 140 // Less is needed for the sort interface. It compares two locks based on 141 // their name. 142 func (l Locks) Less(i, j int) bool { 143 144 // Names are normalized to lowercase because case affects sorting order. For 145 // example, Masterminds comes before kylelemons. Making them lowercase 146 // causes kylelemons to come first which is what is expected. 147 return strings.ToLower(l[i].Name) < strings.ToLower(l[j].Name) 148 } 149 150 // Swap is needed for the sort interface. It swaps the position of two 151 // locks. 152 func (l Locks) Swap(i, j int) { 153 l[i], l[j] = l[j], l[i] 154 } 155 156 // Lock represents an individual locked dependency. 157 type Lock struct { 158 Name string `yaml:"name"` 159 Version string `yaml:"version"` 160 Repository string `yaml:"repo,omitempty"` 161 VcsType string `yaml:"vcs,omitempty"` 162 Subpackages []string `yaml:"subpackages,omitempty"` 163 Arch []string `yaml:"arch,omitempty"` 164 Os []string `yaml:"os,omitempty"` 165 } 166 167 // Clone creates a clone of a Lock. 168 func (l *Lock) Clone() *Lock { 169 return &Lock{ 170 Name: l.Name, 171 Version: l.Version, 172 Repository: l.Repository, 173 VcsType: l.VcsType, 174 Subpackages: l.Subpackages, 175 Arch: l.Arch, 176 Os: l.Os, 177 } 178 } 179 180 // LockFromDependency converts a Dependency to a Lock 181 func LockFromDependency(dep *Dependency) *Lock { 182 return &Lock{ 183 Name: dep.Name, 184 Version: dep.Pin, 185 Repository: dep.Repository, 186 VcsType: dep.VcsType, 187 Subpackages: dep.Subpackages, 188 Arch: dep.Arch, 189 Os: dep.Os, 190 } 191 } 192 193 // NewLockfile is used to create an instance of Lockfile. 194 func NewLockfile(ds, tds Dependencies, hash string) (*Lockfile, error) { 195 lf := &Lockfile{ 196 Hash: hash, 197 Updated: time.Now(), 198 Imports: make([]*Lock, len(ds)), 199 DevImports: make([]*Lock, 0), 200 } 201 202 for i := 0; i < len(ds); i++ { 203 lf.Imports[i] = LockFromDependency(ds[i]) 204 } 205 206 sort.Sort(lf.Imports) 207 208 var found bool 209 for i := 0; i < len(tds); i++ { 210 found = false 211 for ii := 0; ii < len(ds); ii++ { 212 if ds[ii].Name == tds[i].Name { 213 found = true 214 if ds[ii].Reference != tds[i].Reference { 215 return &Lockfile{}, fmt.Errorf("Generating lock produced conflicting versions of %s. import (%s), testImport (%s)", tds[i].Name, ds[ii].Reference, tds[i].Reference) 216 } 217 break 218 } 219 } 220 if !found { 221 lf.DevImports = append(lf.DevImports, LockFromDependency(tds[i])) 222 } 223 } 224 225 sort.Sort(lf.DevImports) 226 227 return lf, nil 228 } 229 230 // LockfileFromMap takes a map of dependencies and generates a lock Lockfile instance. 231 func LockfileFromMap(ds map[string]*Dependency, hash string) *Lockfile { 232 lf := &Lockfile{ 233 Hash: hash, 234 Updated: time.Now(), 235 Imports: make([]*Lock, len(ds)), 236 } 237 238 i := 0 239 for name, dep := range ds { 240 lf.Imports[i] = LockFromDependency(dep) 241 lf.Imports[i].Name = name 242 i++ 243 } 244 245 sort.Sort(lf.Imports) 246 247 return lf 248 }