Files
factory/pkg/manifest/manifest.go
vitya 561a7fd66f feat: L0b bootstrap scripts + L1 Go CLI structure
L0b (factory bootstrap):
- bootstrap.ps1 (Windows): pre-checks, interactive config, clone .factory, home.toml
- bootstrap.sh (Linux/Mac): same functionality for bash
- Idempotent re-run with home.toml verification

L1 (Go CLI):
- factory.yaml: manifest schema with components, deps, health-checks
- cmd/factory: Cobra CLI (install/update/status/diagnose)
- pkg/config: home.toml reader
- pkg/manifest: factory.yaml parser + topological sort
- pkg/health: health-check logic (file/dir/mcp_tool/command)

Scripts:
- build.sh: cross-compile for linux/win/darwin × amd64/arm64
- build.ps1: PowerShell cross-compile

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 23:59:09 +03:00

124 lines
2.5 KiB
Go

package manifest
import (
"fmt"
"os"
"strings"
"github.com/goccy/go-yaml"
)
type GitSource struct {
Type string `yaml:"type"`
URL string `yaml:"url"`
Branch string `yaml:"branch"`
}
type PostInstall struct {
Shell string `yaml:"shell"`
Script string `yaml:"script"`
ShellAlt string `yaml:"shell_alt"`
ScriptAlt string `yaml:"script_alt"`
}
type HealthCheck struct {
Type string `yaml:"type"`
Path string `yaml:"path"`
Tool string `yaml:"tool"`
Command string `yaml:"command"`
}
type Component struct {
Name string `yaml:"name"`
DisplayName string `yaml:"display_name"`
Source *GitSource `yaml:"source"`
Target string `yaml:"target"`
Deps []string `yaml:"deps"`
SetupSkill string `yaml:"setup_skill"`
PostInstall *PostInstall `yaml:"post_install"`
HealthCheck *HealthCheck `yaml:"health_check"`
Optional bool `yaml:"optional"`
Clients []string `yaml:"clients"`
}
type Manifest struct {
Version string `yaml:"version"`
GitHost string `yaml:"git_host"`
GitOrg string `yaml:"git_org"`
Components []Component `yaml:"components"`
}
func Load(path string, vars map[string]string) (*Manifest, error) {
data, err := os.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("failed to read manifest: %w", err)
}
// Substitute variables
content := string(data)
for k, v := range vars {
content = strings.ReplaceAll(content, "{"+k+"}", v)
}
var m Manifest
if err := yaml.Unmarshal([]byte(content), &m); err != nil {
return nil, fmt.Errorf("failed to parse manifest: %w", err)
}
// Set defaults
if m.GitHost == "" {
m.GitHost = "https://github.com"
}
return &m, nil
}
func (m *Manifest) GetComponent(name string) (*Component, bool) {
for i := range m.Components {
if m.Components[i].Name == name {
return &m.Components[i], true
}
}
return nil, false
}
func (m *Manifest) GetDependencyOrder() []string {
// Topological sort for component installation order
visited := make(map[string]bool)
order := []string{}
var visit func(string)
visit = func(name string) {
if visited[name] {
return
}
visited[name] = true
c, ok := m.GetComponent(name)
if ok {
for _, dep := range c.Deps {
visit(dep)
}
}
order = append(order, name)
}
for _, c := range m.Components {
visit(c.Name)
}
return order
}
func (c *Component) IsApplicable(client string) bool {
if len(c.Clients) == 0 {
return true
}
for _, cl := range c.Clients {
if cl == client {
return true
}
}
return false
}