detached 3fc796d

fix(config): handle struct defaults with child keys in migration check

commit
3fc796d46f336e1bf42ecaeb36c64418145449d2
author
bnema · · 2025-12-27 18:56 UTC
parent
2ac17d0
signature
GPG Unverified2B82207D370127A0 The key is unknown
2 files · +22 -5
M internal/infrastructure/config/migrate.go +16 -5
@@ -212,8 +212,8 @@ func (m *Migrator) findMissingKeys(defaultKeys []string, userKeys map[string]boo
212212 missing := make([]string, 0)
213213
214214 for _, key := range defaultKeys {
215- // Check if key or any parent key exists in user config
216- if m.keyOrParentExists(key, userKeys) {
215+ // Check if key or any related key exists in user config
216+ if m.keyOrRelatedExists(key, userKeys) {
217217 continue
218218 }
219219 missing = append(missing, key)
@@ -224,9 +224,11 @@ func (m *Migrator) findMissingKeys(defaultKeys []string, userKeys map[string]boo
224224 return missing
225225 }
226226
227-// keyOrParentExists checks if a key or any of its parent keys exist in the map.
228-// This handles cases where a user has defined a parent section but not all children.
229-func (*Migrator) keyOrParentExists(key string, keys map[string]bool) bool {
227+// keyOrRelatedExists checks if a key, any parent, or any child keys exist in the map.
228+// This handles cases where:
229+// - User has defined a parent section but not all children
230+// - Default is a struct key but user has individual sub-keys (e.g., palettes)
231+func (*Migrator) keyOrRelatedExists(key string, keys map[string]bool) bool {
230232 if keys[key] {
231233 return true
232234 }
@@ -242,6 +244,15 @@ func (*Migrator) keyOrParentExists(key string, keys map[string]bool) bool {
242244 }
243245 }
244246
247+ // Check if any child keys exist (e.g., default is "appearance.dark_palette"
248+ // but user has "appearance.dark_palette.background")
249+ keyPrefix := key + "."
250+ for userKey := range keys {
251+ if strings.HasPrefix(userKey, keyPrefix) {
252+ return true
253+ }
254+ }
255+
245256 return false
246257 }
247258
@@ -212,8 +212,8 @@ func (m *Migrator) findMissingKeys(defaultKeys []string, userKeys map[string]boo
212 missing := make([]string, 0)212 missing := make([]string, 0)
213 213
214 for _, key := range defaultKeys {214 for _, key := range defaultKeys {
215- // Check if key or any parent key exists in user config215+ // Check if key or any related key exists in user config
216- if m.keyOrParentExists(key, userKeys) {216+ if m.keyOrRelatedExists(key, userKeys) {
217 continue217 continue
218 }218 }
219 missing = append(missing, key)219 missing = append(missing, key)
@@ -224,9 +224,11 @@ func (m *Migrator) findMissingKeys(defaultKeys []string, userKeys map[string]boo
224 return missing224 return missing
225 }225 }
226 226
227-// keyOrParentExists checks if a key or any of its parent keys exist in the map.227+// keyOrRelatedExists checks if a key, any parent, or any child keys exist in the map.
228-// This handles cases where a user has defined a parent section but not all children.228+// This handles cases where:
229-func (*Migrator) keyOrParentExists(key string, keys map[string]bool) bool {229+// - User has defined a parent section but not all children
230+// - Default is a struct key but user has individual sub-keys (e.g., palettes)
231+func (*Migrator) keyOrRelatedExists(key string, keys map[string]bool) bool {
230 if keys[key] {232 if keys[key] {
231 return true233 return true
232 }234 }
@@ -242,6 +244,15 @@ func (*Migrator) keyOrParentExists(key string, keys map[string]bool) bool {
242 }244 }
243 }245 }
244 246
247+ // Check if any child keys exist (e.g., default is "appearance.dark_palette"
248+ // but user has "appearance.dark_palette.background")
249+ keyPrefix := key + "."
250+ for userKey := range keys {
251+ if strings.HasPrefix(userKey, keyPrefix) {
252+ return true
253+ }
254+ }
255+
245 return false256 return false
246 }257 }
247 258
M internal/infrastructure/config/migrate_test.go +6 -0
@@ -229,6 +229,12 @@ func TestMigrator_FindMissingKeys(t *testing.T) {
229229 userKeys: map[string]bool{"parent": true},
230230 expected: []string{}, // Parent covers children
231231 },
232+ {
233+ name: "child keys cover parent (struct defaults like palettes)",
234+ defaultKeys: []string{"appearance.dark_palette"},
235+ userKeys: map[string]bool{"appearance.dark_palette.background": true, "appearance.dark_palette.accent": true},
236+ expected: []string{}, // Child keys mean parent is defined
237+ },
232238 }
233239
234240 for _, tt := range tests {
@@ -229,6 +229,12 @@ func TestMigrator_FindMissingKeys(t *testing.T) {
229 userKeys: map[string]bool{"parent": true},229 userKeys: map[string]bool{"parent": true},
230 expected: []string{}, // Parent covers children230 expected: []string{}, // Parent covers children
231 },231 },
232+ {
233+ name: "child keys cover parent (struct defaults like palettes)",
234+ defaultKeys: []string{"appearance.dark_palette"},
235+ userKeys: map[string]bool{"appearance.dark_palette.background": true, "appearance.dark_palette.accent": true},
236+ expected: []string{}, // Child keys mean parent is defined
237+ },
232 }238 }
233 239
234 for _, tt := range tests {240 for _, tt := range tests {

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 8

Tags 42

↑↓ moveenter openesc close