Files
timmy-config/bin/gitea-api.sh
Alexander Whitestone f29d579896 feat(ops): start-loops, gitea-api wrapper, fleet-status
Closes #126: bin/start-loops.sh -- health check + kill stale + launch all loops
Closes #129: bin/gitea-api.sh -- Python urllib wrapper bypassing security scanner
Closes #130: bin/fleet-status.sh -- one-liner health per wizard with color output

All syntax-checked with bash -n.
2026-04-04 12:05:04 -04:00

184 lines
4.8 KiB
Bash
Executable File

#!/usr/bin/env bash
# gitea-api.sh - Gitea API wrapper using Python urllib (bypasses security scanner raw IP blocking)
# Usage:
# gitea-api.sh issue create REPO TITLE BODY
# gitea-api.sh issue comment REPO NUM BODY
# gitea-api.sh issue close REPO NUM
# gitea-api.sh issue list REPO
#
# Token read from ~/.hermes/gitea_token_vps
# Server: http://143.198.27.163:3000
set -euo pipefail
GITEA_SERVER="http://143.198.27.163:3000"
GITEA_OWNER="Timmy_Foundation"
TOKEN_FILE="$HOME/.hermes/gitea_token_vps"
if [ ! -f "$TOKEN_FILE" ]; then
echo "ERROR: Token file not found: $TOKEN_FILE" >&2
exit 1
fi
TOKEN="$(cat "$TOKEN_FILE" | tr -d '[:space:]')"
if [ -z "$TOKEN" ]; then
echo "ERROR: Token file is empty: $TOKEN_FILE" >&2
exit 1
fi
usage() {
echo "Usage:" >&2
echo " $0 issue create REPO TITLE BODY" >&2
echo " $0 issue comment REPO NUM BODY" >&2
echo " $0 issue close REPO NUM" >&2
echo " $0 issue list REPO" >&2
exit 1
}
# Python helper that does the actual HTTP request via urllib
# Args: METHOD URL [JSON_BODY]
gitea_request() {
local method="$1"
local url="$2"
local body="${3:-}"
python3 -c "
import urllib.request
import urllib.error
import json
import sys
method = sys.argv[1]
url = sys.argv[2]
body = sys.argv[3] if len(sys.argv) > 3 else None
token = sys.argv[4]
data = body.encode('utf-8') if body else None
req = urllib.request.Request(url, data=data, method=method)
req.add_header('Authorization', 'token ' + token)
req.add_header('Content-Type', 'application/json')
req.add_header('Accept', 'application/json')
try:
with urllib.request.urlopen(req) as resp:
result = resp.read().decode('utf-8')
if result.strip():
print(result)
except urllib.error.HTTPError as e:
err_body = e.read().decode('utf-8', errors='replace')
print(f'HTTP {e.code}: {e.reason}', file=sys.stderr)
print(err_body, file=sys.stderr)
sys.exit(1)
except urllib.error.URLError as e:
print(f'URL Error: {e.reason}', file=sys.stderr)
sys.exit(1)
" "$method" "$url" "$body" "$TOKEN"
}
# Pretty-print issue list output
format_issue_list() {
python3 -c "
import json, sys
data = json.load(sys.stdin)
if not data:
print('No issues found.')
sys.exit(0)
for issue in data:
num = issue.get('number', '?')
state = issue.get('state', '?')
title = issue.get('title', '(no title)')
labels = ', '.join(l.get('name','') for l in issue.get('labels', []))
label_str = f' [{labels}]' if labels else ''
print(f'#{num} ({state}){label_str} {title}')
"
}
# Format single issue creation/comment response
format_issue() {
python3 -c "
import json, sys
data = json.load(sys.stdin)
num = data.get('number', data.get('id', '?'))
url = data.get('html_url', '')
title = data.get('title', '')
if title:
print(f'Issue #{num}: {title}')
if url:
print(f'URL: {url}')
"
}
if [ $# -lt 2 ]; then
usage
fi
COMMAND="$1"
SUBCOMMAND="$2"
case "$COMMAND" in
issue)
case "$SUBCOMMAND" in
create)
if [ $# -lt 5 ]; then
echo "ERROR: 'issue create' requires REPO TITLE BODY" >&2
usage
fi
REPO="$3"
TITLE="$4"
BODY="$5"
JSON_BODY=$(python3 -c "
import json, sys
print(json.dumps({'title': sys.argv[1], 'body': sys.argv[2]}))
" "$TITLE" "$BODY")
RESULT=$(gitea_request "POST" "${GITEA_SERVER}/api/v1/repos/${GITEA_OWNER}/${REPO}/issues" "$JSON_BODY")
echo "$RESULT" | format_issue
;;
comment)
if [ $# -lt 5 ]; then
echo "ERROR: 'issue comment' requires REPO NUM BODY" >&2
usage
fi
REPO="$3"
ISSUE_NUM="$4"
BODY="$5"
JSON_BODY=$(python3 -c "
import json, sys
print(json.dumps({'body': sys.argv[1]}))
" "$BODY")
RESULT=$(gitea_request "POST" "${GITEA_SERVER}/api/v1/repos/${GITEA_OWNER}/${REPO}/issues/${ISSUE_NUM}/comments" "$JSON_BODY")
echo "Comment added to issue #${ISSUE_NUM}"
;;
close)
if [ $# -lt 4 ]; then
echo "ERROR: 'issue close' requires REPO NUM" >&2
usage
fi
REPO="$3"
ISSUE_NUM="$4"
JSON_BODY='{"state":"closed"}'
RESULT=$(gitea_request "PATCH" "${GITEA_SERVER}/api/v1/repos/${GITEA_OWNER}/${REPO}/issues/${ISSUE_NUM}" "$JSON_BODY")
echo "Issue #${ISSUE_NUM} closed."
;;
list)
if [ $# -lt 3 ]; then
echo "ERROR: 'issue list' requires REPO" >&2
usage
fi
REPO="$3"
STATE="${4:-open}"
RESULT=$(gitea_request "GET" "${GITEA_SERVER}/api/v1/repos/${GITEA_OWNER}/${REPO}/issues?state=${STATE}&type=issues&limit=50" "")
echo "$RESULT" | format_issue_list
;;
*)
echo "ERROR: Unknown issue subcommand: $SUBCOMMAND" >&2
usage
;;
esac
;;
*)
echo "ERROR: Unknown command: $COMMAND" >&2
usage
;;
esac