Add tmux resurrect defaults to rebuilt images
New VMs should come up with tmux session persistence ready instead of requiring per-VM plugin setup, and rebuilt images should stop carrying stale Docker installer scraps. Configure both image build paths to install TPM, tmux-resurrect, and tmux-continuum for root, manage a marked /root/.tmux.conf block with autosave enabled and restore left manual, and remove legacy get-docker helper files during provisioning. Update the README and repo guidance to document the rebuilt-image behavior. Verified with bash -n customize.sh, GOCACHE=/tmp/banger-gocache go test ./internal/daemon -run TestBuildProvisionScriptInstallsDefaultTools, and GOCACHE=/tmp/banger-gocache make build.
This commit is contained in:
parent
3a92362829
commit
38d7eac430
5 changed files with 133 additions and 4 deletions
|
|
@ -28,7 +28,7 @@
|
|||
## Testing Guidelines
|
||||
- Primary automated coverage is `go test ./...`.
|
||||
- Manual verification for VM lifecycle changes: `./banger vm create`, confirm SSH access, then stop/delete the VM.
|
||||
- Rebuilt images now include `mise` plus `opencode` by default; if you change guest provisioning, document whether users need to rebuild `./runtime/rootfs-docker.ext4` or another base image to pick it up.
|
||||
- Rebuilt images now include `mise`, `opencode`, and `tmux-resurrect`/`tmux-continuum` defaults for `root`; if you change guest provisioning, document whether users need to rebuild `./runtime/rootfs-docker.ext4` or another base image to pick it up.
|
||||
- If you add a new operational workflow, document how to exercise it in `README.md`.
|
||||
- For NAT changes, verify both guest outbound access and host rule cleanup, for example with `./verify.sh --nat`.
|
||||
|
||||
|
|
|
|||
|
|
@ -183,8 +183,9 @@ banger image build --name docker-dev --docker
|
|||
```
|
||||
|
||||
Rebuilt images install a pinned `mise` at `/usr/local/bin/mise`, activate it
|
||||
for bash login and interactive shells, and install `opencode` through `mise`
|
||||
by default.
|
||||
for bash login and interactive shells, install `opencode` through `mise`, and
|
||||
configure `tmux-resurrect` plus `tmux-continuum` for `root` with periodic
|
||||
autosaves and manual-only restore by default.
|
||||
|
||||
Show or delete images:
|
||||
```bash
|
||||
|
|
|
|||
60
customize.sh
60
customize.sh
|
|
@ -106,6 +106,13 @@ INSTALL_DOCKER=0
|
|||
MISE_VERSION="v2025.12.0"
|
||||
MISE_INSTALL_PATH="/usr/local/bin/mise"
|
||||
MISE_ACTIVATE_LINE='eval "$(/usr/local/bin/mise activate bash)"'
|
||||
TMUX_PLUGIN_DIR="/root/.tmux/plugins"
|
||||
TMUX_RESURRECT_DIR="/root/.tmux/resurrect"
|
||||
TMUX_TPM_REPO="https://github.com/tmux-plugins/tpm"
|
||||
TMUX_RESURRECT_REPO="https://github.com/tmux-plugins/tmux-resurrect"
|
||||
TMUX_CONTINUUM_REPO="https://github.com/tmux-plugins/tmux-continuum"
|
||||
TMUX_MANAGED_START="# >>> banger tmux plugins >>>"
|
||||
TMUX_MANAGED_END="# <<< banger tmux plugins <<<"
|
||||
MODULES_DIR="$(bundle_path default_modules_dir "$RUNTIME_DIR/wtf/root/lib/modules/6.8.0-94-generic")"
|
||||
PACKAGES_FILE="$(banger_packages_file)"
|
||||
while [[ $# -gt 0 ]]; do
|
||||
|
|
@ -413,9 +420,62 @@ if [[ \"$INSTALL_DOCKER\" == \"1\" ]]; then
|
|||
systemctl enable --now docker || true
|
||||
fi
|
||||
fi
|
||||
rm -f /root/get-docker /root/get-docker.sh /tmp/get-docker /tmp/get-docker.sh
|
||||
git config --system init.defaultBranch main
|
||||
"
|
||||
|
||||
log "configuring tmux resurrect"
|
||||
ssh -i "$SSH_KEY" -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
|
||||
"root@${GUEST_IP}" bash -se <<EOF
|
||||
set -euo pipefail
|
||||
|
||||
install_tmux_plugin() {
|
||||
local dir="\$1"
|
||||
local repo="\$2"
|
||||
|
||||
if [[ -d "\$dir/.git" ]]; then
|
||||
git -C "\$dir" fetch --depth 1 origin
|
||||
git -C "\$dir" reset --hard FETCH_HEAD
|
||||
else
|
||||
rm -rf "\$dir"
|
||||
git clone --depth 1 "\$repo" "\$dir"
|
||||
fi
|
||||
}
|
||||
|
||||
mkdir -p "$TMUX_PLUGIN_DIR" "$TMUX_RESURRECT_DIR"
|
||||
install_tmux_plugin "$TMUX_PLUGIN_DIR/tpm" "$TMUX_TPM_REPO"
|
||||
install_tmux_plugin "$TMUX_PLUGIN_DIR/tmux-resurrect" "$TMUX_RESURRECT_REPO"
|
||||
install_tmux_plugin "$TMUX_PLUGIN_DIR/tmux-continuum" "$TMUX_CONTINUUM_REPO"
|
||||
|
||||
TMUX_CONF="/root/.tmux.conf"
|
||||
tmp_tmux_conf="\$(mktemp)"
|
||||
if [[ -f "\$TMUX_CONF" ]]; then
|
||||
awk -v begin="$TMUX_MANAGED_START" -v end="$TMUX_MANAGED_END" '
|
||||
\$0 == begin { skip = 1; next }
|
||||
\$0 == end { skip = 0; next }
|
||||
!skip { print }
|
||||
' "\$TMUX_CONF" > "\$tmp_tmux_conf"
|
||||
else
|
||||
: > "\$tmp_tmux_conf"
|
||||
fi
|
||||
if [[ -s "\$tmp_tmux_conf" ]]; then
|
||||
printf '\n' >> "\$tmp_tmux_conf"
|
||||
fi
|
||||
cat >> "\$tmp_tmux_conf" <<'TMUXCONF'
|
||||
$TMUX_MANAGED_START
|
||||
set -g @plugin 'tmux-plugins/tpm'
|
||||
set -g @plugin 'tmux-plugins/tmux-resurrect'
|
||||
set -g @plugin 'tmux-plugins/tmux-continuum'
|
||||
set -g @continuum-save-interval '15'
|
||||
set -g @continuum-restore 'off'
|
||||
set -g @resurrect-dir '/root/.tmux/resurrect'
|
||||
run '~/.tmux/plugins/tpm/tpm'
|
||||
$TMUX_MANAGED_END
|
||||
TMUXCONF
|
||||
mv "\$tmp_tmux_conf" "\$TMUX_CONF"
|
||||
chmod 0644 "\$TMUX_CONF"
|
||||
EOF
|
||||
|
||||
if [[ -n "$MODULES_DIR" ]]; then
|
||||
MODULES_BASE="$(basename "$MODULES_DIR")"
|
||||
log "copying kernel modules ($MODULES_BASE) into guest"
|
||||
|
|
|
|||
|
|
@ -24,6 +24,13 @@ const (
|
|||
defaultMiseInstallPath = "/usr/local/bin/mise"
|
||||
defaultMiseActivateLine = `eval "$(/usr/local/bin/mise activate bash)"`
|
||||
defaultOpenCodeTool = "github:anomalyco/opencode"
|
||||
defaultTPMRepo = "https://github.com/tmux-plugins/tpm"
|
||||
defaultResurrectRepo = "https://github.com/tmux-plugins/tmux-resurrect"
|
||||
defaultContinuumRepo = "https://github.com/tmux-plugins/tmux-continuum"
|
||||
defaultTMUXPluginDir = "/root/.tmux/plugins"
|
||||
defaultTMUXResurrectDir = "/root/.tmux/resurrect"
|
||||
tmuxManagedBlockStart = "# >>> banger tmux plugins >>>"
|
||||
tmuxManagedBlockEnd = "# <<< banger tmux plugins <<<"
|
||||
)
|
||||
|
||||
type imageBuildSpec struct {
|
||||
|
|
@ -242,6 +249,7 @@ func buildProvisionScript(vmName, dnsServer string, packages []string, installDo
|
|||
fmt.Fprintf(&script, "PACKAGES=%s\n", shellArray(packages))
|
||||
script.WriteString("DEBIAN_FRONTEND=noninteractive apt-get -y install \"${PACKAGES[@]}\"\n")
|
||||
appendMiseSetup(&script)
|
||||
appendTmuxSetup(&script)
|
||||
if installDocker {
|
||||
script.WriteString("DEBIAN_FRONTEND=noninteractive apt-get -y remove containerd || true\n")
|
||||
script.WriteString("if ! DEBIAN_FRONTEND=noninteractive apt-get -y install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin; then\n")
|
||||
|
|
@ -249,6 +257,7 @@ func buildProvisionScript(vmName, dnsServer string, packages []string, installDo
|
|||
script.WriteString("fi\n")
|
||||
script.WriteString("if command -v systemctl >/dev/null 2>&1; then systemctl enable --now docker || true; fi\n")
|
||||
}
|
||||
appendGuestCleanup(&script)
|
||||
script.WriteString("git config --system init.defaultBranch main\n")
|
||||
return script.String()
|
||||
}
|
||||
|
|
@ -270,6 +279,54 @@ func appendMiseSetup(script *bytes.Buffer) {
|
|||
appendLineIfMissing(script, "/etc/bash.bashrc", defaultMiseActivateLine)
|
||||
}
|
||||
|
||||
func appendTmuxSetup(script *bytes.Buffer) {
|
||||
fmt.Fprintf(script, "TMUX_PLUGIN_DIR=%s\n", shellQuote(defaultTMUXPluginDir))
|
||||
fmt.Fprintf(script, "TMUX_RESURRECT_DIR=%s\n", shellQuote(defaultTMUXResurrectDir))
|
||||
script.WriteString("mkdir -p \"$TMUX_PLUGIN_DIR\" \"$TMUX_RESURRECT_DIR\"\n")
|
||||
appendGitRepo(script, "$TMUX_PLUGIN_DIR/tpm", defaultTPMRepo)
|
||||
appendGitRepo(script, "$TMUX_PLUGIN_DIR/tmux-resurrect", defaultResurrectRepo)
|
||||
appendGitRepo(script, "$TMUX_PLUGIN_DIR/tmux-continuum", defaultContinuumRepo)
|
||||
script.WriteString("TMUX_CONF=/root/.tmux.conf\n")
|
||||
fmt.Fprintf(script, "TMUX_MANAGED_START=%s\n", shellQuote(tmuxManagedBlockStart))
|
||||
fmt.Fprintf(script, "TMUX_MANAGED_END=%s\n", shellQuote(tmuxManagedBlockEnd))
|
||||
script.WriteString("tmp_tmux_conf=$(mktemp)\n")
|
||||
script.WriteString("if [[ -f \"$TMUX_CONF\" ]]; then\n")
|
||||
script.WriteString(" awk -v begin=\"$TMUX_MANAGED_START\" -v end=\"$TMUX_MANAGED_END\" '$0 == begin { skip = 1; next } $0 == end { skip = 0; next } !skip { print }' \"$TMUX_CONF\" > \"$tmp_tmux_conf\"\n")
|
||||
script.WriteString("else\n")
|
||||
script.WriteString(" : > \"$tmp_tmux_conf\"\n")
|
||||
script.WriteString("fi\n")
|
||||
script.WriteString("if [[ -s \"$tmp_tmux_conf\" ]]; then\n")
|
||||
script.WriteString(" printf '\\n' >> \"$tmp_tmux_conf\"\n")
|
||||
script.WriteString("fi\n")
|
||||
script.WriteString("cat >> \"$tmp_tmux_conf\" <<'EOF'\n")
|
||||
script.WriteString(tmuxManagedBlockStart + "\n")
|
||||
script.WriteString("set -g @plugin 'tmux-plugins/tpm'\n")
|
||||
script.WriteString("set -g @plugin 'tmux-plugins/tmux-resurrect'\n")
|
||||
script.WriteString("set -g @plugin 'tmux-plugins/tmux-continuum'\n")
|
||||
script.WriteString("set -g @continuum-save-interval '15'\n")
|
||||
script.WriteString("set -g @continuum-restore 'off'\n")
|
||||
script.WriteString("set -g @resurrect-dir '/root/.tmux/resurrect'\n")
|
||||
script.WriteString("run '~/.tmux/plugins/tpm/tpm'\n")
|
||||
script.WriteString(tmuxManagedBlockEnd + "\n")
|
||||
script.WriteString("EOF\n")
|
||||
script.WriteString("mv \"$tmp_tmux_conf\" \"$TMUX_CONF\"\n")
|
||||
script.WriteString("chmod 0644 \"$TMUX_CONF\"\n")
|
||||
}
|
||||
|
||||
func appendGitRepo(script *bytes.Buffer, dir, repo string) {
|
||||
fmt.Fprintf(script, "if [[ -d \"%s/.git\" ]]; then\n", dir)
|
||||
fmt.Fprintf(script, " git -C \"%s\" fetch --depth 1 origin\n", dir)
|
||||
fmt.Fprintf(script, " git -C \"%s\" reset --hard FETCH_HEAD\n", dir)
|
||||
script.WriteString("else\n")
|
||||
fmt.Fprintf(script, " rm -rf \"%s\"\n", dir)
|
||||
fmt.Fprintf(script, " git clone --depth 1 %s \"%s\"\n", shellQuote(repo), dir)
|
||||
script.WriteString("fi\n")
|
||||
}
|
||||
|
||||
func appendGuestCleanup(script *bytes.Buffer) {
|
||||
script.WriteString("rm -f /root/get-docker /root/get-docker.sh /tmp/get-docker /tmp/get-docker.sh\n")
|
||||
}
|
||||
|
||||
func appendLineIfMissing(script *bytes.Buffer, path, line string) {
|
||||
fmt.Fprintf(script, "touch %s\n", shellQuote(path))
|
||||
fmt.Fprintf(script, "if ! grep -Fqx %s %s; then\n", shellQuote(line), shellQuote(path))
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import (
|
|||
"testing"
|
||||
)
|
||||
|
||||
func TestBuildProvisionScriptInstallsAndActivatesMise(t *testing.T) {
|
||||
func TestBuildProvisionScriptInstallsDefaultTools(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
script := buildProvisionScript("devbox", "1.1.1.1", []string{"git", "curl"}, false)
|
||||
|
|
@ -16,6 +16,17 @@ func TestBuildProvisionScriptInstallsAndActivatesMise(t *testing.T) {
|
|||
"if [ -n \"${BASH_VERSION:-}\" ] && [ -x '/usr/local/bin/mise' ]; then",
|
||||
`eval "$(/usr/local/bin/mise activate bash)"`,
|
||||
`if ! grep -Fqx 'eval "$(/usr/local/bin/mise activate bash)"' '/etc/bash.bashrc'; then`,
|
||||
`git clone --depth 1 'https://github.com/tmux-plugins/tpm' "$TMUX_PLUGIN_DIR/tpm"`,
|
||||
`git clone --depth 1 'https://github.com/tmux-plugins/tmux-resurrect' "$TMUX_PLUGIN_DIR/tmux-resurrect"`,
|
||||
`git clone --depth 1 'https://github.com/tmux-plugins/tmux-continuum' "$TMUX_PLUGIN_DIR/tmux-continuum"`,
|
||||
"# >>> banger tmux plugins >>>",
|
||||
"set -g @plugin 'tmux-plugins/tmux-resurrect'",
|
||||
"set -g @plugin 'tmux-plugins/tmux-continuum'",
|
||||
"set -g @continuum-save-interval '15'",
|
||||
"set -g @continuum-restore 'off'",
|
||||
"set -g @resurrect-dir '/root/.tmux/resurrect'",
|
||||
"run '~/.tmux/plugins/tpm/tpm'",
|
||||
"rm -f /root/get-docker /root/get-docker.sh /tmp/get-docker /tmp/get-docker.sh",
|
||||
} {
|
||||
if !strings.Contains(script, snippet) {
|
||||
t.Fatalf("buildProvisionScript missing snippet %q\nscript:\n%s", snippet, script)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue