From e2f3b43b81a04696127a99ac4b1f1bd98196f0ac Mon Sep 17 00:00:00 2001 From: Jared Mahotiere Date: Wed, 18 Feb 2026 14:35:43 -0500 Subject: [PATCH] refactor: centralize private file writes and tighten config migration coverage Move duplicated writePrivateFile logic into pkg/utils.WritePrivateFile, reuse it from config and cron, add the glm-4.7 no-provider-keys test, and extend CI with go vet plus race-enabled tests. --- .github/workflows/ci.yml | 7 +++++-- pkg/config/config.go | 10 ++-------- pkg/config/config_test.go | 20 ++++++++++++++++++++ pkg/cron/service.go | 10 ++-------- pkg/utils/file.go | 11 +++++++++++ 5 files changed, 40 insertions(+), 18 deletions(-) create mode 100644 pkg/utils/file.go diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index eb92f0daf..366e3deae 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,5 +29,8 @@ jobs: exit 1 fi - - name: Run tests - run: go test ./... + - name: Run go vet + run: go vet ./... + + - name: Run tests (race) + run: go test -race ./... diff --git a/pkg/config/config.go b/pkg/config/config.go index fc7a68d2d..45f05b84b 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -7,6 +7,7 @@ import ( "sync" "github.com/caarlos0/env/v11" + "github.com/sipeed/picoclaw/pkg/utils" ) type Config struct { @@ -252,7 +253,7 @@ func SaveConfig(path string, cfg *Config) error { return err } - return writePrivateFile(path, data) + return utils.WritePrivateFile(path, data) } func (c *Config) WorkspacePath() string { @@ -320,13 +321,6 @@ func expandHome(path string) string { return path } -func writePrivateFile(path string, data []byte) error { - if err := os.WriteFile(path, data, 0600); err != nil { - return err - } - return os.Chmod(path, 0600) -} - func normalizeLegacyModelDefaults(cfg *Config) { if cfg == nil { return diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go index 7ed7862d3..e6e17cbb4 100644 --- a/pkg/config/config_test.go +++ b/pkg/config/config_test.go @@ -86,3 +86,23 @@ func TestLoadConfigKeepsLegacyGLMModelWhenZhipuConfigured(t *testing.T) { t.Fatalf("model = %q, want %q", cfg.Agents.Defaults.Model, "glm-4.7") } } + +func TestLoadConfigKeepsLegacyGLMModelWhenNoProviderKeysConfigured(t *testing.T) { + path := filepath.Join(t.TempDir(), "config.json") + content := `{ + "agents": { "defaults": { "model": "glm-4.7" } } +}` + + if err := os.WriteFile(path, []byte(content), 0644); err != nil { + t.Fatalf("failed to write config fixture: %v", err) + } + + cfg, err := LoadConfig(path) + if err != nil { + t.Fatalf("LoadConfig failed: %v", err) + } + + if cfg.Agents.Defaults.Model != "glm-4.7" { + t.Fatalf("model = %q, want %q", cfg.Agents.Defaults.Model, "glm-4.7") + } +} diff --git a/pkg/cron/service.go b/pkg/cron/service.go index fbd9bdad0..b41f11826 100644 --- a/pkg/cron/service.go +++ b/pkg/cron/service.go @@ -12,6 +12,7 @@ import ( "time" "github.com/adhocore/gronx" + "github.com/sipeed/picoclaw/pkg/utils" ) type CronSchedule struct { @@ -318,7 +319,7 @@ func (cs *CronService) saveStoreUnsafe() error { return err } - return writePrivateFile(cs.storePath, data) + return utils.WritePrivateFile(cs.storePath, data) } func (cs *CronService) AddJob(name string, schedule CronSchedule, message string, deliver bool, channel, to string) (*CronJob, error) { @@ -456,10 +457,3 @@ func generateID() string { } return hex.EncodeToString(b) } - -func writePrivateFile(path string, data []byte) error { - if err := os.WriteFile(path, data, 0600); err != nil { - return err - } - return os.Chmod(path, 0600) -} diff --git a/pkg/utils/file.go b/pkg/utils/file.go new file mode 100644 index 000000000..b43e891b3 --- /dev/null +++ b/pkg/utils/file.go @@ -0,0 +1,11 @@ +package utils + +import "os" + +// WritePrivateFile writes data and enforces 0600 permissions for both new and existing files. +func WritePrivateFile(path string, data []byte) error { + if err := os.WriteFile(path, data, 0600); err != nil { + return err + } + return os.Chmod(path, 0600) +}