fix: bug: credential helper race on every cold boot — configure_git_creds() silently falls back to wrong username when Forgejo is not yet ready (#741)
All checks were successful
ci/woodpecker/push/ci Pipeline was successful
ci/woodpecker/pr/ci Pipeline was successful

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Claude 2026-04-13 11:37:23 +00:00
parent c4ca1e930d
commit 02915456ae
3 changed files with 60 additions and 12 deletions

View file

@ -49,8 +49,10 @@ services:
- ARCHITECT_INTERVAL=${ARCHITECT_INTERVAL:-21600}
- PLANNER_INTERVAL=${PLANNER_INTERVAL:-43200}
depends_on:
- forgejo
- woodpecker
forgejo:
condition: service_healthy
woodpecker:
condition: service_started
networks:
- disinto-net
@ -101,8 +103,10 @@ services:
- POLL_INTERVAL=${POLL_INTERVAL:-300}
- AGENT_ROLES=dev
depends_on:
- forgejo
- woodpecker
forgejo:
condition: service_healthy
woodpecker:
condition: service_started
networks:
- disinto-net
@ -171,6 +175,12 @@ services:
- FORGEJO__security__INSTALL_LOCK=true
- FORGEJO__service__DISABLE_REGISTRATION=true
- FORGEJO__webhook__ALLOWED_HOST_LIST=private
healthcheck:
test: ["CMD", "curl", "-sf", "http://localhost:3000/api/v1/version"]
interval: 5s
timeout: 3s
retries: 30
start_period: 30s
ports:
- "3000:3000"
networks:

View file

@ -49,7 +49,7 @@ source "${DISINTO_BAKED}/lib/git-creds.sh"
# Wrapper that calls the shared configure_git_creds with agent-specific paths,
# then repairs any legacy baked-credential URLs in existing clones.
_setup_git_creds() {
configure_git_creds "/home/agent" "gosu agent"
_GIT_CREDS_LOG_FN=log configure_git_creds "/home/agent" "gosu agent"
if [ -n "${FORGE_PASS:-}" ] && [ -n "${FORGE_URL:-}" ]; then
log "Git credential helper configured (password auth)"
fi
@ -61,16 +61,21 @@ _setup_git_creds() {
# Configure git author identity for commits made by this container.
# Derives identity from the resolved bot user (BOT_USER) to ensure commits
# are visibly attributable to the correct bot in the forge timeline.
# BOT_USER is normally set by configure_git_creds() (#741); this function
# only falls back to its own API call if BOT_USER was not already resolved.
configure_git_identity() {
# Resolve BOT_USER from FORGE_TOKEN if not already set
# Resolve BOT_USER from FORGE_TOKEN if not already set (configure_git_creds
# exports BOT_USER on success, so this is a fallback for edge cases only).
if [ -z "${BOT_USER:-}" ] && [ -n "${FORGE_TOKEN:-}" ]; then
BOT_USER=$(curl -sf --max-time 10 \
-H "Authorization: token ${FORGE_TOKEN}" \
"${FORGE_URL:-http://localhost:3000}/api/v1/user" 2>/dev/null | jq -r '.login // empty') || true
fi
# Default to dev-bot if resolution fails
BOT_USER="${BOT_USER:-dev-bot}"
if [ -z "${BOT_USER:-}" ]; then
log "WARNING: Could not resolve bot username for git identity — commits will use fallback"
BOT_USER="agent"
fi
# Configure git identity for all repositories
gosu agent git config --global user.name "${BOT_USER}"

View file

@ -35,13 +35,35 @@ configure_git_creds() {
forge_host=$(printf '%s' "$FORGE_URL" | sed 's|https\?://||; s|/.*||')
forge_proto=$(printf '%s' "$FORGE_URL" | sed 's|://.*||')
# Determine the bot username from FORGE_TOKEN identity (or default to dev-bot)
local log_fn="${_GIT_CREDS_LOG_FN:-echo}"
# Determine the bot username from FORGE_TOKEN identity with retry/backoff.
# Never fall back to a hardcoded default — a wrong username paired with the
# real password produces a cryptic 401 that's much harder to diagnose than
# a missing credential helper (#741).
local bot_user=""
if [ -n "${FORGE_TOKEN:-}" ]; then
bot_user=$(curl -sf -H "Authorization: token ${FORGE_TOKEN}" \
"${FORGE_URL}/api/v1/user" 2>/dev/null | jq -r '.login // empty') || bot_user=""
local attempt
for attempt in 1 2 3 4 5; do
bot_user=$(curl -sf --max-time 5 -H "Authorization: token ${FORGE_TOKEN}" \
"${FORGE_URL}/api/v1/user" 2>/dev/null | jq -r '.login // empty') || bot_user=""
if [ -n "$bot_user" ]; then
break
fi
$log_fn "WARNING: Forgejo not reachable (attempt ${attempt}/5) — retrying in ${attempt}s"
sleep "$attempt"
done
fi
bot_user="${bot_user:-dev-bot}"
if [ -z "$bot_user" ]; then
$log_fn "ERROR: Could not determine bot username from FORGE_TOKEN after 5 attempts — credential helper NOT configured"
$log_fn "ERROR: git push will fail until this is resolved. Restart the container after Forgejo is healthy."
return 1
fi
# Export BOT_USER so downstream functions (e.g. configure_git_identity) can
# reuse the resolved value without a redundant API call.
export BOT_USER="$bot_user"
local helper_path="${home_dir}/.git-credentials-helper"
@ -77,6 +99,17 @@ CREDEOF
else
git config --global --add safe.directory '*'
fi
# Verify the credential helper actually authenticates (#741).
# A helper that was written with a valid username but a mismatched password
# would silently 401 on every push — catch it now.
if ! curl -sf --max-time 5 -u "${bot_user}:${FORGE_PASS}" \
"${FORGE_URL}/api/v1/user" >/dev/null 2>&1; then
$log_fn "ERROR: credential helper verification failed — ${bot_user}:FORGE_PASS rejected by Forgejo"
rm -f "$helper_path"
return 1
fi
$log_fn "Git credential helper verified: ${bot_user}@${forge_host}"
}
# repair_baked_cred_urls [--as RUN_AS_CMD] DIR [DIR ...]