From 33d1b42d913f0b6f1282e9cb35edb28b260afe8d Mon Sep 17 00:00:00 2001 From: Jun-te Kim Date: Fri, 17 Apr 2026 10:00:13 +0000 Subject: [PATCH] add devcontainre --- .devcontainer/devcontainer.json | 3 +- devcontainer.sh | 102 ++++++++++++++++++++++++++++++++ 2 files changed, 104 insertions(+), 1 deletion(-) create mode 100644 devcontainer.sh diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index d1416d85..d8c62f05 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -5,7 +5,8 @@ "remoteUser": "vscode", "workspaceFolder": "/workspaces/assessment-model", "postStartCommand": "bash .devcontainer/post-install.sh", - "forwardPorts": [3000], + "forwardPorts": [3000], # For vscode + "appPort": ["3000:3000"], # For devcontainer shell "mounts": [ // Optional, just makes getting from Downloads (local env) easier // "source=${localEnv:HOME},target=/workspaces/home,type=bind" diff --git a/devcontainer.sh b/devcontainer.sh new file mode 100644 index 00000000..5c536cb7 --- /dev/null +++ b/devcontainer.sh @@ -0,0 +1,102 @@ +#!/usr/bin/env bash +# +# devcontainer.sh — devcontainer helper for this repo +# +# Usage: +# ./devcontainer.sh +# +# Commands: +# up build + start the devcontainer (idempotent) +# shell attach a bash shell; auto-ups if not running +# down stop the devcontainer +# rebuild remove + rebuild from scratch, no cache +# +# Examples: +# ./devcontainer.sh shell # one-shot: up if needed, then bash +# ./devcontainer.sh rebuild + +set -euo pipefail + +SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd)" +REPO_ROOT="${SCRIPT_DIR}" +CONFIG_PATH="${REPO_ROOT}/.devcontainer/devcontainer.json" + +VALID_COMMANDS=(up shell down rebuild) + +# --- helpers --------------------------------------------------------------- + +usage() { + sed -n '3,15p' "${BASH_SOURCE[0]}" | sed 's/^# \{0,1\}//' + exit "${1:-0}" +} + +die() { + echo "error: $*" >&2 + exit 1 +} + +in_list() { + local needle="$1" + shift + local item + for item in "$@"; do + [[ "${item}" == "${needle}" ]] && return 0 + done + return 1 +} + +container_id() { + # Find the running container for this repo via devcontainer labels. + docker ps -q \ + --filter "label=devcontainer.local_folder=${REPO_ROOT}" \ + --filter "label=devcontainer.config_file=${CONFIG_PATH}" +} + +# --- argument parsing ------------------------------------------------------ + +[[ $# -eq 1 ]] || usage 1 + +COMMAND="$1" + +in_list "${COMMAND}" "${VALID_COMMANDS[@]}" \ + || die "invalid command '${COMMAND}' (expected: ${VALID_COMMANDS[*]})" + +[[ -f "${CONFIG_PATH}" ]] || die "config not found: ${CONFIG_PATH}" + +DC_ARGS=(--workspace-folder "${REPO_ROOT}") + +# --- dispatch -------------------------------------------------------------- + +case "${COMMAND}" in + up) + echo ">> bringing up devcontainer" + 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)" ]]; then + echo ">> devcontainer not running, bringing it up first" + devcontainer up "${DC_ARGS[@]}" + fi + echo ">> attaching shell" + devcontainer exec "${DC_ARGS[@]}" bash 2>/dev/null \ + || devcontainer exec "${DC_ARGS[@]}" sh + ;; + + down) + cid="$(container_id)" + if [[ -z "${cid}" ]]; then + echo ">> devcontainer not running, nothing to stop" + exit 0 + fi + echo ">> stopping devcontainer" + docker stop "${cid}" + ;; + + rebuild) + echo ">> rebuilding devcontainer from scratch" + devcontainer up "${DC_ARGS[@]}" --remove-existing-container --build-no-cache + ;; +esac