This commit is contained in:
@@ -0,0 +1 @@
|
|||||||
|
bin/
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
when:
|
||||||
|
- event: pull_request
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: docker-build
|
||||||
|
image: woodpeckerci/plugin-docker-buildx
|
||||||
|
settings:
|
||||||
|
repo: git.unkin.net/unkin/agent-base
|
||||||
|
dry_run: true
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
when:
|
||||||
|
- event: tag
|
||||||
|
ref: refs/tags/v*
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: docker
|
||||||
|
image: woodpeckerci/plugin-docker-buildx
|
||||||
|
settings:
|
||||||
|
registry: git.unkin.net
|
||||||
|
repo: git.unkin.net/unkin/agent-base
|
||||||
|
username: droneci
|
||||||
|
password:
|
||||||
|
from_secret: DRONECI_PASSWORD
|
||||||
|
tags:
|
||||||
|
- ${CI_COMMIT_TAG}
|
||||||
|
- latest
|
||||||
+19
@@ -0,0 +1,19 @@
|
|||||||
|
FROM git.unkin.net/unkin/almalinux9-base:latest
|
||||||
|
|
||||||
|
RUN dnf install -y \
|
||||||
|
git \
|
||||||
|
git-lfs \
|
||||||
|
jq \
|
||||||
|
curl \
|
||||||
|
claude-code \
|
||||||
|
&& dnf clean all
|
||||||
|
|
||||||
|
RUN useradd -m -s /bin/bash agent
|
||||||
|
|
||||||
|
COPY entrypoint.sh /entrypoint.sh
|
||||||
|
RUN chmod +x /entrypoint.sh
|
||||||
|
|
||||||
|
USER agent
|
||||||
|
WORKDIR /home/agent
|
||||||
|
|
||||||
|
ENTRYPOINT ["/entrypoint.sh"]
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
.PHONY: build clean patch minor major
|
||||||
|
|
||||||
|
VERSION ?= $(shell git describe --tags --always --dirty 2>/dev/null || echo "0.0.0-dev")
|
||||||
|
|
||||||
|
build:
|
||||||
|
docker build -t git.unkin.net/unkin/agent-base:$(VERSION) .
|
||||||
|
|
||||||
|
clean:
|
||||||
|
docker rmi git.unkin.net/unkin/agent-base:$(VERSION) 2>/dev/null || true
|
||||||
|
|
||||||
|
_LATEST := $(shell git tag --sort=-v:refname | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$$' | head -1)
|
||||||
|
_BASE := $(if $(_LATEST),$(_LATEST),v0.0.0)
|
||||||
|
_MAJ := $(shell echo $(_BASE) | sed 's/^v//' | cut -d. -f1)
|
||||||
|
_MIN := $(shell echo $(_BASE) | sed 's/^v//' | cut -d. -f2)
|
||||||
|
_PAT := $(shell echo $(_BASE) | sed 's/^v//' | cut -d. -f3)
|
||||||
|
|
||||||
|
patch:
|
||||||
|
@NEW=v$(_MAJ).$(_MIN).$(shell expr $(_PAT) + 1); \
|
||||||
|
git tag $$NEW && echo "Tagged $$NEW" && git push origin $$NEW
|
||||||
|
|
||||||
|
minor:
|
||||||
|
@NEW=v$(_MAJ).$(shell expr $(_MIN) + 1).0; \
|
||||||
|
git tag $$NEW && echo "Tagged $$NEW" && git push origin $$NEW
|
||||||
|
|
||||||
|
major:
|
||||||
|
@NEW=v$(shell expr $(_MAJ) + 1).0.0; \
|
||||||
|
git tag $$NEW && echo "Tagged $$NEW" && git push origin $$NEW
|
||||||
+159
@@ -0,0 +1,159 @@
|
|||||||
|
#!/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 <status> <message>
|
||||||
|
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 <task_id>
|
||||||
|
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 <message>
|
||||||
|
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
|
||||||
Reference in New Issue
Block a user