#!/bin/sh set -eu ROOT=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) "$ROOT/scripts/bootstrap-kubernetes-tools.sh" PATH="$ROOT/.tools/bin:$PATH" export PATH CLUSTER=who-need-help MARKER="$ROOT/.tools/${CLUSTER}.owned" NAMESPACE=who-need-help SECRET_NAME=who-need-help-local POSTGIS_IMAGE=postgis/postgis:18-3.6-alpine POSTGIS_SOURCE="${POSTGIS_IMAGE}@sha256:05d68c7f0f19b9aa0bf7c4a2049b2e8b38b44a63116392b95726a4c913766cf6" MAILPIT_IMAGE=axllent/mailpit:v1.30.4 MAILPIT_SOURCE="${MAILPIT_IMAGE}@sha256:5a49a77c5bdbe7c5474450b4f46348d09949df3695257729c93a30369382d4f6" kube() { kubectl --context "kind-${CLUSTER}" "$@" } case "$(uname -m)" in x86_64) IMAGE_ARCH=amd64 ;; aarch64|arm64) IMAGE_ARCH=arm64 ;; *) echo "Unsupported container architecture: $(uname -m)" >&2; exit 1 ;; esac load_pinned_image() { source=$1 local_tag=$2 archive=$3 docker pull --platform "linux/${IMAGE_ARCH}" "$source" image_id=$(docker image inspect "$source" --format '{{.Id}}') docker tag "$image_id" "$local_tag" docker image save --platform "linux/${IMAGE_ARCH}" --output "$archive" "$local_tag" kind load image-archive "$archive" --name "$CLUSTER" rm -f "$archive" } remove_legacy_inline_secrets() { legacy_secret=who-need-help-who-need-help # Older chart revisions rendered credentials from tracked Helm values. Once # the new deployments are healthy, remove the obsolete Secret and only those # release-history records that still contain the former inline secret keys. kube --namespace "$NAMESPACE" delete secret "$legacy_secret" \ --ignore-not-found >/dev/null for revision in $( helm history who-need-help \ --kube-context "kind-${CLUSTER}" \ --namespace "$NAMESPACE" | awk 'NR > 1 {print $1}' ); do if helm get values who-need-help \ --revision "$revision" \ --kube-context "kind-${CLUSTER}" \ --namespace "$NAMESPACE" | grep -Eq '^[[:space:]]*(databaseUrl|secretKeyBase|handoverSecret|releaseCookie):'; then kube --namespace "$NAMESPACE" delete secret \ --selector="owner=helm,name=who-need-help,version=${revision}" fi done } if kind get clusters | grep -Fxq "$CLUSTER"; then if [ ! -f "$MARKER" ]; then echo "A kind cluster named '$CLUSTER' already exists but was not created by this project." >&2 echo "Refusing to modify it. Rename/remove it yourself or inspect it first." >&2 exit 1 fi else kind create cluster --config "$ROOT/deploy/kind/cluster.yaml" touch "$MARKER" fi load_pinned_image "$POSTGIS_SOURCE" "$POSTGIS_IMAGE" "$ROOT/.tools/postgis-kind.tar" load_pinned_image "$MAILPIT_SOURCE" "$MAILPIT_IMAGE" "$ROOT/.tools/mailpit-kind.tar" docker build --tag who-need-help:local "$ROOT" kind load docker-image who-need-help:local --name "$CLUSTER" kube create namespace "$NAMESPACE" --dry-run=client --output=yaml | kube apply -f - legacy_backup= if kube --namespace "$NAMESPACE" get deployment postgis >/dev/null 2>&1; then backup_dir="$ROOT/output/backups" mkdir -p "$backup_dir" umask 077 legacy_backup="$backup_dir/kind-who-need-help-$(date +%Y%m%d-%H%M%S).dump" kube --namespace "$NAMESPACE" exec deployment/postgis -- \ pg_dump --username postgres --dbname who_need_help --format custom >"$legacy_backup" test -s "$legacy_backup" kube --namespace "$NAMESPACE" exec --stdin deployment/postgis -- \ pg_restore --list <"$legacy_backup" >/dev/null echo "Validated legacy kind database backup: $legacy_backup" fi if ! kube --namespace "$NAMESPACE" get secret "$SECRET_NAME" >/dev/null 2>&1; then if kube --namespace "$NAMESPACE" get statefulset postgis >/dev/null 2>&1; then echo "Secret '$SECRET_NAME' is missing while the persistent PostGIS StatefulSet exists." >&2 echo "Refusing to generate credentials that would not match the existing database." >&2 exit 1 fi postgres_password=$(openssl rand -hex 32) secret_key_base=$(openssl rand -hex 64) handover_secret=$(openssl rand -hex 64) release_cookie=$(openssl rand -hex 64) metrics_token=$(openssl rand -hex 32) kube --namespace "$NAMESPACE" create secret generic "$SECRET_NAME" \ --from-literal=POSTGRES_DB=who_need_help \ --from-literal=POSTGRES_USER=postgres \ --from-literal="POSTGRES_PASSWORD=$postgres_password" \ --from-literal="DATABASE_URL=ecto://postgres:${postgres_password}@postgis/who_need_help" \ --from-literal="SECRET_KEY_BASE=$secret_key_base" \ --from-literal="HANDOVER_SECRET=$handover_secret" \ --from-literal="RELEASE_COOKIE=$release_cookie" \ --from-literal="METRICS_TOKEN=$metrics_token" unset postgres_password secret_key_base handover_secret release_cookie metrics_token elif [ -z "$(kube --namespace "$NAMESPACE" get secret "$SECRET_NAME" -o jsonpath='{.data.METRICS_TOKEN}')" ]; then metrics_token=$(openssl rand -hex 32) kube --namespace "$NAMESPACE" patch secret "$SECRET_NAME" \ --type merge \ --patch "{\"stringData\":{\"METRICS_TOKEN\":\"$metrics_token\"}}" >/dev/null unset metrics_token fi if [ -n "$legacy_backup" ]; then kube --namespace "$NAMESPACE" delete deployment postgis --wait=true fi kube --namespace "$NAMESPACE" apply -f "$ROOT/deploy/kind/dependencies.yaml" kube --namespace "$NAMESPACE" rollout status statefulset/postgis kube --namespace "$NAMESPACE" rollout status deployment/mailpit if [ -n "$legacy_backup" ]; then kube --namespace "$NAMESPACE" exec --stdin statefulset/postgis -- \ pg_restore --username postgres --dbname who_need_help \ --clean --if-exists --no-owner <"$legacy_backup" echo "Restored legacy kind database backup." fi helm upgrade --install who-need-help "$ROOT/deploy/helm/who-need-help" \ --kube-context "kind-${CLUSTER}" \ --namespace "$NAMESPACE" \ --values "$ROOT/deploy/helm/who-need-help/values-kind.yaml" \ --set-string app.codexSessionId="${CODEX_SESSION_ID:-${CODEX_THREAD_ID:-not-configured}}" \ --wait kube --namespace "$NAMESPACE" rollout restart \ deployment/who-need-help-who-need-help-web \ deployment/who-need-help-who-need-help-worker kube --namespace "$NAMESPACE" rollout status deployment/who-need-help-who-need-help-web kube --namespace "$NAMESPACE" rollout status deployment/who-need-help-who-need-help-worker remove_legacy_inline_secrets "$ROOT/scripts/verify-realtime-cluster.sh" kind echo "Who Need Help: http://localhost:4011" echo "Mailpit: http://localhost:8028"