Initial commit

This commit is contained in:
robojerk 2025-08-11 08:59:41 -07:00
commit 3326d796f0
87 changed files with 15792 additions and 0 deletions

View file

@ -0,0 +1,97 @@
package aptsolver
import (
"strings"
"github.com/osbuild/images/pkg/arch"
"github.com/osbuild/images/pkg/bib/osinfo"
)
// AptSolver implements package dependency resolution for Debian using apt
type AptSolver struct {
arch arch.Arch
osInfo *osinfo.Info
cacheDir string
}
// DepsolveResult represents the result of apt dependency resolution
type DepsolveResult struct {
Packages []string
Repos []interface{}
}
// NewAptSolver creates a new apt-based solver for Debian
func NewAptSolver(cacheDir string, arch arch.Arch, osInfo *osinfo.Info) *AptSolver {
return &AptSolver{
arch: arch,
osInfo: osInfo,
cacheDir: cacheDir,
}
}
// Depsolve resolves package dependencies using apt
func (s *AptSolver) Depsolve(packages []string, maxAttempts int) (*DepsolveResult, error) {
// For now, we'll return the packages as-is since apt dependency resolution
// is more complex and would require running apt in a chroot
// This is a simplified implementation that will be enhanced later
result := &DepsolveResult{
Packages: packages,
Repos: []interface{}{
map[string]interface{}{
"name": "debian",
"baseurls": []string{"http://deb.debian.org/debian"},
},
map[string]interface{}{
"name": "debian-security",
"baseurls": []string{"http://deb.debian.org/debian-security"},
},
},
}
return result, nil
}
// GetArch returns the architecture for this solver
func (s *AptSolver) GetArch() arch.Arch {
return s.arch
}
// GetOSInfo returns the OS information for this solver
func (s *AptSolver) GetOSInfo() *osinfo.Info {
return s.osInfo
}
// ValidatePackages checks if the specified packages are available in Debian repositories
func (s *AptSolver) ValidatePackages(packages []string) error {
// This is a simplified validation - in a real implementation,
// we would query the Debian package database
for _, pkg := range packages {
if !strings.HasPrefix(pkg, "linux-") &&
!strings.HasPrefix(pkg, "grub-") &&
!strings.HasPrefix(pkg, "initramfs-") &&
pkg != "util-linux" &&
pkg != "parted" &&
pkg != "e2fsprogs" &&
pkg != "dosfstools" &&
pkg != "efibootmgr" &&
pkg != "systemd" &&
pkg != "dbus" &&
pkg != "sudo" {
// For now, we'll assume these are valid Debian packages
// In a real implementation, we would validate against the package database
}
}
return nil
}
// GetPackageInfo retrieves information about a specific package
func (s *AptSolver) GetPackageInfo(packageName string) (map[string]interface{}, error) {
// This is a placeholder - in a real implementation, we would query apt
// for detailed package information
return map[string]interface{}{
"name": packageName,
"version": "latest",
"arch": s.arch.String(),
}, nil
}

View file

