Skip to content

Commit 73c0469

Browse files
committed
fix: extract macos_prefs from nested snapshot on config import
Exported configs from the dashboard nest macos_prefs inside the snapshot object rather than at the top level. Import/install only read the top-level field, so macos_prefs were silently dropped. Add backfillMacOSPrefsFromSnapshot fallback that reads from snapshot.macos_prefs when the top-level field is empty. Top-level macos_prefs take precedence when both are present.
1 parent 2f15628 commit 73c0469

2 files changed

Lines changed: 68 additions & 0 deletions

File tree

internal/config/config.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ func UnmarshalRemoteConfigFlexible(data []byte) (*RemoteConfig, error) {
111111
// Try direct unmarshal first (flat string arrays).
112112
var rc RemoteConfig
113113
if err := json.Unmarshal(data, &rc); err == nil {
114+
backfillMacOSPrefsFromSnapshot(&rc, data)
114115
return &rc, nil
115116
}
116117

@@ -177,9 +178,31 @@ func UnmarshalRemoteConfigFlexible(data []byte) (*RemoteConfig, error) {
177178
if err := json.Unmarshal(normalised, &result); err != nil {
178179
return nil, err
179180
}
181+
backfillMacOSPrefsFromSnapshot(&result, data)
180182
return &result, nil
181183
}
182184

185+
// backfillMacOSPrefsFromSnapshot copies macos_prefs from the embedded snapshot
186+
// object when the top-level field is empty. This handles exported configs where
187+
// macos_prefs are nested under "snapshot" rather than at the top level.
188+
// Callers are responsible for calling Validate() on the returned RemoteConfig.
189+
func backfillMacOSPrefsFromSnapshot(rc *RemoteConfig, data []byte) {
190+
if len(rc.MacOSPrefs) > 0 {
191+
return
192+
}
193+
var wrapper struct {
194+
Snapshot struct {
195+
MacOSPrefs []RemoteMacOSPref `json:"macos_prefs"`
196+
} `json:"snapshot"`
197+
}
198+
// Unmarshal error is intentionally ignored: data was already successfully
199+
// parsed once, so failure here means the snapshot sub-object is malformed
200+
// and we simply skip backfill rather than failing the entire load.
201+
if err := json.Unmarshal(data, &wrapper); err == nil && len(wrapper.Snapshot.MacOSPrefs) > 0 {
202+
rc.MacOSPrefs = wrapper.Snapshot.MacOSPrefs
203+
}
204+
}
205+
183206
var (
184207
pkgNameRe = regexp.MustCompile(`^[a-zA-Z0-9@/_.-]+$`)
185208
tapNameRe = regexp.MustCompile(`^[a-zA-Z0-9_-]+/[a-zA-Z0-9_-]+$`)

internal/config/config_test.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,51 @@ func TestUnmarshalRemoteConfigFlexible_PreservesOtherFields(t *testing.T) {
426426
assert.Len(t, rc.MacOSPrefs, 1)
427427
}
428428

429+
func TestUnmarshalRemoteConfigFlexible_MacOSPrefsFromSnapshot(t *testing.T) {
430+
data := []byte(`{
431+
"name": "test config",
432+
"packages": [
433+
{"name": "git", "type": "formula"}
434+
],
435+
"snapshot": {
436+
"version": 1,
437+
"packages": {"formulae": ["git"], "casks": [], "taps": [], "npm": []},
438+
"macos_prefs": [
439+
{"domain": "com.apple.dock", "key": "autohide", "type": "bool", "value": "true", "desc": "Auto-hide dock"},
440+
{"domain": "NSGlobalDomain", "key": "AppleShowScrollBars", "type": "string", "value": "Always", "desc": ""}
441+
]
442+
},
443+
"visibility": "unlisted"
444+
}`)
445+
446+
rc, err := UnmarshalRemoteConfigFlexible(data)
447+
require.NoError(t, err)
448+
assert.Equal(t, []string{"git"}, rc.Packages)
449+
require.Len(t, rc.MacOSPrefs, 2)
450+
assert.Equal(t, "com.apple.dock", rc.MacOSPrefs[0].Domain)
451+
assert.Equal(t, "autohide", rc.MacOSPrefs[0].Key)
452+
assert.Equal(t, "bool", rc.MacOSPrefs[0].Type)
453+
assert.Equal(t, "true", rc.MacOSPrefs[0].Value)
454+
assert.Equal(t, "NSGlobalDomain", rc.MacOSPrefs[1].Domain)
455+
}
456+
457+
func TestUnmarshalRemoteConfigFlexible_TopLevelMacOSPrefsNotOverridden(t *testing.T) {
458+
data := []byte(`{
459+
"packages": ["git"],
460+
"macos_prefs": [{"domain": "com.apple.dock", "key": "autohide", "type": "bool", "value": "true"}],
461+
"snapshot": {
462+
"macos_prefs": [
463+
{"domain": "other.domain", "key": "k", "type": "string", "value": "v"}
464+
]
465+
}
466+
}`)
467+
468+
rc, err := UnmarshalRemoteConfigFlexible(data)
469+
require.NoError(t, err)
470+
require.Len(t, rc.MacOSPrefs, 1)
471+
assert.Equal(t, "com.apple.dock", rc.MacOSPrefs[0].Domain, "top-level macos_prefs should take precedence")
472+
}
473+
429474
func TestUnmarshalRemoteConfigFlexible_InvalidJSON(t *testing.T) {
430475
data := []byte(`not json`)
431476
_, err := UnmarshalRemoteConfigFlexible(data)

0 commit comments

Comments
 (0)