From badeb63f9c10e2c5a4ffab1411a147ef3f0a5ab9 Mon Sep 17 00:00:00 2001 From: Sebastian Cabrera Date: Sat, 4 Jul 2026 03:47:39 -0400 Subject: [PATCH] make temp env curl-able --- temp-env.sh | 240 +++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 172 insertions(+), 68 deletions(-) diff --git a/temp-env.sh b/temp-env.sh index ace43a5..10ea65f 100644 --- a/temp-env.sh +++ b/temp-env.sh @@ -1,101 +1,205 @@ #!/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 -# ---- colors ---- -BOLD='\033[1m' -CYAN='\033[0;36m' -GREEN='\033[0;32m' -YELLOW='\033[0;33m' -RED='\033[0;31m' -NC='\033[0m' +######################################## +# Seby's Ephemeral Debian Env +# curl | bash safe +######################################## -info() { echo -e "${CYAN}==>${NC} $1"; } -ok() { echo -e "${GREEN}✔${NC} $1"; } -warn() { echo -e "${YELLOW}!${NC} $1"; } -err() { echo -e "${RED}✘${NC} $1"; } +######################################## +# Styling +######################################## +TTY="/dev/tty" -# ---- sanity checks ---- -if ! command -v docker &>/dev/null; then - err "Docker isn't installed or isn't on PATH." +BOLD="\033[1m" +GREEN="\033[32m" +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 + fi +} + +prompt() { + 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}" +} + +confirm_default_no() { + local msg="$1" + 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)" -echo -e "${BOLD}Ephemeral Debian Environment${NC}" -echo "--------------------------------" +######################################## +# Configure +######################################## +section "Configuration" -# ---- pick a debian tag ---- -read -rp "$(echo -e "${CYAN}?${NC} Debian tag [bookworm]: ")" TAG -TAG="${TAG:-bookworm}" +TAG="$(prompt "Debian tag" "bookworm")" IMAGE="debian:${TAG}" -# ---- packages to install ---- -read -rp "$(echo -e "${CYAN}?${NC} Packages to install (space-separated, blank for none): ")" PACKAGES +PACKAGES="$(prompt "Packages to install (space-separated, blank for none)")" -# ---- optional extra apt flags ---- -read -rp "$(echo -e "${CYAN}?${NC} Extra apt-get flags (e.g. --no-install-recommends) [none]: ")" APT_FLAGS +APT_FLAGS="$(prompt "Extra apt-get flags (e.g. --no-install-recommends)")" -# ---- mount a local dir? ---- -read -rp "$(echo -e "${CYAN}?${NC} Mount a local directory into the container? (path or blank): ")" MOUNT_DIR +MOUNT_DIR="$(prompt "Mount a local directory into the container (path or blank)")" MOUNT_ARGS=() if [[ -n "$MOUNT_DIR" ]]; then - if [[ -d "$MOUNT_DIR" ]]; then - MOUNT_ARGS=(-v "$(realpath "$MOUNT_DIR"):/mnt/host") - ok "Will mount $(realpath "$MOUNT_DIR") -> /mnt/host" - else - warn "Directory '$MOUNT_DIR' doesn't exist, skipping mount." - fi + if [[ -d "$MOUNT_DIR" ]]; then + MOUNT_ARGS=(-v "$(realpath "$MOUNT_DIR"):/mnt/host") + say "Will mount $(realpath "$MOUNT_DIR") -> /mnt/host" + else + warn "Directory '$MOUNT_DIR' doesn't exist — skipping mount." + MOUNT_DIR="" + fi fi -# ---- remove image after exit too? ---- -read -rp "$(echo -e "${CYAN}?${NC} Remove the image (${IMAGE}) after exiting, not just the container? [y/N]: ")" RM_IMAGE -RM_IMAGE="${RM_IMAGE:-n}" +RM_IMAGE=false +if confirm_default_no "Remove image (${IMAGE}) after exiting, not just the container?"; then + RM_IMAGE=true +fi -# ---- build the in-container command ---- 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 - INNER_CMD="apt-get update && bash" + INNER_CMD="apt-get update && bash" fi -echo -info "Image: ${IMAGE}" -info "Packages: ${PACKAGES:-}" -info "Command: ${INNER_CMD}" -[[ -n "$MOUNT_DIR" && -d "$MOUNT_DIR" ]] && info "Mount: $(realpath "$MOUNT_DIR") -> /mnt/host" -echo +######################################## +# Summary +######################################## +section "Summary" +say "Image: ${IMAGE}" +say "Packages: ${PACKAGES:-}" +[[ -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 -CONFIRM="${CONFIRM:-y}" -if [[ ! "$CONFIRM" =~ ^[Yy] ]]; then - warn "Aborted." - exit 0 +if ! confirm_default_yes "Launch now?"; then + warn "Aborted." + exit 0 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 -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=$? set -e -echo "--------------------------------" -ok "Container exited (code ${EXIT_CODE}) and was automatically removed." +section "Done" +say "Container exited (code ${EXIT_CODE}) and was automatically removed." -if [[ "$RM_IMAGE" =~ ^[Yy] ]]; then - info "Checking if any other containers still use ${IMAGE}..." - IN_USE=$(docker ps -a --filter ancestor="$IMAGE" -q) - if [[ -n "$IN_USE" ]]; then - warn "Other containers still reference ${IMAGE}, skipping removal:" - docker ps -a --filter ancestor="$IMAGE" - else - docker rmi "$IMAGE" && ok "Removed image ${IMAGE}." - fi +######################################## +# Optional image cleanup +######################################## +if [[ "$RM_IMAGE" == true ]]; then + section "Image cleanup" + IN_USE="$(docker ps -a --filter ancestor="$IMAGE" -q)" + if [[ -n "$IN_USE" ]]; then + warn "Other containers still reference ${IMAGE} — skipping removal:" + docker ps -a --filter ancestor="$IMAGE" + else + docker rmi "$IMAGE" >/dev/null 2>&1 && say "Removed image ${IMAGE}." + fi fi -ok "Done." +say "All done."