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 ./....
170 lines
4.3 KiB
Go
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())
|
|
}
|