@ -0,0 +1,66 @@
package debianpatch
import (
"fmt"
)
// IsBootcImage checks if an image is a bootc image by looking for either
// com.redhat.bootc=true or com.debian.bootc=true labels
func IsBootcImage(labels map[string]string) bool {
// Check for Red Hat bootc label (for compatibility)
if val, exists := labels["com.redhat.bootc"]; exists && val == "true" {
return true
}
// Check for Debian bootc label (our addition)
if val, exists := labels["com.debian.bootc"]; exists && val == "true" {
return true
}
return false
}
// ValidateBootcImage validates that an image has the required bootc markers
func ValidateBootcImage(labels map[string]string, imageRef string) error {
if !IsBootcImage(labels) {
return fmt.Errorf("image %s is not a bootc image (missing com.redhat.bootc=true or com.debian.bootc=true label)", imageRef)
}
// Check for required OSTree labels
if val, exists := labels["ostree.bootable"]; !exists || val != "true" {
return fmt.Errorf("image %s is not a bootc image (missing ostree.bootable=true label)", imageRef)
}
return nil
}
// GetBootcType returns the type of bootc image (redhat, debian, or unknown)
func GetBootcType(labels map[string]string) string {
if val, exists := labels["com.redhat.bootc"]; exists && val == "true" {
return "redhat"
}
if val, exists := labels["com.debian.bootc"]; exists && val == "true" {
return "debian"
}
return "unknown"
}
// ValidateDebianBootcImage performs Debian-specific validation
func ValidateDebianBootcImage(labels map[string]string, imageRef string) error {
// First, validate it's a bootc image
if err := ValidateBootcImage(labels, imageRef); err != nil {
return err
}
// Check if it's specifically a Debian bootc image
if GetBootcType(labels) != "debian" {
return fmt.Errorf("image %s is not a Debian bootc image (missing com.debian.bootc=true label)", imageRef)
}
// Additional Debian-specific validations can be added here
// For example, checking for Debian-specific labels or configurations
return nil
}

View file

@ -0,0 +1,85 @@
package debianpatch
import (
"strings"
)
// BootcImageInfo contains the validation logic for bootc images
type BootcImageInfo struct {
Labels map[string]string `json:"Labels"`
}
// ContainerImage represents a container image with labels
type ContainerImage struct {
Labels map[string]string
Ref string
}
// ValidateImage checks if an image is a valid bootc image using our Debian-aware validation
func ValidateImage(imageRef string) error {
// This function will be called before the upstream images library validation
// In practice, you'd need to:
// 1. Inspect the container image to get labels
// 2. Call ValidateBootcImage(labels, imageRef)
// For now, this is a placeholder that demonstrates the integration point
// The actual implementation would need to integrate with the container inspection logic
return nil
}
// PreValidateImage performs validation before the upstream images library processes the image
func PreValidateImage(imageRef string) error {
// This is called before the upstream validation
// We can add Debian-specific pre-validation here
// Check if the image reference looks like a Debian image
if strings.Contains(imageRef, "debian") || strings.Contains(imageRef, "particle-os") {
// This is a hint that we might be dealing with a Debian image
// We could add additional validation here
}
return nil
}
// PostValidateImage performs validation after the upstream images library processes the image
func PostValidateImage(imageRef string, labels map[string]string) error {
// This is called after the upstream validation
// We can add Debian-specific post-validation here
// Check if this is a Debian bootc image
if GetBootcType(labels) == "debian" {
// Perform Debian-specific validations
return ValidateDebianBootcImage(labels, imageRef)
}
return nil
}
// GetImageLabels extracts labels from a container image
// This is a placeholder - in practice, you'd integrate with the actual container inspection
func GetImageLabels(imageRef string) (map[string]string, error) {
// This would integrate with the actual container inspection logic
// For now, return an empty map as a placeholder
return make(map[string]string), nil
}
// IsDebianImage checks if an image is specifically a Debian image
func IsDebianImage(labels map[string]string) bool {
return GetBootcType(labels) == "debian"
}
// GetDebianVersion extracts Debian version information from labels
func GetDebianVersion(labels map[string]string) string {
// Check for Debian-specific version labels
if version, exists := labels["org.debian.version"]; exists {
return version
}
// Check for general version labels
if version, exists := labels["version"]; exists {
return version
}
return "unknown"
}

View file

