make temp env curl-able

This commit is contained in:
Sebastian Cabrera 2026-07-04 03:47:39 -04:00
parent 0d7e4fea95
commit badeb63f9c
Signed by: okseby
GPG key ID: 2DDBFDEE356CF3DE

View file

@ -1,101 +1,205 @@
#!/usr/bin/env bash #!/usr/bin/env bash
#
# temp-env.sh
# Spin up a throwaway Debian container, install packages, drop into a shell,
# and clean up automatically when you exit.
set -euo pipefail set -euo pipefail
# ---- colors ---- ########################################
BOLD='\033[1m' # Seby's Ephemeral Debian Env
CYAN='\033[0;36m' # curl | bash safe
GREEN='\033[0;32m' ########################################
YELLOW='\033[0;33m'
RED='\033[0;31m'
NC='\033[0m'
info() { echo -e "${CYAN}==>${NC} $1"; } ########################################
ok() { echo -e "${GREEN}${NC} $1"; } # Styling
warn() { echo -e "${YELLOW}!${NC} $1"; } ########################################
err() { echo -e "${RED}${NC} $1"; } TTY="/dev/tty"
# ---- sanity checks ---- BOLD="\033[1m"
if ! command -v docker &>/dev/null; then GREEN="\033[32m"
err "Docker isn't installed or isn't on PATH." YELLOW="\033[33m"
RED="\033[31m"
BLUE="\033[34m"
RESET="\033[0m"
section() { printf "\n${BLUE}${BOLD}==> %s${RESET}\n" "$*"; }
say() { printf "${GREEN}✔ %s${RESET}\n" "$*"; }
warn() { printf "${YELLOW}⚠ %s${RESET}\n" "$*"; }
err() { printf "${RED}✖ %s${RESET}\n" "$*"; }
have() { command -v "$1" >/dev/null 2>&1; }
need_tty() {
if [[ ! -r "$TTY" || ! -w "$TTY" ]]; then
err "No interactive TTY available."
exit 1 exit 1
fi fi
}
echo -e "${BOLD}Ephemeral Debian Environment${NC}" prompt() {
echo "--------------------------------" local msg="$1"
local default="${2:-}"
local out
if [[ -n "$default" ]]; then
printf "${BOLD}%s${RESET} " "$msg [$default]:" >"$TTY"
else
printf "${BOLD}%s${RESET} " "$msg:" >"$TTY"
fi
IFS= read -r out <"$TTY"
printf "%s" "${out:-$default}"
}
# ---- pick a debian tag ---- confirm_default_no() {
read -rp "$(echo -e "${CYAN}?${NC} Debian tag [bookworm]: ")" TAG local msg="$1"
TAG="${TAG:-bookworm}" local ans
ans="$(prompt "$msg [y/N]")"
[[ "${ans,,}" == "y" || "${ans,,}" == "yes" ]]
}
confirm_default_yes() {
local msg="$1"
local ans
ans="$(prompt "$msg [Y/n]" "y")"
[[ "${ans,,}" == "y" || "${ans,,}" == "yes" ]]
}
########################################
# Spinner (writes to /dev/tty so curl|bash stays safe)
########################################
run_with_spinner() {
local label="$1"; shift
("$@") &
local pid=$!
local spin='|/-\'
local i=0
printf "\033[?25l" >"$TTY" || true
while kill -0 "$pid" 2>/dev/null; do
i=$(( (i + 1) % 4 ))
printf "\r${BLUE}${BOLD}..${RESET} %s %s" "${spin:$i:1}" "$label" >"$TTY"
sleep 0.12
done
wait "$pid"
local rc=$?
printf "\r\033[K" >"$TTY"
printf "\033[?25h" >"$TTY" || true
return "$rc"
}
########################################
# Header
########################################
clear
cat << "EOF"
_____ _____ ___
|_ _|__ _ __ ___ _ __ | ___|_ ____ __ / _ \
| |/ _ \ '_ ` _ \| '_ \ ____| |_ | '_ \ \ / /| | | |
| | __/ | | | | | |_) |____| _|| | | \ V / | |_| |
|_|\___|_| |_| |_| .__/ |_| |_| |_|\_/ \___/
|_|
Ephemeral Debian Env | v1.0
EOF
need_tty
if ! have docker; then
err "Docker not found. Install it first, then re-run this script."
exit 1
fi
say "Docker detected: $(docker --version)"
########################################
# Configure
########################################
section "Configuration"
TAG="$(prompt "Debian tag" "bookworm")"
IMAGE="debian:${TAG}" IMAGE="debian:${TAG}"
# ---- packages to install ---- PACKAGES="$(prompt "Packages to install (space-separated, blank for none)")"
read -rp "$(echo -e "${CYAN}?${NC} Packages to install (space-separated, blank for none): ")" PACKAGES
# ---- optional extra apt flags ---- APT_FLAGS="$(prompt "Extra apt-get flags (e.g. --no-install-recommends)")"
read -rp "$(echo -e "${CYAN}?${NC} Extra apt-get flags (e.g. --no-install-recommends) [none]: ")" APT_FLAGS
# ---- mount a local dir? ---- MOUNT_DIR="$(prompt "Mount a local directory into the container (path or blank)")"
read -rp "$(echo -e "${CYAN}?${NC} Mount a local directory into the container? (path or blank): ")" MOUNT_DIR
MOUNT_ARGS=() MOUNT_ARGS=()
if [[ -n "$MOUNT_DIR" ]]; then if [[ -n "$MOUNT_DIR" ]]; then
if [[ -d "$MOUNT_DIR" ]]; then if [[ -d "$MOUNT_DIR" ]]; then
MOUNT_ARGS=(-v "$(realpath "$MOUNT_DIR"):/mnt/host") MOUNT_ARGS=(-v "$(realpath "$MOUNT_DIR"):/mnt/host")
ok "Will mount $(realpath "$MOUNT_DIR") -> /mnt/host" say "Will mount $(realpath "$MOUNT_DIR") -> /mnt/host"
else else
warn "Directory '$MOUNT_DIR' doesn't exist, skipping mount." warn "Directory '$MOUNT_DIR' doesn't exist — skipping mount."
MOUNT_DIR=""
fi fi
fi fi
# ---- remove image after exit too? ---- RM_IMAGE=false
read -rp "$(echo -e "${CYAN}?${NC} Remove the image (${IMAGE}) after exiting, not just the container? [y/N]: ")" RM_IMAGE if confirm_default_no "Remove image (${IMAGE}) after exiting, not just the container?"; then
RM_IMAGE="${RM_IMAGE:-n}" RM_IMAGE=true
fi
# ---- build the in-container command ----
if [[ -n "$PACKAGES" ]]; then if [[ -n "$PACKAGES" ]]; then
INNER_CMD="apt-get update && apt-get install -y ${APT_FLAGS} ${PACKAGES} && bash" INNER_CMD="apt-get update && apt-get install -y ${APT_FLAGS} ${PACKAGES} && bash"
else else
INNER_CMD="apt-get update && bash" INNER_CMD="apt-get update && bash"
fi fi
echo ########################################
info "Image: ${IMAGE}" # Summary
info "Packages: ${PACKAGES:-<none>}" ########################################
info "Command: ${INNER_CMD}" section "Summary"
[[ -n "$MOUNT_DIR" && -d "$MOUNT_DIR" ]] && info "Mount: $(realpath "$MOUNT_DIR") -> /mnt/host" say "Image: ${IMAGE}"
echo say "Packages: ${PACKAGES:-<none>}"
[[ -n "$MOUNT_DIR" ]] && say "Mount: $(realpath "$MOUNT_DIR") -> /mnt/host"
say "Cleanup: container removed on exit$([[ "$RM_IMAGE" == true ]] && echo ", image removed too")"
read -rp "$(echo -e "${CYAN}?${NC} Launch now? [Y/n]: ")" CONFIRM if ! confirm_default_yes "Launch now?"; then
CONFIRM="${CONFIRM:-y}"
if [[ ! "$CONFIRM" =~ ^[Yy] ]]; then
warn "Aborted." warn "Aborted."
exit 0 exit 0
fi fi
info "Pulling/starting container (this shell disappears on exit, --rm is set)..." ########################################
echo "--------------------------------" # Pull image (quiet + spinner)
########################################
section "Pulling image"
if run_with_spinner "Pulling ${IMAGE}..." docker pull "$IMAGE" >/dev/null 2>&1; then
say "Image ready: ${IMAGE}"
else
err "Failed to pull ${IMAGE}"
exit 1
fi
########################################
# Launch
########################################
section "Launching ephemeral environment"
say "Type 'exit' when you're done — the container cleans up automatically."
set +e set +e
docker run --rm -it "${MOUNT_ARGS[@]}" "$IMAGE" bash -c "$INNER_CMD" docker run --rm -it "${MOUNT_ARGS[@]}" "$IMAGE" bash -c "$INNER_CMD" <"$TTY"
EXIT_CODE=$? EXIT_CODE=$?
set -e set -e
echo "--------------------------------" section "Done"
ok "Container exited (code ${EXIT_CODE}) and was automatically removed." say "Container exited (code ${EXIT_CODE}) and was automatically removed."
if [[ "$RM_IMAGE" =~ ^[Yy] ]]; then ########################################
info "Checking if any other containers still use ${IMAGE}..." # Optional image cleanup
IN_USE=$(docker ps -a --filter ancestor="$IMAGE" -q) ########################################
if [[ "$RM_IMAGE" == true ]]; then
section "Image cleanup"
IN_USE="$(docker ps -a --filter ancestor="$IMAGE" -q)"
if [[ -n "$IN_USE" ]]; then if [[ -n "$IN_USE" ]]; then
warn "Other containers still reference ${IMAGE}, skipping removal:" warn "Other containers still reference ${IMAGE} skipping removal:"
docker ps -a --filter ancestor="$IMAGE" docker ps -a --filter ancestor="$IMAGE"
else else
docker rmi "$IMAGE" && ok "Removed image ${IMAGE}." docker rmi "$IMAGE" >/dev/null 2>&1 && say "Removed image ${IMAGE}."
fi fi
fi fi
ok "Done." say "All done."