molecule-core/scripts/cleanup-rogue-workspaces.sh
Hongming Wang f7683e3adf fix(provisioner): stop rogue config-missing restart loop (#17)
Resolves #17.

Part A: scripts/cleanup-rogue-workspaces.sh deletes workspaces whose id
or name starts with known test placeholder prefixes (aaaaaaaa-, etc.)
and force-removes the paired Docker container. Documented in
tests/README.md.

Part B: add a pre-flight check in provisionWorkspace() — when neither a
template path nor in-memory configFiles supplies config.yaml, probe the
existing named volume via a throwaway alpine container. If the volume
lacks config.yaml, mark the workspace status='failed' with a clear
last_sample_error instead of handing it to Docker's unless-stopped
restart policy (which otherwise loops forever on FileNotFoundError).

New pure helper provisioner.ValidateConfigSource + unit tests.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 07:32:58 -07:00

60 lines
1.9 KiB
Bash
Executable File

#!/usr/bin/env bash
# cleanup-rogue-workspaces.sh — delete stale test workspaces matching
# well-known placeholder ID patterns (#17).
#
# Usage:
# bash scripts/cleanup-rogue-workspaces.sh # against http://localhost:8080
# MOLECULE_URL=http://host:8080 bash scripts/cleanup-rogue-workspaces.sh
#
# Deletes any workspace whose id OR name starts with one of the
# hard-coded test prefixes. Also force-removes the matching Docker
# container (`ws-<id[:12]>`) in case Docker's unless-stopped policy
# kept it restarting on a missing config.yaml.
#
# Safe to run repeatedly — idempotent. Non-matching workspaces are left
# untouched.
set -euo pipefail
PLATFORM_URL="${MOLECULE_URL:-http://localhost:8080}"
echo "Scanning ${PLATFORM_URL}/workspaces for rogue test workspaces..."
payload="$(curl -sS --fail "${PLATFORM_URL}/workspaces" || true)"
if [[ -z "${payload}" ]]; then
echo " platform unreachable at ${PLATFORM_URL}"
exit 1
fi
# Extract {id\tname} for workspaces whose id or name starts with a test prefix.
# Patterns are passed via env so the heredoc doesn't need shell interpolation.
matches="$(PAYLOAD="${payload}" python3 - <<'PY'
import json, os
data = json.loads(os.environ["PAYLOAD"])
rows = data if isinstance(data, list) else data.get("workspaces", [])
patterns = ("aaaaaaaa-", "bbbbbbbb-", "cccccccc-", "test-ws-")
for w in rows:
wid = w.get("id", "") or ""
name = w.get("name", "") or ""
if any(wid.startswith(p) or name.startswith(p) for p in patterns):
print(f"{wid}\t{name}")
PY
)"
if [[ -z "${matches}" ]]; then
echo " no rogue workspaces found"
exit 0
fi
while IFS=$'\t' read -r wid wname; do
[[ -z "${wid}" ]] && continue
short="${wid:0:12}"
echo " deleting workspace ${wid} (${wname})"
curl -sS -X DELETE "${PLATFORM_URL}/workspaces/${wid}" >/dev/null || true
if command -v docker >/dev/null 2>&1; then
docker rm -f "ws-${short}" >/dev/null 2>&1 || true
fi
done <<<"${matches}"
echo "done."