@ -0,0 +1,78 @@
package main
import (
"encoding/json"
"fmt"
"os"
"os/exec"
)
type ContainerInspect struct {
Labels map[string]string `json:"Labels"`
}
func main() {
if len(os.Args) != 2 {
fmt.Println("Usage: go run test_validation.go <image-tag>")
os.Exit(1)
}
imageTag := os.Args[1]
// Inspect the container image
cmd := exec.Command("podman", "inspect", imageTag)
output, err := cmd.Output()
if err != nil {
fmt.Printf("Error inspecting image %s: %v\n", imageTag, err)
os.Exit(1)
}
// Parse the JSON output
var containers []ContainerInspect
if err := json.Unmarshal(output, &containers); err != nil {
fmt.Printf("Error parsing JSON: %v\n", err)
os.Exit(1)
}
if len(containers) == 0 {
fmt.Printf("No container information found for %s\n", imageTag)
os.Exit(1)
}
labels := containers[0].Labels
fmt.Printf("Image: %s\n", imageTag)
fmt.Printf("Labels: %v\n", labels)
// Test our validation logic
isBootc := false
bootcType := "unknown"
if val, exists := labels["com.redhat.bootc"]; exists && val == "true" {
isBootc = true
bootcType = "redhat"
}
if val, exists := labels["com.debian.bootc"]; exists && val == "true" {
isBootc = true
bootcType = "debian"
}
hasOstreeBootable := false
if val, exists := labels["ostree.bootable"]; exists && val == "true" {
hasOstreeBootable = true
}
fmt.Printf("Is bootc image: %t\n", isBootc)
fmt.Printf("Bootc type: %s\n", bootcType)
fmt.Printf("Has ostree.bootable: %t\n", hasOstreeBootable)
if isBootc && hasOstreeBootable {
fmt.Printf("✅ Image %s is a valid bootc image\n", imageTag)
if bootcType == "debian" {
fmt.Printf("✅ Image %s is specifically a Debian bootc image\n", imageTag)
}
} else {
fmt.Printf("❌ Image %s is not a valid bootc image\n", imageTag)
os.Exit(1)
}
}

View file

@ -0,0 +1,98 @@
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
}

View file

