@@ -13,6 +13,7 @@ import (
1313 "github.com/operator-framework/operator-registry/alpha/declcfg"
1414 "github.com/operator-framework/operator-registry/alpha/property"
1515 "github.com/operator-framework/operator-registry/pkg/image"
16+ "k8s.io/apimachinery/pkg/util/errors"
1617 "k8s.io/apimachinery/pkg/util/sets"
1718 "k8s.io/apimachinery/pkg/util/yaml"
1819)
@@ -140,18 +141,27 @@ func (sv *semverVeneer) getVersionsFromStandardChannels(cfg *declcfg.Declarative
140141 if err != nil {
141142 return nil , err
142143 }
144+ if err = validateVersions (& bdm ); err != nil {
145+ return nil , err
146+ }
143147 versions [candidateChannelName ] = bdm
144148
145149 bdm , err = sv .getVersionsFromChannel (sv .Fast .Bundles , cfg )
146150 if err != nil {
147151 return nil , err
148152 }
153+ if err = validateVersions (& bdm ); err != nil {
154+ return nil , err
155+ }
149156 versions [fastChannelName ] = bdm
150157
151158 bdm , err = sv .getVersionsFromChannel (sv .Stable .Bundles , cfg )
152159 if err != nil {
153160 return nil , err
154161 }
162+ if err = validateVersions (& bdm ); err != nil {
163+ return nil , err
164+ }
155165 versions [stableChannelName ] = bdm
156166
157167 return & versions , nil
@@ -189,9 +199,6 @@ func (sv *semverVeneer) getVersionsFromChannel(semverBundles []semverVeneerBundl
189199 if err != nil {
190200 return nil , fmt .Errorf ("bundle %q has invalid version %q: %v" , b .Name , props .Packages [0 ].Version , err )
191201 }
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- }
195202
196203 // package name detection
197204 if sv .pkg != "" {
@@ -206,6 +213,10 @@ func (sv *semverVeneer) getVersionsFromChannel(semverBundles []semverVeneerBundl
206213 sv .pkg = props .Packages [0 ].PackageName
207214 }
208215
216+ if _ , ok := entries [b .Name ]; ok {
217+ return nil , fmt .Errorf ("duplicate bundle name %q" , b .Name )
218+ }
219+
209220 entries [b .Name ] = v
210221 }
211222
@@ -255,7 +266,7 @@ func (sv *semverVeneer) generateChannels(semverChannels *semverRenderedChannelVe
255266
256267 // sort the bundle names according to their semver, so we can walk in ascending order
257268 bundleNamesByVersion := []string {}
258- for b , _ := range bundles {
269+ for b := range bundles {
259270 bundleNamesByVersion = append (bundleNamesByVersion , b )
260271 }
261272 sort .Slice (bundleNamesByVersion , func (i , j int ) bool {
@@ -434,3 +445,41 @@ func MermaidChannelWriter(cfg declcfg.DeclarativeConfig, out io.Writer) error {
434445 }
435446 return nil
436447}
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