dep/lock.go

189 строки
4.3 KiB
Go
Исходник Обычный вид История

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.
package dep
2016-10-18 07:39:37 +03:00
import (
"bytes"
2016-10-18 07:39:37 +03:00
"encoding/hex"
"io"
"sort"
2016-10-18 07:39:37 +03:00
"github.com/golang/dep/internal/gps"
2017-03-20 21:13:48 +03:00
"github.com/pelletier/go-toml"
"github.com/pkg/errors"
2016-10-18 07:39:37 +03:00
)
const LockName = "Gopkg.lock"
type Lock struct {
2016-10-18 07:39:37 +03:00
Memo []byte
P []gps.LockedProject
}
type rawLock struct {
2017-03-30 03:01:16 +03:00
Memo string `toml:"memo"`
Projects []rawLockedProject `toml:"projects"`
2016-10-18 07:39:37 +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
}
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")
}
return fromRawLock(raw)
}
func fromRawLock(raw rawLock) (*Lock, error) {
var err error
l := &Lock{
P: make([]gps.LockedProject, len(raw.Projects)),
2016-10-18 07:39:37 +03:00
}
l.Memo, err = hex.DecodeString(raw.Memo)
if err != nil {
return nil, errors.Errorf("invalid hash digest in lock's memo field")
}
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 != "" {
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
}
v = gps.NewVersion(ld.Version).Is(r)
} else if ld.Branch != "" {
v = gps.NewBranch(ld.Branch).Is(r)
} else if r == "" {
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),
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
}
return l, nil
}
func (l *Lock) InputHash() []byte {
2016-10-18 07:39:37 +03:00
return l.Memo
}
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
// 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
}
}
return root
}
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{
Memo: hex.EncodeToString(l.Memo),
Projects: make([]rawLockedProject, len(l.P)),
2016-11-30 08:17:59 +03:00
}
sort.Sort(SortedLockedProjects(l.P))
2016-11-30 08:17:59 +03:00
for k, lp := range l.P {
id := lp.Ident()
ld := rawLockedProject{
Name: string(id.ProjectRoot),
Source: id.Source,
Packages: lp.Packages(),
2016-11-30 08:17:59 +03:00
}
v := lp.Version()
ld.Revision, ld.Branch, ld.Version = gps.VersionComponentStrings(v)
raw.Projects[k] = ld
2016-11-30 08:17:59 +03:00
}
2016-11-30 20:50:41 +03:00
// TODO sort output - #15
2017-03-20 21:13:48 +03:00
return raw
}
func (l *Lock) MarshalTOML() ([]byte, error) {
2017-03-20 21:13:48 +03:00
raw := l.toRaw()
2017-03-30 03:01:16 +03:00
result, err := toml.Marshal(raw)
return result, errors.Wrap(err, "Unable to marshal lock to TOML string")
2017-03-20 21:13:48 +03:00
}
// LockFromInterface converts an arbitrary gps.Lock to dep's representation of a
// lock. If the input is already dep's *lock, the input is returned directly.
//
// Data is defensively copied wherever necessary to ensure the resulting *lock
// shares no memory with the original lock.
//
// As gps.Solution is a superset of gps.Lock, this can also be used to convert
// solutions to dep's lock format.
func LockFromInterface(in gps.Lock) *Lock {
if in == nil {
return nil
} else if l, ok := in.(*Lock); ok {
return l
}
h, p := in.InputHash(), in.Projects()
l := &Lock{
Memo: make([]byte, len(h)),
P: make([]gps.LockedProject, len(p)),
}
copy(l.Memo, h)
copy(l.P, p)
return l
}
type SortedLockedProjects []gps.LockedProject
func (s SortedLockedProjects) Len() int { return len(s) }
func (s SortedLockedProjects) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s SortedLockedProjects) Less(i, j int) bool {
l, r := s[i].Ident(), s[j].Ident()
if l.ProjectRoot < r.ProjectRoot {
return true
}
if r.ProjectRoot < l.ProjectRoot {
return false
}
2017-01-04 05:19:24 +03:00
return l.Source < r.Source
}