Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

Commit 0bd3a66

Browse files
authored
🎁 Allow to override version via environmental variables to be used on CI (#22)
* Introducing version.Resolver type for compatibility with #20 * Impementing knative style version resolver using composite, ordered, and git resolvers. * Adding additional tests and fixes * More tests to pinpoint the failure * Knative version resolver work properly * Lint fix
1 parent 2578dcd commit 0bd3a66

File tree

20 files changed

+578
-146
lines changed

20 files changed

+578
-146
lines changed

pkg/cache/noop.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package cache
2+
3+
// NoopCache do not do any caching. Probably, should be only used for testing
4+
// purposes.
5+
type NoopCache struct{}
6+
7+
func (n NoopCache) Compute(_ interface{}, provider func() (interface{}, error)) (interface{}, error) {
8+
return provider()
9+
}
10+
11+
func (n NoopCache) Drop(_ interface{}) interface{} {
12+
return nil
13+
}

pkg/environment/types.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package environment
2+
3+
import (
4+
"os"
5+
"strings"
6+
)
7+
8+
// Key is an environment key.
9+
type Key string
10+
11+
// Value is an environment value.
12+
type Value string
13+
14+
// Pair holds a pair of environment key and value.
15+
type Pair struct {
16+
Key
17+
Value
18+
}
19+
20+
// Values holds environment values together with their keys.
21+
type Values map[Key]Value
22+
23+
// ValuesSupplier is a func that supplies environmental values.
24+
type ValuesSupplier func() Values
25+
26+
// Add a pair to environment values.
27+
func (v Values) Add(pair Pair) {
28+
v[pair.Key] = pair.Value
29+
}
30+
31+
// New returns an environmental values bases on input compatible with the
32+
// os.Environ function.
33+
func New(environ ...string) Values {
34+
vals := Values(map[Key]Value{})
35+
for _, pair := range environ {
36+
vals.Add(NewPair(pair))
37+
}
38+
return vals
39+
}
40+
41+
// Current returns current environment values, from os.Environ method.
42+
func Current() Values {
43+
return New(os.Environ()...)
44+
}
45+
46+
// NewPair creates a pair from os.Environ style string.
47+
func NewPair(environ string) Pair {
48+
parts := strings.SplitN(environ, "=", pairElements)
49+
pair := Pair{Key: Key(parts[0])}
50+
if len(parts) > 1 {
51+
pair.Value = Value(parts[1])
52+
}
53+
return pair
54+
}
55+
56+
const pairElements = 2

pkg/environment/types_test.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package environment_test
2+
3+
import (
4+
"testing"
5+
6+
"github.com/wavesoftware/go-magetasks/pkg/environment"
7+
"gotest.tools/v3/assert"
8+
)
9+
10+
func TestNew(t *testing.T) {
11+
e := environment.New("TAG=v4.5.6", "PUSH_RELEASE=1")
12+
assert.Equal(t, e[environment.Key("TAG")], environment.Value("v4.5.6"))
13+
assert.Equal(t, e[environment.Key("PUSH_RELEASE")], environment.Value("1"))
14+
}

pkg/environment/version.go

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
package environment
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
)
7+
8+
// ErrNotSupported when operation is not supported.
9+
var ErrNotSupported = errors.New("not supported")
10+
11+
// NewVersionResolver creates a VersionResolver using options.
12+
func NewVersionResolver(options ...VersionResolverOption) VersionResolver {
13+
r := VersionResolver{}
14+
15+
for _, option := range options {
16+
option(&r)
17+
}
18+
19+
return r
20+
}
21+
22+
// WithValuesSupplier allows to set the values supplier.
23+
func WithValuesSupplier(vs ValuesSupplier) VersionResolverOption {
24+
return func(r *VersionResolver) {
25+
r.ValuesSupplier = vs
26+
}
27+
}
28+
29+
// VersionResolverOption is used to customize creation of VersionResolver.
30+
type VersionResolverOption func(*VersionResolver)
31+
32+
// VersionResolver is used to resolve version information solely on
33+
// environment variables.
34+
type VersionResolver struct {
35+
VersionKey Key
36+
IsApplicable []Check
37+
ValuesSupplier
38+
}
39+
40+
// Check is used to verify environment values.
41+
type Check Pair
42+
43+
func (e VersionResolver) Version() string {
44+
values := e.environment()
45+
if !e.isApplicable(values) {
46+
return ""
47+
}
48+
return string(values[e.VersionKey])
49+
}
50+
51+
func (e VersionResolver) IsLatest(versionRange string) (bool, error) {
52+
return false, fmt.Errorf(
53+
"%w: IsLatest(versionRange string) by environment.VersionResolver",
54+
ErrNotSupported)
55+
}
56+
57+
func (e VersionResolver) environment() Values {
58+
supplier := Current
59+
if e.ValuesSupplier != nil {
60+
supplier = e.ValuesSupplier
61+
}
62+
return supplier()
63+
}
64+
65+
func (e VersionResolver) isApplicable(values Values) bool {
66+
for _, check := range e.IsApplicable {
67+
if !check.test(values) {
68+
return false
69+
}
70+
}
71+
return true
72+
}
73+
74+
func (c Check) test(values Values) bool {
75+
if c.Value == "" {
76+
_, ok := values[c.Key]
77+
return ok
78+
}
79+
val, ok := values[c.Key]
80+
return ok && val == c.Value
81+
}