@ -0,0 +1,148 @@
package distrodef
import (
"os"
"path/filepath"
"slices"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
const testDefLocation = "test_defs"
func TestLoadSimple(t *testing.T) {
def, err := LoadImageDef([]string{testDefLocation}, "fedoratest", "41", "anaconda-iso")
require.NoError(t, err)
assert.NotEmpty(t, def.Packages)
}
func TestLoadFuzzy(t *testing.T) {
def, err := LoadImageDef([]string{testDefLocation}, "fedoratest", "99", "anaconda-iso")
require.NoError(t, err)
assert.NotEmpty(t, def.Packages)
}
func TestLoadUnhappy(t *testing.T) {
_, err := LoadImageDef([]string{testDefLocation}, "lizard", "42", "anaconda-iso")
assert.ErrorContains(t, err, "could not find def file for distro lizard-42")
_, err = LoadImageDef([]string{testDefLocation}, "fedoratest", "0", "anaconda-iso")
assert.ErrorContains(t, err, "could not find def file for distro fedoratest-0")
_, err = LoadImageDef([]string{testDefLocation}, "fedoratest", "41", "anaconda-disk")
assert.ErrorContains(t, err, "could not find def for distro fedoratest and image type anaconda-disk")
_, err = LoadImageDef([]string{testDefLocation}, "fedoratest", "xxx", "anaconda-disk")
assert.ErrorContains(t, err, `cannot parse wanted version string: `)
}
const fakeDefFileContent = "anaconda-iso:\n packages: \n - foo\n"
func makeFakeDistrodefRoot(t *testing.T, defFiles []string) (searchPaths []string) {
tmp := t.TempDir()
for _, defFile := range defFiles {
p := filepath.Join(tmp, defFile)
err := os.MkdirAll(filepath.Dir(p), 0755)
require.NoError(t, err)
err = os.WriteFile(p, []byte(fakeDefFileContent), 0644)
require.NoError(t, err)
if !slices.Contains(searchPaths, filepath.Dir(p)) {
searchPaths = append(searchPaths, filepath.Dir(p))
}
}
return searchPaths
}
func TestFindDistroDefMultiDirs(t *testing.T) {
defDirs := makeFakeDistrodefRoot(t, []string{
"a/fedora-39.yaml",
"b/fedora-41.yaml",
"c/fedora-41.yaml",
})
assert.Equal(t, 3, len(defDirs))
def, err := findDistroDef(defDirs, "fedora", "41")
assert.NoError(t, err)
assert.True(t, strings.HasSuffix(def, "b/fedora-41.yaml"))
}
func TestFindDistroDefMultiDirsIgnoreENOENT(t *testing.T) {
defDirs := makeFakeDistrodefRoot(t, []string{
"a/fedora-41.yaml",
})
defDirs = append([]string{"/no/such/path"}, defDirs...)
def, err := findDistroDef(defDirs, "fedora", "41")
assert.NoError(t, err)
assert.True(t, strings.HasSuffix(def, "a/fedora-41.yaml"))
}
func TestFindDistroDefMultiFuzzy(t *testing.T) {
defDirs := makeFakeDistrodefRoot(t, []string{
"a/fedora-39.yaml",
"b/fedora-41.yaml",
"b/b/fedora-42.yaml",
"c/fedora-41.yaml",
})
// no fedora-99, pick the closest
def, err := findDistroDef(defDirs, "fedora", "99")
assert.NoError(t, err)
assert.True(t, strings.HasSuffix(def, "b/b/fedora-42.yaml"))
}
func TestFindDistroDefMultiFuzzyMinorReleases(t *testing.T) {
defDirs := makeFakeDistrodefRoot(t, []string{
"a/centos-8.9.yaml",
"b/centos-7.yaml",
"c/centos-9.1.yaml",
"d/centos-9.1.1.yaml",
"b/b/centos-9.10.yaml",
})
def, err := findDistroDef(defDirs, "centos", "9.11")
assert.NoError(t, err)
assert.True(t, strings.HasSuffix(def, "b/b/centos-9.10.yaml"), def)
}
func TestFindDistroDefMultiFuzzyMinorReleasesIsZero(t *testing.T) {
defDirs := makeFakeDistrodefRoot(t, []string{
"a/centos-9.yaml",
"a/centos-10.yaml",
})
def, err := findDistroDef(defDirs, "centos", "10.0")
assert.NoError(t, err)
assert.True(t, strings.HasSuffix(def, "a/centos-10.yaml"), def)
}
func TestFindDistroDefMultiFuzzyError(t *testing.T) {
defDirs := makeFakeDistrodefRoot(t, []string{
"a/fedora-40.yaml",
})
// the best version we have is newer than what is requested, this
// is an error
_, err := findDistroDef(defDirs, "fedora", "30")
assert.ErrorContains(t, err, "could not find def file for distro fedora-30")
}
func TestFindDistroDefBadNumberIgnoresBadFiles(t *testing.T) {
defDirs := makeFakeDistrodefRoot(t, []string{
"a/fedora-NaN.yaml",
})
_, err := findDistroDef(defDirs, "fedora", "40")
assert.ErrorContains(t, err, "could not find def file for distro fedora-40")
}
func TestFindDistroDefCornerCases(t *testing.T) {
defDirs := makeFakeDistrodefRoot(t, []string{
"a/fedora-.yaml",
"b/fedora-1.yaml",
"c/fedora.yaml",
})
def, err := findDistroDef(defDirs, "fedora", "2")
assert.NoError(t, err)
assert.True(t, strings.HasSuffix(def, "b/fedora-1.yaml"))
}

View file

@ -0,0 +1,4 @@
anaconda-iso:
packages:
- anaconda
- curl

View file

@ -0,0 +1,88 @@
package imagetypes
import (
"fmt"
"slices"
"sort"
"strings"
)
type imageType struct {
Export string
ISO bool
}
var supportedImageTypes = map[string]imageType{
"ami": imageType{Export: "image"},
"qcow2": imageType{Export: "qcow2"},
"raw": imageType{Export: "image"},
"vmdk": imageType{Export: "vmdk"},
"vhd": imageType{Export: "vpc"},
"gce": imageType{Export: "gce"},
"anaconda-iso": imageType{Export: "bootiso", ISO: true},
"iso": imageType{Export: "bootiso", ISO: true},
}
// Available() returns a comma-separated list of supported image types
func Available() string {
keys := make([]string, 0, len(supportedImageTypes))
for k := range supportedImageTypes {
keys = append(keys, k)
}
sort.Strings(keys)
return strings.Join(keys, ", ")
}
// ImageTypes contains the image types that are requested to be build
type ImageTypes []string
// New takes image type names as input and returns a ImageTypes
// object or an error if the image types are invalid.
//
// Note that it is not possible to mix iso/disk types
func New(imageTypeNames ...string) (ImageTypes, error) {
if len(imageTypeNames) == 0 {
return nil, fmt.Errorf("cannot use an empty array as a build request")
}
var ISOs, disks int
for _, name := range imageTypeNames {
imgType, ok := supportedImageTypes[name]
if !ok {
return nil, fmt.Errorf("unsupported image type %q, valid types are %s", name, Available())
}
if imgType.ISO {
ISOs++
} else {
disks++
}
}
if ISOs > 0 && disks > 0 {
return nil, fmt.Errorf("cannot mix ISO/disk images in request %v", imageTypeNames)
}
return ImageTypes(imageTypeNames), nil
}
// Exports returns the list of osbuild manifest exports require to build
// all images types.
func (it ImageTypes) Exports() []string {
exports := make([]string, 0, len(it))
// XXX: this assumes a valid ImagTypes object
for _, name := range it {
imgType := supportedImageTypes[name]
if !slices.Contains(exports, imgType.Export) {
exports = append(exports, imgType.Export)
}
}
return exports
}
// BuildsISO returns true if the image types build an ISO, note that
// it is not possible to mix disk/iso.
func (it ImageTypes) BuildsISO() bool {
// XXX: this assumes a valid ImagTypes object
return supportedImageTypes[it[0]].ISO
}

View file

@ -0,0 +1,90 @@
package imagetypes_test
import (
"errors"
"testing"
"github.com/stretchr/testify/assert"
"github.com/osbuild/bootc-image-builder/bib/internal/imagetypes"
)
type testCase struct {
imageTypes []string
expectedExports []string
expectISO bool
expectedErr error
}
func TestImageTypes(t *testing.T) {
testCases := map[string]testCase{
"qcow-disk": {
imageTypes: []string{"qcow2"},
expectedExports: []string{"qcow2"},
expectISO: false,
},
"ami-disk": {
imageTypes: []string{"ami"},
expectedExports: []string{"image"},
expectISO: false,
},
"qcow-ami-disk": {
imageTypes: []string{"qcow2", "ami"},
expectedExports: []string{"qcow2", "image"},
expectISO: false,
},
"ami-raw": {
imageTypes: []string{"ami", "raw"},
expectedExports: []string{"image"},
expectISO: false,
},
"all-disk": {
imageTypes: []string{"ami", "raw", "vmdk", "qcow2"},
expectedExports: []string{"image", "vmdk", "qcow2"},
expectISO: false,
},
"iso": {
imageTypes: []string{"iso"},
expectedExports: []string{"bootiso"},
expectISO: true,
},
"anaconda": {
imageTypes: []string{"anaconda-iso"},
expectedExports: []string{"bootiso"},
expectISO: true,
},
"bad-mix": {
imageTypes: []string{"vmdk", "anaconda-iso"},
expectedErr: errors.New("cannot mix ISO/disk images in request [vmdk anaconda-iso]"),
},
"bad-mix-part-2": {
imageTypes: []string{"ami", "iso"},
expectedErr: errors.New("cannot mix ISO/disk images in request [ami iso]"),
},
"bad-image-type": {
imageTypes: []string{"bad"},
expectedErr: errors.New(`unsupported image type "bad", valid types are ami, anaconda-iso, gce, iso, qcow2, raw, vhd, vmdk`),
},
"bad-in-good": {
imageTypes: []string{"ami", "raw", "vmdk", "qcow2", "something-else-what-is-this"},
expectedErr: errors.New(`unsupported image type "something-else-what-is-this", valid types are ami, anaconda-iso, gce, iso, qcow2, raw, vhd, vmdk`),
},
"all-bad": {
imageTypes: []string{"bad1", "bad2", "bad3", "bad4", "bad5", "bad42"},
expectedErr: errors.New(`unsupported image type "bad1", valid types are ami, anaconda-iso, gce, iso, qcow2, raw, vhd, vmdk`),
},
}
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
it, err := imagetypes.New(tc.imageTypes...)
if tc.expectedErr != nil {
assert.Equal(t, err, tc.expectedErr)
} else {
assert.Equal(t, it.Exports(), tc.expectedExports)
assert.Equal(t, it.BuildsISO(), tc.expectISO)
assert.NoError(t, err)
}
})
}
}

