make temp env curl-able
This commit is contained in:
parent
0d7e4fea95
commit
badeb63f9c
1 changed files with 172 additions and 68 deletions
216
temp-env.sh
216
temp-env.sh
|
|
@ -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."
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue