- 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
169 lines
5.2 KiB
Go
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
|
|
}
|