From d01a6d87d569265afe2e1f4ab6fe69bf4ef1449a Mon Sep 17 00:00:00 2001 From: elel-code Date: Thu, 27 Aug 2026 21:14:10 +0800 Subject: [PATCH] fix(matugen): honor config dir for qtengine sync (#3195) --- core/cmd/dms/commands_matugen.go | 4 ++- core/cmd/dms/commands_matugen_test.go | 14 ++++++++++ core/internal/matugen/matugen.go | 2 +- core/internal/matugen/matugen_test.go | 40 +++++++++++++++++++++++++++ core/internal/matugen/qtengine.go | 18 ++++++++++-- 5 files changed, 74 insertions(+), 4 deletions(-) create mode 100644 core/cmd/dms/commands_matugen_test.go diff --git a/core/cmd/dms/commands_matugen.go b/core/cmd/dms/commands_matugen.go index 8aeaef520..ce7b20190 100644 --- a/core/cmd/dms/commands_matugen.go +++ b/core/cmd/dms/commands_matugen.go @@ -78,6 +78,7 @@ func init() { matugenQueueCmd.Flags().Duration("timeout", 90*time.Second, "Timeout for waiting") matugenPreviewCmd.Flags().String("source-color", "", "Source color used to generate previews") matugenPreviewCmd.Flags().Float64("contrast", 0, "Contrast value from -1 to 1 (0 = standard)") + matugenQtengineCmd.Flags().String("config-dir", "", "User config directory") matugenQtengineCmd.Flags().String("icon-theme", "", "Icon theme name") } @@ -233,9 +234,10 @@ func runMatugenPreview(cmd *cobra.Command, args []string) { } func runMatugenQtengine(cmd *cobra.Command, args []string) { + configDir, _ := cmd.Flags().GetString("config-dir") iconTheme, _ := cmd.Flags().GetString("icon-theme") - if err := matugen.SyncQtengineConfig(iconTheme); err != nil { + if err := matugen.SyncQtengineConfigAt(configDir, iconTheme); err != nil { log.Fatalf("Failed to sync qtengine config: %v", err) } } diff --git a/core/cmd/dms/commands_matugen_test.go b/core/cmd/dms/commands_matugen_test.go new file mode 100644 index 000000000..be2da73d1 --- /dev/null +++ b/core/cmd/dms/commands_matugen_test.go @@ -0,0 +1,14 @@ +package main + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestMatugenQtengineCommandAcceptsConfigDir(t *testing.T) { + flag := matugenQtengineCmd.Flags().Lookup("config-dir") + require.NotNil(t, flag) + assert.Equal(t, "", flag.DefValue) +} diff --git a/core/internal/matugen/matugen.go b/core/internal/matugen/matugen.go index 54cd95814..45a6503e7 100644 --- a/core/internal/matugen/matugen.go +++ b/core/internal/matugen/matugen.go @@ -405,7 +405,7 @@ func buildOnce(opts *Options) (bool, error) { // template off there is nothing to point to and the config would name a // scheme DMS no longer generates. if !opts.ShouldSkipTemplate("qtengine") && !opts.ShouldSkipTemplate("kcolorscheme") && QtengineActive() { - if err := SyncQtengineConfig(opts.IconTheme); err != nil { + if err := SyncQtengineConfigAt(opts.ConfigDir, opts.IconTheme); err != nil { log.Warnf("Failed to sync qtengine config: %v", err) } } diff --git a/core/internal/matugen/matugen_test.go b/core/internal/matugen/matugen_test.go index 7e4dc24cc..e03a42264 100644 --- a/core/internal/matugen/matugen_test.go +++ b/core/internal/matugen/matugen_test.go @@ -860,6 +860,46 @@ func TestSyncQtengineConfig(t *testing.T) { } } +func TestSyncQtengineConfigAtUsesExplicitConfigDir(t *testing.T) { + tempDir := t.TempDir() + xdgConfigDir := filepath.Join(tempDir, "xdg-config") + explicitConfigDir := filepath.Join(tempDir, "explicit-config") + dataHome := filepath.Join(tempDir, "data") + t.Setenv("XDG_CONFIG_HOME", xdgConfigDir) + t.Setenv("XDG_DATA_HOME", dataHome) + + scheme := filepath.Join(dataHome, "color-schemes", "DankMatugen.colors") + if err := os.MkdirAll(filepath.Dir(scheme), 0o755); err != nil { + t.Fatalf("failed to create color-schemes dir: %v", err) + } + if err := os.WriteFile(scheme, []byte("[Colors:Window]\n"), 0o644); err != nil { + t.Fatalf("failed to write color scheme: %v", err) + } + + if err := SyncQtengineConfigAt(explicitConfigDir, "Papirus-Dark"); err != nil { + t.Fatalf("sync with explicit config dir failed: %v", err) + } + + explicitPath := filepath.Join(explicitConfigDir, "qtengine", "config.json") + data, err := os.ReadFile(explicitPath) + if err != nil { + t.Fatalf("failed to read config from explicit directory: %v", err) + } + var cfg map[string]any + if err := json.Unmarshal(data, &cfg); err != nil { + t.Fatalf("written config is not valid JSON: %v", err) + } + theme, ok := cfg["theme"].(map[string]any) + if !ok { + t.Fatalf("written config has no theme object: %#v", cfg["theme"]) + } + assert.Equal(t, scheme, theme["colorScheme"]) + assert.Equal(t, "Papirus-Dark", theme["iconTheme"]) + + _, err = os.Stat(filepath.Join(xdgConfigDir, "qtengine", "config.json")) + assert.ErrorIs(t, err, os.ErrNotExist, "explicit config dir must take precedence over XDG_CONFIG_HOME") +} + // The appended entry is the only thing driving the settings row's indicator. func TestCheckTemplatesIncludesQtengine(t *testing.T) { tests := []struct { diff --git a/core/internal/matugen/qtengine.go b/core/internal/matugen/qtengine.go index 4729437ef..fc6851150 100644 --- a/core/internal/matugen/qtengine.go +++ b/core/internal/matugen/qtengine.go @@ -23,7 +23,7 @@ func QtengineActive() bool { // QtengineConfigPath returns the config qtengine serves both Qt5 and Qt6 from. func QtengineConfigPath() string { - return filepath.Join(utils.XDGConfigHome(), "qtengine", "config.json") + return qtengineConfigPath("") } // SyncQtengineConfig merges the DMS colour scheme path and icon theme into @@ -32,7 +32,14 @@ func QtengineConfigPath() string { // Map round trip rather than a typed struct so keys from qtengine versions we // have not seen survive. func SyncQtengineConfig(iconTheme string) error { - path := QtengineConfigPath() + return SyncQtengineConfigAt("", iconTheme) +} + +// SyncQtengineConfigAt behaves like SyncQtengineConfig but writes below the +// explicitly supplied config directory. An empty directory follows +// XDG_CONFIG_HOME, preserving the standalone CLI's default behaviour. +func SyncQtengineConfigAt(configDir, iconTheme string) error { + path := qtengineConfigPath(configDir) // Empty file and JSON null are both "not set" to qtengine; a top-level null // unmarshals to a nil map, which would panic on write. @@ -108,3 +115,10 @@ func SyncQtengineConfig(iconTheme string) error { return nil } + +func qtengineConfigPath(configDir string) string { + if configDir == "" { + configDir = utils.XDGConfigHome() + } + return filepath.Join(configDir, "qtengine", "config.json") +}