@@ -15,12 +15,55 @@ import (
1515
1616	"github.com/operator-framework/operator-registry/alpha/declcfg" 
1717	"github.com/operator-framework/operator-registry/alpha/property" 
18+ 	"github.com/operator-framework/operator-registry/alpha/template" 
1819)
1920
20- func  (t  Template ) Render (ctx  context.Context ) (* declcfg.DeclarativeConfig , error ) {
21+ // IO structs -- BEGIN 
22+ type  semverTemplateBundleEntry  struct  {
23+ 	Image  string  `json:"image,omitempty"` 
24+ }
25+ 
26+ type  semverTemplateChannelBundles  struct  {
27+ 	Bundles  []semverTemplateBundleEntry  `json:"bundles,omitempty"` 
28+ }
29+ 
30+ type  SemverTemplateData  struct  {
31+ 	Schema                        string                        `json:"schema"` 
32+ 	GenerateMajorChannels         bool                          `json:"generateMajorChannels,omitempty"` 
33+ 	GenerateMinorChannels         bool                          `json:"generateMinorChannels,omitempty"` 
34+ 	DefaultChannelTypePreference  streamType                    `json:"defaultChannelTypePreference,omitempty"` 
35+ 	Candidate                     semverTemplateChannelBundles  `json:"candidate,omitempty"` 
36+ 	Fast                          semverTemplateChannelBundles  `json:"fast,omitempty"` 
37+ 	Stable                        semverTemplateChannelBundles  `json:"stable,omitempty"` 
38+ 
39+ 	pkg             string  `json:"-"`  // the derived package name 
40+ 	defaultChannel  string  `json:"-"`  // detected "most stable" channel head 
41+ }
42+ 
43+ // IO structs -- END 
44+ 
45+ // SemverTemplate implements the common template interface 
46+ type  SemverTemplate  struct  {
47+ 	renderBundle  template.BundleRenderer 
48+ }
49+ 
50+ // NewTemplate creates a new semver template instance 
51+ func  NewTemplate (renderBundle  template.BundleRenderer ) template.Template  {
52+ 	return  & SemverTemplate {
53+ 		renderBundle : renderBundle ,
54+ 	}
55+ }
56+ 
57+ // RenderBundle implements the template.Template interface 
58+ func  (t  * SemverTemplate ) RenderBundle (ctx  context.Context , image  string ) (* declcfg.DeclarativeConfig , error ) {
59+ 	return  t .renderBundle (ctx , image )
60+ }
61+ 
62+ // Render implements the template.Template interface 
63+ func  (t  * SemverTemplate ) Render (ctx  context.Context , reader  io.Reader ) (* declcfg.DeclarativeConfig , error ) {
2164	var  out  declcfg.DeclarativeConfig 
2265
23- 	sv , err  :=  readFile (t . Data )
66+ 	sv , err  :=  readFile (reader )
2467	if  err  !=  nil  {
2568		return  nil , fmt .Errorf ("render: unable to read file: %v" , err )
2669	}
@@ -58,7 +101,79 @@ func (t Template) Render(ctx context.Context) (*declcfg.DeclarativeConfig, error
58101	return  & out , nil 
59102}
60103
61- func  buildBundleList (t  semverTemplate ) map [string ]string  {
104+ // Schema implements the template.Template interface 
105+ func  (t  * SemverTemplate ) Schema () string  {
106+ 	return  schema 
107+ }
108+ 
109+ // Factory implements the template.TemplateFactory interface 
110+ type  Factory  struct {}
111+ 
112+ // CreateTemplate implements the template.TemplateFactory interface 
113+ func  (f  * Factory ) CreateTemplate (renderBundle  template.BundleRenderer ) template.Template  {
114+ 	return  NewTemplate (renderBundle )
115+ }
116+ 
117+ // Schema implements the template.TemplateFactory interface 
118+ func  (f  * Factory ) Schema () string  {
119+ 	return  schema 
120+ }
121+ 
122+ const  schema  string  =  "olm.semver" 
123+ 
124+ // channel "archetypes", restricted in this iteration to just these 
125+ type  channelArchetype  string 
126+ 
127+ const  (
128+ 	candidateChannelArchetype  channelArchetype  =  "candidate" 
129+ 	fastChannelArchetype       channelArchetype  =  "fast" 
130+ 	stableChannelArchetype     channelArchetype  =  "stable" 
131+ )
132+ 
133+ // mapping channel name --> stability, where higher values indicate greater stability 
134+ var  channelPriorities  =  map [channelArchetype ]int {candidateChannelArchetype : 0 , fastChannelArchetype : 1 , stableChannelArchetype : 2 }
135+ 
136+ // sorting capability for a slice according to the assigned channelPriorities 
137+ type  byChannelPriority  []channelArchetype 
138+ 
139+ func  (b  byChannelPriority ) Len () int  { return  len (b ) }
140+ func  (b  byChannelPriority ) Less (i , j  int ) bool  {
141+ 	return  channelPriorities [b [i ]] <  channelPriorities [b [j ]]
142+ }
143+ func  (b  byChannelPriority ) Swap (i , j  int ) { b [i ], b [j ] =  b [j ], b [i ] }
144+ 
145+ type  streamType  string 
146+ 
147+ const  defaultStreamType  streamType  =  "" 
148+ const  minorStreamType  streamType  =  "minor" 
149+ const  majorStreamType  streamType  =  "major" 
150+ 
151+ // general preference for minor channels 
152+ var  streamTypePriorities  =  map [streamType ]int {minorStreamType : 2 , majorStreamType : 1 , defaultStreamType : 0 }
153+ 
154+ // map of archetypes --> bundles --> bundle-version from the input file 
155+ type  bundleVersions  map [channelArchetype ]map [string ]semver.Version  // e.g. srcv["stable"]["example-operator.v1.0.0"] = 1.0.0 
156+ 
157+ // the "high-water channel" struct functions as a freely-rising indicator of the "most stable" channel head, so we can use that 
158+ // later as the package's defaultChannel attribute 
159+ type  highwaterChannel  struct  {
160+ 	archetype  channelArchetype 
161+ 	kind       streamType 
162+ 	version    semver.Version 
163+ 	name       string 
164+ }
165+ 
166+ // entryTuple represents a channel entry with its associated metadata 
167+ type  entryTuple  struct  {
168+ 	arch     channelArchetype 
169+ 	kind     streamType 
170+ 	parent   string 
171+ 	name     string 
172+ 	version  semver.Version 
173+ 	index    int 
174+ }
175+ 
176+ func  buildBundleList (t  SemverTemplateData ) map [string ]string  {
62177	dict  :=  make (map [string ]string )
63178	for  _ , bl  :=  range  []semverTemplateChannelBundles {t .Candidate , t .Fast , t .Stable } {
64179		for  _ , b  :=  range  bl .Bundles  {
@@ -70,13 +185,13 @@ func buildBundleList(t semverTemplate) map[string]string {
70185	return  dict 
71186}
72187
73- func  readFile (reader  io.Reader ) (* semverTemplate , error ) {
188+ func  readFile (reader  io.Reader ) (* SemverTemplateData , error ) {
74189	data , err  :=  io .ReadAll (reader )
75190	if  err  !=  nil  {
76191		return  nil , err 
77192	}
78193
79- 	sv  :=  semverTemplate {}
194+ 	sv  :=  SemverTemplateData {}
80195	if  err  :=  yaml .UnmarshalStrict (data , & sv ); err  !=  nil  {
81196		return  nil , err 
82197	}
@@ -115,7 +230,7 @@ func readFile(reader io.Reader) (*semverTemplate, error) {
115230	return  & sv , nil 
116231}
117232
118- func  (sv  * semverTemplate ) getVersionsFromStandardChannels (cfg  * declcfg.DeclarativeConfig , bundleDict  map [string ]string ) (* bundleVersions , error ) {
233+ func  (sv  * SemverTemplateData ) getVersionsFromStandardChannels (cfg  * declcfg.DeclarativeConfig , bundleDict  map [string ]string ) (* bundleVersions , error ) {
119234	versions  :=  bundleVersions {}
120235
121236	bdm , err  :=  sv .getVersionsFromChannel (sv .Candidate .Bundles , bundleDict , cfg )
@@ -148,7 +263,7 @@ func (sv *semverTemplate) getVersionsFromStandardChannels(cfg *declcfg.Declarati
148263	return  & versions , nil 
149264}
150265
151- func  (sv  * semverTemplate ) getVersionsFromChannel (semverBundles  []semverTemplateBundleEntry , bundleDict  map [string ]string , cfg  * declcfg.DeclarativeConfig ) (map [string ]semver.Version , error ) {
266+ func  (sv  * SemverTemplateData ) getVersionsFromChannel (semverBundles  []semverTemplateBundleEntry , bundleDict  map [string ]string , cfg  * declcfg.DeclarativeConfig ) (map [string ]semver.Version , error ) {
152267	entries  :=  make (map [string ]semver.Version )
153268
154269	// we iterate over the channel bundles from the template, to: 
@@ -210,7 +325,7 @@ func (sv *semverTemplate) getVersionsFromChannel(semverBundles []semverTemplateB
210325// - within the same minor version (Y-stream), the head of the channel should have a 'skips' encompassing all lesser Y.Z versions of the bundle enumerated in the template. 
211326// along the way, uses a highwaterChannel marker to identify the "most stable" channel head to be used as the default channel for the generated package 
212327
213- func  (sv  * semverTemplate ) generateChannels (semverChannels  * bundleVersions ) []declcfg.Channel  {
328+ func  (sv  * SemverTemplateData ) generateChannels (semverChannels  * bundleVersions ) []declcfg.Channel  {
214329	outChannels  :=  []declcfg.Channel {}
215330
216331	// sort the channel archetypes in ascending order so we can traverse the bundles in order of 
@@ -287,7 +402,7 @@ func (sv *semverTemplate) generateChannels(semverChannels *bundleVersions) []dec
287402	return  outChannels 
288403}
289404
290- func  (sv  * semverTemplate ) linkChannels (unlinkedChannels  map [string ]* declcfg.Channel , entries  []entryTuple ) []declcfg.Channel  {
405+ func  (sv  * SemverTemplateData ) linkChannels (unlinkedChannels  map [string ]* declcfg.Channel , entries  []entryTuple ) []declcfg.Channel  {
291406	channels  :=  []declcfg.Channel {}
292407
293408	// sort to force partitioning by archetype --> kind --> semver 
0 commit comments