Skip to content

Commit 29cd8e3

Browse files
author
Eric Stroczynski
authored
feat(diff): change default behavior to prune by omission (#819)
Signed-off-by: Eric Stroczynski <[email protected]>
1 parent d4b7d96 commit 29cd8e3

File tree

5 files changed

+970
-111
lines changed

5 files changed

+970
-111
lines changed

alpha/action/diff.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ type Diff struct {
2626
SkipDependencies bool
2727

2828
IncludeConfig DiffIncludeConfig
29+
// IncludeAdditively catalog objects specified in IncludeConfig.
30+
IncludeAdditively bool
2931

3032
Logger *logrus.Entry
3133
}
@@ -69,9 +71,10 @@ func (a Diff) Run(ctx context.Context) (*declcfg.DeclarativeConfig, error) {
6971
}
7072

7173
g := &declcfg.DiffGenerator{
72-
Logger: a.Logger,
73-
SkipDependencies: a.SkipDependencies,
74-
Includer: convertIncludeConfigToIncluder(a.IncludeConfig),
74+
Logger: a.Logger,
75+
SkipDependencies: a.SkipDependencies,
76+
Includer: convertIncludeConfigToIncluder(a.IncludeConfig),
77+
IncludeAdditively: a.IncludeAdditively,
7578
}
7679
diffModel, err := g.Run(oldModel, newModel)
7780
if err != nil {

alpha/action/diff_test.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ func TestDiff(t *testing.T) {
6060
IncludeConfig: DiffIncludeConfig{
6161
Packages: []DiffIncludePackage{{Name: "baz"}},
6262
},
63+
IncludeAdditively: true,
6364
},
6465
expectedCfg: loadDirFS(t, indicesDir, filepath.Join("testdata", "index-declcfgs", "exp-include-pkg")),
6566
assertion: require.NoError,
@@ -77,6 +78,7 @@ func TestDiff(t *testing.T) {
7778
},
7879
},
7980
},
81+
IncludeAdditively: true,
8082
},
8183
expectedCfg: loadDirFS(t, indicesDir, filepath.Join("testdata", "index-declcfgs", "exp-include-channel")),
8284
assertion: require.NoError,
@@ -94,6 +96,7 @@ func TestDiff(t *testing.T) {
9496
},
9597
},
9698
},
99+
IncludeAdditively: true,
97100
},
98101
expectedCfg: loadDirFS(t, indicesDir, filepath.Join("testdata", "index-declcfgs", "exp-include-channel")),
99102
assertion: require.NoError,
@@ -111,6 +114,7 @@ func TestDiff(t *testing.T) {
111114
},
112115
},
113116
},
117+
IncludeAdditively: true,
114118
},
115119
expectedCfg: loadDirFS(t, indicesDir, filepath.Join("testdata", "index-declcfgs", "exp-include-channel")),
116120
assertion: require.NoError,
@@ -129,6 +133,7 @@ func TestDiff(t *testing.T) {
129133
},
130134
},
131135
},
136+
IncludeAdditively: true,
132137
},
133138
expectedCfg: loadDirFS(t, indicesDir, filepath.Join("testdata", "index-declcfgs", "exp-include-channel")),
134139
assertion: require.NoError,

alpha/declcfg/diff.go

Lines changed: 99 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -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.
4252
func (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

Comments
 (0)