View file

@ -0,0 +1,78 @@
package solver
import (
"github.com/osbuild/images/pkg/arch"
"github.com/osbuild/images/pkg/dnfjson"
"github.com/osbuild/images/pkg/bib/osinfo"
"github.com/particle-os/debian-bootc-image-builder/bib/internal/aptsolver"
)
// Solver interface that can work with both dnfjson and apt solvers
type Solver interface {
Depsolve(packages []string, maxAttempts int) (interface{}, error)
GetArch() arch.Arch
GetOSInfo() *osinfo.Info
}
// DNFJSONSolver wraps the original dnfjson.Solver
type DNFJSONSolver struct {
*dnfjson.Solver
}
// NewDNFSolver creates a new DNF solver
func NewDNFSolver(solver *dnfjson.Solver) *DNFJSONSolver {
return &DNFJSONSolver{Solver: solver}
}
// Depsolve resolves package dependencies using DNF
func (s *DNFJSONSolver) Depsolve(packages []string, maxAttempts int) (interface{}, error) {
// This is a simplified implementation - in a real implementation,
// we would need to convert the packages to the proper format
// For now, we'll return a mock result
return &aptsolver.DepsolveResult{
Packages: packages,
Repos: []interface{}{},
}, nil
}
// GetArch returns the architecture for this solver
func (s *DNFJSONSolver) GetArch() arch.Arch {
// This is a simplified implementation - in a real implementation,
// we would need to extract the arch from the dnfjson.Solver
return arch.Current()
}
// GetOSInfo returns the OS information for this solver
func (s *DNFJSONSolver) GetOSInfo() *osinfo.Info {
// This is a simplified implementation - in a real implementation,
// we would need to extract the OS info from the dnfjson.Solver
return &osinfo.Info{}
}
// AptSolverWrapper wraps our apt solver
type AptSolverWrapper struct {
*aptsolver.AptSolver
}
// NewAptSolver creates a new apt solver
func NewAptSolver(cacheDir string, arch arch.Arch, osInfo *osinfo.Info) *AptSolverWrapper {
return &AptSolverWrapper{
AptSolver: aptsolver.NewAptSolver(cacheDir, arch, osInfo),
}
}
// Depsolve resolves package dependencies using apt
func (s *AptSolverWrapper) Depsolve(packages []string, maxAttempts int) (interface{}, error) {
return s.AptSolver.Depsolve(packages, maxAttempts)
}
// NewSolver creates the appropriate solver based on the OS
func NewSolver(osInfo *osinfo.Info, cacheDir string, arch arch.Arch, dnfSolver *dnfjson.Solver) (Solver, error) {
switch osInfo.OSRelease.ID {
case "debian":
return NewAptSolver(cacheDir, arch, osInfo), nil
default:
// For Fedora, RHEL, CentOS, etc., use the DNF solver
return NewDNFSolver(dnfSolver), nil
}
}