package main import ( "fmt" "os" "github.com/sirupsen/logrus" "github.com/spf13/cobra" "github.com/osbuild/images/pkg/arch" "github.com/particle-os/debian-bootc-image-builder/bib/internal/debos" "github.com/osbuild/images/pkg/bib/osinfo" "github.com/osbuild/image-builder-cli/pkg/progress" "github.com/osbuild/image-builder-cli/pkg/setup" podman_container "github.com/osbuild/images/pkg/bib/container" ) // DebosBuildConfig contains configuration for debos-based builds type DebosBuildConfig struct { Architecture arch.Arch Suite string ContainerImage string ImageTypes []string OutputDir string WorkDir string CustomPackages []string OSTreeEnabled bool OSTreeConfig *debos.OSTreeConfig } // cmdBuildDebos implements the debos-based build functionality func cmdBuildDebos(cmd *cobra.Command, args []string) error { chown, _ := cmd.Flags().GetString("chown") imgTypes, _ := cmd.Flags().GetStringArray("type") outputDir, _ := cmd.Flags().GetString("output") targetArch, _ := cmd.Flags().GetString("target-arch") progressType, _ := cmd.Flags().GetString("progress") useDebos, _ := cmd.Flags().GetBool("use-debos") // If --use-debos is not specified, fall back to the original osbuild path if !useDebos { logrus.Debug("--use-debos not specified, falling back to osbuild path") return cmdBuild(cmd, args) } logrus.Info("Using debos backend for image building") // Validate environment logrus.Debug("Validating environment") if err := setup.Validate(targetArch); err != nil { return fmt.Errorf("cannot validate the setup: %w", err) } // Create output directory if err := os.MkdirAll(outputDir, 0o777); err != nil { return fmt.Errorf("cannot setup build dir: %w", err) } // Check ownership permissions canChown, err := canChownInPath(outputDir) if err != nil { return fmt.Errorf("cannot ensure ownership: %w", err) } if !canChown && chown != "" { return fmt.Errorf("chowning is not allowed in output directory") } // Setup progress bar pbar, err := progress.New(progressType) if err != nil { return fmt.Errorf("cannot create progress bar: %w", err) } defer pbar.Stop() // Get container image reference imgref := args[0] // For debos backend, we don't need strict bootc validation since we're building from scratch // Just validate that it's a valid container reference if err := setup.ValidateHasContainerTags(imgref); err != nil { logrus.Warnf("Container validation warning: %v", err) // Continue anyway for debos builds } // Get container size for disk sizing (used for future disk sizing calculations) _, err = getContainerSize(imgref) if err != nil { return fmt.Errorf("cannot get container size: %w", err) } // Create container instance to extract OS information container, err := podman_container.New(imgref) if err != nil { return fmt.Errorf("cannot create container instance: %w", err) } defer func() { if err := container.Stop(); err != nil { logrus.Warnf("error stopping container: %v", err) } }() // Extract OS information from container sourceinfo, err := osinfo.Load(container.Root()) if err != nil { return fmt.Errorf("cannot load OS info from container: %w", err) } // Determine architecture cntArch := arch.Current() if targetArch != "" { target, err := arch.FromString(targetArch) if err != nil { return fmt.Errorf("invalid target architecture: %w", err) } if target != arch.Current() { fmt.Fprintf(os.Stderr, "WARNING: target-arch is experimental and needs an installed 'qemu-user' package\n") cntArch = target } } // Determine Debian suite from container suite := determineDebianSuite(sourceinfo) logrus.Infof("Detected Debian suite: %s", suite) // Create work directory for debos workDir, err := os.MkdirTemp("", "debos-build-*") if err != nil { return fmt.Errorf("cannot create work directory: %w", err) } defer os.RemoveAll(workDir) // Create debos build configuration buildConfig := &DebosBuildConfig{ Architecture: cntArch, Suite: suite, ContainerImage: imgref, ImageTypes: imgTypes, OutputDir: outputDir, WorkDir: workDir, CustomPackages: []string{}, // TODO: Extract from blueprint config OSTreeEnabled: true, // Enable OSTree by default for bootc compatibility OSTreeConfig: &debos.OSTreeConfig{ Repository: "/ostree/repo", Branch: fmt.Sprintf("debian/%s/%s", suite, cntArch.String()), Subject: fmt.Sprintf("Debian %s bootc image", suite), Body: fmt.Sprintf("Generated by debos backend for %s", imgref), Mode: "bare-user", }, } // Check if dry-run is requested dryRun, _ := cmd.Flags().GetBool("debos-dry-run") // Execute debos build pbar.SetMessagef("Building image with debos backend...") if err := executeDebosBuild(buildConfig, pbar, dryRun); err != nil { return fmt.Errorf("debos build failed: %w", err) } pbar.SetMessagef("Build complete!") pbar.SetMessagef("Results saved in %s", outputDir) // Handle ownership changes if err := chownR(outputDir, chown); err != nil { return fmt.Errorf("cannot setup owner for %q: %w", outputDir, err) } return nil } // executeDebosBuild runs the actual debos build process func executeDebosBuild(config *DebosBuildConfig, pbar progress.ProgressBar, dryRun bool) error { logrus.Infof("Starting debos build for %s on %s", config.Suite, config.Architecture.String()) // Create OSTree builder builder, err := debos.NewOSTreeBuilder(config.WorkDir, config.OutputDir) if err != nil { return fmt.Errorf("failed to create debos builder: %w", err) } // Create build options buildOptions := &debos.BuildOptions{ Architecture: config.Architecture, Suite: config.Suite, ContainerImage: config.ContainerImage, ImageTypes: config.ImageTypes, OutputDir: config.OutputDir, WorkDir: config.WorkDir, CustomPackages: config.CustomPackages, } // Execute the build if dryRun { pbar.SetMessagef("Performing debos dry-run...") logrus.Info("DRY RUN MODE: Would execute debos build with the following configuration:") logrus.Infof(" Suite: %s", config.Suite) logrus.Infof(" Architecture: %s", config.Architecture.String()) logrus.Infof(" Container Image: %s", config.ContainerImage) logrus.Infof(" Image Types: %v", config.ImageTypes) logrus.Infof(" OSTree Repository: %s", config.OSTreeConfig.Repository) logrus.Infof(" OSTree Branch: %s", config.OSTreeConfig.Branch) logrus.Infof(" Work Directory: %s", config.WorkDir) logrus.Infof(" Output Directory: %s", config.OutputDir) // In dry-run mode, we don't actually execute debos pbar.SetMessagef("Dry run completed - no actual build performed") return nil } pbar.SetMessagef("Executing debos build...") result, err := builder.BuildBootcOSTree(buildOptions) if err != nil { return fmt.Errorf("debos execution failed: %w", err) } if !result.Success { return fmt.Errorf("debos build failed: %s", result.Error) } logrus.Infof("Debos build completed successfully: %s", result.OutputPath) return nil } // determineDebianSuite extracts the Debian suite from OS information func determineDebianSuite(sourceinfo *osinfo.Info) string { // Try to extract from OS release information if sourceinfo.OSRelease.ID != "" { // Check for Debian version if sourceinfo.OSRelease.ID == "debian" { if sourceinfo.OSRelease.VersionID != "" { // Map version numbers to suite names switch sourceinfo.OSRelease.VersionID { case "12": return "bookworm" case "13": return "trixie" case "14": return "sid" default: // If we can't map the version, use the version ID return sourceinfo.OSRelease.VersionID } } // Default to trixie if no version specified return "trixie" } // Check for Ubuntu (Debian derivative) if sourceinfo.OSRelease.ID == "ubuntu" { // For Ubuntu, we'll use the Debian base that it's derived from if sourceinfo.OSRelease.VersionID != "" { switch sourceinfo.OSRelease.VersionID { case "22.04", "22.10", "23.04", "23.10": return "bookworm" // Ubuntu 22.04+ is based on Debian bookworm case "24.04", "24.10": return "trixie" // Ubuntu 24.04+ is based on Debian trixie default: return "trixie" // Default to trixie for newer versions } } return "trixie" } } // Fallback to default suite logrus.Warn("Could not determine Debian suite from container, using default: trixie") return "trixie" } // addDebosFlags adds debos-specific command line flags func addDebosFlags(cmd *cobra.Command) { cmd.Flags().Bool("use-debos", false, "Use debos backend instead of osbuild") cmd.Flags().String("debos-suite", "", "Override Debian suite detection (e.g., bookworm, trixie)") cmd.Flags().StringArray("debos-packages", []string{}, "Additional packages to install during debos build") cmd.Flags().Bool("debos-ostree", true, "Enable OSTree integration for bootc compatibility") cmd.Flags().String("debos-repository", "/ostree/repo", "OSTree repository path") cmd.Flags().String("debos-branch", "", "OSTree branch name (auto-generated if not specified)") }