forked from gruntwork-io/fetch
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtag.go
More file actions
127 lines (106 loc) · 3.24 KB
/
tag.go
File metadata and controls
127 lines (106 loc) · 3.24 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
package main
import (
"fmt"
"github.com/hashicorp/go-version"
"sort"
"strings"
)
// INVALID_TAG_CONSTRAINT : - err tmpl
const INVALID_TAG_CONSTRAINT = `
The --tag value you entered is not a valid constraint expression.
See https://github.com/opsgang/fetch#version-constraint-operators for examples.
Underlying error message:
%s
`
// NO_VALID_TAG_FOUND : - err tmpl
const NO_VALID_TAG_FOUND = `
Error occurred computing tag that best satisfies version contraint expression:
%s
`
func (o *fetchOpts) tagToGet(tags []string) (tag string, err error) {
specific, tag := isTagConstraintOrExactTag(o.tagConstraint)
if !specific {
// Find the specific release that matches the latest version constraint
latestTag, err := bestFitTag(o.tagConstraint, tags)
if err != nil {
return tag, fmt.Errorf(NO_VALID_TAG_FOUND, err)
}
tag = latestTag
c := o.tagConstraint
if c == "" {
c = "[empty]"
}
fmt.Printf("Most suitable tag for constraint %s is %s\n", c, tag)
}
return
}
func isTagConstraintOrExactTag(tagConstraint string) (bool, string) {
if len(tagConstraint) > 0 {
switch tagConstraint[0] {
// Check for a tagConstraint '='
case '=':
return true, strings.TrimSpace(tagConstraint[1:])
// Check for a tagConstraint without constraint specifier
// Neither of '!=', '>', '>=', '<', '<=', '~>' is prefixed before tag
case '>', '<', '!', '~':
return false, tagConstraint
default:
return true, strings.TrimSpace(tagConstraint)
}
}
return false, tagConstraint
}
func bestFitTag(tagConstraint string, tags []string) (string, error) {
var latestTag string
if len(tags) == 0 {
return latestTag, nil
}
// We use Hashicorp go-versions comparison to find tags that are
// semantic version strings.
var versions []*version.Version
for _, tag := range tags {
v, err := version.NewVersion(tag)
if err != nil {
if strings.Contains(err.Error(), "Malformed version") {
continue // ignore tags that do not fit expected semver
}
return latestTag, err
}
versions = append(versions, v)
}
if len(versions) == 0 {
return latestTag, fmt.Errorf("No valid git tags found")
}
// Sort all tags so that last is latest.
sort.Sort(version.Collection(versions))
// If the tag constraint is empty, set it to the latest tag
if tagConstraint == "" {
tagConstraint = versions[len(versions)-1].String()
}
// Find the latest version that matches the given tag constraint
constraints, err := version.NewConstraint(tagConstraint)
if err != nil {
// more useful err msg if hashicorp consider a constraint invalid.
if strings.Contains(err.Error(), "Malformed constraint") {
err = fmt.Errorf(INVALID_TAG_CONSTRAINT, err.Error())
}
return latestTag, err
}
bestFitVersion := versions[0]
for _, version := range versions {
if constraints.Check(version) && version.GreaterThan(bestFitVersion) {
bestFitVersion = version
}
}
// check constraint against latest acceptable version
if !constraints.Check(bestFitVersion) {
return latestTag, fmt.Errorf("No tag met constraint.\n")
}
// The tag name may have started with a "v". If so, re-apply that string now
for _, originalTagName := range tags {
if strings.Contains(originalTagName, bestFitVersion.String()) {
latestTag = originalTagName
}
}
return latestTag, nil
}