Fixes the 'no GitHub credentials' deadlock observed in the first
NAS-Docker batch run (2 May 2026): scrum4me-mcp's `wait_for_job`
expects a local clone at `~/Projects/<repo-name>` (convention-fallback
in resolveRepoRoot) but the container had no credentials and no clone.
Agent asked the user how to proceed; turn closed without claim.
Changes:
- `.env.example`: GH_TOKEN (fine-grained PAT, repo+PR scope) and
GH_PRECLONE_REPOS (comma-separated owner/name list, default covers
Scrum4Me + scrum4me-mcp).
- `bin/repo-bootstrap.sh` (new): runs as agent-user; configures git
credential-helper with HTTPS oauth2 token, then clones-or-fetches
each entry in GH_PRECLONE_REPOS into ~/Projects/<name>. Idempotent.
- `bin/entrypoint.sh`: hooks repo-bootstrap before run-agent.sh.
- `Dockerfile`:
- installs `gh` CLI (used for auto_pr `gh pr create`; reads GH_TOKEN
from env directly).
- pre-creates `~agent/Projects` and `~agent/.scrum4me-agent-worktrees`
so directory-ownership is right from the first boot.
- `README.md`: 'Repo bootstrap (clone-on-start)' section + GH_TOKEN
step in the deploy checklist; corrects the obsolete 'no push
credentials' note (agent now pushes feature-branches, gh creates PRs).
Same token covers clone, push and PR-creation — one secret to rotate.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
75 lines
2.4 KiB
Bash
75 lines
2.4 KiB
Bash
#!/usr/bin/env bash
|
|
# entrypoint.sh — container-startup
|
|
#
|
|
# Verantwoordelijkheden:
|
|
# 1. Schrijfbare dirs op de bind-mounts garanderen (UID/GID matching)
|
|
# 2. Health-server starten als achtergrondproces
|
|
# 3. gosu naar de agent-user en daemon-loop starten
|
|
#
|
|
# Loopt als root tot stap 3 — daarvoor hebben we root nodig om
|
|
# bind-mounts goed te zetten als de share met andere ownership is
|
|
# aangemaakt.
|
|
|
|
set -euo pipefail
|
|
|
|
log() { printf '[entrypoint] %s\n' "$*" >&2; }
|
|
|
|
: "${AGENT_UID:=1000}"
|
|
: "${AGENT_GID:=1000}"
|
|
: "${AGENT_STATE_DIR:=/var/run/agent}"
|
|
: "${AGENT_LOG_DIR:=/var/log/agent}"
|
|
: "${AGENT_REPO_CACHE:=/var/cache/repos}"
|
|
: "${AGENT_HEALTH_PORT:=8080}"
|
|
|
|
# ----- 1. dirs op bind-mounts -------------------------------------------
|
|
log "ensuring directories on bind-mounts"
|
|
mkdir -p \
|
|
"${AGENT_STATE_DIR}" \
|
|
"${AGENT_LOG_DIR}/runs" \
|
|
"${AGENT_LOG_DIR}/jobs" \
|
|
"${AGENT_REPO_CACHE}" \
|
|
/var/cache/npm \
|
|
/var/cache/pnpm
|
|
|
|
# Alleen ownership corrigeren als de share als andere user is aangemaakt
|
|
# — niet recursief op /var/cache/repos want dat kan groot zijn en de
|
|
# eerste boot vertragen.
|
|
chown "${AGENT_UID}:${AGENT_GID}" \
|
|
"${AGENT_STATE_DIR}" \
|
|
"${AGENT_LOG_DIR}" \
|
|
"${AGENT_LOG_DIR}/runs" \
|
|
"${AGENT_LOG_DIR}/jobs" \
|
|
"${AGENT_REPO_CACHE}" \
|
|
/var/cache/npm \
|
|
/var/cache/pnpm 2>/dev/null || true
|
|
|
|
# ----- 2. health-server in de achtergrond -------------------------------
|
|
log "starting health-server on :${AGENT_HEALTH_PORT}"
|
|
gosu agent node /opt/agent/bin/health-server.js \
|
|
> "${AGENT_LOG_DIR}/health-server.log" 2>&1 &
|
|
HEALTH_PID=$!
|
|
log "health-server pid=${HEALTH_PID}"
|
|
|
|
# Initial state: starting
|
|
gosu agent /bin/bash -c 'cat > "${AGENT_STATE_DIR}/state.json"' <<EOF
|
|
{
|
|
"status": "starting",
|
|
"startedAt": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
|
|
"lastBatchAt": null,
|
|
"lastBatchExit": null,
|
|
"consecutiveFailures": 0
|
|
}
|
|
EOF
|
|
|
|
# ----- 3. drop privileges, bootstrap repos, start daemon-loop -----------
|
|
# repo-bootstrap.sh runs as agent (NOT root) so that the cloned repos
|
|
# are owned by the agent user and live under ~agent/Projects/<name> —
|
|
# that is exactly where scrum4me-mcp's resolveRepoRoot looks via its
|
|
# convention fallback.
|
|
log "dropping to agent user and bootstrapping repos"
|
|
gosu agent /opt/agent/bin/repo-bootstrap.sh \
|
|
>> "${AGENT_LOG_DIR}/repo-bootstrap.log" 2>&1 \
|
|
|| log "WARN: repo-bootstrap returned non-zero (continuing)"
|
|
|
|
log "starting run-agent.sh"
|
|
exec gosu agent /opt/agent/bin/run-agent.sh
|