#!/usr/bin/env bash set -euo pipefail ############################################################################### # entrypoint.sh - Base entrypoint for forgebot AI agent containers ############################################################################### # --------------------------------------------------------------------------- # Required environment variables # --------------------------------------------------------------------------- REQUIRED_VARS=( FORGEBOT_REPO FORGEBOT_REF FORGEBOT_COMMAND FORGEBOT_API_URL FORGEBOT_TASK_ID ANTHROPIC_API_KEY ) for var in "${REQUIRED_VARS[@]}"; do if [[ -z "${!var:-}" ]]; then echo "FATAL: required env var $var is not set" >&2 exit 1 fi done # --------------------------------------------------------------------------- # Defaults # --------------------------------------------------------------------------- export ANTHROPIC_BASE_URL="${ANTHROPIC_BASE_URL:-https://litellm.k8s.syd1.au.unkin.net}" export GITEA_URL="${GITEA_URL:-https://git.unkin.net}" FORGEBOT_MODEL="${FORGEBOT_MODEL:-claude-sonnet-4-20250514}" # --------------------------------------------------------------------------- # Helper functions # --------------------------------------------------------------------------- # Report task status back to the forgebot API. # report_status report_status() { local status="$1" local message="$2" curl -sf -X PATCH \ -H "Content-Type: application/json" \ "${FORGEBOT_API_URL}/tasks/${FORGEBOT_TASK_ID}" \ -d "$(jq -n --arg s "$status" --arg m "$message" '{status: $s, message: $m}')" \ || echo "WARNING: failed to report status '$status'" >&2 } # Create a subtask. Reads JSON body from stdin or from the first argument. # echo '{"kind":"review",...}' | create_subtask # create_subtask '{"kind":"review",...}' create_subtask() { local body if [[ $# -gt 0 ]]; then body="$1" else body="$(cat)" fi curl -sf -X POST \ -H "Content-Type: application/json" \ "${FORGEBOT_API_URL}/tasks" \ -d "$body" } # Check the status of a task by ID. # check_task check_task() { local task_id="$1" curl -sf -X GET \ -H "Content-Type: application/json" \ "${FORGEBOT_API_URL}/tasks/${task_id}" } # Post a comment on the current task. # post_comment post_comment() { local message="$1" curl -sf -X POST \ -H "Content-Type: application/json" \ "${FORGEBOT_API_URL}/tasks/${FORGEBOT_TASK_ID}/comment" \ -d "$(jq -n --arg m "$message" '{message: $m}')" } # --------------------------------------------------------------------------- # Clone & checkout # --------------------------------------------------------------------------- REPO_DIR="/home/agent/workspace" echo "Cloning ${FORGEBOT_REPO} ..." git clone "${GITEA_URL}/${FORGEBOT_REPO}.git" "$REPO_DIR" cd "$REPO_DIR" echo "Checking out ref ${FORGEBOT_REF} ..." git checkout "${FORGEBOT_REF}" # --------------------------------------------------------------------------- # Extra tools (optional) # --------------------------------------------------------------------------- if [[ -n "${FORGEBOT_EXTRA_TOOLS:-}" ]]; then echo "Downloading extra tools: ${FORGEBOT_EXTRA_TOOLS}" mkdir -p /home/agent/bin export PATH="/home/agent/bin:${PATH}" IFS=',' read -ra TOOLS <<< "$FORGEBOT_EXTRA_TOOLS" for tool in "${TOOLS[@]}"; do tool="$(echo "$tool" | xargs)" # trim whitespace echo " -> fetching $tool" curl -sf -o "/home/agent/bin/${tool}" \ "${FORGEBOT_API_URL}/tools/${tool}" \ && chmod +x "/home/agent/bin/${tool}" \ || echo "WARNING: failed to download tool '${tool}'" >&2 done fi # --------------------------------------------------------------------------- # Load skill (optional configmap mount) # --------------------------------------------------------------------------- SKILL_CONTEXT="" if [[ -n "${FORGEBOT_SKILL:-}" && -f "/skills/${FORGEBOT_SKILL}/SKILL.md" ]]; then echo "Loading skill: ${FORGEBOT_SKILL}" SKILL_CONTEXT="$(cat "/skills/${FORGEBOT_SKILL}/SKILL.md")" fi # --------------------------------------------------------------------------- # Build the prompt # --------------------------------------------------------------------------- PROMPT="${FORGEBOT_COMMAND}" if [[ -n "$SKILL_CONTEXT" ]]; then PROMPT="$(printf '%s\n\n---\n\n%s' "$SKILL_CONTEXT" "$PROMPT")" fi # --------------------------------------------------------------------------- # Run claude # --------------------------------------------------------------------------- report_status "running" "Agent started, executing command" set +e RESULT="$(claude \ --model "$FORGEBOT_MODEL" \ --print \ --dangerously-skip-permissions \ <<< "$PROMPT" 2>&1)" EXIT_CODE=$? set -e # --------------------------------------------------------------------------- # Report result # --------------------------------------------------------------------------- if [[ $EXIT_CODE -eq 0 ]]; then echo "Agent completed successfully." report_status "completed" "$RESULT" else echo "Agent failed with exit code ${EXIT_CODE}." >&2 report_status "failed" "Exit code ${EXIT_CODE}: ${RESULT}" fi exit $EXIT_CODE