cleanup
Some checks failed
particle-os CI / Test particle-os (push) Failing after 1s
particle-os CI / Integration Test (push) Has been skipped
particle-os CI / Security & Quality (push) Failing after 1s
Test particle-os Basic Functionality / test-basic (push) Failing after 1s
particle-os CI / Build and Release (push) Has been skipped
Some checks failed
particle-os CI / Test particle-os (push) Failing after 1s
particle-os CI / Integration Test (push) Has been skipped
particle-os CI / Security & Quality (push) Failing after 1s
Test particle-os Basic Functionality / test-basic (push) Failing after 1s
particle-os CI / Build and Release (push) Has been skipped
This commit is contained in:
parent
d782a8a4fb
commit
126ee1a849
76 changed files with 1683 additions and 470 deletions
149
bib/internal/builder/recipe.go
Normal file
149
bib/internal/builder/recipe.go
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
package particle_os
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
// Recipe represents a particle-os recipe configuration
|
||||
type Recipe struct {
|
||||
Name string `yaml:"name"`
|
||||
Description string `yaml:"description"`
|
||||
BaseImage string `yaml:"base-image"`
|
||||
ImageVersion string `yaml:"image-version"`
|
||||
Stages []Stage `yaml:"stages"`
|
||||
Output OutputConfig `yaml:"output"`
|
||||
Metadata map[string]interface{} `yaml:"metadata,omitempty"`
|
||||
}
|
||||
|
||||
// Stage represents a build stage in the recipe
|
||||
type Stage struct {
|
||||
Type string `yaml:"type"`
|
||||
Options map[string]interface{} `yaml:"options,omitempty"`
|
||||
Inputs map[string]interface{} `yaml:"inputs,omitempty"`
|
||||
Devices map[string]interface{} `yaml:"devices,omitempty"`
|
||||
Mounts []interface{} `yaml:"mounts,omitempty"`
|
||||
}
|
||||
|
||||
// User represents a user account configuration
|
||||
type User struct {
|
||||
Password string `yaml:"password,omitempty"`
|
||||
Shell string `yaml:"shell,omitempty"`
|
||||
Groups []string `yaml:"groups,omitempty"`
|
||||
UID int `yaml:"uid"`
|
||||
GID int `yaml:"gid"`
|
||||
Home string `yaml:"home,omitempty"`
|
||||
Comment string `yaml:"comment,omitempty"`
|
||||
}
|
||||
|
||||
// UsersOptions represents the options for the users stage
|
||||
type UsersOptions struct {
|
||||
Users map[string]User `yaml:"users"`
|
||||
DefaultShell string `yaml:"default_shell,omitempty"`
|
||||
DefaultHome string `yaml:"default_home,omitempty"`
|
||||
}
|
||||
|
||||
// OutputConfig defines the output image configuration
|
||||
type OutputConfig struct {
|
||||
Formats []string `yaml:"formats"`
|
||||
Size string `yaml:"size"`
|
||||
Path string `yaml:"path,omitempty"`
|
||||
}
|
||||
|
||||
// LoadRecipe loads a recipe from a YAML file
|
||||
func LoadRecipe(recipePath string) (*Recipe, error) {
|
||||
data, err := os.ReadFile(recipePath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read recipe file: %w", err)
|
||||
}
|
||||
|
||||
var recipe Recipe
|
||||
if err := yaml.Unmarshal(data, &recipe); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse recipe YAML: %w", err)
|
||||
}
|
||||
|
||||
// Validate required fields
|
||||
if recipe.Name == "" {
|
||||
return nil, fmt.Errorf("recipe must have a name")
|
||||
}
|
||||
if recipe.BaseImage == "" {
|
||||
return nil, fmt.Errorf("recipe must specify a base-image")
|
||||
}
|
||||
if len(recipe.Stages) == 0 {
|
||||
return nil, fmt.Errorf("recipe must have at least one stage")
|
||||
}
|
||||
|
||||
return &recipe, nil
|
||||
}
|
||||
|
||||
// SaveRecipe saves a recipe to a YAML file
|
||||
func (r *Recipe) SaveRecipe(outputPath string) error {
|
||||
data, err := yaml.Marshal(r)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to marshal recipe: %w", err)
|
||||
}
|
||||
|
||||
// Ensure output directory exists
|
||||
outputDir := filepath.Dir(outputPath)
|
||||
if err := os.MkdirAll(outputDir, 0755); err != nil {
|
||||
return fmt.Errorf("failed to create output directory: %w", err)
|
||||
}
|
||||
|
||||
if err := os.WriteFile(outputPath, data, 0644); err != nil {
|
||||
return fmt.Errorf("failed to write recipe file: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate checks if the recipe is valid
|
||||
func (r *Recipe) Validate() error {
|
||||
// Check for required fields
|
||||
if r.Name == "" {
|
||||
return fmt.Errorf("recipe name is required")
|
||||
}
|
||||
if r.BaseImage == "" {
|
||||
return fmt.Errorf("base-image is required")
|
||||
}
|
||||
if len(r.Stages) == 0 {
|
||||
return fmt.Errorf("at least one stage is required")
|
||||
}
|
||||
|
||||
// Validate stages
|
||||
for i, stage := range r.Stages {
|
||||
if stage.Type == "" {
|
||||
return fmt.Errorf("stage %d: type is required", i)
|
||||
}
|
||||
}
|
||||
|
||||
// Validate output
|
||||
if len(r.Output.Formats) == 0 {
|
||||
return fmt.Errorf("at least one output format is required")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetStageByType returns all stages of a specific type
|
||||
func (r *Recipe) GetStageByType(stageType string) []Stage {
|
||||
var stages []Stage
|
||||
for _, stage := range r.Stages {
|
||||
if stage.Type == stageType {
|
||||
stages = append(stages, stage)
|
||||
}
|
||||
}
|
||||
return stages
|
||||
}
|
||||
|
||||
// HasStage checks if the recipe has a stage of the specified type
|
||||
func (r *Recipe) HasStage(stageType string) bool {
|
||||
for _, stage := range r.Stages {
|
||||
if stage.Type == stageType {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue