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

169 lines
5.2 KiB
Go

package phases
import (
"fmt"
"os"
"path/filepath"
"time"
"deb-bootc-compose/internal/types"
)
// OutputPhase handles generating final output artifacts
type OutputPhase struct {
PhaseBase
}
// NewOutputPhase creates a new OutputPhase
func NewOutputPhase() *OutputPhase {
return &OutputPhase{
PhaseBase: NewPhaseBase("output", []string{"ostree"}),
}
}
// Run executes the output phase
func (p *OutputPhase) Run(engine types.Engine) error {
logger := engine.GetLogger()
logger.Printf("Starting output generation phase")
// Get output manager
outputManager := engine.GetOutputManager()
if outputManager == nil {
logger.Printf("No output manager configured, skipping output phase")
return nil
}
// Get treefile and process variants
treefile := engine.GetTreefile()
for _, variant := range treefile.Variants {
for _, arch := range variant.Architectures {
logger.Printf("Generating output for variant %s on architecture %s", variant.Name, arch)
// Create output work directory
outputWorkDir := filepath.Join(engine.GetWorkDir(), "variants", variant.Name, arch, "output")
if err := os.MkdirAll(outputWorkDir, 0755); err != nil {
return fmt.Errorf("failed to create output work directory: %w", err)
}
// Generate outputs for this variant/architecture
if err := p.generateOutputs(engine, outputWorkDir, variant, arch); err != nil {
return fmt.Errorf("failed to generate outputs for variant %s on architecture %s: %w", variant.Name, arch, err)
}
logger.Printf("Completed output generation for variant %s on architecture %s", variant.Name, arch)
}
}
logger.Printf("Output generation phase completed successfully")
return nil
}
// generateOutputs generates all configured output formats
func (p *OutputPhase) generateOutputs(engine types.Engine, workDir string, variant types.Variant, arch string) error {
logger := engine.GetLogger()
outputManager := engine.GetOutputManager()
// Get OSTree reference
ref := engine.GetTreefile().GetOSTreeRef(variant.Name, arch)
// Get output configuration
outputConfig := engine.GetTreefile().Output
// Generate container image if requested
if p.containsFormat(outputConfig.Formats, "container") {
logger.Printf("Generating container image for variant %s on architecture %s", variant.Name, arch)
containerConfig := map[string]interface{}{
"base_image": outputConfig.Container.BaseImage,
"labels": outputConfig.Container.Labels,
"entrypoint": outputConfig.Container.Entrypoint,
"cmd": outputConfig.Container.CMD,
}
containerPath := filepath.Join(workDir, "container")
if err := outputManager.GenerateContainerImage(ref, containerPath, containerConfig); err != nil {
logger.Printf("Warning: failed to generate container image: %v", err)
} else {
logger.Printf("Successfully generated container image")
}
}
// Generate disk image if requested
if p.containsFormat(outputConfig.Formats, "disk") {
logger.Printf("Generating disk image for variant %s on architecture %s", variant.Name, arch)
diskConfig := map[string]interface{}{
"size": outputConfig.DiskImage.Size,
"formats": outputConfig.DiskImage.Formats,
"bootloader": outputConfig.DiskImage.Bootloader,
"kernel": outputConfig.DiskImage.Kernel,
"initramfs": outputConfig.DiskImage.Initramfs,
}
diskPath := filepath.Join(workDir, "disk")
if err := outputManager.GenerateDiskImage(ref, diskPath, diskConfig); err != nil {
logger.Printf("Warning: failed to generate disk image: %v", err)
} else {
logger.Printf("Successfully generated disk image")
}
}
// Generate tarball if requested
if p.containsFormat(outputConfig.Formats, "tarball") {
logger.Printf("Generating tarball for variant %s on architecture %s", variant.Name, arch)
tarballConfig := map[string]interface{}{
"compression": "gzip",
"format": "tar.gz",
}
tarballPath := filepath.Join(workDir, "tarball")
if err := outputManager.GenerateTarball(ref, tarballPath, tarballConfig); err != nil {
logger.Printf("Warning: failed to generate tarball: %v", err)
} else {
logger.Printf("Successfully generated tarball")
}
}
// Write output summary
if err := p.writeOutputSummary(workDir, variant, arch, outputConfig.Formats); err != nil {
return fmt.Errorf("failed to write output summary: %w", err)
}
return nil
}
// containsFormat checks if a format is in the list
func (p *OutputPhase) containsFormat(formats []string, format string) bool {
for _, f := range formats {
if f == format {
return true
}
}
return false
}
// writeOutputSummary writes a summary of generated outputs
func (p *OutputPhase) writeOutputSummary(workDir string, variant types.Variant, arch string, formats []string) error {
summaryFile := filepath.Join(workDir, "output-summary.txt")
summary := fmt.Sprintf(`Output Generation Summary
Variant: %s
Architecture: %s
Timestamp: %s
Formats: %v
Generated outputs:
`, variant.Name, arch, time.Now().Format(time.RFC3339), formats)
for _, format := range formats {
summary += fmt.Sprintf(" - %s\n", format)
}
if err := os.WriteFile(summaryFile, []byte(summary), 0644); err != nil {
return fmt.Errorf("failed to write output summary: %w", err)
}
return nil
}