pkg/environment/version_test.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package environment_test
2+
3+
import (
4+
"fmt"
5+
"testing"
6+
7+
"github.com/wavesoftware/go-magetasks/pkg/environment"
8+
"github.com/wavesoftware/go-magetasks/pkg/testing/errors"
9+
"github.com/wavesoftware/go-magetasks/pkg/version"
10+
"gotest.tools/v3/assert"
11+
)
12+
13+
func TestVersionResolver(t *testing.T) {
14+
tests := []testCase{{
15+
environment: environment.New(),
16+
}, {
17+
environment: environment.New("TAG=v4.6.23", "TAG_RELEASE=1"),
18+
version: "v4.6.23",
19+
}, {
20+
environment: environment.New("TAG=v6.23.0", "TAG_RELEASE=1", "is_auto_release=1"),
21+
version: "v6.23.0",
22+
}}
23+
for i, tc := range tests {
24+
tc := tc
25+
t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
26+
resolver := tc.resolver()
27+
assert.Equal(t, resolver.Version(), tc.version)
28+
_, err := resolver.IsLatest(version.AnyVersion)
29+
errors.Check(t, err, environment.ErrNotSupported)
30+
})
31+
}
32+
}
33+
34+
type testCase struct {
35+
environment environment.Values
36+
version string
37+
}
38+
39+
func (tc testCase) resolver() version.Resolver {
40+
return environment.VersionResolver{
41+
VersionKey: "TAG",
42+
IsApplicable: []environment.Check{{
43+
Key: "TAG_RELEASE", Value: "1",
44+
}},
45+
ValuesSupplier: func() environment.Values {
46+
return tc.environment
47+
},
48+
}
49+
}

pkg/git/resolver.go

Lines changed: 51 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,82 +1,104 @@
11
package git
22

33
import (
4-
"errors"
5-
64
"github.com/wavesoftware/go-ensure"
75
"github.com/wavesoftware/go-magetasks/config"
86
"github.com/wavesoftware/go-magetasks/pkg/cache"
97
"github.com/wavesoftware/go-magetasks/pkg/version"
108
)
119

12-
// IsLatestStrategy is used to determine if current version is latest one.
13-
type IsLatestStrategy func(Resolver) func(versionRange string) (bool, error)
10+
type VersionResolverOption func(*VersionResolver)
1411

15-
// Remote represents a remote repository name and address.
16-
type Remote struct {
17-
Name string
18-
URL string
12+
func NewVersionResolver(options ...VersionResolverOption) VersionResolver {
13+
r := VersionResolver{}
14+
15+
for _, option := range options {
16+
option(&r)
17+
}
18+
19+
return r
20+
}
21+
22+
func WithCache(cache cache.Cache) VersionResolverOption {
23+
return func(r *VersionResolver) {
24+
r.Cache = cache
25+
}
1926
}
2027

21-
// Resolver implements version.Resolver for git SCM.
22-
type Resolver struct {
28+
func WithIsLatestStrategy(strategy IsLatestStrategy) VersionResolverOption {
29+
return func(r *VersionResolver) {
30+
r.IsLatestStrategy = strategy
31+
}
32+
}
33+
34+
func WithRepository(repository Repository) VersionResolverOption {
35+
return func(r *VersionResolver) {
36+
r.Repository = repository
37+
}
38+
}
39+
40+
func WithRemote(remote Remote) VersionResolverOption {
41+
return func(r *VersionResolver) {
42+
r.Remote = &remote
43+
}
44+
}
45+
46+
// VersionResolver implements version.Resolver for git SCM.
47+
type VersionResolver struct {
2348
Cache cache.Cache
2449
IsLatestStrategy
2550
Repository
2651
*Remote
2752
}
2853

54+
// Remote represents a remote repository name and address.
55+
type Remote struct {
56+
Name string
57+
URL string
58+
}
59+
60+
// IsLatestStrategy is used to determine if current version is latest one.
61+
type IsLatestStrategy func(version.Resolver) func(string) (bool, error)
62+
2963
type cacheKey struct {
3064
typee string
3165
}
3266

33-
func (r Resolver) Version() string {
67+
func (r VersionResolver) Version() string {
3468
ver, err := r.cache().Compute(cacheKey{"version"}, func() (interface{}, error) {
3569
return r.repository().Describe()
3670
})
3771
ensure.NoError(err)
3872
return ver.(string)
3973
}
4074

41-
func (r Resolver) IsLatest(versionRange string) (bool, error) {
42-
strategy := TagBasedIsLatestStrategy
43-
if r.IsLatestStrategy != nil {
44-
strategy = r.IsLatestStrategy
45-
}
46-
fn := strategy(r)
47-
latest, err := fn(versionRange)
48-
if err != nil {
49-
if !errors.Is(err, version.ErrVersionIsNotValid) {
50-
return false, err
51-
}
52-
return false, nil
53-
}
54-
return latest, nil
75+
func (r VersionResolver) IsLatest(versionRange string) (bool, error) {
76+
return ResolveIsLatest(r, r, versionRange)
5577
}
5678

57-
func (r Resolver) cache() cache.Cache {
79+
func (r VersionResolver) cache() cache.Cache {
5880
if r.Cache == nil {
5981
return config.Cache()
6082
}
6183
return r.Cache
6284
}
6385

64-
func (r Resolver) repository() Repository {
86+
func (r VersionResolver) repository() Repository {
6587
if r.Repository == nil {
6688
return installedGitBinaryRepo{r.remote()}
6789
}
6890
return r.Repository
6991
}
7092

71-
func (r Resolver) remote() Remote {
93+
func (r VersionResolver) remote() Remote {
7294
remote := Remote{Name: "origin"}
7395
if r.Remote != nil {
7496
remote = *r.Remote
7597
}
7698
return remote
7799
}
78100

79-
func (r Resolver) resolveTags() []string {
101+
func (r VersionResolver) resolveTags() []string {
80102
tt, err := r.cache().Compute(cacheKey{"tags"}, func() (interface{}, error) {
81103
return r.repository().Tags()
82104
})

0 commit comments

Comments
 (0)