package phases import ( "fmt" "os" "path/filepath" "time" "deb-bootc-compose/internal/types" ) // OSTreePhase handles OSTree commit creation and repository management type OSTreePhase struct { PhaseBase } // NewOSTreePhase creates a new OSTreePhase func NewOSTreePhase() *OSTreePhase { return &OSTreePhase{ PhaseBase: NewPhaseBase("ostree", []string{"build"}), } } // Run executes the OSTree phase func (p *OSTreePhase) Run(engine types.Engine) error { logger := engine.GetLogger() logger.Printf("Starting OSTree phase...") // Get package information from treefile treefile := engine.GetTreefile() // Process each variant and architecture for _, variant := range treefile.Variants { for _, arch := range variant.Architectures { logger.Printf("Processing variant %s for architecture %s", variant.Name, arch) // Get packages for this variant/architecture combination packages := treefile.GetPackagesForVariant(variant.Name, arch) // Create OSTree commit using apt-ostree ref := treefile.GetOSTreeRef(variant.Name, arch) subject := fmt.Sprintf("Debian %s %s variant for %s", treefile.Release, variant.Name, arch) body := fmt.Sprintf("Debian %s %s variant for %s architecture\nGenerated by deb-bootc-compose", treefile.Release, variant.Name, arch) // Collect all package names 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...) commitID, err := engine.GetOSTreeTool().CreateCommit(ref, subject, body, allPackages) if err != nil { return fmt.Errorf("failed to create OSTree commit for variant %s/%s: %w", variant.Name, arch, err) } // Create variant-specific work directory variantWorkDir := filepath.Join(engine.GetWorkDir(), "variants", variant.Name, arch) if err := os.MkdirAll(variantWorkDir, 0755); err != nil { return fmt.Errorf("failed to create variant work directory: %w", err) } // Write commit information if err := p.writeCommitInfo(variantWorkDir, ref, commitID, subject, body, allPackages); err != nil { return fmt.Errorf("failed to write commit info for variant %s/%s: %w", variant.Name, arch, err) } logger.Printf("Completed OSTree commit for variant %s/%s: %s", variant.Name, arch, commitID) } } logger.Printf("OSTree phase completed successfully") return nil } // createOSTreeCommit creates an OSTree commit for a specific variant and architecture func (p *OSTreePhase) createOSTreeCommit(engine types.Engine, workDir string, variant types.Variant, arch string) error { logger := engine.GetLogger() ostreeTool := engine.GetOSTreeTool() // Get packages for this variant packages := engine.GetTreefile().GetPackagesForVariant(variant.Name, arch) // Generate OSTree reference ref := engine.GetTreefile().GetOSTreeRef(variant.Name, arch) // Generate commit subject and body subject := fmt.Sprintf("Debian %s %s variant for %s", engine.GetTreefile().Release, variant.Name, arch) body := fmt.Sprintf(`Debian %s %s variant for %s architecture Generated by deb-bootc-compose Timestamp: %s Variant: %s Architecture: %s Packages included: - Required: %d - Optional: %d - Recommended: %d - Development: %d - Kernel: %d - Bootloader: %d`, engine.GetTreefile().Release, variant.Name, arch, time.Now().Format(time.RFC3339), variant.Name, arch, len(packages.Required), len(packages.Optional), len(packages.Recommended), len(packages.Development), len(packages.Kernel), len(packages.Bootloader)) // Collect all package names 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 } uniqueList := make([]string, 0, len(uniquePackages)) for pkg := range uniquePackages { uniqueList = append(uniqueList, pkg) } // Create OSTree commit logger.Printf("Creating OSTree commit for reference: %s", ref) commitID, err := ostreeTool.CreateCommit(ref, subject, body, uniqueList) if err != nil { return fmt.Errorf("failed to create OSTree commit: %w", err) } // Update the reference to point to the new commit if err := ostreeTool.UpdateRef(ref, commitID); err != nil { return fmt.Errorf("failed to update OSTree reference: %w", err) } // Validate the commit if err := ostreeTool.ValidateCommit(commitID); err != nil { return fmt.Errorf("failed to validate OSTree commit: %w", err) } // Write commit information if err := p.writeCommitInfo(workDir, ref, commitID, subject, body, uniqueList); err != nil { return fmt.Errorf("failed to write commit information: %w", err) } logger.Printf("Successfully created OSTree commit %s for reference %s", commitID, ref) return nil } // writeCommitInfo writes commit information to files func (p *OSTreePhase) writeCommitInfo(workDir string, ref, commitID, subject, body string, packages []string) error { // Write commit details commitFile := filepath.Join(workDir, "commit-info.txt") content := fmt.Sprintf(`OSTree Commit Information Reference: %s Commit ID: %s Subject: %s Timestamp: %s Body: %s Packages (%d): `, ref, commitID, subject, time.Now().Format(time.RFC3339), body, len(packages)) for _, pkg := range packages { content += fmt.Sprintf(" - %s\n", pkg) } if err := os.WriteFile(commitFile, []byte(content), 0644); err != nil { return fmt.Errorf("failed to write commit info: %w", err) } // Write package list packagesFile := filepath.Join(workDir, "packages.txt") pkgContent := "" for _, pkg := range packages { pkgContent += pkg + "\n" } if err := os.WriteFile(packagesFile, []byte(pkgContent), 0644); err != nil { return fmt.Errorf("failed to write packages list: %w", err) } return nil }