Basic manifest implementation (#4)

* Basic first pass at JSON-based manifest

* Add rudimentary ReadManifest() tests

* Implement gps.RootManifest methods on Manifest

* Add copyright headers

* Fatalf on test that invalidates later checks

* Rewrite TestDependencyConstraints comment as TODO

* Consolidate types package back into main
This commit is contained in:
sam boyer 2016-10-18 00:07:38 -04:00 коммит произвёл Andrew Gerrand
Родитель d05d5aca9f
Коммит 095637b4d3
2 изменённых файлов: 215 добавлений и 0 удалений

117
manifest.go Normal file
Просмотреть файл

@ -0,0 +1,117 @@
// 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 main
import (
"encoding/json"
"fmt"
"io"
"github.com/sdboyer/gps"
)
type Manifest struct {
Dependencies gps.ProjectConstraints
Ovr gps.ProjectConstraints
Ignores []string
}
type rawManifest struct {
Dependencies map[string]possibleProps `json:"dependencies"`
Overrides map[string]possibleProps `json:"overrides"`
Ignores []string `json:"ignores"`
}
type possibleProps struct {
Branch string `json:"branch"`
Revision string `json:"revision"`
Version string `json:"version"`
NetworkName string `json:"network_name"`
}
func ReadManifest(r io.Reader) (*Manifest, error) {
rm := rawManifest{}
err := json.NewDecoder(r).Decode(&rm)
if err != nil {
return nil, err
}
m := &Manifest{
Dependencies: make(gps.ProjectConstraints, len(rm.Dependencies)),
Ovr: make(gps.ProjectConstraints, len(rm.Overrides)),
Ignores: rm.Ignores,
}
for n, pp := range rm.Dependencies {
m.Dependencies[gps.ProjectRoot(n)], err = toProps(n, pp)
if err != nil {
return nil, err
}
}
for n, pp := range rm.Overrides {
m.Ovr[gps.ProjectRoot(n)], err = toProps(n, pp)
if err != nil {
return nil, err
}
}
return m, nil
}
func toProps(n string, p possibleProps) (pp gps.ProjectProperties, err error) {
if p.Branch != "" {
if p.Version != "" || p.Revision != "" {
return pp, fmt.Errorf("multiple constraints specified for %s, can only specify one", n)
}
pp.Constraint = gps.NewBranch(p.Branch)
} else if p.Version != "" {
if p.Revision != "" {
return pp, fmt.Errorf("multiple constraints specified for %s, can only specify one", n)
}
// always semver if we can
pp.Constraint, err = gps.NewSemverConstraint(p.Version)
if err != nil {
// but if not, fall back on plain versions
pp.Constraint = gps.NewVersion(p.Version)
}
} else if p.Revision != "" {
pp.Constraint = gps.Revision(p.Revision)
} else {
// If the user specifies nothing, it means an open constraint (accept
// anything).
pp.Constraint = gps.Any()
}
pp.NetworkName = p.NetworkName
return pp, nil
}
func (m *Manifest) DependencyConstraints() gps.ProjectConstraints {
return m.Dependencies
}
func (m *Manifest) TestDependencyConstraints() gps.ProjectConstraints {
// TODO decide whether we're going to incorporate this or not
return nil
}
func (m *Manifest) Overrides() gps.ProjectConstraints {
return m.Ovr
}
func (m *Manifest) IgnorePackages() map[string]bool {
if len(m.Ignores) == 0 {
return nil
}
mp := make(map[string]bool, len(m.Ignores))
for _, i := range m.Ignores {
mp[i] = true
}
return mp
}

98
manifest_test.go Normal file
Просмотреть файл

@ -0,0 +1,98 @@
// 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 main
import (
"reflect"
"strings"
"testing"
"github.com/sdboyer/gps"
)
func TestReadManifest(t *testing.T) {
const je = `{
"dependencies": {
"github.com/sdboyer/gps": {
"branch": "master",
"revision": "d05d5aca9f895d19e9265839bffeadd74a2d2ecb",
"version": "^v0.12.0",
"network_name": "https://github.com/sdboyer/gps"
}
},
"overrides": {
"github.com/sdboyer/gps": {
"branch": "master",
"revision": "d05d5aca9f895d19e9265839bffeadd74a2d2ecb",
"version": "^v0.12.0",
"network_name": "https://github.com/sdboyer/gps"
}
},
"ignores": [
"github.com/foo/bar"
]
}`
const jg = `{
"dependencies": {
"github.com/sdboyer/gps": {
"version": "^v0.12.0"
},
"github.com/babble/brook": {
"revision": "d05d5aca9f895d19e9265839bffeadd74a2d2ecb"
}
},
"overrides": {
"github.com/sdboyer/gps": {
"branch": "master",
"network_name": "https://github.com/sdboyer/gps"
}
},
"ignores": [
"github.com/foo/bar"
]
}`
_, err := ReadManifest(strings.NewReader(je))
if err == nil {
t.Error("Reading manifest with invalid props should have caused error, but did not")
} else if !strings.Contains(err.Error(), "multiple constraints") {
t.Errorf("Unexpected error %q; expected multiple constraint error", err)
}
m2, err := ReadManifest(strings.NewReader(jg))
if err != nil {
t.Fatalf("Should have read Manifest correctly, but got err %q", err)
}
c, _ := gps.NewSemverConstraint("^v0.12.0")
em := Manifest{
Dependencies: map[gps.ProjectRoot]gps.ProjectProperties{
gps.ProjectRoot("github.com/sdboyer/gps"): {
Constraint: c,
},
gps.ProjectRoot("github.com/babble/brook"): {
Constraint: gps.Revision("d05d5aca9f895d19e9265839bffeadd74a2d2ecb"),
},
},
Ovr: map[gps.ProjectRoot]gps.ProjectProperties{
gps.ProjectRoot("github.com/sdboyer/gps"): {
NetworkName: "https://github.com/sdboyer/gps",
Constraint: gps.NewBranch("master"),
},
},
Ignores: []string{"github.com/foo/bar"},
}
if !reflect.DeepEqual(m2.Dependencies, em.Dependencies) {
t.Error("Valid manifest's dependencies did not parse as expected")
}
if !reflect.DeepEqual(m2.Ovr, em.Ovr) {
t.Error("Valid manifest's overrides did not parse as expected")
}
if !reflect.DeepEqual(m2.Ignores, em.Ignores) {
t.Error("Valid manifest's ignores did not parse as expected")
}
}