#!/usr/bin/env bash set -euo pipefail ROOT_DIR=$(cd "$(dirname "$0")/../.." && pwd) PLATFORM_REPO_DIR="${PLATFORM_REPO_DIR:-$ROOT_DIR/../unrip3}" BOOTSTRAP_ENV_FILE="${BOOTSTRAP_ENV_FILE:-$PLATFORM_REPO_DIR/.state/hetzner/bootstrap-secrets.resolved.env}" PLATFORM_APP_ENV_FILE="${PLATFORM_APP_ENV_FILE:-$PLATFORM_REPO_DIR/.env}" APP_ENV_FILE="${APP_ENV_FILE:-$ROOT_DIR/.env}" FORGEJO_REMOTE_NAME="${FORGEJO_REMOTE_NAME:-forgejo}" PROJECT_NAME="${PROJECT_NAME:-unrip}" PROJECT_NAMESPACE="${PROJECT_NAMESPACE:-$PROJECT_NAME}" PROJECT_DEPLOYMENTS="${PROJECT_DEPLOYMENTS:-$(python3 "$ROOT_DIR/scripts/deploy/repo_deployments.py" --format csv)}" PROJECT_REGISTRY_SECRET_NAME="${PROJECT_REGISTRY_SECRET_NAME:-${PROJECT_NAME}-registry-creds}" APP_SECRET_NAME="${APP_SECRET_NAME:-${PROJECT_NAME}-secrets}" SYNC_FORGEJO_REMOTE="${SYNC_FORGEJO_REMOTE:-1}" require() { command -v "$1" >/dev/null 2>&1 || { echo "missing required command: $1" >&2 exit 1 } } load_env_defaults() { local file="$1" [[ -f "$file" ]] || return 0 eval "$( python3 - "$file" <<'PY' import os import shlex import sys for raw in open(sys.argv[1], 'r', encoding='utf-8'): line = raw.strip() if not line or line.startswith('#'): continue if line.startswith('export '): line = line[len('export '):] if '=' not in line: continue key, value = line.split('=', 1) key = key.strip() if key in os.environ: continue print(f'export {key}={shlex.quote(value)}') PY )" } require git require kubectl require python3 require base64 load_env_defaults "$BOOTSTRAP_ENV_FILE" load_env_defaults "$PLATFORM_APP_ENV_FILE" load_env_defaults "$APP_ENV_FILE" REMOTE_URL="${FORGEJO_REMOTE_URL:-$(git -C "$ROOT_DIR" remote get-url "$FORGEJO_REMOTE_NAME")}" eval "$( python3 - "$REMOTE_URL" <<'PY' import sys from urllib.parse import urlparse remote = sys.argv[1] parsed = urlparse(remote) if parsed.scheme not in {'http', 'https'}: raise SystemExit('forgejo remote must use http(s) for automatic bootstrap') path = parsed.path.rstrip('/') if path.endswith('.git'): path = path[:-4] parts = [part for part in path.split('/') if part] if len(parts) < 2: raise SystemExit('could not parse repo owner/name from forgejo remote') base_url = f'{parsed.scheme}://{parsed.hostname}' if parsed.port: base_url += f':{parsed.port}' if parsed.username and parsed.password: print(f'export FORGEJO_API_PASSWORD={parsed.password}') if parsed.username: print(f'export FORGEJO_API_USERNAME={parsed.username}') print(f'export FORGEJO_URL={base_url}') print(f'export FORGEJO_REPO_OWNER={parts[-2]}') print(f'export FORGEJO_REPO_NAME={parts[-1]}') PY )" : "${KUBECONFIG_PATH:=${KUBECONFIG:-$PLATFORM_REPO_DIR/.state/hetzner/kubeconfig.yaml}}" : "${CI_KUBECONFIG_PATH:=$PLATFORM_REPO_DIR/.state/hetzner/kubeconfig.incluster.yaml}" : "${FORGEJO_URL:?set FORGEJO_URL or configure a forgejo remote}" : "${FORGEJO_REPO_OWNER:?set FORGEJO_REPO_OWNER}" : "${FORGEJO_REPO_NAME:?set FORGEJO_REPO_NAME}" : "${FORGEJO_ADMIN_USERNAME:=${FORGEJO_API_USERNAME:-}}" if [[ -z "${FORGEJO_TOKEN:-}" ]]; then : "${FORGEJO_ADMIN_USERNAME:=${FORGEJO_API_USERNAME:-}}" : "${FORGEJO_ADMIN_USERNAME:?set FORGEJO_TOKEN or FORGEJO_ADMIN_USERNAME}" : "${FORGEJO_ADMIN_PASSWORD:=${FORGEJO_API_PASSWORD:-}}" : "${FORGEJO_ADMIN_PASSWORD:?set FORGEJO_TOKEN or FORGEJO_ADMIN_PASSWORD}" fi if [[ ! -f "$KUBECONFIG_PATH" ]]; then echo "missing kubeconfig: $KUBECONFIG_PATH" >&2 exit 1 fi if [[ ! -f "$CI_KUBECONFIG_PATH" ]]; then echo "missing in-cluster kubeconfig: $CI_KUBECONFIG_PATH" >&2 exit 1 fi export KUBECONFIG="$KUBECONFIG_PATH" if [[ -z "${REGISTRY_HOST:-}" ]]; then REGISTRY_HOST="${REGISTRY_DOMAIN:-}" fi if [[ -z "${REGISTRY_HOST:-}" ]]; then REGISTRY_HOST="$(kubectl get ingress -n registry registry -o jsonpath='{.spec.rules[0].host}' 2>/dev/null || true)" fi if [[ -z "${REGISTRY_USERNAME:-}" ]]; then REGISTRY_USERNAME="$(kubectl get secret registry-secrets -n registry -o jsonpath='{.data.htpasswd}' 2>/dev/null | base64 -d | cut -d: -f1 || true)" fi : "${REGISTRY_HOST:?set REGISTRY_HOST or configure the registry ingress first}" : "${REGISTRY_USERNAME:?set REGISTRY_USERNAME or bootstrap the shared registry first}" : "${REGISTRY_PASSWORD:?set REGISTRY_PASSWORD}" : "${NEAR_INTENTS_API_KEY:?set NEAR_INTENTS_API_KEY}" : "${POSTGRES_PASSWORD:=}" : "${POSTGRES_URL:=}" : "${NEAR_INTENTS_SIGNER_PRIVATE_KEY:=}" : "${NOTIFICATION_NTFY_TOKEN:=}" secret_value() { local key="$1" kubectl -n "$PROJECT_NAMESPACE" get secret "$APP_SECRET_NAME" -o "jsonpath={.data.${key}}" 2>/dev/null | base64 -d 2>/dev/null || true } if [[ -z "$POSTGRES_PASSWORD" ]]; then POSTGRES_PASSWORD="$(secret_value POSTGRES_PASSWORD)" fi if [[ -z "$POSTGRES_URL" ]]; then POSTGRES_URL="$(secret_value POSTGRES_URL)" fi if [[ -z "$NEAR_INTENTS_SIGNER_PRIVATE_KEY" ]]; then NEAR_INTENTS_SIGNER_PRIVATE_KEY="$(secret_value NEAR_INTENTS_SIGNER_PRIVATE_KEY)" fi if [[ -z "$NOTIFICATION_NTFY_TOKEN" ]]; then NOTIFICATION_NTFY_TOKEN="$(secret_value NOTIFICATION_NTFY_TOKEN)" fi if [[ -z "$POSTGRES_PASSWORD" ]]; then POSTGRES_PASSWORD="$(python3 - <<'PY' import secrets print(secrets.token_urlsafe(24)) PY )" fi if [[ -z "$POSTGRES_URL" ]]; then POSTGRES_URL="postgresql://unrip:${POSTGRES_PASSWORD}@postgres:5432/unrip" fi echo "bootstrapping namespace $PROJECT_NAMESPACE" kubectl apply -f "$ROOT_DIR/deploy/k8s/base/namespace.yaml" echo "upserting runtime secret $APP_SECRET_NAME" secret_args=( --from-literal=NEAR_INTENTS_API_KEY="$NEAR_INTENTS_API_KEY" --from-literal=POSTGRES_PASSWORD="$POSTGRES_PASSWORD" --from-literal=POSTGRES_URL="$POSTGRES_URL" ) if [[ -n "$NEAR_INTENTS_SIGNER_PRIVATE_KEY" ]]; then secret_args+=(--from-literal=NEAR_INTENTS_SIGNER_PRIVATE_KEY="$NEAR_INTENTS_SIGNER_PRIVATE_KEY") fi if [[ -n "$NOTIFICATION_NTFY_TOKEN" ]]; then secret_args+=(--from-literal=NOTIFICATION_NTFY_TOKEN="$NOTIFICATION_NTFY_TOKEN") fi kubectl -n "$PROJECT_NAMESPACE" create secret generic "$APP_SECRET_NAME" \ "${secret_args[@]}" \ --dry-run=client -o yaml | kubectl apply -f - echo "upserting registry pull/push secret $PROJECT_REGISTRY_SECRET_NAME" kubectl -n "$PROJECT_NAMESPACE" create secret docker-registry "$PROJECT_REGISTRY_SECRET_NAME" \ --docker-server="$REGISTRY_HOST" \ --docker-username="$REGISTRY_USERNAME" \ --docker-password="$REGISTRY_PASSWORD" \ --dry-run=client -o yaml | kubectl apply -f - echo "applying app manifests" kubectl apply -k "$ROOT_DIR/deploy/k8s/base" echo "upserting Forgejo repo settings" forgejo_args=() if [[ -n "${FORGEJO_TOKEN:-}" ]]; then forgejo_args+=(--token "$FORGEJO_TOKEN") fi if [[ -n "${FORGEJO_ADMIN_USERNAME:-}" ]]; then forgejo_args+=(--admin-username "$FORGEJO_ADMIN_USERNAME") fi if [[ -n "${FORGEJO_ADMIN_PASSWORD:-}" ]]; then forgejo_args+=(--admin-password "$FORGEJO_ADMIN_PASSWORD") fi python3 "$ROOT_DIR/scripts/deploy/forgejo_repo_bootstrap.py" \ --forgejo-url "$FORGEJO_URL" \ --repo-owner "$FORGEJO_REPO_OWNER" \ --repo-name "$FORGEJO_REPO_NAME" \ --ci-kubeconfig "$CI_KUBECONFIG_PATH" \ --registry-host "$REGISTRY_HOST" \ --project-name "$PROJECT_NAME" \ --project-namespace "$PROJECT_NAMESPACE" \ --project-deployments "$PROJECT_DEPLOYMENTS" \ --project-registry-secret-name "$PROJECT_REGISTRY_SECRET_NAME" \ "${forgejo_args[@]}" if [[ "$SYNC_FORGEJO_REMOTE" == "1" ]]; then : "${FORGEJO_PUSH_USERNAME:=${FORGEJO_API_USERNAME:-$FORGEJO_REPO_OWNER}}" : "${FORGEJO_PUSH_PASSWORD:=${FORGEJO_ADMIN_PASSWORD:-${FORGEJO_API_PASSWORD:-}}}" if [[ -n "${FORGEJO_PUSH_USERNAME:-}" && -n "${FORGEJO_PUSH_PASSWORD:-}" ]]; then push_url="$( python3 - "$FORGEJO_URL" "$FORGEJO_REPO_OWNER" "$FORGEJO_REPO_NAME" "$FORGEJO_PUSH_USERNAME" "$FORGEJO_PUSH_PASSWORD" <<'PY' import sys from urllib.parse import quote base_url, owner, repo, username, password = sys.argv[1:] print(f"{base_url.rstrip('/')}".replace('://', f'://{quote(username, safe="")}:{quote(password, safe="")}@') + f'/{owner}/{repo}.git') PY )" git -C "$ROOT_DIR" remote set-url "$FORGEJO_REMOTE_NAME" "$push_url" echo "updated git remote $FORGEJO_REMOTE_NAME for HTTPS push auth" fi fi cat <