253 lines
7.9 KiB
Bash
Executable File
253 lines
7.9 KiB
Bash
Executable File
#!/bin/bash
|
|
set -euo pipefail
|
|
|
|
# ─── Kordant Scheduler — Pan Server Setup ─────────────────────
|
|
# Usage:
|
|
# bash scripts/setup-pan.sh # interactively
|
|
# bash scripts/setup-pan.sh user@pan # interactively, remote
|
|
# bash scripts/setup-pan.sh -u <url> -d <url> ...
|
|
#
|
|
# Flags:
|
|
# -u, --gitea-url URL Gitea clone URL (skip prompt)
|
|
# -d, --db-url URL Turso database URL (skip prompt)
|
|
# -t, --db-token TOKEN Turso auth token (skip prompt)
|
|
# -k, --hooks-dir DIR Gitea hooks directory (skip prompt; empty=skip hook)
|
|
# --host HOST SSH host (default: pan)
|
|
# -y, --non-interactive Skip all prompts (requires -u, -d, -t)
|
|
# -h, --help Show this message
|
|
|
|
# ─── Defaults ───────────────────────────────────────────────────
|
|
|
|
PAN_HOST=""
|
|
GITEA_URL=""
|
|
DB_URL=""
|
|
DB_TOKEN=""
|
|
HOOKS_DIR=""
|
|
NON_INTERACTIVE=false
|
|
REPO_DIR="/opt/kordant"
|
|
|
|
# ─── Help ───────────────────────────────────────────────────────
|
|
|
|
usage() {
|
|
sed -n 's/^# //p; s/^#$//p' "$0"
|
|
exit 0
|
|
}
|
|
|
|
# ─── Parse flags ────────────────────────────────────────────────
|
|
|
|
while [ $# -gt 0 ]; do
|
|
case "$1" in
|
|
-u|--gitea-url) GITEA_URL="$2"; shift 2 ;;
|
|
-d|--db-url) DB_URL="$2"; shift 2 ;;
|
|
-t|--db-token) DB_TOKEN="$2"; shift 2 ;;
|
|
-k|--hooks-dir) HOOKS_DIR="$2"; shift 2 ;;
|
|
--host) PAN_HOST="$2"; shift 2 ;;
|
|
-y|--non-interactive) NON_INTERACTIVE=true; shift ;;
|
|
-h|--help) usage ;;
|
|
-*)
|
|
echo "Unknown option: $1"
|
|
usage
|
|
;;
|
|
*) PAN_HOST="${PAN_HOST:-$1}"; shift ;;
|
|
esac
|
|
done
|
|
|
|
PAN_HOST="${PAN_HOST:-pan}"
|
|
|
|
# ─── Remote detection ───────────────────────────────────────────
|
|
|
|
if [ "$(hostname)" != "pan" ] && [ "$(hostname -s 2>/dev/null)" != "pan" ]; then
|
|
if [ -n "${SSH_CONNECTION:-}" ]; then
|
|
echo "Already connected to pan via SSH."
|
|
else
|
|
echo "Not on pan. Connecting to $PAN_HOST via SSH..."
|
|
# Rebuild the flag string to pass through
|
|
FLAGS=""
|
|
[ -n "$GITEA_URL" ] && FLAGS="$FLAGS -u '$GITEA_URL'"
|
|
[ -n "$DB_URL" ] && FLAGS="$FLAGS -d '$DB_URL'"
|
|
[ -n "$DB_TOKEN" ] && FLAGS="$FLAGS -t '$DB_TOKEN'"
|
|
[ -n "$HOOKS_DIR" ] && FLAGS="$FLAGS -k '$HOOKS_DIR'"
|
|
$NON_INTERACTIVE && FLAGS="$FLAGS -y"
|
|
scp "$0" "${PAN_HOST}:/tmp/kordant-setup.sh"
|
|
ssh -t "$PAN_HOST" "sudo bash /tmp/kordant-setup.sh $FLAGS && rm /tmp/kordant-setup.sh"
|
|
exit $?
|
|
fi
|
|
fi
|
|
|
|
echo "=== Kordant Scheduler Setup (running on pan) ==="
|
|
|
|
# ─── Step 1: Install prerequisites ─────────────────────────────
|
|
|
|
echo "--- Step 1: Checking prerequisites ---"
|
|
|
|
if ! command -v docker &>/dev/null; then
|
|
echo "Installing Docker..."
|
|
curl -fsSL https://get.docker.com | bash
|
|
systemctl enable --now docker
|
|
else
|
|
echo "Docker already installed."
|
|
fi
|
|
|
|
if ! docker compose version &>/dev/null; then
|
|
echo "Installing Docker Compose plugin..."
|
|
apt-get update && apt-get install -y docker-compose-plugin
|
|
else
|
|
echo "Docker Compose plugin already installed."
|
|
fi
|
|
|
|
if ! command -v git &>/dev/null; then
|
|
echo "Installing git..."
|
|
apt-get update && apt-get install -y git
|
|
else
|
|
echo "Git already installed."
|
|
fi
|
|
|
|
# ─── Step 2: Clone or pull the repo ────────────────────────────
|
|
|
|
echo "--- Step 2: Setting up repo at $REPO_DIR ---"
|
|
|
|
if [ ! -d "$REPO_DIR/.git" ]; then
|
|
if [ -z "$GITEA_URL" ]; then
|
|
if $NON_INTERACTIVE; then
|
|
echo "❌ --gitea-url is required in non-interactive mode"
|
|
exit 1
|
|
fi
|
|
while [ -z "$GITEA_URL" ]; do
|
|
read -rp "Gitea clone URL (e.g. http://localhost:3000/kordant/kordant.git): " GITEA_URL
|
|
done
|
|
fi
|
|
echo "Cloning $GITEA_URL ..."
|
|
git clone "$GITEA_URL" "$REPO_DIR"
|
|
cd "$REPO_DIR"
|
|
else
|
|
echo "Repo exists. Pulling latest..."
|
|
cd "$REPO_DIR"
|
|
git pull
|
|
fi
|
|
|
|
# ─── Step 3: Create .env with credentials ──────────────────────
|
|
|
|
echo "--- Step 3: Environment file ---"
|
|
|
|
if [ ! -f "$REPO_DIR/.env" ]; then
|
|
if [ -z "$DB_URL" ]; then
|
|
if $NON_INTERACTIVE; then
|
|
echo "❌ --db-url is required in non-interactive mode"
|
|
exit 1
|
|
fi
|
|
read -rp " DATABASE_URL (e.g. libsql://kordant.turso.io): " DB_URL
|
|
fi
|
|
if [ -z "$DB_TOKEN" ]; then
|
|
if $NON_INTERACTIVE; then
|
|
echo "❌ --db-token is required in non-interactive mode"
|
|
exit 1
|
|
fi
|
|
read -rsp " DATABASE_AUTH_TOKEN: " DB_TOKEN
|
|
echo ""
|
|
fi
|
|
|
|
cat > "$REPO_DIR/.env" << ENVEOF
|
|
# ─── Kordant Scheduler Environment ─────────────────────────────
|
|
# Turso database
|
|
DATABASE_URL="${DB_URL}"
|
|
DATABASE_AUTH_TOKEN="${DB_TOKEN}"
|
|
|
|
# Job queue (local Redis container)
|
|
REDIS_URL="redis://redis:6379"
|
|
|
|
# Node environment
|
|
NODE_ENV=production
|
|
JOB_WORKER=true
|
|
JOB_PRIMARY=true
|
|
ENVEOF
|
|
chmod 600 "$REPO_DIR/.env"
|
|
echo "✅ Created $REPO_DIR/.env"
|
|
else
|
|
echo "$REPO_DIR/.env already exists, keeping it."
|
|
fi
|
|
|
|
# ─── Step 4: Gitea post-receive hook ────────────────────────────
|
|
|
|
echo "--- Step 4: Gitea post-receive hook ---"
|
|
|
|
if [ -z "$HOOKS_DIR" ] && ! $NON_INTERACTIVE; then
|
|
read -rp "Gitea repo hooks directory (or leave blank to skip): " HOOKS_DIR
|
|
fi
|
|
|
|
if [ -n "$HOOKS_DIR" ]; then
|
|
if [ ! -d "$HOOKS_DIR" ]; then
|
|
echo " Directory not found: $HOOKS_DIR"
|
|
if $NON_INTERACTIVE; then
|
|
echo "❌ hooks-dir does not exist"
|
|
exit 1
|
|
fi
|
|
else
|
|
HOOK_FILE="$HOOKS_DIR/post-receive"
|
|
cat > "$HOOK_FILE" << 'HOOKEOF'
|
|
#!/bin/bash
|
|
cd /opt/kordant
|
|
git pull origin main
|
|
docker compose -f scheduler/docker-compose.yml up -d --build
|
|
HOOKEOF
|
|
chmod +x "$HOOK_FILE"
|
|
echo "✅ Post-receive hook installed at $HOOK_FILE"
|
|
fi
|
|
else
|
|
echo "Skipping post-receive hook."
|
|
fi
|
|
|
|
# ─── Step 5: Create systemd service ────────────────────────────
|
|
|
|
echo "--- Step 5: Systemd service ---"
|
|
|
|
SERVICE_NAME="kordant-scheduler"
|
|
SERVICE_FILE="/etc/systemd/system/${SERVICE_NAME}.service"
|
|
|
|
if [ ! -f "$SERVICE_FILE" ]; then
|
|
echo "Creating $SERVICE_FILE..."
|
|
cat > "$SERVICE_FILE" << SERVICEEOF
|
|
[Unit]
|
|
Description=Kordant Background Job Scheduler
|
|
After=docker.service network-online.target
|
|
Wants=docker.service network-online.target
|
|
|
|
[Service]
|
|
Type=oneshot
|
|
RemainAfterExit=yes
|
|
WorkingDirectory=$REPO_DIR
|
|
ExecStart=docker compose -f scheduler/docker-compose.yml up -d --build
|
|
ExecStop=docker compose -f scheduler/docker-compose.yml down
|
|
ExecReload=docker compose -f scheduler/docker-compose.yml pull && docker compose -f scheduler/docker-compose.yml up -d --build
|
|
StandardOutput=journal
|
|
StandardError=journal
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|
|
SERVICEEOF
|
|
systemctl daemon-reload
|
|
else
|
|
echo "$SERVICE_FILE already exists."
|
|
fi
|
|
|
|
# ─── Step 6: Start / restart the scheduler ─────────────────────
|
|
|
|
echo "--- Step 6: Starting scheduler ---"
|
|
|
|
systemctl enable "$SERVICE_NAME" 2>/dev/null || true
|
|
|
|
cd "$REPO_DIR"
|
|
docker compose -f scheduler/docker-compose.yml pull 2>/dev/null || true
|
|
docker compose -f scheduler/docker-compose.yml up -d --build
|
|
|
|
echo ""
|
|
echo "=== Scheduler status ==="
|
|
sleep 3
|
|
docker compose -f scheduler/docker-compose.yml ps
|
|
|
|
echo ""
|
|
echo "=== Kordant scheduler setup complete ==="
|
|
echo " Repo: $REPO_DIR"
|
|
echo " Logs: journalctl -u kordant-scheduler -f"
|
|
echo " Shell: cd $REPO_DIR && docker compose -f scheduler/docker-compose.yml logs -f"
|
|
echo " .env: $REPO_DIR/.env"
|