Model/devcontainer.sh
2026-04-17 14:50:57 +00:00

110 lines
2.9 KiB
Bash

#!/usr/bin/env bash
#
# dc.sh — devcontainer helper for this repo
#
# Usage:
# ./devcontainer.sh <config> <command>
#
# Configs: backend | asset_list
# Commands: up, shell, down, rebuild
#
# `shell` auto-runs `up` first if the container isn't already running,
# so it's safe to call cold.
#
# Examples:
# ./devcontainer.sh backend shell # up + exec bash
# ./devcontainer.sh asset_list up
# ./devcontainer.sh backend rebuild
# ./devcontainer.sh backend down
set -euo pipefail
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd)"
REPO_ROOT="${SCRIPT_DIR}"
VALID_CONFIGS=(backend asset_list)
VALID_COMMANDS=(up shell down rebuild)
# --- helpers ---------------------------------------------------------------
usage() {
sed -n '3,20p' "${BASH_SOURCE[0]}" | sed 's/^# \{0,1\}//'
exit "${1:-0}"
}
die() {
echo "error: $*" >&2
exit 1
}
in_list() {
# in_list <needle> <haystack...>
local needle="$1"
shift
local item
for item in "$@"; do
[[ "${item}" == "${needle}" ]] && return 0
done
return 1
}
container_id_for() {
# Find a running container for the given config path via devcontainer labels.
local config_path="$1"
docker ps -q \
--filter "label=devcontainer.local_folder=${REPO_ROOT}" \
--filter "label=devcontainer.config_file=${config_path}"
}
# --- argument parsing ------------------------------------------------------
[[ $# -eq 2 ]] || usage 1
CONFIG_NAME="$1"
COMMAND="$2"
in_list "${CONFIG_NAME}" "${VALID_CONFIGS[@]}" \
|| die "invalid config '${CONFIG_NAME}' (expected: ${VALID_CONFIGS[*]})"
in_list "${COMMAND}" "${VALID_COMMANDS[@]}" \
|| die "invalid command '${COMMAND}' (expected: ${VALID_COMMANDS[*]})"
CONFIG_PATH="${REPO_ROOT}/.devcontainer/${CONFIG_NAME}/devcontainer.json"
[[ -f "${CONFIG_PATH}" ]] || die "config not found: ${CONFIG_PATH}"
DC_ARGS=(--workspace-folder "${REPO_ROOT}" --config "${CONFIG_PATH}")
# --- dispatch --------------------------------------------------------------
case "${COMMAND}" in
up)
echo ">> bringing up '${CONFIG_NAME}'"
devcontainer up "${DC_ARGS[@]}"
;;
shell)
# Auto-up if not already running. `devcontainer up` is idempotent —
# it reuses an existing container, so this is cheap on warm starts.
if [[ -z "$(container_id_for "${CONFIG_PATH}")" ]]; then
echo ">> '${CONFIG_NAME}' not running, bringing it up first"
devcontainer up "${DC_ARGS[@]}"
fi
echo ">> attaching shell to '${CONFIG_NAME}'"
devcontainer exec "${DC_ARGS[@]}" bash 2>/dev/null \
|| devcontainer exec "${DC_ARGS[@]}" sh
;;
down)
cid="$(container_id_for "${CONFIG_PATH}")"
if [[ -z "${cid}" ]]; then
echo ">> '${CONFIG_NAME}' not running, nothing to stop"
exit 0
fi
echo ">> stopping '${CONFIG_NAME}'"
docker stop "${cid}"
;;
rebuild)
echo ">> rebuilding '${CONFIG_NAME}' from scratch"
devcontainer up "${DC_ARGS[@]}" --remove-existing-container --build-no-cache
;;
esac