Skip to content

Commit f66edbf

Browse files
authored
Merge pull request #2540 from keboola/mvasko/PSGO-198-kbcignore-branch-ignore
feat(.kbcignore): support ignoring whole branches
2 parents cbea339 + f32f62b commit f66edbf

59 files changed

Lines changed: 596 additions & 4 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

internal/pkg/project/ignore/file_test.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,16 @@ func Test_loadFile(t *testing.T) {
2929
assert.Equal(t, "keboola.bar/678/34\nkeboola.foo/345", file.rawStringPattern)
3030
}
3131

32+
func newTestRegistryWithSlashBranch(t *testing.T) *registry.Registry {
33+
t.Helper()
34+
r := registry.New(knownpaths.NewNop(t.Context()), naming.NewRegistry(), model.NewComponentsMap(nil), model.SortByPath)
35+
require.NoError(t, r.Set(&model.BranchState{
36+
BranchManifest: &model.BranchManifest{BranchKey: model.BranchKey{ID: 789}},
37+
Local: &model.Branch{Name: "feature/foo"},
38+
}))
39+
return r
40+
}
41+
3242
func newTestRegistry(t *testing.T) *registry.Registry {
3343
t.Helper()
3444

internal/pkg/project/ignore/ignore.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,17 @@ func (f *File) parseIgnoredPatterns() []string {
3636

3737
// applyIgnorePattern applies a single ignore pattern, marking the appropriate config or row as ignored.
3838
func (f *File) applyIgnorePattern(ignoreConfig string) error {
39-
parts := strings.Split(ignoreConfig, "/")
39+
// Branch pattern: "branch/<name>" — name may itself contain "/".
40+
if strings.HasPrefix(ignoreConfig, "branch/") {
41+
branchName := strings.TrimPrefix(ignoreConfig, "branch/")
42+
f.state.IgnoreBranch(branchName)
43+
return nil
44+
}
4045

46+
parts := strings.Split(ignoreConfig, "/")
4147
switch len(parts) {
4248
case 2:
43-
// Ignore config by ID and name.
49+
// Ignore config by componentID/configID.
4450
configID, componentID := parts[1], parts[0]
4551
f.state.IgnoreConfig(configID, componentID)
4652
case 3:

internal/pkg/project/ignore/ignore_test.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,46 @@ func TestFile_IgnoreConfigsOrRows(t *testing.T) {
3131
assert.Equal(t, "345", registry.IgnoredConfigs()[0].ID.String())
3232
}
3333

34+
func TestFile_IgnoreConfigsOrRows_Branch(t *testing.T) {
35+
t.Parallel()
36+
37+
ctx := t.Context()
38+
r := newTestRegistry(t)
39+
fs := aferofs.NewMemoryFs()
40+
41+
require.NoError(t, fs.WriteFile(ctx, filesystem.NewRawFile(`foo/bar1`, "branch/Main")))
42+
43+
file, err := LoadFile(ctx, fs, r, "foo/bar1")
44+
require.NoError(t, err)
45+
46+
require.NoError(t, file.IgnoreConfigsOrRows())
47+
48+
ignored := r.IgnoredBranches()
49+
require.Len(t, ignored, 1)
50+
assert.Equal(t, "123", ignored[0].ID.String())
51+
}
52+
53+
func TestFile_IgnoreConfigsOrRows_BranchWithSlashInName(t *testing.T) {
54+
t.Parallel()
55+
56+
ctx := t.Context()
57+
r := newTestRegistryWithSlashBranch(t)
58+
fs := aferofs.NewMemoryFs()
59+
60+
require.NoError(t, fs.WriteFile(ctx, filesystem.NewRawFile(`foo/bar1`, "branch/feature/foo")))
61+
62+
file, err := LoadFile(ctx, fs, r, "foo/bar1")
63+
require.NoError(t, err)
64+
65+
require.NoError(t, file.IgnoreConfigsOrRows())
66+
67+
// Branch "feature/foo" should be ignored, not misidentified as a config-row pattern.
68+
ignored := r.IgnoredBranches()
69+
require.Len(t, ignored, 1)
70+
assert.Equal(t, "789", ignored[0].ID.String())
71+
assert.Empty(t, r.IgnoredConfigRows())
72+
}
73+
3474
func Test_applyIgnoredPatterns(t *testing.T) {
3575
t.Parallel()
3676
projectState := newTestRegistry(t)
@@ -83,6 +123,20 @@ func Test_applyIgnoredPatterns(t *testing.T) {
83123
},
84124
wantErr: assert.NoError,
85125
},
126+
{
127+
name: "branch pattern",
128+
args: args{
129+
pattern: "branch/Main",
130+
},
131+
wantErr: assert.NoError,
132+
},
133+
{
134+
name: "branch pattern with slash in name",
135+
args: args{
136+
pattern: "branch/feature/foo",
137+
},
138+
wantErr: assert.NoError,
139+
},
86140
}
87141
for _, tt := range tests {
88142
t.Run(tt.name, func(t *testing.T) {

internal/pkg/state/registry/registry.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,59 @@ func (s *Registry) Configs() (configs []*model.ConfigState) {
150150
return configs
151151
}
152152

153+
func (s *Registry) IgnoreBranch(branchName string) {
154+
for _, branch := range s.Branches() {
155+
if branch.LocalOrRemoteState().(*model.Branch).Name == branchName {
156+
branch.Ignore = true
157+
}
158+
}
159+
}
160+
161+
func (s *Registry) IgnoredBranches() (branches []*model.BranchState) {
162+
for _, branch := range s.Branches() {
163+
if branch.Ignore {
164+
branches = append(branches, branch)
165+
}
166+
}
167+
return branches
168+
}
169+
170+
// NullIgnoredBranchStates nulls local+remote state for every ignored branch and for all
171+
// configs/rows belonging to those branches, using a single pass over All() to avoid
172+
// repeated sorts.
173+
func (s *Registry) NullIgnoredBranchStates() {
174+
ignored := s.IgnoredBranches()
175+
if len(ignored) == 0 {
176+
return
177+
}
178+
179+
// Build set of ignored branch keys for O(1) lookup.
180+
ignoredKeys := make(map[model.BranchKey]struct{}, len(ignored))
181+
for _, b := range ignored {
182+
ignoredKeys[b.BranchKey] = struct{}{}
183+
}
184+
185+
for _, obj := range s.All() {
186+
switch v := obj.(type) {
187+
case *model.BranchState:
188+
if _, ok := ignoredKeys[v.BranchKey]; ok {
189+
v.SetLocalState(nil)
190+
v.SetRemoteState(nil)
191+
}
192+
case *model.ConfigState:
193+
if _, ok := ignoredKeys[model.BranchKey{ID: v.BranchID}]; ok {
194+
v.SetLocalState(nil)
195+
v.SetRemoteState(nil)
196+
}
197+
case *model.ConfigRowState:
198+
if _, ok := ignoredKeys[model.BranchKey{ID: v.BranchID}]; ok {
199+
v.SetLocalState(nil)
200+
v.SetRemoteState(nil)
201+
}
202+
}
203+
}
204+
}
205+
153206
func (s *Registry) IgnoreConfig(ignoreID string, componentID string) {
154207
for _, object := range s.All() {
155208
if v, ok := object.(*model.ConfigState); ok {

internal/pkg/state/registry/registry_test.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,50 @@ func TestRegistry_GetByPath(t *testing.T) {
228228
assert.True(t, found)
229229
}
230230

231+
func TestNullIgnoredBranchStates(t *testing.T) {
232+
t.Parallel()
233+
s := newTestState(t, knownpaths.NewNop(t.Context()))
234+
235+
// Initially all 6 objects (2 branches, 2 configs, 2 rows) are present.
236+
assert.Len(t, s.All(), 6)
237+
238+
// Ignore the "Main" branch (ID 123).
239+
s.IgnoreBranch("Main")
240+
s.NullIgnoredBranchStates()
241+
242+
// Branch 123, its 2 configs, and 2 config rows are now invisible (both states nil).
243+
// Only branch 567 remains.
244+
assert.Len(t, s.All(), 1)
245+
assert.Equal(t, BranchKey{ID: 567}, s.Branches()[0].BranchKey)
246+
assert.Empty(t, s.Configs())
247+
assert.Empty(t, s.ConfigRows())
248+
}
249+
250+
func TestIgnoreBranch(t *testing.T) {
251+
t.Parallel()
252+
s := newTestState(t, knownpaths.NewNop(t.Context()))
253+
254+
// Before ignoring, no branches are ignored.
255+
assert.Empty(t, s.IgnoredBranches())
256+
257+
// Ignore branch "Main" (ID 123).
258+
s.IgnoreBranch("Main")
259+
260+
// Only the "Main" branch is ignored.
261+
ignored := s.IgnoredBranches()
262+
require.Len(t, ignored, 1)
263+
assert.Equal(t, BranchKey{ID: 123}, ignored[0].BranchKey)
264+
265+
// "Foo Bar Branch" (ID 567) is not affected.
266+
allBranches := s.Branches()
267+
require.Len(t, allBranches, 2)
268+
for _, b := range allBranches {
269+
if b.ID == 567 {
270+
assert.False(t, b.Ignore)
271+
}
272+
}
273+
}
274+
231275
func newTestState(t *testing.T, paths *knownpaths.Paths) *Registry {
232276
t.Helper()
233277
registry := New(paths, naming.NewRegistry(), NewComponentsMap(nil), SortByPath)

pkg/lib/operation/project/sync/pull/operation.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ func Run(ctx context.Context, projectState *project.State, o Options, d dependen
6363
return err
6464
}
6565

66-
ignoreConfigsAndRows(projectState)
66+
ignoreBranchesConfigsAndRows(projectState)
6767
}
6868

6969
// Diff
@@ -156,12 +156,15 @@ func Run(ctx context.Context, projectState *project.State, o Options, d dependen
156156
return nil
157157
}
158158

159-
func ignoreConfigsAndRows(projectState *project.State) {
159+
func ignoreBranchesConfigsAndRows(projectState *project.State) {
160160
for _, v := range projectState.IgnoredConfigRows() {
161161
v.SetRemoteState(nil)
162162
}
163163

164164
for _, v := range projectState.IgnoredConfigs() {
165165
v.SetRemoteState(nil)
166166
}
167+
168+
// Null both states for ignored branches so they are invisible to the diff.
169+
projectState.NullIgnoredBranchStates()
167170
}

pkg/lib/operation/project/sync/push/operation.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,9 @@ func Run(ctx context.Context, projectState *project.State, o Options, d dependen
7878
if err = file.IgnoreConfigsOrRows(); err != nil {
7979
return err
8080
}
81+
82+
// Make ignored branches invisible to the push diff.
83+
projectState.NullIgnoredBranchStates()
8184
}
8285

8386
// Diff

test/cli/pull/ignore-branch/args

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pull --verbose --storage-api-token %%TEST_KBC_STORAGE_API_TOKEN%%
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
0
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+

0 commit comments

Comments
 (0)