deb-bootc-compose/internal/phases/build.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

250 lines
7.2 KiB
Go

package phases
import (
"fmt"
"os"
"path/filepath"
"time"
"deb-bootc-compose/internal/types"
)
// BuildPhase handles package building using the build system
type BuildPhase struct {
PhaseBase
}
// NewBuildPhase creates a new BuildPhase
func NewBuildPhase() *BuildPhase {
return &BuildPhase{
PhaseBase: NewPhaseBase("build", []string{"gather"}),
}
}
// Run executes the build phase
func (p *BuildPhase) Run(engine types.Engine) error {
logger := engine.GetLogger()
logger.Printf("Starting build phase")
// Get build system
buildSystem := engine.GetBuildSystem()
if buildSystem == nil {
logger.Printf("No build system configured, skipping build phase")
return nil
}
// Test build system connection
if err := buildSystem.TestConnection(); err != nil {
return fmt.Errorf("build system connection failed: %w", err)
}
// Get treefile and process variants
treefile := engine.GetTreefile()
for _, variant := range treefile.Variants {
for _, arch := range variant.Architectures {
logger.Printf("Building variant %s for architecture %s", variant.Name, arch)
// Create build work directory
buildWorkDir := filepath.Join(engine.GetWorkDir(), "variants", variant.Name, arch, "build")
if err := os.MkdirAll(buildWorkDir, 0755); err != nil {
return fmt.Errorf("failed to create build work directory: %w", err)
}
// Build packages for this variant/architecture
if err := p.buildVariant(engine, buildWorkDir, variant, arch); err != nil {
return fmt.Errorf("failed to build variant %s for architecture %s: %w", variant.Name, arch, err)
}
logger.Printf("Completed building variant %s for architecture %s", variant.Name, arch)
}
}
logger.Printf("Build phase completed successfully")
return nil
}
// buildVariant builds packages for a specific variant and architecture
func (p *BuildPhase) buildVariant(engine types.Engine, workDir string, variant types.Variant, arch string) error {
logger := engine.GetLogger()
buildSystem := engine.GetBuildSystem()
// Get packages for this variant
packages := engine.GetTreefile().GetPackagesForVariant(variant.Name, arch)
// Create build environment
logger.Printf("Creating build environment for architecture %s", arch)
buildEnv, err := buildSystem.CreateBuildEnvironment(arch)
if err != nil {
return fmt.Errorf("failed to create build environment: %w", err)
}
// Ensure cleanup of build environment
defer func() {
if err := buildSystem.CleanupBuildEnvironment(buildEnv); err != nil {
logger.Printf("Warning: failed to cleanup build environment: %v", err)
}
}()
// Collect all packages to build
allPackages := make([]string, 0)
allPackages = append(allPackages, packages.Required...)
allPackages = append(allPackages, packages.Optional...)
allPackages = append(allPackages, packages.Recommended...)
allPackages = append(allPackages, packages.Development...)
allPackages = append(allPackages, packages.Kernel...)
allPackages = append(allPackages, packages.Bootloader...)
// Remove duplicates
uniquePackages := make(map[string]bool)
for _, pkg := range allPackages {
uniquePackages[pkg] = true
}
// Build each package
buildResults := make([]*types.BuildResult, 0)
for pkgName := range uniquePackages {
logger.Printf("Building package: %s", pkgName)
// Build the package
result, err := buildSystem.BuildPackage(pkgName, arch)
if err != nil {
logger.Printf("Warning: failed to build package %s: %v", pkgName, err)
continue
}
// Wait for build to complete
completedResult, err := buildSystem.WaitForBuild(result.ID, 30*time.Minute)
if err != nil {
logger.Printf("Warning: failed to wait for build of package %s: %v", pkgName, err)
continue
}
if completedResult == nil {
logger.Printf("Warning: build result is nil for package %s", pkgName)
continue
}
buildResults = append(buildResults, completedResult)
logger.Printf("Successfully built package: %s", pkgName)
}
// Write build results
if err := p.writeBuildResults(workDir, buildResults); err != nil {
return fmt.Errorf("failed to write build results: %w", err)
}
// Write build summary
if err := p.writeBuildSummary(workDir, buildResults, variant, arch); err != nil {
return fmt.Errorf("failed to write build summary: %w", err)
}
logger.Printf("Built %d packages for variant %s on architecture %s", len(buildResults), variant.Name, arch)
return nil
}
// writeBuildResults writes build results to files
func (p *BuildPhase) writeBuildResults(workDir string, results []*types.BuildResult) error {
// Create results directory
resultsDir := filepath.Join(workDir, "results")
if err := os.MkdirAll(resultsDir, 0755); err != nil {
return fmt.Errorf("failed to create results directory: %w", err)
}
// Write successful builds
successful := make([]string, 0)
failed := make([]string, 0)
for _, result := range results {
if result.Result == "success" {
successful = append(successful, result.PackageName)
} else {
failed = append(failed, result.PackageName)
}
}
// Write successful packages
if err := p.writePackageList(resultsDir, "successful", successful); err != nil {
return fmt.Errorf("failed to write successful packages: %w", err)
}
// Write failed packages
if err := p.writePackageList(resultsDir, "failed", failed); err != nil {
return fmt.Errorf("failed to write failed packages: %w", err)
}
return nil
}
// writeBuildSummary writes a summary of the build process
func (p *BuildPhase) writeBuildSummary(workDir string, results []*types.BuildResult, variant types.Variant, arch string) error {
summaryFile := filepath.Join(workDir, "build-summary.txt")
summary := fmt.Sprintf(`Build Summary for Variant: %s
Architecture: %s
Timestamp: %s
Total Packages: %d
Successful Builds: %d
Failed Builds: %d
Successful Packages:
`, variant.Name, arch, time.Now().Format(time.RFC3339), len(results),
len(results), 0) // For now, assume all successful
// Count successful builds
successfulCount := 0
for _, result := range results {
if result.Result == "success" {
successfulCount++
summary += fmt.Sprintf(" - %s\n", result.PackageName)
}
}
summary += fmt.Sprintf("\nFailed Packages:\n")
failedCount := 0
for _, result := range results {
if result.Result != "success" {
failedCount++
summary += fmt.Sprintf(" - %s (%s)\n", result.PackageName, result.Result)
}
}
// Update counts
summary = fmt.Sprintf(`Build Summary for Variant: %s
Architecture: %s
Timestamp: %s
Total Packages: %d
Successful Builds: %d
Failed Builds: %d
Successful Packages:
`, variant.Name, arch, time.Now().Format(time.RFC3339), len(results),
successfulCount, failedCount) + summary
if err := os.WriteFile(summaryFile, []byte(summary), 0644); err != nil {
return fmt.Errorf("failed to write build summary: %w", err)
}
return nil
}
// writePackageList writes a list of packages to a file
func (p *BuildPhase) writePackageList(dir, name string, packages []string) error {
if len(packages) == 0 {
return nil
}
filename := filepath.Join(dir, name+".txt")
content := ""
for _, pkg := range packages {
content += pkg + "\n"
}
if err := os.WriteFile(filename, []byte(content), 0644); err != nil {
return fmt.Errorf("failed to write package list %s: %w", name, err)
}
return nil
}