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:
131
pkg/health/health.go
Normal file
131
pkg/health/health.go
Normal file
@@ -0,0 +1,131 @@
|
||||
package health
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/OpeItcLoc03/factory/pkg/manifest"
|
||||
)
|
||||
|
||||
type Result struct {
|
||||
Name string `json:"name"`
|
||||
Healthy bool `json:"healthy"`
|
||||
Version string `json:"version,omitempty"`
|
||||
Message string `json:"message,omitempty"`
|
||||
}
|
||||
|
||||
func Check(comp *manifest.Component, vars map[string]string) (*Result, error) {
|
||||
if comp.HealthCheck == nil {
|
||||
return &Result{
|
||||
Name: comp.Name,
|
||||
Healthy: true,
|
||||
Message: "no health check defined",
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Substitute variables in path
|
||||
path := comp.HealthCheck.Path
|
||||
for k, v := range vars {
|
||||
path = filepath.Clean(strings.ReplaceAll(path, "{"+k+"}", v))
|
||||
}
|
||||
|
||||
switch comp.HealthCheck.Type {
|
||||
case "file_exists":
|
||||
return checkFileExists(comp.Name, path)
|
||||
case "directory_exists":
|
||||
return checkDirExists(comp.Name, path)
|
||||
case "mcp_tool":
|
||||
return checkMCPTool(comp.Name, comp.HealthCheck.Tool)
|
||||
case "command":
|
||||
return checkCommand(comp.Name, comp.HealthCheck.Command)
|
||||
default:
|
||||
return &Result{
|
||||
Name: comp.Name,
|
||||
Healthy: false,
|
||||
Message: fmt.Sprintf("unknown health check type: %s", comp.HealthCheck.Type),
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func checkFileExists(name, path string) (*Result, error) {
|
||||
if _, err := os.Stat(path); err == nil {
|
||||
return &Result{Name: name, Healthy: true}, nil
|
||||
}
|
||||
return &Result{
|
||||
Name: name,
|
||||
Healthy: false,
|
||||
Message: fmt.Sprintf("file not found: %s", path),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func checkDirExists(name, path string) (*Result, error) {
|
||||
if info, err := os.Stat(path); err == nil && info.IsDir() {
|
||||
return &Result{Name: name, Healthy: true}, nil
|
||||
}
|
||||
return &Result{
|
||||
Name: name,
|
||||
Healthy: false,
|
||||
Message: fmt.Sprintf("directory not found: %s", path),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func checkMCPTool(name, tool string) (*Result, error) {
|
||||
// Check if tool is available in ~/.claude.json
|
||||
home, _ := os.UserHomeDir()
|
||||
configPath := filepath.Join(home, ".claude.json")
|
||||
|
||||
data, err := os.ReadFile(configPath)
|
||||
if err != nil {
|
||||
return &Result{
|
||||
Name: name,
|
||||
Healthy: false,
|
||||
Message: "claude.json not found",
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Simple string check (proper parsing would be better)
|
||||
toolKey := `"` + tool + `"`
|
||||
if strings.Contains(string(data), toolKey) {
|
||||
return &Result{Name: name, Healthy: true}, nil
|
||||
}
|
||||
|
||||
return &Result{
|
||||
Name: name,
|
||||
Healthy: false,
|
||||
Message: fmt.Sprintf("MCP tool not found: %s", tool),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func checkCommand(name, cmd string) (*Result, error) {
|
||||
var shell string
|
||||
var args []string
|
||||
|
||||
switch runtime.GOOS {
|
||||
case "windows":
|
||||
shell = "pwsh"
|
||||
args = []string{"-Command", cmd}
|
||||
default:
|
||||
shell = "bash"
|
||||
args = []string{"-c", cmd}
|
||||
}
|
||||
|
||||
execCmd := exec.Command(shell, args...)
|
||||
output, err := execCmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return &Result{
|
||||
Name: name,
|
||||
Healthy: false,
|
||||
Message: fmt.Sprintf("command failed: %s", output),
|
||||
}, nil
|
||||
}
|
||||
|
||||
return &Result{
|
||||
Name: name,
|
||||
Healthy: true,
|
||||
Message: strings.TrimSpace(string(output)),
|
||||
}, nil
|
||||
}
|
||||
Reference in New Issue
Block a user