2016-10-18 07:39:37 +03:00
|
|
|
// Copyright 2016 The Go Authors. All rights reserved.
|
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
2017-01-27 23:39:07 +03:00
|
|
|
package dep
|
2016-10-18 07:39:37 +03:00
|
|
|
|
|
|
|
import (
|
2017-04-30 20:08:37 +03:00
|
|
|
"bytes"
|
2016-10-18 07:39:37 +03:00
|
|
|
"encoding/hex"
|
|
|
|
"io"
|
2016-12-06 00:30:33 +03:00
|
|
|
"sort"
|
2016-10-18 07:39:37 +03:00
|
|
|
|
2017-11-03 16:05:41 +03:00
|
|
|
"github.com/golang/dep/gps"
|
2017-03-20 21:13:48 +03:00
|
|
|
"github.com/pelletier/go-toml"
|
2017-03-22 18:58:50 +03:00
|
|
|
"github.com/pkg/errors"
|
2016-10-18 07:39:37 +03:00
|
|
|
)
|
|
|
|
|
2017-06-01 02:51:37 +03:00
|
|
|
// LockName is the lock file name used by dep.
|
2017-04-06 18:15:54 +03:00
|
|
|
const LockName = "Gopkg.lock"
|
2017-01-27 23:39:07 +03:00
|
|
|
|
2017-06-01 02:51:37 +03:00
|
|
|
// Lock holds lock file data and implements gps.Lock.
|
2017-01-27 23:39:07 +03:00
|
|
|
type Lock struct {
|
2017-05-22 20:41:13 +03:00
|
|
|
SolveMeta SolveMeta
|
|
|
|
P []gps.LockedProject
|
2017-05-16 06:05:45 +03:00
|
|
|
}
|
|
|
|
|
2017-06-01 02:51:37 +03:00
|
|
|
// SolveMeta holds solver meta data.
|
2017-05-22 20:41:13 +03:00
|
|
|
type SolveMeta struct {
|
2017-05-27 06:50:29 +03:00
|
|
|
InputsDigest []byte
|
2017-05-16 06:05:45 +03:00
|
|
|
AnalyzerName string
|
|
|
|
AnalyzerVersion int
|
2017-05-22 20:41:13 +03:00
|
|
|
SolverName string
|
|
|
|
SolverVersion int
|
2016-10-18 07:39:37 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
type rawLock struct {
|
2017-05-22 20:41:13 +03:00
|
|
|
SolveMeta solveMeta `toml:"solve-meta"`
|
|
|
|
Projects []rawLockedProject `toml:"projects"`
|
2016-10-18 07:39:37 +03:00
|
|
|
}
|
|
|
|
|
2017-05-22 20:41:13 +03:00
|
|
|
type solveMeta struct {
|
2017-05-23 05:35:18 +03:00
|
|
|
InputsDigest string `toml:"inputs-digest"`
|
2017-05-22 20:41:13 +03:00
|
|
|
AnalyzerName string `toml:"analyzer-name"`
|
|
|
|
AnalyzerVersion int `toml:"analyzer-version"`
|
|
|
|
SolverName string `toml:"solver-name"`
|
|
|
|
SolverVersion int `toml:"solver-version"`
|
2016-10-18 07:39:37 +03:00
|
|
|
}
|
|
|
|
|
2017-03-21 22:53:53 +03:00
|
|
|
type rawLockedProject struct {
|
2017-03-30 03:01:16 +03:00
|
|
|
Name string `toml:"name"`
|
|
|
|
Branch string `toml:"branch,omitempty"`
|
|
|
|
Revision string `toml:"revision"`
|
|
|
|
Version string `toml:"version,omitempty"`
|
|
|
|
Source string `toml:"source,omitempty"`
|
|
|
|
Packages []string `toml:"packages"`
|
2016-10-18 07:39:37 +03:00
|
|
|
}
|
|
|
|
|
2017-01-27 23:39:07 +03:00
|
|
|
func readLock(r io.Reader) (*Lock, error) {
|
2017-03-30 03:01:16 +03:00
|
|
|
buf := &bytes.Buffer{}
|
|
|
|
_, err := buf.ReadFrom(r)
|
2016-10-18 07:39:37 +03:00
|
|
|
if err != nil {
|
2017-03-30 03:01:16 +03:00
|
|
|
return nil, errors.Wrap(err, "Unable to read byte stream")
|
2016-10-18 07:39:37 +03:00
|
|
|
}
|
|
|
|
|
2017-03-30 03:01:16 +03:00
|
|
|
raw := rawLock{}
|
|
|
|
err = toml.Unmarshal(buf.Bytes(), &raw)
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Wrap(err, "Unable to parse the lock as TOML")
|
2017-03-21 22:53:53 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return fromRawLock(raw)
|
|
|
|
}
|
|
|
|
|
|
|
|
func fromRawLock(raw rawLock) (*Lock, error) {
|
|
|
|
var err error
|
2017-01-27 23:39:07 +03:00
|
|
|
l := &Lock{
|
2017-03-21 22:53:53 +03:00
|
|
|
P: make([]gps.LockedProject, len(raw.Projects)),
|
2016-10-18 07:39:37 +03:00
|
|
|
}
|
|
|
|
|
2017-05-27 06:50:29 +03:00
|
|
|
l.SolveMeta.InputsDigest, err = hex.DecodeString(raw.SolveMeta.InputsDigest)
|
2017-03-21 22:53:53 +03:00
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Errorf("invalid hash digest in lock's memo field")
|
|
|
|
}
|
|
|
|
|
2017-05-22 20:41:13 +03:00
|
|
|
l.SolveMeta.AnalyzerName = raw.SolveMeta.AnalyzerName
|
|
|
|
l.SolveMeta.AnalyzerVersion = raw.SolveMeta.AnalyzerVersion
|
|
|
|
l.SolveMeta.SolverName = raw.SolveMeta.SolverName
|
|
|
|
l.SolveMeta.SolverVersion = raw.SolveMeta.SolverVersion
|
2017-05-16 06:05:45 +03:00
|
|
|
|
2017-03-21 22:53:53 +03:00
|
|
|
for i, ld := range raw.Projects {
|
2016-10-18 07:39:37 +03:00
|
|
|
r := gps.Revision(ld.Revision)
|
|
|
|
|
2017-01-25 00:48:14 +03:00
|
|
|
var v gps.Version = r
|
2016-10-18 07:39:37 +03:00
|
|
|
if ld.Version != "" {
|
|
|
|
if ld.Branch != "" {
|
2017-03-22 18:58:50 +03:00
|
|
|
return nil, errors.Errorf("lock file specified both a branch (%s) and version (%s) for %s", ld.Branch, ld.Version, ld.Name)
|
2016-10-18 07:39:37 +03:00
|
|
|
}
|
2017-06-14 17:44:59 +03:00
|
|
|
v = gps.NewVersion(ld.Version).Pair(r)
|
2016-10-18 07:39:37 +03:00
|
|
|
} else if ld.Branch != "" {
|
2017-06-14 17:44:59 +03:00
|
|
|
v = gps.NewBranch(ld.Branch).Pair(r)
|
2016-10-18 07:39:37 +03:00
|
|
|
} else if r == "" {
|
2017-03-22 18:58:50 +03:00
|
|
|
return nil, errors.Errorf("lock file has entry for %s, but specifies no branch or version", ld.Name)
|
2016-10-18 07:39:37 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
id := gps.ProjectIdentifier{
|
|
|
|
ProjectRoot: gps.ProjectRoot(ld.Name),
|
2017-03-07 22:47:13 +03:00
|
|
|
Source: ld.Source,
|
2016-10-18 07:39:37 +03:00
|
|
|
}
|
2016-10-18 08:18:18 +03:00
|
|
|
l.P[i] = gps.NewLockedProject(id, v, ld.Packages)
|
2016-10-18 07:39:37 +03:00
|
|
|
}
|
2017-05-16 06:05:45 +03:00
|
|
|
|
2016-10-18 07:39:37 +03:00
|
|
|
return l, nil
|
|
|
|
}
|
|
|
|
|
2017-10-02 04:17:01 +03:00
|
|
|
// InputsDigest returns the hash of inputs which produced this lock data.
|
|
|
|
func (l *Lock) InputsDigest() []byte {
|
2017-05-27 06:50:29 +03:00
|
|
|
return l.SolveMeta.InputsDigest
|
2016-10-18 07:39:37 +03:00
|
|
|
}
|
|
|
|
|
2017-06-01 02:51:37 +03:00
|
|
|
// Projects returns the list of LockedProjects contained in the lock data.
|
2017-01-27 23:39:07 +03:00
|
|
|
func (l *Lock) Projects() []gps.LockedProject {
|
2016-10-18 07:39:37 +03:00
|
|
|
return l.P
|
|
|
|
}
|
2016-11-30 08:17:59 +03:00
|
|
|
|
2017-04-30 20:08:37 +03:00
|
|
|
// HasProjectWithRoot checks if the lock contains a project with the provided
|
|
|
|
// ProjectRoot.
|
|
|
|
//
|
|
|
|
// This check is O(n) in the number of projects.
|
|
|
|
func (l *Lock) HasProjectWithRoot(root gps.ProjectRoot) bool {
|
|
|
|
for _, p := range l.P {
|
|
|
|
if p.Ident().ProjectRoot == root {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-05 03:07:51 +03:00
|
|
|
return false
|
2017-04-30 20:08:37 +03:00
|
|
|
}
|
|
|
|
|
2017-03-20 21:13:48 +03:00
|
|
|
// toRaw converts the manifest into a representation suitable to write to the lock file
|
|
|
|
func (l *Lock) toRaw() rawLock {
|
2016-11-30 08:17:59 +03:00
|
|
|
raw := rawLock{
|
2017-05-22 20:41:13 +03:00
|
|
|
SolveMeta: solveMeta{
|
2017-05-27 06:50:29 +03:00
|
|
|
InputsDigest: hex.EncodeToString(l.SolveMeta.InputsDigest),
|
2017-05-22 20:41:13 +03:00
|
|
|
AnalyzerName: l.SolveMeta.AnalyzerName,
|
|
|
|
AnalyzerVersion: l.SolveMeta.AnalyzerVersion,
|
|
|
|
SolverName: l.SolveMeta.SolverName,
|
|
|
|
SolverVersion: l.SolveMeta.SolverVersion,
|
2017-05-16 06:05:45 +03:00
|
|
|
},
|
2017-03-21 22:53:53 +03:00
|
|
|
Projects: make([]rawLockedProject, len(l.P)),
|
2016-11-30 08:17:59 +03:00
|
|
|
}
|
|
|
|
|
2017-06-03 19:48:14 +03:00
|
|
|
sort.Slice(l.P, func(i, j int) bool {
|
|
|
|
return l.P[i].Ident().Less(l.P[j].Ident())
|
|
|
|
})
|
2016-12-03 20:33:40 +03:00
|
|
|
|
2016-11-30 08:17:59 +03:00
|
|
|
for k, lp := range l.P {
|
|
|
|
id := lp.Ident()
|
2017-03-21 22:53:53 +03:00
|
|
|
ld := rawLockedProject{
|
2017-03-07 22:47:13 +03:00
|
|
|
Name: string(id.ProjectRoot),
|
|
|
|
Source: id.Source,
|
|
|
|
Packages: lp.Packages(),
|
2016-11-30 08:17:59 +03:00
|
|
|
}
|
|
|
|
|
2016-11-30 19:50:57 +03:00
|
|
|
v := lp.Version()
|
2017-04-11 18:38:50 +03:00
|
|
|
ld.Revision, ld.Branch, ld.Version = gps.VersionComponentStrings(v)
|
2016-11-30 19:50:57 +03:00
|
|
|
|
2017-03-21 22:53:53 +03:00
|
|
|
raw.Projects[k] = ld
|
2016-11-30 08:17:59 +03:00
|
|
|
}
|
|
|
|
|
2017-03-20 21:13:48 +03:00
|
|
|
return raw
|
|
|
|
}
|
|
|
|
|
2017-06-01 02:51:37 +03:00
|
|
|
// MarshalTOML serializes this lock into TOML via an intermediate raw form.
|
2017-04-02 23:20:21 +03:00
|
|
|
func (l *Lock) MarshalTOML() ([]byte, error) {
|
2017-03-20 21:13:48 +03:00
|
|
|
raw := l.toRaw()
|
2017-12-18 17:18:13 +03:00
|
|
|
var buf bytes.Buffer
|
|
|
|
enc := toml.NewEncoder(&buf).ArraysWithOneElementPerLine(true)
|
|
|
|
err := enc.Encode(raw)
|
|
|
|
return buf.Bytes(), errors.Wrap(err, "Unable to marshal lock to TOML string")
|
2017-03-20 21:13:48 +03:00
|
|
|
}
|
|
|
|
|
2017-05-16 06:12:06 +03:00
|
|
|
// LockFromSolution converts a gps.Solution to dep's representation of a lock.
|
2016-12-01 08:25:48 +03:00
|
|
|
//
|
|
|
|
// Data is defensively copied wherever necessary to ensure the resulting *lock
|
|
|
|
// shares no memory with the original lock.
|
2017-05-16 06:12:06 +03:00
|
|
|
func LockFromSolution(in gps.Solution) *Lock {
|
2017-10-02 04:17:01 +03:00
|
|
|
h, p := in.InputsDigest(), in.Projects()
|
2016-12-01 08:25:48 +03:00
|
|
|
|
2017-01-27 23:39:07 +03:00
|
|
|
l := &Lock{
|
2017-05-22 20:41:13 +03:00
|
|
|
SolveMeta: SolveMeta{
|
2017-05-27 06:50:29 +03:00
|
|
|
InputsDigest: make([]byte, len(h)),
|
2017-05-16 06:05:45 +03:00
|
|
|
AnalyzerName: in.AnalyzerName(),
|
|
|
|
AnalyzerVersion: in.AnalyzerVersion(),
|
2017-05-22 20:41:13 +03:00
|
|
|
SolverName: in.SolverName(),
|
|
|
|
SolverVersion: in.SolverVersion(),
|
2017-05-16 06:05:45 +03:00
|
|
|
},
|
|
|
|
P: make([]gps.LockedProject, len(p)),
|
2016-12-01 08:25:48 +03:00
|
|
|
}
|
|
|
|
|
2017-05-27 06:50:29 +03:00
|
|
|
copy(l.SolveMeta.InputsDigest, h)
|
2016-12-01 08:25:48 +03:00
|
|
|
copy(l.P, p)
|
|
|
|
return l
|
|
|
|
}
|