#!/bin/sh # ── entrypoint.sh ───────────────────────────────────────────────────────────── # HF Spaces runs as UID 1000 (not root), so the original docker-entrypoint.sh # fails — it uses `gosu` to drop root→node, which requires being root. # We skip it entirely and exec the server directly from /app. # # Recovery logic: # 1. Fix permissions and recreate missing empty PostgreSQL dirs # 2. Remove stale postmaster.pid from unclean shutdowns # 3. Probe whether PostgreSQL can start cleanly # 4. If the cluster is unrecoverable, restore from the latest backup # 5. Start Paperclip # ────────────────────────────────────────────────────────────────────────────── set -e DB_DIR="/data/paperclip/instances/default/db" BACKUP_DIR="/data/paperclip/instances/default/data/backups" DB_PORT=54329 # ── 1. Ensure /data/paperclip is writable ──────────────────────────────────── mkdir -p "${DB_DIR}" # ── 1a. Fix PostgreSQL data dir permissions ─────────────────────────────────── chmod 0700 "${DB_DIR}" if [ -d "${DB_DIR}/pg_notify" ]; then chmod 0700 "${DB_DIR}/pg_notify" fi # ── 1b. Remove stale postmaster.pid ────────────────────────────────────────── # If the Space was killed mid-run, PostgreSQL leaves behind postmaster.pid. # Paperclip sees it, thinks Postgres is running, tries to connect, and fails. # Safe to delete — PostgreSQL recreates it fresh on startup. if [ -f "${DB_DIR}/postmaster.pid" ]; then echo "[entrypoint] Removing stale postmaster.pid (leftover from unclean shutdown)..." rm -f "${DB_DIR}/postmaster.pid" echo "[entrypoint] postmaster.pid removed." fi # ── 1c. Recreate missing PostgreSQL dirs ───────────────────────────────────── # These are empty directories — safe to recreate if missing after unclean shutdown. if [ -f "${DB_DIR}/PG_VERSION" ]; then for dir in pg_notify pg_tblspc pg_replslot pg_twophase pg_snapshots pg_commit_ts pg_logical pg_logical/snapshots pg_logical/mappings; do if [ ! -d "${DB_DIR}/${dir}" ]; then echo "[entrypoint] ${dir} missing — recreating (cluster data preserved)..." mkdir -p "${DB_DIR}/${dir}" chmod 0700 "${DB_DIR}/${dir}" fi done echo "[entrypoint] PostgreSQL directories verified." fi # ── 1d. Probe whether PostgreSQL can start — restore from backup if not ─────── restore_from_backup() { echo "[entrypoint] ⚠️ Live PostgreSQL cluster is unrecoverable." echo "[entrypoint] Looking for latest backup in ${BACKUP_DIR}..." LATEST_BACKUP=$(ls -t "${BACKUP_DIR}"/*.tar.gz 2>/dev/null | head -n 1 || true) if [ -z "${LATEST_BACKUP}" ]; then echo "[entrypoint] ❌ No backup found in ${BACKUP_DIR}. Starting fresh — all previous data is lost." rm -rf "${DB_DIR}" mkdir -p "${DB_DIR}" chmod 0700 "${DB_DIR}" return fi echo "[entrypoint] ✅ Found backup: ${LATEST_BACKUP}" echo "[entrypoint] Wiping corrupted cluster and restoring..." rm -rf "${DB_DIR}" mkdir -p "${DB_DIR}" chmod 0700 "${DB_DIR}" tar -xzf "${LATEST_BACKUP}" -C "${DB_DIR}" --strip-components=1 chmod 0700 "${DB_DIR}" echo "[entrypoint] ✅ Restore complete from: $(basename ${LATEST_BACKUP})" echo "[entrypoint] Note: work done after this backup was taken will be missing." } if [ -f "${DB_DIR}/PG_VERSION" ]; then echo "[entrypoint] Probing PostgreSQL cluster health..." PG_CTL=$(find /usr -name "pg_ctl" 2>/dev/null | head -n 1 || true) if [ -n "${PG_CTL}" ]; then if ! "${PG_CTL}" -D "${DB_DIR}" -o "-p ${DB_PORT}" -w -t 15 start > /tmp/pg_probe.log 2>&1; then echo "[entrypoint] PostgreSQL probe failed. Logs:" cat /tmp/pg_probe.log restore_from_backup else echo "[entrypoint] PostgreSQL cluster is healthy. Stopping probe instance..." "${PG_CTL}" -D "${DB_DIR}" -o "-p ${DB_PORT}" stop > /dev/null 2>&1 || true fi else echo "[entrypoint] pg_ctl not found — skipping probe, Paperclip will handle startup." fi fi # ── 2. Write opencode provider config ──────────────────────────────────────── OPENCODE_CONFIG_DIR="${HOME}/.config/opencode" OPENCODE_STATE_DIR="${HOME}/.local/share/opencode" mkdir -p "${OPENCODE_CONFIG_DIR}" cp /app/opencode.json "${OPENCODE_CONFIG_DIR}/config.json" # ── 3. Write opencode auth file ─────────────────────────────────────────────── mkdir -p "${OPENCODE_STATE_DIR}" cat > "${OPENCODE_STATE_DIR}/auth.json" <