Spaces:
Sleeping
Sleeping
| # ββ 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" <<AUTHEOF | |
| { | |
| "zai-coding-plan": { | |
| "type": "api", | |
| "key": "${ZAI_API_KEY}" | |
| } | |
| } | |
| AUTHEOF | |
| echo "[entrypoint] OpenCode configured β Z.AI Coding Plan (https://api.z.ai/api/coding/paas/v4)" | |
| echo "[entrypoint] Models: glm-5.2 (primary) | glm-4.7 (small/fast)" | |
| # ββ 4. Brief pause to let HF storage fully settle before Paperclip starts ββββ | |
| # Without this, embedded PostgreSQL can get an ECONNREFUSED on the very first | |
| # boot cycle because the storage mount isn't quite ready when Paperclip tries | |
| # its first DB connection (~03:00:54 vs Postgres ready at ~03:01:20). | |
| echo "[entrypoint] Waiting for storage to settle..." | |
| sleep 8 | |
| echo "[entrypoint] Starting Paperclip..." | |
| # ββ 5. Start the server βββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| cd /app | |
| exec node --import ./server/node_modules/tsx/dist/loader.mjs server/dist/index.js | |