#!/usr/bin/env bash set -euo pipefail ROOT_DIR=$(cd "$(dirname "$0")/../.." && pwd) # shellcheck disable=SC1091 source "$ROOT_DIR/scripts/hetzner/lib.sh" load_bootstrap_env TF_DIR="$ROOT_DIR/infra/terraform/hetzner" STATE_DIR="$ROOT_DIR/.state/hetzner" GENERATED_OVERLAY_DIR="$ROOT_DIR/deploy/k8s/overlays/hetzner-single-node/generated" DESTROY_DNS="${DESTROY_DNS:-false}" DESTROY_LOCAL_STATE="${DESTROY_LOCAL_STATE:-false}" DESTROY_FORGEJO_REPO="${DESTROY_FORGEJO_REPO:-false}" require terraform require curl resolve_secret_var HCLOUD_TOKEN required resolve_secret_var TAILSCALE_AUTH_KEY optional resolve_secret_var CLOUDFLARE_API_TOKEN optional resolve_secret_var CLOUDFLARE_ZONE_ID optional resolve_secret_var PORKBUN_API_KEY optional resolve_secret_var PORKBUN_SECRET_API_KEY optional resolve_secret_var FORGEJO_ADMIN_PASSWORD optional : "${SSH_PUBLIC_KEY_PATH:?set SSH_PUBLIC_KEY_PATH}" : "${PUBLIC_DOMAIN:=bootstrap.example.com}" : "${BASE_DOMAIN:?set BASE_DOMAIN}" : "${TAILSCALE_CONTROL_PLANE_HOSTNAME:=}" : "${TF_ADMIN_CIDR_BLOCKS:=}" : "${FORGEJO_DOMAIN:=}" : "${FORGEJO_REPO_OWNER:=${FORGEJO_ADMIN_USERNAME:-}}" : "${FORGEJO_REPO_NAME:=unrip}" SSH_PUBLIC_KEY=$(cat "$SSH_PUBLIC_KEY_PATH") TF_VARS=( -var "hcloud_token=$HCLOUD_TOKEN" -var "ssh_public_key=$SSH_PUBLIC_KEY" -var "public_domain=$PUBLIC_DOMAIN" -var "tailscale_auth_key=${TAILSCALE_AUTH_KEY:-}" -var "tailscale_control_plane_hostname=$TAILSCALE_CONTROL_PLANE_HOSTNAME" ) if [[ -n "$TF_ADMIN_CIDR_BLOCKS" && "$TF_ADMIN_CIDR_BLOCKS" != '[]' ]]; then TF_VARS+=( -var "admin_cidr_blocks=$TF_ADMIN_CIDR_BLOCKS" ) fi terraform -chdir="$TF_DIR" init terraform -chdir="$TF_DIR" destroy -auto-approve "${TF_VARS[@]}" cleanup_dns() { if [[ "$DESTROY_DNS" != "true" ]]; then echo "skipping DNS cleanup (set DESTROY_DNS=true to remove bootstrap-managed records)" return 0 fi if [[ -n "${CLOUDFLARE_API_TOKEN:-}" && -n "${CLOUDFLARE_ZONE_ID:-}" ]]; then DNS_MODE=delete BASE_DOMAIN="$BASE_DOMAIN" bash "$ROOT_DIR/scripts/hetzner/configure-cloudflare-dns.sh" elif [[ -n "${PORKBUN_API_KEY:-}" && -n "${PORKBUN_SECRET_API_KEY:-}" ]]; then DNS_MODE=delete BASE_DOMAIN="$BASE_DOMAIN" bash "$ROOT_DIR/scripts/hetzner/configure-porkbun-dns.sh" else echo "skipping DNS cleanup (no supported DNS provider credentials available)" fi } cleanup_local_state() { if [[ "$DESTROY_LOCAL_STATE" != "true" ]]; then echo "skipping local artifact cleanup (set DESTROY_LOCAL_STATE=true to remove generated bootstrap outputs)" return 0 fi rm -rf "$STATE_DIR" "$GENERATED_OVERLAY_DIR" rm -f "$TF_DIR/terraform.tfstate" "$TF_DIR/terraform.tfstate.backup" rm -rf "$TF_DIR/.terraform" echo "removed local bootstrap artifacts from .state/hetzner, generated overlay outputs, and Terraform working state" } cleanup_forgejo_repo() { if [[ "$DESTROY_FORGEJO_REPO" != "true" ]]; then echo "skipping Forgejo repo cleanup (set DESTROY_FORGEJO_REPO=true to delete the bootstrap-managed repo)" return 0 fi if [[ -z "$FORGEJO_DOMAIN" || -z "${FORGEJO_ADMIN_USERNAME:-}" || -z "${FORGEJO_ADMIN_PASSWORD:-}" || -z "$FORGEJO_REPO_OWNER" || -z "$FORGEJO_REPO_NAME" ]]; then echo "skipping Forgejo repo cleanup (set FORGEJO_DOMAIN, FORGEJO_ADMIN_USERNAME, FORGEJO_ADMIN_PASSWORD, FORGEJO_REPO_OWNER, and FORGEJO_REPO_NAME)" return 0 fi local base_url="https://${FORGEJO_DOMAIN}" local status status=$(curl -ksS -o /dev/null -w '%{http_code}' -u "$FORGEJO_ADMIN_USERNAME:$FORGEJO_ADMIN_PASSWORD" \ -X DELETE "$base_url/api/v1/repos/$FORGEJO_REPO_OWNER/$FORGEJO_REPO_NAME") case "$status" in 204) echo "deleted Forgejo repo $FORGEJO_REPO_OWNER/$FORGEJO_REPO_NAME" ;; 404) echo "skipped missing Forgejo repo $FORGEJO_REPO_OWNER/$FORGEJO_REPO_NAME" ;; *) echo "warning: Forgejo repo cleanup returned HTTP $status for $FORGEJO_REPO_OWNER/$FORGEJO_REPO_NAME" >&2 ;; esac } cleanup_dns cleanup_local_state cleanup_forgejo_repo