@@ -32,10 +32,9 @@ import (
32
32
"path"
33
33
"path/filepath"
34
34
"runtime"
35
- "sort"
36
35
"strings"
37
36
38
- "github.com/blang /semver/v4 "
37
+ "github.com/Masterminds /semver/v3 "
39
38
"sigs.k8s.io/yaml"
40
39
)
41
40
@@ -111,6 +110,33 @@ type archive struct {
111
110
SelfLink string `json:"selfLink"`
112
111
}
113
112
113
+ // interpretVersion parses a "SemVer-ish" request into a single [semver.Version]
114
+ // or a looser set of [semver.Constraints].
115
+ //
116
+ // This does *not* understand wildcards, comparators, ranges, or other special syntax.
117
+ func interpretVersion (request string ) (* semver.Version , * semver.Constraints ) {
118
+ // When no version is requested, look for one in the release index.
119
+ if request == "" {
120
+ c , _ := semver .NewConstraint ("*" )
121
+ return nil , c
122
+ }
123
+
124
+ // When an exact version is requested, don't look for it in the release index.
125
+ if v , err := semver .StrictNewVersion (strings .TrimPrefix (request , "v" )); err == nil {
126
+ return v , nil
127
+ }
128
+
129
+ // When it is an inexact version, turn it into a search of the release index.
130
+ if _ , err := semver .NewVersion (request ); err == nil {
131
+ if c , err := semver .NewConstraint (request ); err == nil {
132
+ return nil , c
133
+ }
134
+ }
135
+
136
+ // The request isn't a version at all.
137
+ return nil , nil
138
+ }
139
+
114
140
func downloadBinaryAssets (ctx context.Context , binaryAssetsDirectory , binaryAssetsVersion , binaryAssetsIndexURL string ) (string , string , string , error ) {
115
141
if binaryAssetsIndexURL == "" {
116
142
binaryAssetsIndexURL = DefaultBinaryAssetsIndexURL
@@ -124,54 +150,24 @@ func downloadBinaryAssets(ctx context.Context, binaryAssetsDirectory, binaryAsse
124
150
}
125
151
}
126
152
127
- var requestedRange semver.Range
128
- if binaryAssetsVersion != "" {
129
- binaryAssetsVersion = strings .TrimPrefix (binaryAssetsVersion , "v" )
130
- parsedVersion , errV := semver .ParseTolerant (binaryAssetsVersion )
131
- parsedRange , errR := semver .ParseRange (binaryAssetsVersion )
132
-
133
- switch {
134
- // When an exact version is requested, don't look for it in the release index.
135
- case errV == nil && parsedVersion .String () == binaryAssetsVersion :
136
- requestedRange = nil
137
-
138
- // When the version looks like a range, apply it to the index.
139
- case errR == nil :
140
- requestedRange = parsedRange
141
-
142
- // When the version isn't exact, turn it into a range.
143
- //
144
- // The `setup-envtest` tool interprets a partial version to mean "latest stable with that prefix."
145
- // For example, "1" and "1.2" are akin to "1.x.x" and "1.2.x" in [semver.ParseRange].
146
- // [semver.ParseTolerant] fills in missing minor or patch with "0", so this replaces those with "x".
147
- //
148
- // That *should* produce a valid range. If it doesn't, use the original value and skip the index.
149
- case errV == nil :
150
- suffix := strings .TrimPrefix (parsedVersion .FinalizeVersion (), binaryAssetsVersion )
151
- suffix = strings .ReplaceAll (suffix , "0" , "x" )
152
- parsedRange , errR = semver .ParseRange (binaryAssetsVersion + suffix )
153
-
154
- if errR == nil {
155
- requestedRange = parsedRange
156
- } else {
157
- requestedRange = nil
158
- }
159
- }
160
- } else {
161
- // When no version is requested, look for one in the release index.
162
- requestedRange = semver .MustParseRange (">0.0.0" )
153
+ version , search := interpretVersion (binaryAssetsVersion )
154
+
155
+ // When an exact version is requested, use its canonical form to download without the release index.
156
+ if version != nil {
157
+ binaryAssetsVersion = version .String ()
163
158
}
164
159
165
- // When a range a versions is requested, select one from the release index.
160
+ // When a range of versions is requested, select a stable one from the release index.
166
161
var binaryAssetsIndex * index
167
- if requestedRange != nil {
162
+ if search != nil {
168
163
var err error
169
164
binaryAssetsIndex , err = getIndex (ctx , binaryAssetsIndexURL )
170
165
if err != nil {
171
166
return "" , "" , "" , err
172
167
}
173
168
174
- binaryAssetsVersion , err = latestStableVersionFromIndex (binaryAssetsIndex , requestedRange )
169
+ search .IncludePrerelease = false
170
+ binaryAssetsVersion , err = latestVersionFromIndex (binaryAssetsIndex , search )
175
171
if err != nil {
176
172
return "" , "" , "" , err
177
173
}
@@ -293,34 +289,33 @@ func downloadBinaryAssetsArchive(ctx context.Context, index *index, version stri
293
289
return readBody (resp , out , archiveName , archive .Hash )
294
290
}
295
291
296
- func latestStableVersionFromIndex (index * index , satisfying semver.Range ) (string , error ) {
292
+ func latestVersionFromIndex (index * index , satisfying * semver.Constraints ) (string , error ) {
297
293
if len (index .Releases ) == 0 {
298
- return "" , fmt .Errorf ("failed to find latest stable version from index: index is empty" )
294
+ return "" , fmt .Errorf ("failed to find latest version from index: index is empty" )
299
295
}
300
296
301
- parsedVersions := [] semver.Version {}
297
+ var maxVersion * semver.Version
302
298
for releaseVersion := range index .Releases {
303
- v , err := semver .ParseTolerant (releaseVersion )
299
+ v , err := semver .NewVersion (releaseVersion )
304
300
if err != nil {
305
301
return "" , fmt .Errorf ("failed to parse version %q: %w" , releaseVersion , err )
306
302
}
307
303
308
- // Filter out pre-releases and undesirable versions.
309
- if len ( v . Pre ) > 0 || ! satisfying (v ) {
304
+ // Filter out undesirable versions.
305
+ if ! satisfying . Check (v ) {
310
306
continue
311
307
}
312
308
313
- parsedVersions = append (parsedVersions , v )
309
+ if maxVersion == nil || v .GreaterThan (maxVersion ) {
310
+ maxVersion = v
311
+ }
314
312
}
315
313
316
- if len ( parsedVersions ) == 0 {
317
- return "" , fmt .Errorf ("failed to find latest stable version from index: index does not have stable versions" )
314
+ if maxVersion == nil {
315
+ return "" , fmt .Errorf ("failed to find latest version from index: nothing matches %q" , satisfying )
318
316
}
319
317
320
- sort .Slice (parsedVersions , func (i , j int ) bool {
321
- return parsedVersions [i ].GT (parsedVersions [j ])
322
- })
323
- return "v" + parsedVersions [0 ].String (), nil
318
+ return "v" + maxVersion .String (), nil
324
319
}
325
320
326
321
func getIndex (ctx context.Context , indexURL string ) (* index , error ) {
0 commit comments