diff --git a/docker/agents/entrypoint-llama.sh b/docker/agents/entrypoint-llama.sh index c142aad..fa2c6ed 100755 --- a/docker/agents/entrypoint-llama.sh +++ b/docker/agents/entrypoint-llama.sh @@ -24,7 +24,8 @@ if [ ! -d "${PROJECT_REPO_ROOT}/.git" ]; then log "Cloning repo..." mkdir -p "$(dirname "$PROJECT_REPO_ROOT")" chown -R agent:agent /home/agent/repos 2>/dev/null || true - su -s /bin/bash agent -c "git clone http://dev-bot:${FORGE_TOKEN}@forgejo:3000/${FORGE_REPO:-disinto-admin/disinto}.git ${PROJECT_REPO_ROOT}" + # Use password auth for git HTTP — Forgejo 11.x rejects API tokens for push (#361) + su -s /bin/bash agent -c "git clone http://dev-bot:${FORGE_PASS:-${FORGE_TOKEN}}@forgejo:3000/${FORGE_REPO:-disinto-admin/disinto}.git ${PROJECT_REPO_ROOT}" log "Repo cloned" fi diff --git a/docker/agents/entrypoint.sh b/docker/agents/entrypoint.sh index 7f3cbac..9d336aa 100644 --- a/docker/agents/entrypoint.sh +++ b/docker/agents/entrypoint.sh @@ -100,6 +100,38 @@ fi install_project_crons +# Configure git credential helper for password-based HTTP auth. +# Forgejo 11.x rejects API tokens for git push (#361); password auth works. +# This ensures all git operations (clone, fetch, push) from worktrees use +# password auth without needing tokens embedded in remote URLs. +if [ -n "${FORGE_PASS:-}" ] && [ -n "${FORGE_URL:-}" ]; then + _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) + _bot_user=$(curl -sf -H "Authorization: token ${FORGE_TOKEN}" \ + "${FORGE_URL}/api/v1/user" 2>/dev/null | jq -r '.login // empty') || _bot_user="" + _bot_user="${_bot_user:-dev-bot}" + + # Write a static credential helper script (git credential protocol) + cat > /home/agent/.git-credentials-helper </dev/null +echo "protocol=${_forge_proto}" +echo "host=${_forge_host}" +echo "username=${_bot_user}" +echo "password=${FORGE_PASS}" +CREDEOF + chmod 755 /home/agent/.git-credentials-helper + chown agent:agent /home/agent/.git-credentials-helper + + su -s /bin/bash agent -c "git config --global credential.helper '/home/agent/.git-credentials-helper'" + log "Git credential helper configured for ${_bot_user}@${_forge_host} (password auth)" +fi + # Configure tea CLI login for forge operations (runs as agent user). # tea stores config in ~/.config/tea/ — persistent across container restarts # only if that directory is on a mounted volume. diff --git a/lib/forge-push.sh b/lib/forge-push.sh index dba6e42..1da61f7 100644 --- a/lib/forge-push.sh +++ b/lib/forge-push.sh @@ -6,7 +6,8 @@ # # Globals expected: # FORGE_URL - Forge instance URL (e.g. http://localhost:3000) -# FORGE_TOKEN - API token for Forge operations +# FORGE_TOKEN - API token for Forge operations (used for API verification) +# FORGE_PASS - Bot password for git HTTP push (#361: tokens rejected by Forgejo 11.x) # FACTORY_ROOT - Root of the disinto factory # PRIMARY_BRANCH - Primary branch name (e.g. main) # @@ -20,6 +21,7 @@ set -euo pipefail _assert_forge_push_globals() { local missing=() [ -z "${FORGE_URL:-}" ] && missing+=("FORGE_URL") + [ -z "${FORGE_PASS:-}" ] && missing+=("FORGE_PASS") [ -z "${FORGE_TOKEN:-}" ] && missing+=("FORGE_TOKEN") [ -z "${FACTORY_ROOT:-}" ] && missing+=("FACTORY_ROOT") [ -z "${PRIMARY_BRANCH:-}" ] && missing+=("PRIMARY_BRANCH") @@ -33,13 +35,14 @@ _assert_forge_push_globals() { push_to_forge() { local repo_root="$1" forge_url="$2" repo_slug="$3" - # Build authenticated remote URL: http://dev-bot:@host:port/org/repo.git - if [ -z "${FORGE_TOKEN:-}" ]; then - echo "Error: FORGE_TOKEN not set — cannot push to Forgejo" >&2 + # Build authenticated remote URL: http://dev-bot:@host:port/org/repo.git + # Forgejo 11.x rejects API tokens for git HTTP push (#361); password auth works. + if [ -z "${FORGE_PASS:-}" ]; then + echo "Error: FORGE_PASS not set — cannot push to Forgejo (see #361)" >&2 return 1 fi local auth_url - auth_url=$(printf '%s' "$forge_url" | sed "s|://|://dev-bot:${FORGE_TOKEN}@|") + auth_url=$(printf '%s' "$forge_url" | sed "s|://|://dev-bot:${FORGE_PASS}@|") local remote_url="${auth_url}/${repo_slug}.git" # Display URL without token local display_url="${forge_url}/${repo_slug}.git" diff --git a/lib/forge-setup.sh b/lib/forge-setup.sh index 7e75434..40909c0 100644 --- a/lib/forge-setup.sh +++ b/lib/forge-setup.sh @@ -294,8 +294,21 @@ setup_forge() { [predictor-bot]="FORGE_PREDICTOR_TOKEN" [architect-bot]="FORGE_ARCHITECT_TOKEN" ) + # Map: bot-username -> env-var-name for the password + # Forgejo 11.x API tokens don't work for git HTTP push (#361). + # Store passwords so agents can use password auth for git operations. + local -A bot_pass_vars=( + [dev-bot]="FORGE_PASS" + [review-bot]="FORGE_REVIEW_PASS" + [planner-bot]="FORGE_PLANNER_PASS" + [gardener-bot]="FORGE_GARDENER_PASS" + [vault-bot]="FORGE_VAULT_PASS" + [supervisor-bot]="FORGE_SUPERVISOR_PASS" + [predictor-bot]="FORGE_PREDICTOR_PASS" + [architect-bot]="FORGE_ARCHITECT_PASS" + ) - local bot_user bot_pass token token_var + local bot_user bot_pass token token_var pass_var for bot_user in dev-bot review-bot planner-bot gardener-bot vault-bot supervisor-bot predictor-bot architect-bot; do bot_pass="bot-$(head -c 16 /dev/urandom | base64 | tr -dc 'a-zA-Z0-9' | head -c 20)" @@ -389,6 +402,17 @@ setup_forge() { export "${token_var}=${token}" echo " ${bot_user} token generated and saved (${token_var})" + # Store password in .env for git HTTP push (#361) + # Forgejo 11.x API tokens don't work for git push; password auth does. + pass_var="${bot_pass_vars[$bot_user]}" + if grep -q "^${pass_var}=" "$env_file" 2>/dev/null; then + sed -i "s|^${pass_var}=.*|${pass_var}=${bot_pass}|" "$env_file" + else + printf '%s=%s\n' "$pass_var" "$bot_pass" >> "$env_file" + fi + export "${pass_var}=${bot_pass}" + echo " ${bot_user} password saved (${pass_var})" + # Backwards-compat aliases for dev-bot and review-bot if [ "$bot_user" = "dev-bot" ]; then export CODEBERG_TOKEN="$token"