go-build/pkg/build/linuxkit_image.go
2026-04-15 11:59:51 +01:00

173 lines
4.3 KiB
Go

package build
import "strings"
// LinuxKitImage models an immutable LinuxKit image definition.
//
// image := build.LinuxKit(
// build.WithBase("core-dev"),
// build.WithPackages("git", "task"),
// build.WithMount("/workspace"),
// build.WithGPU(true),
// )
type LinuxKitImage struct {
Config LinuxKitConfig
}
// LinuxKitConfig defines an immutable LinuxKit image.
//
// cfg := build.DefaultLinuxKitConfig()
type LinuxKitConfig struct {
Base string `json:"base,omitempty" yaml:"base,omitempty"`
Packages []string `json:"packages,omitempty" yaml:"packages,omitempty"`
Mounts []string `json:"mounts,omitempty" yaml:"mounts,omitempty"`
GPU bool `json:"gpu,omitempty" yaml:"gpu,omitempty"`
Formats []string `json:"formats,omitempty" yaml:"formats,omitempty"`
Registry string `json:"registry,omitempty" yaml:"registry,omitempty"`
}
// LinuxKitOption configures an immutable LinuxKit image definition.
type LinuxKitOption func(*LinuxKitConfig)
// DefaultLinuxKitConfig returns the RFC defaults for immutable image builds.
func DefaultLinuxKitConfig() LinuxKitConfig {
return LinuxKitConfig{
Base: "core-dev",
Packages: []string{},
Mounts: []string{"/workspace"},
GPU: false,
Formats: []string{"oci", "apple"},
}
}
// LinuxKit builds an immutable LinuxKit image definition with sensible defaults.
func LinuxKit(opts ...LinuxKitOption) *LinuxKitImage {
cfg := DefaultLinuxKitConfig()
for _, opt := range opts {
if opt != nil {
opt(&cfg)
}
}
cfg = normalizeLinuxKitConfig(cfg)
return &LinuxKitImage{Config: cfg}
}
// WithBase overrides the base image template name.
func WithBase(base string) LinuxKitOption {
return func(cfg *LinuxKitConfig) {
cfg.Base = strings.TrimSpace(base)
}
}
// WithPackages appends extra OS packages to the immutable image.
func WithPackages(packages ...string) LinuxKitOption {
return func(cfg *LinuxKitConfig) {
cfg.Packages = append(cfg.Packages, packages...)
}
}
// WithMount appends a writable mount point exposed inside the image.
func WithMount(path string) LinuxKitOption {
return func(cfg *LinuxKitConfig) {
path = strings.TrimSpace(path)
if path == "" {
return
}
cfg.Mounts = append(cfg.Mounts, path)
}
}
// WithGPU toggles GPU support for the immutable image.
func WithGPU(enabled bool) LinuxKitOption {
return func(cfg *LinuxKitConfig) {
cfg.GPU = enabled
}
}
// WithFormats overrides the requested output formats.
func WithFormats(formats ...string) LinuxKitOption {
return func(cfg *LinuxKitConfig) {
cfg.Formats = normalizeLinuxKitFormats(formats)
}
}
// WithRegistry sets the OCI registry namespace for image publication metadata.
func WithRegistry(registry string) LinuxKitOption {
return func(cfg *LinuxKitConfig) {
cfg.Registry = strings.TrimSpace(registry)
}
}
func normalizeLinuxKitValues(values []string) []string {
if len(values) == 0 {
return values
}
seen := make(map[string]struct{}, len(values))
result := make([]string, 0, len(values))
for _, value := range values {
value = strings.TrimSpace(value)
if value == "" {
continue
}
if _, ok := seen[value]; ok {
continue
}
seen[value] = struct{}{}
result = append(result, value)
}
return result
}
func normalizeLinuxKitFormats(values []string) []string {
if len(values) == 0 {
return values
}
seen := make(map[string]struct{}, len(values))
result := make([]string, 0, len(values))
for _, value := range values {
value = strings.ToLower(strings.TrimSpace(value))
if value == "" {
continue
}
if _, ok := seen[value]; ok {
continue
}
seen[value] = struct{}{}
result = append(result, value)
}
return result
}
func normalizeLinuxKitConfig(cfg LinuxKitConfig) LinuxKitConfig {
cfg = applyLinuxKitDefaults(cfg)
cfg.Base = strings.TrimSpace(cfg.Base)
cfg.Registry = strings.TrimSpace(cfg.Registry)
cfg.Packages = normalizeLinuxKitValues(cfg.Packages)
cfg.Mounts = normalizeLinuxKitValues(cfg.Mounts)
cfg.Formats = normalizeLinuxKitFormats(cfg.Formats)
cfg = applyLinuxKitDefaults(cfg)
return cfg
}
func applyLinuxKitDefaults(cfg LinuxKitConfig) LinuxKitConfig {
defaults := DefaultLinuxKitConfig()
if strings.TrimSpace(cfg.Base) == "" {
cfg.Base = defaults.Base
}
if len(cfg.Mounts) == 0 {
cfg.Mounts = append([]string(nil), defaults.Mounts...)
}
if len(cfg.Formats) == 0 {
cfg.Formats = append([]string(nil), defaults.Formats...)
}
return cfg
}