@@ -23,6 +23,8 @@ type DiffGenerator struct {
2323 SkipDependencies bool
2424 // Includer for adding catalog objects to Run() output.
2525 Includer DiffIncluder
26+ // IncludeAdditively catalog objects specified in Includer in headsOnly mode.
27+ IncludeAdditively bool
2628
2729 initOnce sync.Once
2830}
@@ -32,13 +34,21 @@ func (g *DiffGenerator) init() {
3234 if g .Logger == nil {
3335 g .Logger = & logrus.Entry {}
3436 }
37+ if g .Includer .Logger == nil {
38+ g .Includer .Logger = g .Logger
39+ }
3540 })
3641}
3742
38- // Run returns a Model containing everything in newModel not in oldModel,
39- // and all bundles that exist in oldModel but are different in newModel.
40- // If oldModel is empty, only channel heads in newModel's packages are
41- // added to the output Model. All dependencies not in oldModel are also added.
43+ // Run returns a Model containing a subset of catalog objects in newModel:
44+ // - If g.Includer contains objects:
45+ // - If g.IncludeAdditively is false, a diff will be generated only on those objects,
46+ // depending on the mode.
47+ // - If g.IncludeAdditionally is true, the diff will contain included objects,
48+ // plus those added by the mode.
49+ // - If in heads-only mode (oldModel == nil), then the heads of channels are added to the output.
50+ // - If in latest mode, a diff between old and new Models is added to the output.
51+ // - Dependencies are added in all modes if g.SkipDependencies is false.
4252func (g * DiffGenerator ) Run (oldModel , newModel model.Model ) (model.Model , error ) {
4353 g .init ()
4454
@@ -48,73 +58,101 @@ func (g *DiffGenerator) Run(oldModel, newModel model.Model) (model.Model, error)
4858
4959 outputModel := model.Model {}
5060
51- // Add included packages/channels/bundles from newModel to outputModel.
52- // Code further down handles packages and channels included in outputModel.
53- g .Includer .Logger = g .Logger
54- if err := g .Includer .Run (newModel , outputModel ); err != nil {
55- return nil , err
56- }
57-
58- if len (oldModel ) == 0 {
59- // Heads-only mode.
61+ // Prunes old objects from outputModel if they exist.
62+ latestPruneFromOutput := func () error {
6063
61- // Make shallow copies of packages and channels that are only
62- // filled with channel heads.
63- for _ , newPkg := range newModel {
64- // This package may have been created in the include step.
65- outputPkg , pkgIncluded := outputModel [newPkg .Name ]
66- if ! pkgIncluded {
67- outputPkg = copyPackageNoChannels (newPkg )
68- outputModel [outputPkg .Name ] = outputPkg
69- }
70- for _ , newCh := range newPkg .Channels {
71- if _ , chIncluded := outputPkg .Channels [newCh .Name ]; chIncluded {
72- // Head (and other bundles) were added in the include step.
73- continue
74- }
75- outputCh := copyChannelNoBundles (newCh , outputPkg )
76- outputPkg .Channels [outputCh .Name ] = outputCh
77- head , err := newCh .Head ()
78- if err != nil {
79- return nil , err
80- }
81- outputBundle := copyBundle (head , outputCh , outputPkg )
82- outputModel .AddBundle (* outputBundle )
83- }
84- }
85- } else {
86- // Latest mode.
87-
88- // Copy newModel to create an output model by deletion,
89- // which is more succinct than by addition and potentially
90- // more memory efficient.
91- for _ , newPkg := range newModel {
92- if _ , pkgIncluded := outputModel [newPkg .Name ]; pkgIncluded {
93- // The user has specified the state they want this package to have in the diff
94- // via an inclusion entry, so the package created above should not be changed.
95- continue
96- }
97- outputModel [newPkg .Name ] = copyPackage (newPkg )
98- }
99-
100- // NB(estroz): if a net-new package or channel is published,
101- // this currently adds the entire package. I'm fairly sure
102- // this behavior is ok because the next diff after a new
103- // package is published still has only new data.
10464 for _ , outputPkg := range outputModel {
10565 oldPkg , oldHasPkg := oldModel [outputPkg .Name ]
10666 if ! oldHasPkg {
10767 // outputPkg was already copied to outputModel above.
10868 continue
10969 }
110- if err := diffPackages (oldPkg , outputPkg ); err != nil {
111- return nil , err
70+ if err := pruneOldFromNewPackage (oldPkg , outputPkg ); err != nil {
71+ return err
11272 }
11373 if len (outputPkg .Channels ) == 0 {
11474 // Remove empty packages.
11575 delete (outputModel , outputPkg .Name )
11676 }
11777 }
78+
79+ return nil
80+ }
81+
82+ headsOnlyMode := len (oldModel ) == 0
83+ latestMode := ! headsOnlyMode
84+ isInclude := len (g .Includer .Packages ) != 0
85+
86+ switch {
87+ case ! g .IncludeAdditively && isInclude : // Only diff between included objects.
88+
89+ // Add included packages/channels/bundles from newModel to outputModel.
90+ if err := g .Includer .Run (newModel , outputModel ); err != nil {
91+ return nil , err
92+ }
93+
94+ if latestMode {
95+ if err := latestPruneFromOutput (); err != nil {
96+ return nil , err
97+ }
98+ }
99+
100+ case isInclude : // Add included objects to outputModel.
101+
102+ // Add included packages/channels/bundles from newModel to outputModel.
103+ if err := g .Includer .Run (newModel , outputModel ); err != nil {
104+ return nil , err
105+ }
106+
107+ fallthrough
108+ default :
109+
110+ if headsOnlyMode { // Net-new diff of heads only.
111+
112+ // Make shallow copies of packages and channels that are only
113+ // filled with channel heads.
114+ for _ , newPkg := range newModel {
115+ // This package may have been created in the include step.
116+ outputPkg , pkgIncluded := outputModel [newPkg .Name ]
117+ if ! pkgIncluded {
118+ outputPkg = copyPackageNoChannels (newPkg )
119+ outputModel [outputPkg .Name ] = outputPkg
120+ }
121+ for _ , newCh := range newPkg .Channels {
122+ if _ , chIncluded := outputPkg .Channels [newCh .Name ]; chIncluded {
123+ // Head (and other bundles) were added in the include step.
124+ continue
125+ }
126+ outputCh := copyChannelNoBundles (newCh , outputPkg )
127+ outputPkg .Channels [outputCh .Name ] = outputCh
128+ head , err := newCh .Head ()
129+ if err != nil {
130+ return nil , err
131+ }
132+ outputBundle := copyBundle (head , outputCh , outputPkg )
133+ outputModel .AddBundle (* outputBundle )
134+ }
135+ }
136+
137+ } else { // Diff between old and new Model.
138+
139+ // Copy newModel to create an output model by deletion,
140+ // which is more succinct than by addition.
141+ for _ , newPkg := range newModel {
142+ if _ , pkgIncluded := outputModel [newPkg .Name ]; pkgIncluded {
143+ // The user has specified the state they want this package to have in the diff
144+ // via an inclusion entry, so the package created above should not be changed.
145+ continue
146+ }
147+ outputModel [newPkg .Name ] = copyPackage (newPkg )
148+ }
149+
150+ if err := latestPruneFromOutput (); err != nil {
151+ return nil , err
152+ }
153+
154+ }
155+
118156 }
119157
120158 if ! g .SkipDependencies {
@@ -126,8 +164,8 @@ func (g *DiffGenerator) Run(oldModel, newModel model.Model) (model.Model, error)
126164
127165 // Default channel may not have been copied, so set it to the new default channel here.
128166 for _ , outputPkg := range outputModel {
129- outputHasDefault := false
130167 newPkg := newModel [outputPkg .Name ]
168+ var outputHasDefault bool
131169 outputPkg .DefaultChannel , outputHasDefault = outputPkg .Channels [newPkg .DefaultChannel .Name ]
132170 if ! outputHasDefault {
133171 // Create a name-only channel since oldModel contains the channel already.
@@ -138,9 +176,9 @@ func (g *DiffGenerator) Run(oldModel, newModel model.Model) (model.Model, error)
138176 return outputModel , nil
139177}
140178
141- // diffPackages removes any bundles and channels from newPkg that
179+ // pruneOldFromNewPackage prune any bundles and channels from newPkg that
142180// are in oldPkg, but not those that differ in any way.
143- func diffPackages (oldPkg , newPkg * model.Package ) error {
181+ func pruneOldFromNewPackage (oldPkg , newPkg * model.Package ) error {
144182 for _ , newCh := range newPkg .Channels {
145183 oldCh , oldHasCh := oldPkg .Channels [newCh .Name ]
146184 if ! oldHasCh {
0 commit comments