Files
gastown/internal/formula/embed.go
PV 4c9bc36d61 fix(install): Copy embedded formulas to new installations
When `gt install` creates a new HQ, formulas were not being provisioned
to `.beads/formulas/`. This embeds the formula library into the gt binary
and copies them during installation.

- Add go:generate directive to copy formulas from .beads/formulas/
- Add internal/formula/embed.go with ProvisionFormulas() function
- Call ProvisionFormulas() from runInstall() after beads init
- Add generate target to Makefile (build depends on it)
- Add TestInstallFormulasProvisioned integration test
- Log warning if formula stat fails with unexpected error

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-03 18:54:26 -08:00

63 lines
1.9 KiB
Go

package formula
import (
"embed"
"fmt"
"log"
"os"
"path/filepath"
)
// Generate formulas directory from canonical source at .beads/formulas/
//go:generate sh -c "rm -rf formulas && mkdir -p formulas && cp ../../.beads/formulas/*.formula.toml ../../.beads/formulas/*.formula.json formulas/ 2>/dev/null || cp ../../.beads/formulas/*.formula.toml formulas/"
//go:embed formulas/*.formula.toml formulas/*.formula.json
var formulasFS embed.FS
// ProvisionFormulas creates the .beads/formulas/ directory with embedded formulas.
// This ensures new installations have the standard formula library.
// If a formula already exists, it is skipped (no overwrite).
// Returns the number of formulas provisioned.
func ProvisionFormulas(beadsPath string) (int, error) {
entries, err := formulasFS.ReadDir("formulas")
if err != nil {
return 0, fmt.Errorf("reading formulas directory: %w", err)
}
// Create .beads/formulas/ directory
formulasDir := filepath.Join(beadsPath, ".beads", "formulas")
if err := os.MkdirAll(formulasDir, 0755); err != nil {
return 0, fmt.Errorf("creating formulas directory: %w", err)
}
count := 0
for _, entry := range entries {
if entry.IsDir() {
continue
}
destPath := filepath.Join(formulasDir, entry.Name())
// Skip if formula already exists (don't overwrite user customizations)
if _, err := os.Stat(destPath); err == nil {
continue
} else if !os.IsNotExist(err) {
// Log unexpected errors (e.g., permission denied) but continue
log.Printf("warning: could not check formula %s: %v", entry.Name(), err)
continue
}
content, err := formulasFS.ReadFile("formulas/" + entry.Name())
if err != nil {
return count, fmt.Errorf("reading %s: %w", entry.Name(), err)
}
if err := os.WriteFile(destPath, content, 0644); err != nil {
return count, fmt.Errorf("writing %s: %w", entry.Name(), err)
}
count++
}
return count, nil
}