deb-bootc-image-builder/bib/internal/distrodef/distrodef.go
2025-08-11 08:59:41 -07:00

98 lines
2.7 KiB
Go

package distrodef
import (
"fmt"
"os"
"path/filepath"
"strings"
"golang.org/x/exp/maps"
"gopkg.in/yaml.v3"
"github.com/hashicorp/go-version"
)
// ImageDef is a structure containing extra information needed to build an image that cannot be extracted
// from the container image itself. Currently, this is only the list of packages needed for the installer
// ISO.
type ImageDef struct {
Packages []string `yaml:"packages"`
}
func findDistroDef(defDirs []string, distro, wantedVerStr string) (string, error) {
var bestFuzzyMatch string
bestFuzzyVer := &version.Version{}
wantedVer, err := version.NewVersion(wantedVerStr)
if err != nil {
return "", fmt.Errorf("cannot parse wanted version string: %w", err)
}
for _, defDir := range defDirs {
// exact match
matches, err := filepath.Glob(filepath.Join(defDir, fmt.Sprintf("%s-%s.yaml", distro, wantedVerStr)))
if err != nil {
return "", err
}
if len(matches) == 1 {
return matches[0], nil
}
// fuzzy match
matches, err = filepath.Glob(filepath.Join(defDir, fmt.Sprintf("%s-[0-9]*.yaml", distro)))
if err != nil {
return "", err
}
for _, m := range matches {
baseNoExt := strings.TrimSuffix(filepath.Base(m), ".yaml")
haveVerStr := strings.SplitN(baseNoExt, "-", 2)[1]
// this should never error (because of the glob above) but be defensive
haveVer, err := version.NewVersion(haveVerStr)
if err != nil {
return "", fmt.Errorf("cannot parse distro version from %q: %w", m, err)
}
if wantedVer.Compare(haveVer) >= 0 && haveVer.Compare(bestFuzzyVer) > 0 {
bestFuzzyVer = haveVer
bestFuzzyMatch = m
}
}
}
if bestFuzzyMatch == "" {
return "", fmt.Errorf("could not find def file for distro %s-%s", distro, wantedVerStr)
}
return bestFuzzyMatch, nil
}
func loadFile(defDirs []string, distro, ver string) ([]byte, error) {
defPath, err := findDistroDef(defDirs, distro, ver)
if err != nil {
return nil, err
}
content, err := os.ReadFile(defPath)
if err != nil {
return nil, fmt.Errorf("could not read def file %s for distro %s-%s: %v", defPath, distro, ver, err)
}
return content, nil
}
// Loads a definition file for a given distro and image type
func LoadImageDef(defDirs []string, distro, ver, it string) (*ImageDef, error) {
data, err := loadFile(defDirs, distro, ver)
if err != nil {
return nil, err
}
var defs map[string]ImageDef
if err := yaml.Unmarshal(data, &defs); err != nil {
return nil, fmt.Errorf("could not unmarshal def file for distro %s: %v", distro, err)
}
d, ok := defs[it]
if !ok {
return nil, fmt.Errorf("could not find def for distro %s and image type %s, available types: %s", distro, it, strings.Join(maps.Keys(defs), ", "))
}
return &d, nil
}