From 55859811be5fb2a37c59619d2506a8a225b0cbe8 Mon Sep 17 00:00:00 2001 From: Hein Date: Tue, 21 Apr 2026 21:31:05 +0200 Subject: [PATCH] fix(loader): disable config file rewrite during startup * migrate legacy schemas in memory only * log hint to use amcs-migrate-config for persistence --- README.md | 1 + changelog.md | 5 +++++ internal/config/loader.go | 22 ++-------------------- internal/config/loader_test.go | 13 +++++++++++-- 4 files changed, 19 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index b9219e3..64b2570 100644 --- a/README.md +++ b/README.md @@ -260,6 +260,7 @@ go run ./cmd/amcs-migrate-config --config ./configs/dev.yaml ``` Use `--dry-run` to print migrated YAML without writing. +Server startup migrates older config formats in memory only and does not write files. **OAuth Client Credentials flow**: diff --git a/changelog.md b/changelog.md index 1a563e8..c32709b 100644 --- a/changelog.md +++ b/changelog.md @@ -83,3 +83,8 @@ - Added `amcs-migrate-config` binary to the Docker image build output. - Added `migrate-config` service in `docker-compose.yml` under the `tools` profile. - Documented compose-based migration commands (dry-run and in-place apply) in the README. + +### 2026-04-21 21h - Startup Migration Write Disabled +- Changed config loading to migrate legacy schemas in memory only during startup. +- Removed automatic file rewrite and backup creation from the startup config loader. +- Added loader log hint to use `amcs-migrate-config` when persistent conversion is needed. diff --git a/internal/config/loader.go b/internal/config/loader.go index f133b59..bdd5d7c 100644 --- a/internal/config/loader.go +++ b/internal/config/loader.go @@ -40,16 +40,14 @@ func LoadWithLogger(explicitPath string, log *slog.Logger) (*Config, string, err } if len(applied) > 0 { - if err := rewriteConfigFile(path, data, raw); err != nil { - return nil, path, err - } if log != nil { for _, step := range applied { - log.Warn("config migrated", + log.Warn("config migrated in memory", slog.String("path", path), slog.Int("from_version", step.From), slog.Int("to_version", step.To), slog.String("describe", step.Describe), + slog.String("hint", "persist with amcs-migrate-config"), ) } } @@ -81,22 +79,6 @@ func decodeTyped(raw map[string]any) (Config, error) { return cfg, nil } -func rewriteConfigFile(path string, original []byte, migrated map[string]any) error { - backupPath := fmt.Sprintf("%s.bak.%d", path, time.Now().Unix()) - if err := os.WriteFile(backupPath, original, 0o600); err != nil { - return fmt.Errorf("write backup %q: %w", backupPath, err) - } - - out, err := yaml.Marshal(migrated) - if err != nil { - return fmt.Errorf("marshal migrated config: %w", err) - } - if err := os.WriteFile(path, out, 0o600); err != nil { - return fmt.Errorf("write migrated config %q: %w", path, err) - } - return nil -} - func ResolvePath(explicitPath string) string { if path := strings.TrimSpace(explicitPath); path != "" { if path != ".yaml" && path != ".yml" { diff --git a/internal/config/loader_test.go b/internal/config/loader_test.go index c0d685c..1df107a 100644 --- a/internal/config/loader_test.go +++ b/internal/config/loader_test.go @@ -3,6 +3,7 @@ package config import ( "os" "path/filepath" + "strings" "testing" "time" ) @@ -215,7 +216,15 @@ logging: if err != nil { t.Fatalf("glob backups: %v", err) } - if len(entries) != 1 { - t.Fatalf("backup files = %d, want 1", len(entries)) + if len(entries) != 0 { + t.Fatalf("backup files = %d, want 0 (load should not rewrite config)", len(entries)) + } + + originalOnDisk, err := os.ReadFile(configPath) + if err != nil { + t.Fatalf("read original config: %v", err) + } + if !strings.Contains(string(originalOnDisk), "provider: \"litellm\"") { + t.Fatalf("expected source config to remain unchanged on disk") } }