2026-04-01 17:58:04 +00:00
|
|
|
#!/usr/bin/env bash
|
|
|
|
|
set -euo pipefail
|
|
|
|
|
|
2026-04-01 20:09:34 +00:00
|
|
|
# Set USER before sourcing env.sh (Alpine doesn't set USER)
|
|
|
|
|
export USER="${USER:-root}"
|
|
|
|
|
|
2026-04-07 17:11:59 +00:00
|
|
|
FORGE_URL="${FORGE_URL:-http://forgejo:3000}"
|
2026-04-10 07:58:10 +00:00
|
|
|
|
|
|
|
|
# Derive FORGE_REPO from PROJECT_TOML if available, otherwise require explicit env var
|
|
|
|
|
if [ -z "${FORGE_REPO:-}" ]; then
|
2026-04-10 08:03:55 +00:00
|
|
|
# Try to find a project TOML to derive FORGE_REPO from
|
2026-04-10 07:58:10 +00:00
|
|
|
_project_toml="${PROJECT_TOML:-}"
|
2026-04-10 08:03:55 +00:00
|
|
|
if [ -z "$_project_toml" ] && [ -d "${FACTORY_ROOT:-/opt/disinto}/projects" ]; then
|
2026-04-10 07:58:10 +00:00
|
|
|
for toml in "${FACTORY_ROOT:-/opt/disinto}"/projects/*.toml; do
|
|
|
|
|
if [ -f "$toml" ]; then
|
|
|
|
|
_project_toml="$toml"
|
|
|
|
|
break
|
|
|
|
|
fi
|
|
|
|
|
done
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
if [ -n "$_project_toml" ] && [ -f "$_project_toml" ]; then
|
|
|
|
|
# Parse FORGE_REPO from project TOML using load-project.sh
|
2026-04-10 08:03:55 +00:00
|
|
|
if source "${FACTORY_ROOT:-/opt/disinto}/lib/load-project.sh" "$_project_toml" 2>/dev/null; then
|
2026-04-10 07:58:10 +00:00
|
|
|
if [ -n "${FORGE_REPO:-}" ]; then
|
|
|
|
|
echo "Derived FORGE_REPO from PROJECT_TOML: $_project_toml" >&2
|
|
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# If still not set, fail fast with a clear error message
|
|
|
|
|
if [ -z "${FORGE_REPO:-}" ]; then
|
|
|
|
|
echo "FATAL: FORGE_REPO environment variable not set" >&2
|
|
|
|
|
echo "Set FORGE_REPO=<owner>/<repo> in .env (e.g. FORGE_REPO=disinto-admin/disinto)" >&2
|
|
|
|
|
exit 1
|
|
|
|
|
fi
|
|
|
|
|
fi
|
2026-04-01 17:58:04 +00:00
|
|
|
|
2026-04-10 16:17:08 +00:00
|
|
|
# Detect bind-mount of a non-git directory before attempting clone
|
|
|
|
|
if [ -d /opt/disinto ] && [ ! -d /opt/disinto/.git ] && [ -n "$(ls -A /opt/disinto 2>/dev/null)" ]; then
|
|
|
|
|
echo "FATAL: /opt/disinto contains files but no .git directory." >&2
|
|
|
|
|
echo "If you bind-mounted a directory at /opt/disinto, ensure it is a git working tree." >&2
|
|
|
|
|
echo "Sleeping 60s before exit to throttle the restart loop..." >&2
|
|
|
|
|
sleep 60
|
|
|
|
|
exit 1
|
|
|
|
|
fi
|
|
|
|
|
|
2026-04-07 17:11:59 +00:00
|
|
|
# Shallow clone at the pinned version (inject token to support auth-required Forgejo)
|
2026-04-01 17:58:04 +00:00
|
|
|
if [ ! -d /opt/disinto/.git ]; then
|
2026-04-07 17:11:59 +00:00
|
|
|
_auth_url=$(printf '%s' "$FORGE_URL" | sed "s|://|://token:${FORGE_TOKEN}@|")
|
2026-04-10 16:17:08 +00:00
|
|
|
echo "edge: cloning ${FORGE_URL}/${FORGE_REPO} (branch ${DISINTO_VERSION:-main})..." >&2
|
|
|
|
|
if ! git clone --depth 1 --branch "${DISINTO_VERSION:-main}" "${_auth_url}/${FORGE_REPO}.git" /opt/disinto; then
|
|
|
|
|
echo >&2
|
|
|
|
|
echo "FATAL: failed to clone ${FORGE_URL}/${FORGE_REPO}.git (branch ${DISINTO_VERSION:-main})" >&2
|
|
|
|
|
echo "Likely causes:" >&2
|
|
|
|
|
echo " - Forgejo at ${FORGE_URL} is unreachable from the edge container" >&2
|
|
|
|
|
echo " - Repository '${FORGE_REPO}' does not exist on this forge" >&2
|
|
|
|
|
echo " - FORGE_TOKEN is invalid or has no read access to '${FORGE_REPO}'" >&2
|
|
|
|
|
echo " - Branch '${DISINTO_VERSION:-main}' does not exist in '${FORGE_REPO}'" >&2
|
|
|
|
|
echo "Workaround: bind-mount a local git checkout into /opt/disinto." >&2
|
|
|
|
|
echo "Sleeping 60s before exit to throttle the restart loop..." >&2
|
|
|
|
|
sleep 60
|
|
|
|
|
exit 1
|
|
|
|
|
fi
|
2026-04-01 17:58:04 +00:00
|
|
|
fi
|
|
|
|
|
|
2026-04-10 09:13:11 +00:00
|
|
|
# Set HOME so that claude OAuth credentials and session.lock are found at the
|
|
|
|
|
# same in-container path as in disinto-agents (/home/agent/.claude), which makes
|
|
|
|
|
# flock cross-serialize across containers on the same host inode.
|
|
|
|
|
export HOME=/home/agent
|
|
|
|
|
mkdir -p "$HOME"
|
|
|
|
|
|
2026-04-10 09:32:47 +00:00
|
|
|
# Ensure log directory exists
|
|
|
|
|
mkdir -p /opt/disinto-logs
|
|
|
|
|
|
2026-04-01 17:58:04 +00:00
|
|
|
# Start dispatcher in background
|
|
|
|
|
bash /opt/disinto/docker/edge/dispatcher.sh &
|
|
|
|
|
|
2026-04-07 09:11:24 +00:00
|
|
|
# Start supervisor loop in background
|
2026-04-10 07:48:58 +00:00
|
|
|
PROJECT_TOML="${PROJECT_TOML:-projects/disinto.toml}"
|
2026-04-10 09:32:47 +00:00
|
|
|
(while true; do
|
2026-04-10 07:48:58 +00:00
|
|
|
bash /opt/disinto/supervisor/supervisor-run.sh "/opt/disinto/${PROJECT_TOML}" 2>&1 | tee -a /opt/disinto-logs/supervisor.log || true
|
2026-04-07 09:11:24 +00:00
|
|
|
sleep 1200 # 20 minutes
|
2026-04-10 09:32:47 +00:00
|
|
|
done) &
|
|
|
|
|
|
|
|
|
|
# Caddy as main process — run in foreground via wait so background jobs survive
|
|
|
|
|
# (exec replaces the shell, which can orphan backgrounded subshells)
|
|
|
|
|
caddy run --config /etc/caddy/Caddyfile --adapter caddyfile &
|
2026-04-07 09:11:24 +00:00
|
|
|
|
2026-04-10 09:32:47 +00:00
|
|
|
# Exit when any child dies (caddy crash → container restart via docker compose)
|
|
|
|
|
wait -n
|
|
|
|
|
exit 1
|