banger/internal/paths/paths.go
Thales Maciel 01c7cb5e65
Reorganize the source checkout layout
Separate tracked source from generated artifacts so the repo root stops accumulating helper scripts, manifests, and local runtime outputs.

Move manual shell entrypoints under scripts/, manifests under config/, and the Firecracker API reference under docs/reference/. Make build and runtimebundle now target build/bin, build/runtime, and build/dist as the canonical source-checkout paths.

Update runtime discovery, helper scripts, tests, and docs to follow the new layout while keeping legacy source-checkout runtime fallbacks for existing local bundles during migration.

Validated with bash -n on the moved scripts, make build, and GOCACHE=/tmp/banger-gocache go test ./....
2026-03-21 17:22:57 -03:00

170 lines
4.3 KiB
Go

package paths
import (
"errors"
"fmt"
"os"
"path/filepath"
"strconv"
"strings"
"banger/internal/runtimebundle"
)
type Layout struct {
ConfigHome string
StateHome string
CacheHome string
RuntimeHome string
ConfigDir string
StateDir string
CacheDir string
RuntimeDir string
SocketPath string
DBPath string
DaemonLog string
VMsDir string
ImagesDir string
}
func Resolve() (Layout, error) {
home, err := os.UserHomeDir()
if err != nil {
return Layout{}, err
}
configHome := getenvDefault("XDG_CONFIG_HOME", filepath.Join(home, ".config"))
stateHome := getenvDefault("XDG_STATE_HOME", filepath.Join(home, ".local", "state"))
cacheHome := getenvDefault("XDG_CACHE_HOME", filepath.Join(home, ".cache"))
runtimeHome := os.Getenv("XDG_RUNTIME_DIR")
if runtimeHome == "" {
runtimeHome = filepath.Join(os.TempDir(), fmt.Sprintf("banger-runtime-%d", os.Getuid()))
}
layout := Layout{
ConfigHome: configHome,
StateHome: stateHome,
CacheHome: cacheHome,
RuntimeHome: runtimeHome,
ConfigDir: filepath.Join(configHome, "banger"),
StateDir: filepath.Join(stateHome, "banger"),
CacheDir: filepath.Join(cacheHome, "banger"),
RuntimeDir: filepath.Join(runtimeHome, "banger"),
}
layout.SocketPath = filepath.Join(layout.RuntimeDir, "bangerd.sock")
layout.DBPath = filepath.Join(layout.StateDir, "state.db")
layout.DaemonLog = filepath.Join(layout.StateDir, "bangerd.log")
layout.VMsDir = filepath.Join(layout.StateDir, "vms")
layout.ImagesDir = filepath.Join(layout.StateDir, "images")
return layout, nil
}
func Ensure(layout Layout) error {
for _, dir := range []string{layout.ConfigDir, layout.StateDir, layout.CacheDir, layout.RuntimeDir, layout.VMsDir, layout.ImagesDir} {
if err := os.MkdirAll(dir, 0o755); err != nil {
return err
}
}
return nil
}
var executablePath = os.Executable
func ResolveRuntimeDir(configuredRuntimeDir, deprecatedRepoRoot string) string {
for _, candidate := range []string{
os.Getenv("BANGER_RUNTIME_DIR"),
os.Getenv("BANGER_REPO_ROOT"),
configuredRuntimeDir,
deprecatedRepoRoot,
} {
if candidate = strings.TrimSpace(candidate); candidate != "" {
return filepath.Clean(candidate)
}
}
exe, err := executablePath()
if err != nil {
return ""
}
exeDir := filepath.Dir(exe)
if filepath.Base(exeDir) == "bin" {
if filepath.Base(filepath.Dir(exeDir)) == "build" {
buildRuntimeDir := filepath.Clean(filepath.Join(exeDir, "..", "runtime"))
if HasRuntimeBundle(buildRuntimeDir) {
return buildRuntimeDir
}
}
installRuntimeDir := filepath.Clean(filepath.Join(exeDir, "..", "lib", "banger"))
if HasRuntimeBundle(installRuntimeDir) {
return installRuntimeDir
}
}
for _, sourceRuntimeDir := range []string{
filepath.Join(exeDir, "build", "runtime"),
filepath.Join(exeDir, "runtime"),
} {
if HasRuntimeBundle(sourceRuntimeDir) {
return sourceRuntimeDir
}
}
return ""
}
func HasRuntimeBundle(dir string) bool {
if strings.TrimSpace(dir) == "" {
return false
}
if _, err := runtimebundle.LoadBundleMetadata(dir); err == nil {
return true
}
required := []string{
"firecracker",
"customize.sh",
"packages.apt",
"wtf/root/boot/vmlinux-6.8.0-94-generic",
}
for _, name := range required {
if _, err := os.Stat(filepath.Join(dir, name)); err != nil {
return false
}
}
for _, name := range []string{"rootfs-docker.ext4", "rootfs.ext4"} {
if _, err := os.Stat(filepath.Join(dir, name)); err == nil {
return true
}
}
return false
}
func BangerdPath() (string, error) {
if env := os.Getenv("BANGER_DAEMON_BIN"); env != "" {
return env, nil
}
exe, err := executablePath()
if err != nil {
return "", err
}
dir := filepath.Dir(exe)
for _, candidate := range []string{
filepath.Join(dir, "bangerd"),
filepath.Join(dir, "bangerd.exe"),
} {
if _, err := os.Stat(candidate); err == nil {
return candidate, nil
}
}
return "", errors.New("bangerd binary not found next to banger; run `make build`")
}
func RuntimeBundleHint() string {
return "run `make runtime-bundle` or set runtime_dir in ~/.config/banger/config.toml"
}
func getenvDefault(key, fallback string) string {
if value := strings.TrimSpace(os.Getenv(key)); value != "" {
return value
}
return fallback
}
func RuntimeFallbackLabel() string {
return strconv.Itoa(os.Getuid())
}