@@ -13,6 +13,7 @@ import (
13
13
"github.com/operator-framework/operator-registry/alpha/declcfg"
14
14
"github.com/operator-framework/operator-registry/alpha/property"
15
15
"github.com/operator-framework/operator-registry/pkg/image"
16
+ "k8s.io/apimachinery/pkg/util/errors"
16
17
"k8s.io/apimachinery/pkg/util/sets"
17
18
"k8s.io/apimachinery/pkg/util/yaml"
18
19
)
@@ -140,18 +141,27 @@ func (sv *semverVeneer) getVersionsFromStandardChannels(cfg *declcfg.Declarative
140
141
if err != nil {
141
142
return nil , err
142
143
}
144
+ if err = validateVersions (& bdm ); err != nil {
145
+ return nil , err
146
+ }
143
147
versions [candidateChannelName ] = bdm
144
148
145
149
bdm , err = sv .getVersionsFromChannel (sv .Fast .Bundles , cfg )
146
150
if err != nil {
147
151
return nil , err
148
152
}
153
+ if err = validateVersions (& bdm ); err != nil {
154
+ return nil , err
155
+ }
149
156
versions [fastChannelName ] = bdm
150
157
151
158
bdm , err = sv .getVersionsFromChannel (sv .Stable .Bundles , cfg )
152
159
if err != nil {
153
160
return nil , err
154
161
}
162
+ if err = validateVersions (& bdm ); err != nil {
163
+ return nil , err
164
+ }
155
165
versions [stableChannelName ] = bdm
156
166
157
167
return & versions , nil
@@ -189,9 +199,6 @@ func (sv *semverVeneer) getVersionsFromChannel(semverBundles []semverVeneerBundl
189
199
if err != nil {
190
200
return nil , fmt .Errorf ("bundle %q has invalid version %q: %v" , b .Name , props .Packages [0 ].Version , err )
191
201
}
192
- if len (v .Build ) > 0 {
193
- return nil , fmt .Errorf ("bundle %q uses build metadata in its versioning, which is not sortable: %v" , b .Name , v .String ())
194
- }
195
202
196
203
// package name detection
197
204
if sv .pkg != "" {
@@ -206,6 +213,10 @@ func (sv *semverVeneer) getVersionsFromChannel(semverBundles []semverVeneerBundl
206
213
sv .pkg = props .Packages [0 ].PackageName
207
214
}
208
215
216
+ if _ , ok := entries [b .Name ]; ok {
217
+ return nil , fmt .Errorf ("duplicate bundle name %q" , b .Name )
218
+ }
219
+
209
220
entries [b .Name ] = v
210
221
}
211
222
@@ -255,7 +266,7 @@ func (sv *semverVeneer) generateChannels(semverChannels *semverRenderedChannelVe
255
266
256
267
// sort the bundle names according to their semver, so we can walk in ascending order
257
268
bundleNamesByVersion := []string {}
258
- for b , _ := range bundles {
269
+ for b := range bundles {
259
270
bundleNamesByVersion = append (bundleNamesByVersion , b )
260
271
}
261
272
sort .Slice (bundleNamesByVersion , func (i , j int ) bool {
@@ -434,3 +445,41 @@ func MermaidChannelWriter(cfg declcfg.DeclarativeConfig, out io.Writer) error {
434
445
}
435
446
return nil
436
447
}
448
+
449
+ func withoutBuildMetadataConflict (versions * map [string ]semver.Version ) error {
450
+ errs := []error {}
451
+
452
+ // using the stringified semver because the semver package generates deterministic representations,
453
+ // and because the semver.Version contains slice fields which make it unsuitable as a map key
454
+ // stringified-semver.Version ==> incidence count
455
+ seen := make (map [string ]int )
456
+ for b := range * versions {
457
+ stripped := stripBuildMetadata ((* versions )[b ])
458
+ if _ , ok := seen [stripped ]; ! ok {
459
+ seen [stripped ] = 1
460
+ } else {
461
+ seen [stripped ] = seen [stripped ] + 1
462
+ errs = append (errs , fmt .Errorf ("bundle version %q cannot be compared to %q" , (* versions )[b ].String (), stripped ))
463
+ }
464
+ }
465
+
466
+ if len (errs ) != 0 {
467
+ return fmt .Errorf ("encountered bundle versions which differ only by build metadata, which cannot be ordered: %v" , errors .NewAggregate (errs ))
468
+ }
469
+
470
+ return nil
471
+ }
472
+
473
+ func validateVersions (versions * map [string ]semver.Version ) error {
474
+ // short-circuit if empty, since that is not an error
475
+ if len (* versions ) == 0 {
476
+ return nil
477
+ }
478
+ return withoutBuildMetadataConflict (versions )
479
+ }
480
+
481
+ // strips out the build metadata from a semver.Version and then stringifies it to make it suitable for collision detection
482
+ func stripBuildMetadata (v semver.Version ) string {
483
+ v .Build = nil
484
+ return v .String ()
485
+ }
0 commit comments