diff --git a/images/golden/Dockerfile b/images/golden/Dockerfile new file mode 100644 index 0000000..5723ede --- /dev/null +++ b/images/golden/Dockerfile @@ -0,0 +1,88 @@ +# banger golden image — Debian bookworm sandbox for development + testing. +# +# Two sections: +# 1. ESSENTIAL — what banger's lifecycle requires to boot the guest. +# 2. OPINION — developer conveniences curated for banger sandboxes. +# +# Banger's guest agents (vsock agent, network bootstrap, first-boot unit) +# are injected at `banger image pull` time, not baked here. Keeping them +# out means this image stays portable enough to run in other contexts. + +FROM debian:bookworm-slim + +ENV DEBIAN_FRONTEND=noninteractive \ + LANG=C.UTF-8 \ + LC_ALL=C.UTF-8 + +# -------- 1. ESSENTIAL -------- +# Banger needs: an init (systemd), sshd (the only control channel), +# TLS roots + curl (first-boot installs + mise installer), iproute2 +# (debugging; `ip` is still useful even when the kernel sets IP via cmdline). +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + systemd systemd-sysv \ + openssh-server \ + ca-certificates \ + curl \ + iproute2 \ + && rm -rf /var/lib/apt/lists/* + +# -------- 2. OPINION -------- +# Developer sandbox conveniences. Language runtimes are deliberately +# absent — `mise` (below) handles per-repo `.mise.toml`/`.tool-versions` +# on first `vm run`. + +# Core CLI + search/nav + build toolchain + lint/debug + editor/session. +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + git jq less tree file unzip zip rsync \ + ripgrep fd-find \ + build-essential pkg-config make \ + shellcheck sqlite3 \ + iputils-ping dnsutils \ + vim-tiny tmux htop \ + && rm -rf /var/lib/apt/lists/* + +# Docker CE (with Compose v2 + buildx) from the official apt repo. +# Nested-VM docker gives Compose workflows hostname/port isolation +# per banger VM, which is a big part of the sandbox story. +RUN install -m 0755 -d /etc/apt/keyrings \ + && curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc \ + && chmod a+r /etc/apt/keyrings/docker.asc \ + && printf 'deb [arch=%s signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian bookworm stable\n' \ + "$(dpkg --print-architecture)" > /etc/apt/sources.list.d/docker.list \ + && apt-get update \ + && apt-get install -y --no-install-recommends \ + docker-ce docker-ce-cli containerd.io \ + docker-buildx-plugin docker-compose-plugin \ + && rm -rf /var/lib/apt/lists/* + +# mise — per-repo version manager. Installed system-wide so the +# bashrc activation reaches every shell. +RUN curl -fsSL https://mise.run | MISE_INSTALL_PATH=/usr/local/bin/mise sh \ + && chmod 0755 /usr/local/bin/mise \ + && install -d /etc/profile.d \ + && printf '%s\n' 'if [ -x /usr/local/bin/mise ]; then eval "$(/usr/local/bin/mise activate bash)"; fi' \ + > /etc/profile.d/mise.sh \ + && chmod 0644 /etc/profile.d/mise.sh + +# Git default branch — matches the old customize.sh opinion. +RUN git config --system init.defaultBranch main + +# `fd-find` installs as `fdfind` on Debian to avoid a long-standing name +# clash. Expose the ergonomic name for interactive use. +RUN ln -s /usr/bin/fdfind /usr/local/bin/fd + +# Strip per-image identity so every banger VM gets its own. +# - /etc/machine-id: systemd-firstboot regenerates at boot when empty. +# - SSH host keys: removed here; a ssh.service drop-in (below) runs +# `ssh-keygen -A` before sshd so the VM's first boot generates a +# unique set. +RUN : > /etc/machine-id \ + && rm -f /etc/ssh/ssh_host_*_key /etc/ssh/ssh_host_*_key.pub \ + && install -d /etc/systemd/system/ssh.service.d \ + && printf '[Service]\nExecStartPre=-/usr/bin/ssh-keygen -A\n' \ + > /etc/systemd/system/ssh.service.d/regen-host-keys.conf + +# No CMD / ENTRYPOINT: banger boots this via systemd as PID 1 after +# first-boot, not via `docker run`. diff --git a/scripts/publish-golden-image.sh b/scripts/publish-golden-image.sh new file mode 100755 index 0000000..2b7606b --- /dev/null +++ b/scripts/publish-golden-image.sh @@ -0,0 +1,104 @@ +#!/usr/bin/env bash +# Build and optionally push the banger golden image. +# +# Examples: +# ./scripts/publish-golden-image.sh --tag thaloco/banger-golden:debian-bookworm +# ./scripts/publish-golden-image.sh --tag thaloco/banger-golden:debian-bookworm --push +# ./scripts/publish-golden-image.sh --tag ghcr.io/thaloco/banger-golden:latest --push --platform linux/amd64 +# +# The script expects the user to be logged in to the target registry +# (docker login / gh auth token) when --push is set. + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" +DOCKERFILE="$REPO_ROOT/images/golden/Dockerfile" +CONTEXT="$REPO_ROOT/images/golden" + +TAG="" +PUSH=0 +PLATFORM="linux/amd64" +EXTRA_TAGS=() + +usage() { + cat <<'EOF' +Usage: publish-golden-image.sh --tag [--tag ] [--push] [--platform ] + +Options: + --tag Primary image reference (required). Repeat --tag for extra tags + (e.g. to publish both :latest and :debian-bookworm). + --push Push all tags after building. Requires prior `docker login`. + --platform Build platform (default: linux/amd64). banger x86_64-only today. + -h, --help This help. +EOF +} + +while [[ $# -gt 0 ]]; do + case "$1" in + --tag) + if [[ -z "$TAG" ]]; then + TAG="${2:-}" + else + EXTRA_TAGS+=("${2:-}") + fi + shift 2 + ;; + --push) + PUSH=1 + shift + ;; + --platform) + PLATFORM="${2:-}" + shift 2 + ;; + -h|--help) + usage + exit 0 + ;; + *) + echo "unknown option: $1" >&2 + usage >&2 + exit 1 + ;; + esac +done + +if [[ -z "$TAG" ]]; then + echo "--tag is required" >&2 + usage >&2 + exit 1 +fi + +if ! command -v docker >/dev/null 2>&1; then + echo "docker binary not found in PATH" >&2 + exit 1 +fi + +BUILD_ARGS=(build --platform "$PLATFORM" -t "$TAG" -f "$DOCKERFILE") +for t in "${EXTRA_TAGS[@]}"; do + BUILD_ARGS+=(-t "$t") +done +BUILD_ARGS+=("$CONTEXT") + +echo "==> building $TAG (platform=$PLATFORM)" +docker "${BUILD_ARGS[@]}" + +if [[ "$PUSH" -eq 1 ]]; then + echo "==> pushing $TAG" + docker push "$TAG" + for t in "${EXTRA_TAGS[@]}"; do + echo "==> pushing $t" + docker push "$t" + done +fi + +echo "==> done" +echo " primary tag: $TAG" +for t in "${EXTRA_TAGS[@]}"; do + echo " extra tag : $t" +done +if [[ "$PUSH" -eq 0 ]]; then + echo + echo "Image is built locally but not pushed. Pass --push to publish." +fi