From 727596176950c4009fa3c22924b5fd5bbc4556f6 Mon Sep 17 00:00:00 2001 From: Snider Date: Thu, 29 Jan 2026 02:04:01 +0000 Subject: [PATCH] feat(devops): add config loading Loads ~/.core/config.yaml with image source preferences. Defaults to auto-detection with host-uk/core-images. Co-Authored-By: Claude Opus 4.5 --- pkg/devops/config.go | 86 +++++++++++++++++++++++++++++++++++++++ pkg/devops/config_test.go | 48 ++++++++++++++++++++++ pkg/devops/go.mod | 2 + pkg/devops/go.sum | 2 + 4 files changed, 138 insertions(+) create mode 100644 pkg/devops/config.go create mode 100644 pkg/devops/config_test.go diff --git a/pkg/devops/config.go b/pkg/devops/config.go new file mode 100644 index 00000000..6db1e6ab --- /dev/null +++ b/pkg/devops/config.go @@ -0,0 +1,86 @@ +package devops + +import ( + "os" + "path/filepath" + + "gopkg.in/yaml.v3" +) + +// Config holds global devops configuration from ~/.core/config.yaml. +type Config struct { + Version int `yaml:"version"` + Images ImagesConfig `yaml:"images"` +} + +// ImagesConfig holds image source configuration. +type ImagesConfig struct { + Source string `yaml:"source"` // auto, github, registry, cdn + GitHub GitHubConfig `yaml:"github,omitempty"` + Registry RegistryConfig `yaml:"registry,omitempty"` + CDN CDNConfig `yaml:"cdn,omitempty"` +} + +// GitHubConfig holds GitHub Releases configuration. +type GitHubConfig struct { + Repo string `yaml:"repo"` // owner/repo format +} + +// RegistryConfig holds container registry configuration. +type RegistryConfig struct { + Image string `yaml:"image"` // e.g., ghcr.io/host-uk/core-devops +} + +// CDNConfig holds CDN/S3 configuration. +type CDNConfig struct { + URL string `yaml:"url"` // base URL for downloads +} + +// DefaultConfig returns sensible defaults. +func DefaultConfig() *Config { + return &Config{ + Version: 1, + Images: ImagesConfig{ + Source: "auto", + GitHub: GitHubConfig{ + Repo: "host-uk/core-images", + }, + Registry: RegistryConfig{ + Image: "ghcr.io/host-uk/core-devops", + }, + }, + } +} + +// ConfigPath returns the path to the config file. +func ConfigPath() (string, error) { + home, err := os.UserHomeDir() + if err != nil { + return "", err + } + return filepath.Join(home, ".core", "config.yaml"), nil +} + +// LoadConfig loads configuration from ~/.core/config.yaml. +// Returns default config if file doesn't exist. +func LoadConfig() (*Config, error) { + configPath, err := ConfigPath() + if err != nil { + return DefaultConfig(), nil + } + + data, err := os.ReadFile(configPath) + if err != nil { + if os.IsNotExist(err) { + return DefaultConfig(), nil + } + return nil, err + } + + cfg := DefaultConfig() + if err := yaml.Unmarshal(data, cfg); err != nil { + return nil, err + } + + return cfg, nil +} diff --git a/pkg/devops/config_test.go b/pkg/devops/config_test.go new file mode 100644 index 00000000..1c3439b8 --- /dev/null +++ b/pkg/devops/config_test.go @@ -0,0 +1,48 @@ +package devops + +import ( + "os" + "path/filepath" + "testing" +) + +func TestLoadConfig_Good_Default(t *testing.T) { + // Use temp home dir + tmpDir := t.TempDir() + t.Setenv("HOME", tmpDir) + + cfg, err := LoadConfig() + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if cfg.Images.Source != "auto" { + t.Errorf("expected source 'auto', got %q", cfg.Images.Source) + } +} + +func TestLoadConfig_Good_FromFile(t *testing.T) { + tmpDir := t.TempDir() + t.Setenv("HOME", tmpDir) + + configDir := filepath.Join(tmpDir, ".core") + os.MkdirAll(configDir, 0755) + + configContent := `version: 1 +images: + source: github + github: + repo: myorg/images +` + os.WriteFile(filepath.Join(configDir, "config.yaml"), []byte(configContent), 0644) + + cfg, err := LoadConfig() + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if cfg.Images.Source != "github" { + t.Errorf("expected source 'github', got %q", cfg.Images.Source) + } + if cfg.Images.GitHub.Repo != "myorg/images" { + t.Errorf("expected repo 'myorg/images', got %q", cfg.Images.GitHub.Repo) + } +} diff --git a/pkg/devops/go.mod b/pkg/devops/go.mod index 62022ff5..181702a8 100644 --- a/pkg/devops/go.mod +++ b/pkg/devops/go.mod @@ -4,4 +4,6 @@ go 1.25 require github.com/host-uk/core v0.0.0 +require gopkg.in/yaml.v3 v3.0.1 // indirect + replace github.com/host-uk/core => ../.. diff --git a/pkg/devops/go.sum b/pkg/devops/go.sum index b4dfe5ea..bed8a887 100644 --- a/pkg/devops/go.sum +++ b/pkg/devops/go.sum @@ -1,4 +1,6 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=