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>
This commit is contained in:
123
pkg/manifest/manifest.go
Normal file
123
pkg/manifest/manifest.go
Normal file
@@ -0,0 +1,123 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user