|  | package cfg | 
|  |  | 
|  | import ( | 
|  | "crypto/sha256" | 
|  | "fmt" | 
|  | "io/ioutil" | 
|  | "sort" | 
|  | "strings" | 
|  | "time" | 
|  |  | 
|  | "gopkg.in/yaml.v2" | 
|  | ) | 
|  |  | 
|  | // Lockfile represents a glide.lock file. | 
|  | type Lockfile struct { | 
|  | Hash       string    `yaml:"hash"` | 
|  | Updated    time.Time `yaml:"updated"` | 
|  | Imports    Locks     `yaml:"imports"` | 
|  | DevImports Locks     `yaml:"testImports"` | 
|  | } | 
|  |  | 
|  | // LockfileFromYaml returns an instance of Lockfile from YAML | 
|  | func LockfileFromYaml(yml []byte) (*Lockfile, error) { | 
|  | lock := &Lockfile{} | 
|  | err := yaml.Unmarshal([]byte(yml), &lock) | 
|  | return lock, err | 
|  | } | 
|  |  | 
|  | // Marshal converts a Config instance to YAML | 
|  | func (lf *Lockfile) Marshal() ([]byte, error) { | 
|  | yml, err := yaml.Marshal(&lf) | 
|  | if err != nil { | 
|  | return []byte{}, err | 
|  | } | 
|  | return yml, nil | 
|  | } | 
|  |  | 
|  | // MarshalYAML is a hook for gopkg.in/yaml.v2. | 
|  | // It sorts import subpackages lexicographically for reproducibility. | 
|  | func (lf *Lockfile) MarshalYAML() (interface{}, error) { | 
|  | for _, imp := range lf.Imports { | 
|  | sort.Strings(imp.Subpackages) | 
|  | } | 
|  |  | 
|  | // Ensure elements on testImport don't already exist on import. | 
|  | var newDI Locks | 
|  | var found bool | 
|  | for _, imp := range lf.DevImports { | 
|  | found = false | 
|  | for i := 0; i < len(lf.Imports); i++ { | 
|  | if lf.Imports[i].Name == imp.Name { | 
|  | found = true | 
|  | if lf.Imports[i].Version != imp.Version { | 
|  | return lf, fmt.Errorf("Generating lock YAML produced conflicting versions of %s. import (%s), testImport (%s)", imp.Name, lf.Imports[i].Version, imp.Version) | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | if !found { | 
|  | newDI = append(newDI, imp) | 
|  | } | 
|  | } | 
|  | lf.DevImports = newDI | 
|  |  | 
|  | for _, imp := range lf.DevImports { | 
|  | sort.Strings(imp.Subpackages) | 
|  | } | 
|  | return lf, nil | 
|  | } | 
|  |  | 
|  | // WriteFile writes a Glide lock file. | 
|  | // | 
|  | // This is a convenience function that marshals the YAML and then writes it to | 
|  | // the given file. If the file exists, it will be clobbered. | 
|  | func (lf *Lockfile) WriteFile(lockpath string) error { | 
|  | o, err := lf.Marshal() | 
|  | if err != nil { | 
|  | return err | 
|  | } | 
|  | return ioutil.WriteFile(lockpath, o, 0666) | 
|  | } | 
|  |  | 
|  | // Clone returns a clone of Lockfile | 
|  | func (lf *Lockfile) Clone() *Lockfile { | 
|  | n := &Lockfile{} | 
|  | n.Hash = lf.Hash | 
|  | n.Updated = lf.Updated | 
|  | n.Imports = lf.Imports.Clone() | 
|  | n.DevImports = lf.DevImports.Clone() | 
|  |  | 
|  | return n | 
|  | } | 
|  |  | 
|  | // Fingerprint returns a hash of the contents minus the date. This allows for | 
|  | // two lockfiles to be compared irrespective of their updated times. | 
|  | func (lf *Lockfile) Fingerprint() ([32]byte, error) { | 
|  | c := lf.Clone() | 
|  | c.Updated = time.Time{} // Set the time to be the nil equivalent | 
|  | sort.Sort(c.Imports) | 
|  | sort.Sort(c.DevImports) | 
|  | yml, err := c.Marshal() | 
|  | if err != nil { | 
|  | return [32]byte{}, err | 
|  | } | 
|  |  | 
|  | return sha256.Sum256(yml), nil | 
|  | } | 
|  |  | 
|  | // ReadLockFile loads the contents of a glide.lock file. | 
|  | func ReadLockFile(lockpath string) (*Lockfile, error) { | 
|  | yml, err := ioutil.ReadFile(lockpath) | 
|  | if err != nil { | 
|  | return nil, err | 
|  | } | 
|  | lock, err := LockfileFromYaml(yml) | 
|  | if err != nil { | 
|  | return nil, err | 
|  | } | 
|  | return lock, nil | 
|  | } | 
|  |  | 
|  | // Locks is a slice of locked dependencies. | 
|  | type Locks []*Lock | 
|  |  | 
|  | // Clone returns a Clone of Locks. | 
|  | func (l Locks) Clone() Locks { | 
|  | n := make(Locks, 0, len(l)) | 
|  | for _, v := range l { | 
|  | n = append(n, v.Clone()) | 
|  | } | 
|  | return n | 
|  | } | 
|  |  | 
|  | // Len returns the length of the Locks. This is needed for sorting with | 
|  | // the sort package. | 
|  | func (l Locks) Len() int { | 
|  | return len(l) | 
|  | } | 
|  |  | 
|  | // Less is needed for the sort interface. It compares two locks based on | 
|  | // their name. | 
|  | func (l Locks) Less(i, j int) bool { | 
|  |  | 
|  | // Names are normalized to lowercase because case affects sorting order. For | 
|  | // example, Masterminds comes before kylelemons. Making them lowercase | 
|  | // causes kylelemons to come first which is what is expected. | 
|  | return strings.ToLower(l[i].Name) < strings.ToLower(l[j].Name) | 
|  | } | 
|  |  | 
|  | // Swap is needed for the sort interface. It swaps the position of two | 
|  | // locks. | 
|  | func (l Locks) Swap(i, j int) { | 
|  | l[i], l[j] = l[j], l[i] | 
|  | } | 
|  |  | 
|  | // Lock represents an individual locked dependency. | 
|  | type Lock struct { | 
|  | Name        string   `yaml:"name"` | 
|  | Version     string   `yaml:"version"` | 
|  | Repository  string   `yaml:"repo,omitempty"` | 
|  | VcsType     string   `yaml:"vcs,omitempty"` | 
|  | Subpackages []string `yaml:"subpackages,omitempty"` | 
|  | Arch        []string `yaml:"arch,omitempty"` | 
|  | Os          []string `yaml:"os,omitempty"` | 
|  | } | 
|  |  | 
|  | // Clone creates a clone of a Lock. | 
|  | func (l *Lock) Clone() *Lock { | 
|  | return &Lock{ | 
|  | Name:        l.Name, | 
|  | Version:     l.Version, | 
|  | Repository:  l.Repository, | 
|  | VcsType:     l.VcsType, | 
|  | Subpackages: l.Subpackages, | 
|  | Arch:        l.Arch, | 
|  | Os:          l.Os, | 
|  | } | 
|  | } | 
|  |  | 
|  | // LockFromDependency converts a Dependency to a Lock | 
|  | func LockFromDependency(dep *Dependency) *Lock { | 
|  | return &Lock{ | 
|  | Name:        dep.Name, | 
|  | Version:     dep.Pin, | 
|  | Repository:  dep.Repository, | 
|  | VcsType:     dep.VcsType, | 
|  | Subpackages: dep.Subpackages, | 
|  | Arch:        dep.Arch, | 
|  | Os:          dep.Os, | 
|  | } | 
|  | } | 
|  |  | 
|  | // NewLockfile is used to create an instance of Lockfile. | 
|  | func NewLockfile(ds, tds Dependencies, hash string) (*Lockfile, error) { | 
|  | lf := &Lockfile{ | 
|  | Hash:       hash, | 
|  | Updated:    time.Now(), | 
|  | Imports:    make([]*Lock, len(ds)), | 
|  | DevImports: make([]*Lock, 0), | 
|  | } | 
|  |  | 
|  | for i := 0; i < len(ds); i++ { | 
|  | lf.Imports[i] = LockFromDependency(ds[i]) | 
|  | } | 
|  |  | 
|  | sort.Sort(lf.Imports) | 
|  |  | 
|  | var found bool | 
|  | for i := 0; i < len(tds); i++ { | 
|  | found = false | 
|  | for ii := 0; ii < len(ds); ii++ { | 
|  | if ds[ii].Name == tds[i].Name { | 
|  | found = true | 
|  | if ds[ii].Reference != tds[i].Reference { | 
|  | return &Lockfile{}, fmt.Errorf("Generating lock produced conflicting versions of %s. import (%s), testImport (%s)", tds[i].Name, ds[ii].Reference, tds[i].Reference) | 
|  | } | 
|  | break | 
|  | } | 
|  | } | 
|  | if !found { | 
|  | lf.DevImports = append(lf.DevImports, LockFromDependency(tds[i])) | 
|  | } | 
|  | } | 
|  |  | 
|  | sort.Sort(lf.DevImports) | 
|  |  | 
|  | return lf, nil | 
|  | } | 
|  |  | 
|  | // LockfileFromMap takes a map of dependencies and generates a lock Lockfile instance. | 
|  | func LockfileFromMap(ds map[string]*Dependency, hash string) *Lockfile { | 
|  | lf := &Lockfile{ | 
|  | Hash:    hash, | 
|  | Updated: time.Now(), | 
|  | Imports: make([]*Lock, len(ds)), | 
|  | } | 
|  |  | 
|  | i := 0 | 
|  | for name, dep := range ds { | 
|  | lf.Imports[i] = LockFromDependency(dep) | 
|  | lf.Imports[i].Name = name | 
|  | i++ | 
|  | } | 
|  |  | 
|  | sort.Sort(lf.Imports) | 
|  |  | 
|  | return lf | 
|  | } |