github.com/ownercz/glide@v0.13.4-0.20210823072704-79572d1b8747/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, Ownercz 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  }