dep/lock.go

224 строки
4.9 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 (
"encoding/hex"
"encoding/json"
"io"
"sort"
2016-10-18 07:39:37 +03:00
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
"github.com/sdboyer/gps"
)
const LockName = "lock.toml"
type Lock struct {
2016-10-18 07:39:37 +03:00
Memo []byte
P []gps.LockedProject
}
type rawLock struct {
Memo string
P []lockedDep
2016-10-18 07:39:37 +03:00
}
type lockedDep struct {
Name string
Version string
Branch string
Revision string
Source string
Packages []string
2016-10-18 07:39:37 +03:00
}
func readLock(r io.Reader) (*Lock, error) {
2016-10-18 07:39:37 +03:00
rl := rawLock{}
err := json.NewDecoder(r).Decode(&rl)
if err != nil {
return nil, err
}
b, err := hex.DecodeString(rl.Memo)
if err != nil {
return nil, errors.Errorf("invalid hash digest in lock's memo field")
2016-10-18 07:39:37 +03:00
}
l := &Lock{
2016-10-18 07:39:37 +03:00
Memo: b,
P: make([]gps.LockedProject, len(rl.P)),
}
2016-10-18 08:18:18 +03:00
for i, ld := range rl.P {
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
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),
2016-11-30 19:32:24 +03:00
P: make([]lockedDep, 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 := lockedDep{
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 = getVersionInfo(v)
2016-11-30 08:17:59 +03:00
raw.P[k] = ld
}
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() (string, error) {
raw := l.toRaw()
// TODO(carolynvs) Consider adding reflection-based marshal functionality to go-toml
m := make(map[string]interface{})
m["memo"] = raw.Memo
p := make([]map[string]interface{}, len(raw.P))
for i := 0; i < len(p); i++ {
srcPrj := raw.P[i]
2017-03-20 21:13:48 +03:00
prj := make(map[string]interface{})
prj["name"] = srcPrj.Name
prj["revision"] = srcPrj.Revision
2017-03-20 21:13:48 +03:00
if srcPrj.Source != "" {
prj["source"] = srcPrj.Source
2017-03-20 21:13:48 +03:00
}
if srcPrj.Branch != "" {
prj["branch"] = srcPrj.Branch
2017-03-20 21:13:48 +03:00
}
if srcPrj.Version != "" {
prj["version"] = srcPrj.Version
2017-03-20 21:13:48 +03:00
}
pkgs := make([]interface{}, len(srcPrj.Packages))
for j := range srcPrj.Packages {
pkgs[j] = srcPrj.Packages[j]
2017-03-20 21:13:48 +03:00
}
prj["packages"] = pkgs
p[i] = prj
}
m["projects"] = p
t, err := toml.TreeFromMap(m)
if err != nil {
return "", errors.Wrap(err, "Unable to marshal lock to TOML tree")
}
result, err := t.ToTomlString()
return result, errors.Wrap(err, "Unable to marshal lock to TOML string")
}
// TODO(carolynvs) this should be moved to gps
func getVersionInfo(v gps.Version) (revision string, branch string, version string) {
2017-03-09 22:28:42 +03:00
// Figure out how to get the underlying revision
switch tv := v.(type) {
case gps.UnpairedVersion:
// TODO we could error here, if we want to be very defensive about not
// allowing a lock to be written if without an immmutable revision
case gps.Revision:
revision = tv.String()
2017-03-09 22:28:42 +03:00
case gps.PairedVersion:
revision = tv.Underlying().String()
2017-03-09 22:28:42 +03:00
}
switch v.Type() {
case gps.IsBranch:
branch = v.String()
case gps.IsSemver, gps.IsVersion:
version = v.String()
}
return
2017-03-09 22:28:42 +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
}