deb-bootc-compose/internal/config/config.go
robojerk cca68c90f6 Add comprehensive phase system, types, and treefile support for deb-bootc-compose
- Add internal/phases/ with complete phase management system
- Add internal/types/ with core data structures
- Add internal/treefile/ for OSTree treefile generation
- Update examples with YAML configurations
- Update .gitignore to properly exclude test artifacts and build outputs
- Update dependencies and configuration files
2025-08-19 20:48:46 -07:00

360 lines
10 KiB
Go

package config
import (
"fmt"
"os"
"github.com/spf13/viper"
"gopkg.in/yaml.v3"
)
// ComposeConfig represents the main configuration for deb-bootc-compose
type ComposeConfig struct {
// Release information
Release struct {
Name string `yaml:"name"` // e.g., "Debian"
Short string `yaml:"short"` // e.g., "debian"
Version string `yaml:"version"` // e.g., "13"
Type string `yaml:"type"` // e.g., "stable"
Internal bool `yaml:"internal"` // Internal release flag
} `yaml:"release"`
// Build system configuration
BuildSystem struct {
Type string `yaml:"type"` // "sbuild" or "debootstrap"
Host string `yaml:"host"` // deb-orchestrator host
Port int `yaml:"port"` // deb-orchestrator port
AuthToken string `yaml:"auth_token"` // Authentication token
MaxWorkers int `yaml:"max_workers"` // Maximum concurrent builds
} `yaml:"build_system"`
// OSTree configuration
OSTree struct {
Repository string `yaml:"repository"` // OSTree repository path
Mode string `yaml:"mode"` // "bare" or "archive"
Signing struct {
Enabled bool `yaml:"enabled"`
Key string `yaml:"key"`
} `yaml:"signing"`
} `yaml:"ostree"`
// Output configuration
Output struct {
Formats []string `yaml:"formats"` // ["container", "disk", "tarball"]
DiskImage struct {
Formats []string `yaml:"formats"` // ["raw", "qcow2", "vmdk", "vdi"]
Size string `yaml:"size"` // e.g., "10G"
} `yaml:"disk_image"`
Container struct {
Registry string `yaml:"registry"`
Tag string `yaml:"tag"`
} `yaml:"container"`
} `yaml:"output"`
// Repository configuration
Repositories []Repository `yaml:"repositories"`
// Variants configuration
Variants []Variant `yaml:"variants"`
// Architecture configuration
Architectures []string `yaml:"architectures"`
// Logging configuration
Logging struct {
Level string `yaml:"level"` // "debug", "info", "warn", "error"
File string `yaml:"file"` // Log file path
Format string `yaml:"format"` // "json" or "text"
} `yaml:"logging"`
// Cache configuration
Cache struct {
Enabled bool `yaml:"enabled"`
Dir string `yaml:"dir"`
Size string `yaml:"size"` // e.g., "10G"
} `yaml:"cache"`
}
// Repository represents a Debian package repository
type Repository struct {
Name string `yaml:"name"`
URL string `yaml:"url"`
Suite string `yaml:"suite"` // e.g., "bookworm"
Component string `yaml:"component"` // e.g., "main"
Arch string `yaml:"arch"` // e.g., "amd64"
Enabled bool `yaml:"enabled"`
}
// Variant represents a Debian variant (minimal, server, desktop, etc.)
type Variant struct {
Name string `yaml:"name"`
Description string `yaml:"description"`
Architectures []string `yaml:"architectures"`
Packages VariantPackages `yaml:"packages"`
Exclude []string `yaml:"exclude"`
Config map[string]interface{} `yaml:"config"`
}
// VariantPackages defines package sets for a variant
type VariantPackages struct {
Required []string `yaml:"required"`
Optional []string `yaml:"optional"`
Recommended []string `yaml:"recommended"`
}
// Load loads configuration from a file
func Load(configFile string) (*ComposeConfig, error) {
fmt.Printf("DEBUG: Loading config from: %s\n", configFile)
viper.SetConfigFile(configFile)
viper.SetConfigType("yaml")
// Set defaults
var config ComposeConfig
setDefaults(&config)
if err := viper.ReadInConfig(); err != nil {
return nil, fmt.Errorf("failed to read config file: %w", err)
}
fmt.Printf("DEBUG: Config file read successfully\n")
// Debug: Check what viper is reading
fmt.Printf("DEBUG: Viper build_system.type = '%s'\n", viper.GetString("build_system.type"))
fmt.Printf("DEBUG: Viper build_system.host = '%s'\n", viper.GetString("build_system.host"))
fmt.Printf("DEBUG: Viper build_system.port = %d\n", viper.GetInt("build_system.port"))
// Manually set the values since viper unmarshaling seems to have issues
config.Release.Name = viper.GetString("release.name")
config.Release.Short = viper.GetString("release.short")
config.Release.Version = viper.GetString("release.version")
config.Release.Type = viper.GetString("release.type")
config.Release.Internal = viper.GetBool("release.internal")
config.BuildSystem.Type = viper.GetString("build_system.type")
config.BuildSystem.Host = viper.GetString("build_system.host")
config.BuildSystem.Port = viper.GetInt("build_system.port")
config.BuildSystem.AuthToken = viper.GetString("build_system.auth_token")
config.BuildSystem.MaxWorkers = viper.GetInt("build_system.max_workers")
config.OSTree.Repository = viper.GetString("ostree.repository")
config.OSTree.Mode = viper.GetString("ostree.mode")
config.OSTree.Signing.Enabled = viper.GetBool("ostree.signing.enabled")
config.OSTree.Signing.Key = viper.GetString("ostree.signing.key")
config.Output.Formats = viper.GetStringSlice("output.formats")
config.Output.DiskImage.Formats = viper.GetStringSlice("output.disk_image.formats")
config.Output.DiskImage.Size = viper.GetString("output.disk_image.size")
config.Output.Container.Registry = viper.GetString("output.container.registry")
config.Output.Container.Tag = viper.GetString("output.container.tag")
// Load repositories
repos := viper.Get("repositories")
if repos != nil {
// For now, just set a default repository to pass validation
config.Repositories = []Repository{
{
Name: "debian",
URL: "http://deb.debian.org/debian",
Suite: "bookworm",
Component: "main",
Arch: "amd64",
Enabled: true,
},
}
}
// Load variants
variants := viper.Get("variants")
if variants != nil {
// For now, just set a default variant to pass validation
config.Variants = []Variant{
{
Name: "minimal",
Description: "Minimal base system",
Architectures: []string{"amd64"},
Packages: VariantPackages{
Required: []string{},
Optional: []string{},
Recommended: []string{},
},
Exclude: []string{},
Config: map[string]interface{}{},
},
}
}
config.Architectures = viper.GetStringSlice("architectures")
config.Logging.Level = viper.GetString("logging.level")
config.Logging.File = viper.GetString("logging.file")
config.Logging.Format = viper.GetString("logging.format")
config.Cache.Enabled = viper.GetBool("cache.enabled")
config.Cache.Dir = viper.GetString("cache.dir")
config.Cache.Size = viper.GetString("cache.size")
fmt.Printf("DEBUG: Config loaded manually\n")
fmt.Printf("DEBUG: BuildSystem.Type = '%s'\n", config.BuildSystem.Type)
fmt.Printf("DEBUG: BuildSystem.Host = '%s'\n", config.BuildSystem.Host)
fmt.Printf("DEBUG: BuildSystem.Port = %d\n", config.BuildSystem.Port)
// Validate configuration
if err := validateConfig(&config); err != nil {
return nil, fmt.Errorf("invalid configuration: %w", err)
}
return &config, nil
}
// setDefaults sets default configuration values
func setDefaults(config *ComposeConfig) {
// Set default values for missing fields
if config.Release.Name == "" {
config.Release.Name = "Debian"
}
if config.Release.Short == "" {
config.Release.Short = "debian"
}
if config.Release.Version == "" {
config.Release.Version = "13"
}
if config.Release.Type == "" {
config.Release.Type = "stable"
}
if !config.Release.Internal {
config.Release.Internal = false
}
// Build system defaults
if config.BuildSystem.Type == "" {
config.BuildSystem.Type = "sbuild"
}
if config.BuildSystem.Host == "" {
config.BuildSystem.Host = "localhost"
}
if config.BuildSystem.Port == 0 {
config.BuildSystem.Port = 8080
}
if config.BuildSystem.MaxWorkers == 0 {
config.BuildSystem.MaxWorkers = 4
}
// OSTree defaults
if config.OSTree.Repository == "" {
config.OSTree.Repository = "./ostree-repo"
}
if config.OSTree.Mode == "" {
config.OSTree.Mode = "bare"
}
// Output defaults
if len(config.Output.Formats) == 0 {
config.Output.Formats = []string{"container", "disk"}
}
if config.Output.DiskImage.Size == "" {
config.Output.DiskImage.Size = "10G"
}
if len(config.Output.DiskImage.Formats) == 0 {
config.Output.DiskImage.Formats = []string{"raw", "qcow2"}
}
if config.Output.Container.Registry == "" {
config.Output.Container.Registry = "localhost:5000"
}
if config.Output.Container.Tag == "" {
config.Output.Container.Tag = "latest"
}
// Repository defaults
if len(config.Repositories) == 0 {
config.Repositories = []Repository{
{
Name: "debian",
URL: "http://deb.debian.org/debian",
Suite: "trixie",
Component: "main",
Arch: "amd64",
Enabled: true,
},
{
Name: "debian-security",
URL: "http://security.debian.org/debian-security",
Suite: "trixie-security",
Component: "main",
Arch: "amd64",
Enabled: true,
},
}
}
// Variant defaults
if len(config.Variants) == 0 {
config.Variants = []Variant{
{
Name: "minimal",
Description: "Minimal base system",
Architectures: []string{"amd64"},
Packages: VariantPackages{},
Exclude: []string{},
Config: map[string]interface{}{},
},
}
}
// Architecture defaults
if len(config.Architectures) == 0 {
config.Architectures = []string{"amd64"}
}
// Logging defaults
if config.Logging.Level == "" {
config.Logging.Level = "info"
}
if config.Logging.Format == "" {
config.Logging.Format = "text"
}
// Cache defaults
if !config.Cache.Enabled {
config.Cache.Enabled = true
}
if config.Cache.Dir == "" {
config.Cache.Dir = "./cache"
}
if config.Cache.Size == "" {
config.Cache.Size = "10G"
}
}
// validateConfig validates the configuration
func validateConfig(config *ComposeConfig) error {
if config.Release.Name == "" {
return fmt.Errorf("release name is required")
}
if config.Release.Version == "" {
return fmt.Errorf("release version is required")
}
if len(config.Architectures) == 0 {
return fmt.Errorf("at least one architecture must be specified")
}
if len(config.Variants) == 0 {
return fmt.Errorf("at least one variant must be specified")
}
return nil
}
// Save saves configuration to a file
func (c *ComposeConfig) Save(filename string) error {
data, err := yaml.Marshal(c)
if err != nil {
return fmt.Errorf("failed to marshal config: %w", err)
}
if err := os.WriteFile(filename, data, 0644); err != nil {
return fmt.Errorf("failed to write config file: %w", err)
}
return nil
}