Make installed banger self-contained

Fix the misleading make install path where banger and bangerd still depended on a repo checkout for Firecracker, guest artifacts, image builds, and the SSH key.

Replace repo-root inference with an explicit runtime bundle model: resolve a runtime_dir from env/config/install layout, derive concrete artifact paths from it, and update the daemon, CLI, and image-build flow to use those paths. Keep repo_root only as an explicit compatibility alias instead of auto-detecting it.

Teach customize.sh to run from a read-only bundled runtime tree while writing transient state under XDG/BANGER_STATE_DIR, and make make install copy the runtime assets into PREFIX/lib/banger so installed binaries stay usable outside the repo.

Validate with go test ./..., make build, bash -n customize.sh, and make install DESTDIR=/tmp/banger-install PREFIX=/usr. An out-of-repo installed-binary smoke test was attempted, but this sandbox blocked bangerd from binding its Unix socket (setsockopt: operation not permitted).
This commit is contained in:
Thales Maciel 2026-03-16 14:26:50 -03:00
parent 375900cf65
commit ce1be52047
No known key found for this signature in database
GPG key ID: 33112E6833C34679
13 changed files with 437 additions and 107 deletions

View file

@ -131,6 +131,7 @@ func newVMCommand() *cobra.Command {
return cmd
}
func newVMCreateCommand() *cobra.Command {
var params api.VMCreateParams
cmd := &cobra.Command{
@ -372,6 +373,9 @@ func newImageBuildCommand() *cobra.Command {
Short: "Build an image",
Args: noArgsUsage("usage: banger image build"),
RunE: func(cmd *cobra.Command, args []string) error {
if err := absolutizeImageBuildPaths(&params); err != nil {
return err
}
if err := system.EnsureSudo(cmd.Context()); err != nil {
return err
}
@ -631,14 +635,28 @@ func sshCommandArgs(cfg model.DaemonConfig, guestIP string, extra []string) ([]s
return nil, errors.New("vm has no guest IP")
}
args := []string{}
if cfg.RepoRoot != "" {
args = append(args, "-i", filepath.Join(cfg.RepoRoot, "id_ed25519"))
if cfg.SSHKeyPath != "" {
args = append(args, "-i", cfg.SSHKeyPath)
}
args = append(args, "-o", "StrictHostKeyChecking=no", "-o", "UserKnownHostsFile=/dev/null", "root@"+guestIP)
args = append(args, extra...)
return args, nil
}
func absolutizeImageBuildPaths(params *api.ImageBuildParams) error {
var err error
for _, value := range []*string{&params.BaseRootfs, &params.KernelPath, &params.InitrdPath, &params.ModulesDir} {
if *value == "" || filepath.IsAbs(*value) {
continue
}
*value, err = filepath.Abs(*value)
if err != nil {
return err
}
}
return nil
}
func printJSON(out anyWriter, v any) error {
data, err := json.MarshalIndent(v, "", " ")
if err != nil {

View file

@ -6,6 +6,7 @@ import (
"reflect"
"testing"
"banger/internal/api"
"banger/internal/model"
)
@ -38,6 +39,7 @@ func TestVMCreateFlagsExist(t *testing.T) {
}
}
func TestVMSetParamsFromFlags(t *testing.T) {
params, err := vmSetParamsFromFlags("devbox", 4, 2048, "16G", true, false)
if err != nil {
@ -64,12 +66,12 @@ func TestVMSetParamsFromFlagsConflict(t *testing.T) {
}
func TestSSHCommandArgs(t *testing.T) {
args, err := sshCommandArgs(model.DaemonConfig{RepoRoot: "/repo"}, "172.16.0.2", []string{"--", "uname", "-a"})
args, err := sshCommandArgs(model.DaemonConfig{SSHKeyPath: "/bundle/id_ed25519"}, "172.16.0.2", []string{"--", "uname", "-a"})
if err != nil {
t.Fatalf("sshCommandArgs: %v", err)
}
want := []string{
"-i", "/repo/id_ed25519",
"-i", "/bundle/id_ed25519",
"-o", "StrictHostKeyChecking=no",
"-o", "UserKnownHostsFile=/dev/null",
"root@172.16.0.2",
@ -127,3 +129,37 @@ func TestDaemonOutdated(t *testing.T) {
t.Fatal("expected replaced daemon executable to be outdated")
}
}
func TestAbsolutizeImageBuildPaths(t *testing.T) {
dir := t.TempDir()
prev, err := os.Getwd()
if err != nil {
t.Fatalf("getwd: %v", err)
}
if err := os.Chdir(dir); err != nil {
t.Fatalf("chdir: %v", err)
}
t.Cleanup(func() {
_ = os.Chdir(prev)
})
params := api.ImageBuildParams{
BaseRootfs: "images/base.ext4",
KernelPath: "/kernel",
InitrdPath: "boot/initrd.img",
ModulesDir: "modules",
}
if err := absolutizeImageBuildPaths(&params); err != nil {
t.Fatalf("absolutizeImageBuildPaths: %v", err)
}
want := api.ImageBuildParams{
BaseRootfs: filepath.Join(dir, "images/base.ext4"),
KernelPath: "/kernel",
InitrdPath: filepath.Join(dir, "boot/initrd.img"),
ModulesDir: filepath.Join(dir, "modules"),
}
if !reflect.DeepEqual(params, want) {
t.Fatalf("params = %+v, want %+v", params, want)
}
}