Files
Jyotisha/tools/agent-loop/gitea.sh
T

88 lines
4.8 KiB
Bash
Executable File

#!/usr/bin/env bash
# Minimal Gitea issue API wrapper for the agent loop (Claude side, Linux).
# Token is read from a 0600 file and never echoed. Usage: gitea.sh <command> [args]
set -euo pipefail
set +x
GITEA_URL="${GITEA_URL:-https://git.copse.top}"
GITEA_REPO="${GITEA_REPO:-root/Jyotisha}"
TOKEN_FILE="${JYOTISHA_AGENT_TOKEN_FILE:-$HOME/.config/jyotisha-agent/gitea-token}"
API="$GITEA_URL/api/v1/repos/$GITEA_REPO"
# Accounts whose issue text and comments are trusted: the product owner and the two agents.
# Everyone else's text is dropped before it reaches a model.
TRUSTED="${GITEA_TRUSTED_USERS:-jesse,Claude,Grok}"
_trusted_jq='($t | split(",")) as $ok | ($ok | index($u)) != null'
_token() {
[[ -r "$TOKEN_FILE" ]] || { echo "gitea.sh: token file missing: $TOKEN_FILE" >&2; exit 2; }
tr -d '[:space:]' < "$TOKEN_FILE"
}
# _call METHOD PATH [JSON_BODY] — prints the response body; fails on HTTP >= 400
# without echoing request headers.
_call() {
local method="$1" path="$2" body="${3:-}" out code
out="$(mktemp)"
if [[ -n "$body" ]]; then
code="$(curl -sS -o "$out" -w '%{http_code}' -X "$method" \
-H "Authorization: token $(_token)" -H 'Content-Type: application/json' \
--data-binary "$body" "$API$path")"
else
code="$(curl -sS -o "$out" -w '%{http_code}' -X "$method" \
-H "Authorization: token $(_token)" "$API$path")"
fi
if [[ "$code" -ge 400 ]]; then
echo "gitea.sh: $method $path -> HTTP $code: $(head -c 300 "$out")" >&2
rm -f "$out"
return 1
fi
cat "$out"
rm -f "$out"
}
cmd="${1:-}"; shift || true
case "$cmd" in
whoami)
curl -sS -H "Authorization: token $(_token)" "$GITEA_URL/api/v1/user" | jq -r .login ;;
label-id) # label-id NAME
_call GET "/labels?limit=100" | jq -r --arg n "$1" '.[] | select(.name == $n) | .id' ;;
ensure-label) # ensure-label NAME COLOR DESCRIPTION
if [[ -z "$("$0" label-id "$1")" ]]; then
_call POST "/labels" "$(jq -nc --arg n "$1" --arg c "$2" --arg d "$3" '{name:$n,color:$c,description:$d}')" | jq -r '"created \(.name)"'
else
echo "exists $1"
fi ;;
issues) # issues LABEL — open issues carrying LABEL, oldest first, as JSON array
_call GET "/issues?state=open&type=issues&limit=50&labels=$(jq -rn --arg l "$1" '$l|@uri')" \
| jq -c 'sort_by(.created_at)' ;;
issue) # issue N — body blanked unless written by a trusted account
_call GET "/issues/$1" | jq --arg t "$TRUSTED" '. as $i | .user.login as $u | if ('"$_trusted_jq"') then $i else ($i | .body = "(非可信账号创建的 issue,正文已忽略)") end' ;;
labels) # labels N — label names on issue N, one per line
_call GET "/issues/$1/labels" | jq -r '.[].name' ;;
add-label) # add-label N NAME
_call POST "/issues/$1/labels" "$(jq -nc --arg n "$2" '{labels:[$n]}')" >/dev/null ;;
remove-label) # remove-label N NAME (no-op when absent)
id="$("$0" label-id "$2")"
[[ -n "$id" ]] && _call DELETE "/issues/$1/labels/$id" >/dev/null || true ;;
comments) # comments N — JSON array, trusted accounts only
_call GET "/issues/$1/comments" | jq --arg t "$TRUSTED" '[.[] | .user.login as $u | select('"$_trusted_jq"')]' ;;
comment) # comment N FILE — post FILE's content as a comment
_call POST "/issues/$1/comments" "$(jq -Rsc '{body:.}' < "$2")" >/dev/null ;;
create-issue) # create-issue TITLE FILE — prints the new issue number
_call POST "/issues" "$(jq -Rsc --arg t "$1" '{title:$t, body:.}' < "$2")" | jq -r .number ;;
close) # close N
_call PATCH "/issues/$1" '{"state":"closed"}' >/dev/null ;;
pr) # pr N — number, state, head ref/sha, base, mergeable, merged, merge commit
_call GET "/pulls/$1" | jq '{number, state, title, head: .head.ref, head_sha: .head.sha, base: .base.ref, mergeable, merged, merge_commit_sha, author: .user.login}' ;;
pr-for-branch) # pr-for-branch BRANCH — open PR number whose head is BRANCH (empty if none)
_call GET "/pulls?state=open&limit=50" | jq -r --arg b "$1" '[.[] | select(.head.ref == $b)][0].number // empty' ;;
merge-pr) # merge-pr N HEAD_SHA — merge commit; Gitea refuses if the head moved past HEAD_SHA
_call POST "/pulls/$1/merge" "$(jq -nc --arg s "$2" '{Do:"merge", head_commit_id:$s}')" >/dev/null
"$0" pr "$1" | jq -r '"merged=\(.merged) merge_commit=\(.merge_commit_sha)"' ;;
runs) # runs SHA — Actions runs for a commit (workflow path, status, conclusion)
_call GET "/actions/runs?head_sha=$1&limit=20" | jq -c '[.workflow_runs[]? | {id, run_number, path, status, conclusion}]' ;;
*)
echo "usage: gitea.sh whoami|label-id|ensure-label|issues|issue|labels|add-label|remove-label|comments|comment|create-issue|close|pr|pr-for-branch|merge-pr|runs (repo: GITEA_REPO, default root/Jyotisha)" >&2
exit 64 ;;
esac