linuxbox/update/debug.sh

154 lines
4.4 KiB
Bash
Executable File

#!/bin/bash
set -euo pipefail
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
PROJECT_ROOT=$(dirname "$SCRIPT_DIR")
usage() {
cat <<'USAGE'
Usage: update/debug.sh [--keep] [arch|ubuntu] [-- update-args...]
Spins up a temporary VM-like container, mounts the repository, and runs the update
script with UPDATE_DEBUG=1. Requires podman (preferred) or docker with systemd
support. Pass --keep to leave the container running for inspection. Any arguments
after -- are forwarded to update/update.sh.
USAGE
}
KEEP_CONTAINER=0
DISTRO=""
FORWARDED_ARGS=()
while (( $# )); do
case "$1" in
--keep)
KEEP_CONTAINER=1
shift
;;
arch|ubuntu)
DISTRO="$1"
shift
;;
--)
shift
FORWARDED_ARGS=("$@")
break
;;
-h|--help)
usage
exit 0
;;
*)
usage
exit 1
;;
esac
done
if [[ -z "$DISTRO" ]]; then
DISTRO="ubuntu"
fi
case "$DISTRO" in
arch)
IMAGE="docker.io/archlinux:latest"
INIT_PATH="/usr/lib/systemd/systemd"
PREPARE_CMD='pacman -Syyu --noconfirm sudo git base-devel';
;;
ubuntu)
IMAGE="docker.io/library/ubuntu:24.04"
INIT_PATH="/sbin/init"
PREPARE_CMD='apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y sudo git'
;;
*)
usage
exit 1
;;
esac
if command -v podman >/dev/null 2>&1; then
RUNTIME="podman"
MOUNT_OPTS=":Z"
elif command -v docker >/dev/null 2>&1; then
RUNTIME="docker"
MOUNT_OPTS=""
else
echo "[!] Neither podman nor docker found in PATH." >&2
exit 1
fi
CONTAINER_NAME="linuxbox-update-debug-${DISTRO}-$$"
cleanup() {
if (( KEEP_CONTAINER )); then
echo "[i] Keeping container ${CONTAINER_NAME} running for inspection." >&2
return
fi
if "$RUNTIME" ps -a --format '{{.Names}}' 2>/dev/null | grep -qx "$CONTAINER_NAME"; then
"$RUNTIME" rm -f "$CONTAINER_NAME" >/dev/null 2>&1 || true
fi
}
trap cleanup EXIT
start_container() {
if [[ "$RUNTIME" == "podman" ]]; then
"$RUNTIME" run -d --name "$CONTAINER_NAME" --privileged \
--tmpfs /tmp --tmpfs /run \
-v /sys/fs/cgroup:/sys/fs/cgroup:ro \
-v "$PROJECT_ROOT:/workspace${MOUNT_OPTS}" \
-w /workspace "$IMAGE" "$INIT_PATH" >/dev/null
else
"$RUNTIME" run -d --name "$CONTAINER_NAME" --privileged --cgroupns=host \
--tmpfs /tmp --tmpfs /run \
-v /sys/fs/cgroup:/sys/fs/cgroup:ro \
-v "$PROJECT_ROOT:/workspace" \
-w /workspace "$IMAGE" "$INIT_PATH" >/dev/null
fi
}
wait_for_systemd() {
local attempts=0
while (( attempts < 30 )); do
local status
status=$("$RUNTIME" exec "$CONTAINER_NAME" systemctl is-system-running 2>/dev/null || true)
case "$status" in
running|degraded)
return 0
;;
esac
sleep 1
((attempts++))
done
echo "[!] systemd did not reach running state inside container (last status: $status)." >&2
}
bootstrap_container() {
if [[ "$DISTRO" == "ubuntu" ]]; then
"$RUNTIME" exec "$CONTAINER_NAME" bash -lc "export DEBIAN_FRONTEND=noninteractive; $PREPARE_CMD"
"$RUNTIME" exec "$CONTAINER_NAME" bash -lc "useradd -m debugger || true"
"$RUNTIME" exec "$CONTAINER_NAME" bash -lc "echo 'debugger ALL=(ALL) NOPASSWD:ALL' >/etc/sudoers.d/debugger && chmod 0440 /etc/sudoers.d/debugger"
else
"$RUNTIME" exec "$CONTAINER_NAME" bash -lc "$PREPARE_CMD"
"$RUNTIME" exec "$CONTAINER_NAME" bash -lc "useradd -m debugger || true"
"$RUNTIME" exec "$CONTAINER_NAME" bash -lc "echo 'debugger ALL=(ALL) NOPASSWD:ALL' >/etc/sudoers.d/debugger && chmod 0440 /etc/sudoers.d/debugger"
fi
"$RUNTIME" exec "$CONTAINER_NAME" bash -lc "chown -R debugger:debugger /workspace"
}
run_update() {
local update_cmd
printf -v update_cmd 'UPDATE_DEBUG=1 ./update/update.sh %q' "$DISTRO"
for arg in "${FORWARDED_ARGS[@]}"; do
printf -v update_cmd '%s %q' "$update_cmd" "$arg"
done
"$RUNTIME" exec "$CONTAINER_NAME" bash -lc "su - debugger -c \"cd /workspace && $update_cmd\""
}
start_container
wait_for_systemd
bootstrap_container
run_update
echo "[✓] Debug run completed." >&2