Files
Claude-Code-Monitor/deployments/scripts/teardown.sh
T
nntrivi2001 57dc91585d feat: Claude Code Monitor — lanes, pipelines and a merged workspace
Internal SmartGift build of a Claude Code monitoring dashboard.

Lanes: a durable unit of parallel agent work, one per working directory,
tracked across session restarts. Managed lanes are git worktrees the
dashboard provisions and can reset or remove behind a three-check destroy
guard and a counted preflight; adopted lanes are directories you already
own and are never destroyable.

Pipelines: a lane moves through pipeline stages. A stage the agent declares
with evidence renders green; a stage inferred from the tool-event stream
renders dashed amber and never counts as done. Detection is forward-only
within a 30-minute window, and never writes the declared stage.

Workspace: one page at /run with a lane grid, the selected lane's pipeline,
and a full Claude console behind a disclosure.
2026-07-30 14:39:03 +07:00

336 lines
14 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
# ─────────────────────────────────────────────────────────────────────────────
# teardown.sh Clean removal of Claude Code Agent Monitor infrastructure
#
# Usage:
# ./teardown.sh --env dev --method helm
# ./teardown.sh --env production --method terraform
# ./teardown.sh --help
# ─────────────────────────────────────────────────────────────────────────────
# @author Nguyễn Ngọc Trí Vĩ <vinnt@smartgift.vn>
set -euo pipefail
readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
readonly PROJECT_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
readonly DEPLOY_DIR="${PROJECT_ROOT}/deployments"
readonly APP_NAME="agent-monitor"
# ── Colors & logging ───────────────────────────────────────────────────────
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
BOLD='\033[1m'
NC='\033[0m'
log() { echo -e "${CYAN}[$(date +'%Y-%m-%d %H:%M:%S')]${NC} $*"; }
info() { echo -e "${CYAN}[$(date +'%Y-%m-%d %H:%M:%S')]${NC} ${BLUE}${NC} $*"; }
ok() { echo -e "${CYAN}[$(date +'%Y-%m-%d %H:%M:%S')]${NC} ${GREEN}${NC} $*"; }
warn() { echo -e "${CYAN}[$(date +'%Y-%m-%d %H:%M:%S')]${NC} ${YELLOW}${NC} $*" >&2; }
err() { echo -e "${CYAN}[$(date +'%Y-%m-%d %H:%M:%S')]${NC} ${RED}${NC} $*" >&2; }
fatal() { err "$@"; exit 1; }
# ── Defaults ────────────────────────────────────────────────────────────────
ENVIRONMENT=""
METHOD=""
NAMESPACE=""
HELM_RELEASE="${APP_NAME}"
SKIP_BACKUP=false
FORCE=false
DELETE_NAMESPACE=false
DELETE_PVC=false
# ── Usage ───────────────────────────────────────────────────────────────────
usage() {
cat <<EOF
${BOLD}Usage:${NC}
$(basename "$0") --env <environment> --method <method> [options]
${BOLD}Required:${NC}
--env, -e Environment: dev, staging, production
--method, -m Method: helm, kustomize, terraform
${BOLD}Options:${NC}
--namespace, -n Kubernetes namespace (default: agent-monitor-<env>)
--release Helm release name (default: ${APP_NAME})
--delete-namespace Also delete the Kubernetes namespace
--delete-pvc Also delete PersistentVolumeClaims (data loss!)
--skip-backup Skip data backup before teardown
--force Skip all confirmation prompts
--help, -h Show this help message
${BOLD}Examples:${NC}
$(basename "$0") --env dev --method helm
$(basename "$0") --env staging --method kustomize --delete-namespace
$(basename "$0") --env production --method terraform
EOF
exit 0
}
# ── Argument parsing ────────────────────────────────────────────────────────
parse_args() {
[[ $# -eq 0 ]] && usage
while [[ $# -gt 0 ]]; do
case "$1" in
--env|-e) ENVIRONMENT="$2"; shift 2 ;;
--method|-m) METHOD="$2"; shift 2 ;;
--namespace|-n) NAMESPACE="$2"; shift 2 ;;
--release) HELM_RELEASE="$2"; shift 2 ;;
--delete-namespace) DELETE_NAMESPACE=true; shift ;;
--delete-pvc) DELETE_PVC=true; shift ;;
--skip-backup) SKIP_BACKUP=true; shift ;;
--force) FORCE=true; shift ;;
--help|-h) usage ;;
*) fatal "Unknown option: $1" ;;
esac
done
[[ -z "$ENVIRONMENT" ]] && fatal "Missing required argument: --env"
[[ -z "$METHOD" ]] && fatal "Missing required argument: --method"
[[ -z "$NAMESPACE" ]] && NAMESPACE="agent-monitor-${ENVIRONMENT}"
}
# ── Confirm teardown ───────────────────────────────────────────────────────
confirm_teardown() {
if [[ "$FORCE" == true ]]; then
return
fi
echo ""
echo -e " ${RED}${BOLD}╔══════════════════════════════════════════╗${NC}"
echo -e " ${RED}${BOLD}║ ⚠ TEARDOWN WARNING ⚠ ║${NC}"
echo -e " ${RED}${BOLD}╚══════════════════════════════════════════╝${NC}"
echo ""
echo -e " This will ${RED}${BOLD}DESTROY${NC} the following resources:"
echo -e " • Environment: ${BOLD}${ENVIRONMENT}${NC}"
echo -e " • Method: ${BOLD}${METHOD}${NC}"
echo -e " • Namespace: ${BOLD}${NAMESPACE}${NC}"
[[ "$DELETE_NAMESPACE" == true ]] && echo -e "${RED}Namespace will be deleted${NC}"
[[ "$DELETE_PVC" == true ]] && echo -e "${RED}PVCs will be deleted (DATA LOSS!)${NC}"
echo ""
if [[ "$ENVIRONMENT" == "production" ]]; then
echo -e " ${RED}${BOLD}THIS IS A PRODUCTION ENVIRONMENT!${NC}"
echo ""
read -r -p "$(echo -e "${RED}Type 'destroy ${ENVIRONMENT}' to confirm:${NC} ")" confirm
[[ "$confirm" == "destroy ${ENVIRONMENT}" ]] || fatal "Teardown cancelled."
# Second confirmation for production
echo ""
read -r -p "$(echo -e "${RED}Are you absolutely sure? Type 'YES' in caps:${NC} ")" confirm2
[[ "$confirm2" == "YES" ]] || fatal "Teardown cancelled."
else
read -r -p "$(echo -e "${YELLOW}Type 'yes' to confirm:${NC} ")" confirm
[[ "$confirm" == "yes" ]] || fatal "Teardown cancelled."
fi
}
# ── Pre-teardown backup ────────────────────────────────────────────────────
backup_data() {
if [[ "$SKIP_BACKUP" == true ]]; then
info "Skipping pre-teardown backup (--skip-backup)"
return
fi
info "Creating backup before teardown..."
if [[ -x "${SCRIPT_DIR}/db-backup.sh" ]]; then
local backup_dir="${PROJECT_ROOT}/data/pre-teardown-backups"
"${SCRIPT_DIR}/db-backup.sh" \
--env "${ENVIRONMENT}" \
--output "${backup_dir}" \
--namespace "${NAMESPACE}" \
&& ok "Pre-teardown backup created" \
|| warn "Backup failed continuing with teardown"
else
warn "db-backup.sh not found skipping backup"
fi
}
# ── Show current resources ──────────────────────────────────────────────────
show_resources() {
info "Current resources in namespace '${NAMESPACE}':"
echo ""
kubectl get all -n "${NAMESPACE}" 2>/dev/null || warn "Could not list resources"
if [[ "$METHOD" != "terraform" ]]; then
echo ""
info "PersistentVolumeClaims:"
kubectl get pvc -n "${NAMESPACE}" 2>/dev/null || warn "No PVCs found"
fi
echo ""
}
# ── Helm teardown ──────────────────────────────────────────────────────────
teardown_helm() {
info "Uninstalling Helm release '${HELM_RELEASE}'..."
if helm status "${HELM_RELEASE}" -n "${NAMESPACE}" &>/dev/null; then
helm uninstall "${HELM_RELEASE}" -n "${NAMESPACE}" --wait --timeout 300s \
|| fatal "Helm uninstall failed"
ok "Helm release '${HELM_RELEASE}' uninstalled"
else
warn "Helm release '${HELM_RELEASE}' not found in namespace '${NAMESPACE}'"
fi
# Also try uninstalling blue/green releases
for color in blue green; do
if helm status "${HELM_RELEASE}-${color}" -n "${NAMESPACE}" &>/dev/null; then
info "Uninstalling ${color} slot release..."
helm uninstall "${HELM_RELEASE}-${color}" -n "${NAMESPACE}" --wait --timeout 300s \
&& ok "Release '${HELM_RELEASE}-${color}' uninstalled" \
|| warn "Failed to uninstall ${color} release"
fi
done
}
# ── Kustomize teardown ─────────────────────────────────────────────────────
teardown_kustomize() {
local overlay_dir="${DEPLOY_DIR}/kubernetes/overlays/${ENVIRONMENT}"
if [[ -d "$overlay_dir" ]]; then
info "Deleting Kustomize resources..."
kubectl delete -k "${overlay_dir}" --ignore-not-found=true --wait=true --timeout=300s \
&& ok "Kustomize resources deleted" \
|| warn "Some resources may not have been deleted"
else
warn "Kustomize overlay not found at ${overlay_dir}"
info "Deleting resources by label..."
kubectl delete all -l "app.kubernetes.io/name=${APP_NAME}" -n "${NAMESPACE}" --wait=true \
|| warn "Could not delete resources by label"
fi
}
# ── Terraform teardown ─────────────────────────────────────────────────────
teardown_terraform() {
local tf_dir="${DEPLOY_DIR}/terraform"
local env_vars_file="${tf_dir}/environments/${ENVIRONMENT}/terraform.tfvars"
info "Destroying Terraform-managed infrastructure..."
pushd "${tf_dir}" > /dev/null
terraform init -input=false
local destroy_args=(-input=false -auto-approve)
if [[ -f "$env_vars_file" ]]; then
destroy_args+=(-var-file="$env_vars_file")
fi
# Need to provide required variables that may not have defaults
destroy_args+=(-var "environment=${ENVIRONMENT}")
if ! terraform destroy "${destroy_args[@]}"; then
popd > /dev/null
fatal "Terraform destroy failed! Review state manually."
fi
popd > /dev/null
ok "Terraform infrastructure destroyed"
}
# ── Cleanup PVCs ────────────────────────────────────────────────────────────
cleanup_pvcs() {
if [[ "$DELETE_PVC" != true ]]; then
local pvc_count
pvc_count=$(kubectl get pvc -n "${NAMESPACE}" --no-headers 2>/dev/null | wc -l | tr -d ' ')
if [[ "$pvc_count" -gt 0 ]]; then
warn "PersistentVolumeClaims still exist. Use --delete-pvc to remove them."
kubectl get pvc -n "${NAMESPACE}" 2>/dev/null
fi
return
fi
info "Deleting PersistentVolumeClaims..."
kubectl delete pvc --all -n "${NAMESPACE}" --wait=true \
&& ok "PVCs deleted" \
|| warn "Some PVCs could not be deleted"
}
# ── Cleanup namespace ──────────────────────────────────────────────────────
cleanup_namespace() {
if [[ "$DELETE_NAMESPACE" != true ]]; then
return
fi
if [[ "$ENVIRONMENT" == "production" ]]; then
warn "Refusing to delete production namespace automatically."
warn "Delete manually: kubectl delete namespace ${NAMESPACE}"
return
fi
info "Deleting namespace '${NAMESPACE}'..."
kubectl delete namespace "${NAMESPACE}" --ignore-not-found=true --wait=true --timeout=120s \
&& ok "Namespace '${NAMESPACE}' deleted" \
|| warn "Namespace deletion may be stuck. Check: kubectl get namespace ${NAMESPACE}"
}
# ── Verify teardown ────────────────────────────────────────────────────────
verify_teardown() {
info "Verifying teardown..."
if [[ "$METHOD" == "terraform" ]]; then
ok "Terraform state should reflect no resources"
return
fi
local remaining
remaining=$(kubectl get all -n "${NAMESPACE}" --no-headers 2>/dev/null | wc -l | tr -d ' ')
if [[ "$remaining" -eq 0 ]]; then
ok "No resources remaining in namespace '${NAMESPACE}'"
else
warn "${remaining} resources still exist in namespace '${NAMESPACE}':"
kubectl get all -n "${NAMESPACE}" 2>/dev/null
fi
}
# ── Main ────────────────────────────────────────────────────────────────────
main() {
echo ""
echo -e "${BOLD}${RED}╔══════════════════════════════════════════════════╗${NC}"
echo -e "${BOLD}${RED}║ Claude Code Agent Monitor Teardown ║${NC}"
echo -e "${BOLD}${RED}╚══════════════════════════════════════════════════╝${NC}"
echo ""
parse_args "$@"
info "Teardown configuration:"
echo -e " ${BOLD}Environment:${NC} ${ENVIRONMENT}"
echo -e " ${BOLD}Method:${NC} ${METHOD}"
echo -e " ${BOLD}Namespace:${NC} ${NAMESPACE}"
echo ""
if [[ "$METHOD" != "terraform" ]]; then
show_resources
fi
confirm_teardown
backup_data
case "$METHOD" in
helm) teardown_helm ;;
kustomize) teardown_kustomize ;;
terraform) teardown_terraform ;;
*) fatal "Invalid method: ${METHOD}" ;;
esac
if [[ "$METHOD" != "terraform" ]]; then
cleanup_pvcs
cleanup_namespace
verify_teardown
fi
echo ""
ok "${BOLD}Teardown complete!${NC}"
echo -e " ${BOLD}Environment:${NC} ${ENVIRONMENT}"
echo -e " ${BOLD}Method:${NC} ${METHOD}"
echo -e " ${BOLD}Timestamp:${NC} $(date -u +%Y-%m-%dT%H:%M:%SZ)"
echo ""
}
main "$@"