Cut AGENTS.md from 1296 to ~210 lines: removed development timeline, changelog, stale AI agent section, verbose architecture examples, and duplicate content. Kept tech stack, project structure, key commands, config reference, API table, ADR index, and commit conventions. Cut README.md from 423 to ~80 lines: removed duplicate CI/CD sections (one had merge conflict markers), non-existent Cobra CLI and documentation/ references, and the AI agent usage section. Kept features, quick start, config table, API table, testing, and Gitea client setup. Also includes gitea-client.sh fixes from earlier session: create-pr sub-command and safe jq-based JSON body in comment-pr. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
578 lines
17 KiB
Bash
Executable File
578 lines
17 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# Gitea Client - Main script for interacting with Gitea API
|
|
# Usage: gitea-client.sh <command> [args...]
|
|
|
|
set -e
|
|
|
|
# Configuration
|
|
GITEA_API_BASE="https://gitea.arcodange.lab/api/v1"
|
|
|
|
# Get authentication token
|
|
get_auth_token() {
|
|
if [[ -n "${GITEA_API_TOKEN_FILE:-}" ]]; then
|
|
if [[ -f "$GITEA_API_TOKEN_FILE" ]]; then
|
|
cat "$GITEA_API_TOKEN_FILE"
|
|
return
|
|
else
|
|
echo "Error: Token file $GITEA_API_TOKEN_FILE not found" >&2
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
if [[ -n "${GITEA_API_TOKEN:-}" ]]; then
|
|
echo "$GITEA_API_TOKEN"
|
|
return
|
|
fi
|
|
|
|
echo "Error: No Gitea API token provided" >&2
|
|
echo "Set either GITEA_API_TOKEN or GITEA_API_TOKEN_FILE environment variable" >&2
|
|
exit 1
|
|
}
|
|
|
|
# Make API request
|
|
api_request() {
|
|
local method="$1"
|
|
local endpoint="$2"
|
|
local data="${3:-}"
|
|
|
|
local token=$(get_auth_token)
|
|
local url="${GITEA_API_BASE}${endpoint}"
|
|
|
|
local headers=(
|
|
"Authorization: token ${token}"
|
|
"Accept: application/json"
|
|
"Content-Type: application/json"
|
|
)
|
|
|
|
if [[ "$method" == "GET" ]]; then
|
|
curl -s -X GET "$url" -H "${headers[0]}" -H "${headers[1]}"
|
|
else
|
|
curl -s -X "$method" "$url" -H "${headers[0]}" -H "${headers[1]}" -H "${headers[2]}" -d "$data"
|
|
fi
|
|
}
|
|
|
|
# List workflows
|
|
cmd_list_workflows() {
|
|
local owner="$1"
|
|
local repo="$2"
|
|
|
|
if [[ -z "$owner" || -z "$repo" ]]; then
|
|
echo "Usage: $0 list-workflows <owner> <repo>" >&2
|
|
exit 1
|
|
fi
|
|
|
|
local endpoint="/repos/${owner}/${repo}/actions/workflows"
|
|
api_request "GET" "$endpoint"
|
|
}
|
|
|
|
# List jobs
|
|
cmd_list_jobs() {
|
|
local owner="$1"
|
|
local repo="$2"
|
|
local workflow_id="$3"
|
|
local limit="${4:-10}"
|
|
|
|
if [[ -z "$owner" || -z "$repo" || -z "$workflow_id" ]]; then
|
|
echo "Usage: $0 list-jobs <owner> <repo> <workflow_id> [limit]" >&2
|
|
exit 1
|
|
fi
|
|
|
|
local endpoint="/repos/${owner}/${repo}/actions/workflows/${workflow_id}/runs?limit=${limit}"
|
|
api_request "GET" "$endpoint"
|
|
}
|
|
|
|
# Get job status
|
|
cmd_job_status() {
|
|
local owner="$1"
|
|
local repo="$2"
|
|
local job_id="$3"
|
|
|
|
if [[ -z "$owner" || -z "$repo" || -z "$job_id" ]]; then
|
|
echo "Usage: $0 job-status <owner> <repo> <job_id>" >&2
|
|
exit 1
|
|
fi
|
|
|
|
local endpoint="/repos/${owner}/${repo}/actions/runs/${job_id}"
|
|
api_request "GET" "$endpoint"
|
|
}
|
|
|
|
# Get job logs
|
|
cmd_job_logs() {
|
|
local owner="$1"
|
|
local repo="$2"
|
|
local job_id="$3"
|
|
local output_file="${4:-}"
|
|
|
|
if [[ -z "$owner" || -z "$repo" || -z "$job_id" ]]; then
|
|
echo "Usage: $0 job-logs <owner> <repo> <job_id> [output_file]" >&2
|
|
exit 1
|
|
fi
|
|
|
|
local endpoint="/repos/${owner}/${repo}/actions/jobs/${job_id}/logs"
|
|
local logs=$(api_request "GET" "$endpoint")
|
|
|
|
if [[ -n "$output_file" ]]; then
|
|
echo "$logs" > "$output_file"
|
|
echo "Logs saved to: $output_file"
|
|
else
|
|
echo "$logs"
|
|
fi
|
|
}
|
|
|
|
# Get action job logs
|
|
cmd_action_logs() {
|
|
local owner="$1"
|
|
local repo="$2"
|
|
local action_job_id="$3"
|
|
local output_file="${4:-}"
|
|
|
|
if [[ -z "$owner" || -z "$repo" || -z "$action_job_id" ]]; then
|
|
echo "Usage: $0 action-logs <owner> <repo> <action_job_id> [output_file]" >&2
|
|
exit 1
|
|
fi
|
|
|
|
local endpoint="/repos/${owner}/${repo}/actions/jobs/${action_job_id}/logs"
|
|
local logs=$(api_request "GET" "$endpoint")
|
|
|
|
if [[ -n "$output_file" ]]; then
|
|
echo "$logs" > "$output_file"
|
|
echo "Logs saved to: $output_file"
|
|
else
|
|
echo "$logs"
|
|
fi
|
|
}
|
|
|
|
# List workflow jobs
|
|
cmd_list_workflow_jobs() {
|
|
local owner="$1"
|
|
local repo="$2"
|
|
local workflow_run_id="$3"
|
|
|
|
if [[ -z "$owner" || -z "$repo" || -z "$workflow_run_id" ]]; then
|
|
echo "Usage: $0 list-workflow-jobs <owner> <repo> <workflow_run_id>" >&2
|
|
exit 1
|
|
fi
|
|
|
|
local endpoint="/repos/${owner}/${repo}/actions/runs/${workflow_run_id}/jobs"
|
|
api_request "GET" "$endpoint"
|
|
}
|
|
|
|
# Wait for job completion
|
|
cmd_wait_job() {
|
|
local owner="$1"
|
|
local repo="$2"
|
|
local job_id="$3"
|
|
local timeout="${4:-300}"
|
|
|
|
if [[ -z "$owner" || -z "$repo" || -z "$job_id" ]]; then
|
|
echo "Usage: $0 wait-job <owner> <repo> <job_id> [timeout]" >&2
|
|
exit 1
|
|
fi
|
|
|
|
local start_time=$(date +%s)
|
|
local end_time=$((start_time + timeout))
|
|
|
|
echo "Waiting for job ${job_id} to complete (timeout: ${timeout}s)..."
|
|
|
|
while [[ $(date +%s) -lt $end_time ]]; do
|
|
local status=$(cmd_job_status "$owner" "$repo" "$job_id" | jq -r '.status')
|
|
|
|
case "$status" in
|
|
"completed")
|
|
echo "Job completed successfully"
|
|
return 0
|
|
;;
|
|
"failed")
|
|
echo "Job failed"
|
|
return 1
|
|
;;
|
|
"cancelled")
|
|
echo "Job was cancelled"
|
|
return 2
|
|
;;
|
|
*)
|
|
echo "Current status: ${status}"
|
|
sleep 5
|
|
;;
|
|
esac
|
|
done
|
|
|
|
echo "Timeout reached"
|
|
return 3
|
|
}
|
|
|
|
# Comment on PR
|
|
# Create a pull request
|
|
cmd_create_pr() {
|
|
local owner="$1"
|
|
local repo="$2"
|
|
local title="$3"
|
|
local body="$4"
|
|
local head="$5"
|
|
local base="${6:-main}"
|
|
|
|
if [[ -z "$owner" || -z "$repo" || -z "$title" || -z "$head" ]]; then
|
|
echo "Usage: $0 create-pr <owner> <repo> <title> <body> <head_branch> [base_branch]" >&2
|
|
exit 1
|
|
fi
|
|
|
|
local endpoint="/repos/${owner}/${repo}/pulls"
|
|
local data
|
|
data=$(jq -n \
|
|
--arg title "$title" \
|
|
--arg body "$body" \
|
|
--arg head "$head" \
|
|
--arg base "$base" \
|
|
'{title: $title, body: $body, head: $head, base: $base}')
|
|
api_request "POST" "$endpoint" "$data"
|
|
}
|
|
|
|
cmd_comment_pr() {
|
|
local owner="$1"
|
|
local repo="$2"
|
|
local pr_number="$3"
|
|
local comment="$4"
|
|
|
|
if [[ -z "$owner" || -z "$repo" || -z "$pr_number" || -z "$comment" ]]; then
|
|
echo "Usage: $0 comment-pr <owner> <repo> <pr_number> <comment>" >&2
|
|
exit 1
|
|
fi
|
|
|
|
local endpoint="/repos/${owner}/${repo}/issues/${pr_number}/comments"
|
|
local data
|
|
data=$(jq -n --arg body "$comment" '{body: $body}')
|
|
api_request "POST" "$endpoint" "$data"
|
|
}
|
|
|
|
# Get PR status
|
|
cmd_pr_status() {
|
|
local owner="$1"
|
|
local repo="$2"
|
|
local pr_number="$3"
|
|
|
|
if [[ -z "$owner" || -z "$repo" || -z "$pr_number" ]]; then
|
|
echo "Usage: $0 pr-status <owner> <repo> <pr_number>" >&2
|
|
exit 1
|
|
fi
|
|
|
|
local endpoint="/repos/${owner}/${repo}/pulls/${pr_number}"
|
|
api_request "GET" "$endpoint"
|
|
}
|
|
|
|
# Main command routing
|
|
main() {
|
|
local command="${1:-}"
|
|
shift || true
|
|
|
|
case "$command" in
|
|
list-workflows) cmd_list_workflows "$@" ;;
|
|
list-jobs) cmd_list_jobs "$@" ;;
|
|
job-status) cmd_job_status "$@" ;;
|
|
job-logs) cmd_job_logs "$@" ;;
|
|
action-logs) cmd_action_logs "$@" ;;
|
|
list-workflow-jobs) cmd_list_workflow_jobs "$@" ;;
|
|
wait-job) cmd_wait_job "$@" ;;
|
|
monitor-workflow) cmd_monitor_workflow "$@" ;;
|
|
diagnose-job) cmd_diagnose_job "$@" ;;
|
|
recent-workflows) cmd_recent_workflows "$@" ;;
|
|
create-pr) cmd_create_pr "$@" ;;
|
|
comment-pr) cmd_comment_pr "$@" ;;
|
|
pr-status) cmd_pr_status "$@" ;;
|
|
list-issues) cmd_list_issues "$@" ;;
|
|
create-issue) cmd_create_issue "$@" ;;
|
|
show-issue) cmd_show_issue "$@" ;;
|
|
comment-issue) cmd_comment_issue "$@" ;;
|
|
list-wiki) cmd_list_wiki "$@" ;;
|
|
create-wiki) cmd_create_wiki "$@" ;;
|
|
get-wiki) cmd_get_wiki "$@" ;;
|
|
trigger-workflow) cmd_trigger_workflow "$@" ;;
|
|
*)
|
|
echo "Usage: $0 <command> [args...]" >&2
|
|
echo "" >&2
|
|
echo "Commands:" >&2
|
|
echo " list-workflows <owner> <repo>" >&2
|
|
echo " list-jobs <owner> <repo> <workflow_id> [limit]" >&2
|
|
echo " job-status <owner> <repo> <job_id>" >&2
|
|
echo " job-logs <owner> <repo> <job_id> [output_file]" >&2
|
|
echo " action-logs <owner> <repo> <action_job_id> [output_file]" >&2
|
|
echo " list-workflow-jobs <owner> <repo> <workflow_run_id>" >&2
|
|
echo " wait-job <owner> <repo> <job_id> [timeout]" >&2
|
|
echo " monitor-workflow <owner> <repo> <workflow_run_id> [interval_seconds]" >&2
|
|
echo " diagnose-job <owner> <repo> <job_id>" >&2
|
|
echo " recent-workflows <owner> <repo> [limit] [status_filter]" >&2
|
|
echo " create-pr <owner> <repo> <title> <body> <head_branch> [base_branch]" >&2
|
|
echo " comment-pr <owner> <repo> <pr_number> <comment>" >&2
|
|
echo " pr-status <owner> <repo> <pr_number>" >&2
|
|
echo " list-issues <owner> <repo> [state]" >&2
|
|
echo " create-issue <owner> <repo> <title> <description>" >&2
|
|
echo " show-issue <owner> <repo> <issue_number>" >&2
|
|
echo " comment-issue <owner> <repo> <issue_number> <comment>" >&2
|
|
echo " list-wiki <owner> <repo>" >&2
|
|
echo " create-wiki <owner> <repo> <title> <content> [message]" >&2
|
|
echo " get-wiki <owner> <repo> <page_name>" >&2
|
|
echo " trigger-workflow <owner> <repo> <workflow_file> <branch>" >&2
|
|
exit 1
|
|
;;
|
|
esac
|
|
}
|
|
|
|
# List issues
|
|
cmd_list_issues() {
|
|
local owner="$1"
|
|
local repo="$2"
|
|
local state="${3:-open}"
|
|
|
|
if [[ -z "$owner" || -z "$repo" ]]; then
|
|
echo "Usage: $0 list-issues <owner> <repo> [state]" >&2
|
|
exit 1
|
|
fi
|
|
|
|
local endpoint="/repos/$owner/$repo/issues?state=$state"
|
|
api_request "GET" "$endpoint"
|
|
}
|
|
|
|
# Create a new issue
|
|
cmd_create_issue() {
|
|
local owner="$1"
|
|
local repo="$2"
|
|
local title="$3"
|
|
local description="$4"
|
|
|
|
if [[ -z "$owner" || -z "$repo" || -z "$title" || -z "$description" ]]; then
|
|
echo "Usage: $0 create-issue <owner> <repo> <title> <description>" >&2
|
|
exit 1
|
|
fi
|
|
|
|
local endpoint="/repos/$owner/$repo/issues"
|
|
local data=$(jq -n --arg title "$title" --arg body "$description" '{
|
|
title: $title,
|
|
body: $body
|
|
}')
|
|
|
|
api_request "POST" "$endpoint" "$data"
|
|
}
|
|
|
|
# Show issue details
|
|
cmd_show_issue() {
|
|
local owner="$1"
|
|
local repo="$2"
|
|
local issue_number="$3"
|
|
|
|
if [[ -z "$owner" || -z "$repo" || -z "$issue_number" ]]; then
|
|
echo "Usage: $0 show-issue <owner> <repo> <issue_number>" >&2
|
|
exit 1
|
|
fi
|
|
|
|
local endpoint="/repos/$owner/$repo/issues/$issue_number"
|
|
api_request "GET" "$endpoint"
|
|
}
|
|
|
|
# Comment on an issue
|
|
cmd_comment_issue() {
|
|
local owner="$1"
|
|
local repo="$2"
|
|
local issue_number="$3"
|
|
local comment="$4"
|
|
|
|
if [[ -z "$owner" || -z "$repo" || -z "$issue_number" || -z "$comment" ]]; then
|
|
echo "Usage: $0 comment-issue <owner> <repo> <issue_number> <comment>" >&2
|
|
exit 1
|
|
fi
|
|
|
|
local endpoint="/repos/$owner/$repo/issues/$issue_number/comments"
|
|
local data=$(jq -n --arg body "$comment" '{
|
|
body: $body
|
|
}')
|
|
|
|
api_request "POST" "$endpoint" "$data"
|
|
}
|
|
|
|
# List wiki pages
|
|
cmd_list_wiki() {
|
|
local owner="$1"
|
|
local repo="$2"
|
|
|
|
if [[ -z "$owner" || -z "$repo" ]]; then
|
|
echo "Usage: $0 list-wiki <owner> <repo>" >&2
|
|
exit 1
|
|
fi
|
|
|
|
local endpoint="/repos/$owner/$repo/wiki/pages"
|
|
api_request "GET" "$endpoint"
|
|
}
|
|
|
|
# Create wiki page
|
|
cmd_create_wiki() {
|
|
local owner="$1"
|
|
local repo="$2"
|
|
local title="$3"
|
|
local content="$4"
|
|
local message="${5:-Initial creation}"
|
|
|
|
if [[ -z "$owner" || -z "$repo" || -z "$title" || -z "$content" ]]; then
|
|
echo "Usage: $0 create-wiki <owner> <repo> <title> <content> [message]" >&2
|
|
exit 1
|
|
fi
|
|
|
|
local content_b64=$(echo "$content" | base64)
|
|
local endpoint="/repos/$owner/$repo/wiki/new"
|
|
local data=$(jq -n --arg title "$title" --arg content "$content_b64" --arg msg "$message" '{
|
|
title: $title,
|
|
content_base64: $content,
|
|
message: $msg
|
|
}')
|
|
|
|
api_request "POST" "$endpoint" "$data"
|
|
}
|
|
|
|
# Get wiki page
|
|
cmd_get_wiki() {
|
|
local owner="$1"
|
|
local repo="$2"
|
|
local page_name="$3"
|
|
|
|
if [[ -z "$owner" || -z "$repo" || -z "$page_name" ]]; then
|
|
echo "Usage: $0 get-wiki <owner> <repo> <page_name>" >&2
|
|
exit 1
|
|
fi
|
|
|
|
local endpoint="/repos/$owner/$repo/wiki/page/$page_name"
|
|
local response=$(api_request "GET" "$endpoint")
|
|
|
|
# Extract and decode the content_base64 field
|
|
local content_b64=$(echo "$response" | jq -r '.content_base64')
|
|
if [[ "$content_b64" != "null" && -n "$content_b64" ]]; then
|
|
echo "$content_b64" | base64 --decode
|
|
else
|
|
echo "$response"
|
|
fi
|
|
}
|
|
|
|
# Trigger workflow
|
|
cmd_trigger_workflow() {
|
|
local owner="$1"
|
|
local repo="$2"
|
|
local workflow_file="$3"
|
|
local branch="$4"
|
|
|
|
if [[ -z "$owner" || -z "$repo" || -z "$workflow_file" || -z "$branch" ]]; then
|
|
echo "Usage: $0 trigger-workflow <owner> <repo> <workflow_file> <branch>" >&2
|
|
exit 1
|
|
fi
|
|
|
|
local endpoint="/repos/${owner}/${repo}/actions/workflows/${workflow_file}/dispatches"
|
|
local data="{\"ref\": \"${branch}\"}"
|
|
|
|
echo "Triggering workflow: ${workflow_file} on branch: ${branch}"
|
|
api_request "POST" "$endpoint" "$data"
|
|
echo "Workflow triggered successfully!"
|
|
}
|
|
|
|
# Monitor workflow run until completion
|
|
cmd_monitor_workflow() {
|
|
local owner="$1"
|
|
local repo="$2"
|
|
local workflow_run_id="$3"
|
|
local interval="${4:-30}"
|
|
|
|
if [[ -z "$owner" || -z "$repo" || -z "$workflow_run_id" ]]; then
|
|
echo "Usage: $0 monitor-workflow <owner> <repo> <workflow_run_id> [interval_seconds]" >&2
|
|
exit 1
|
|
fi
|
|
|
|
echo "Monitoring workflow run $workflow_run_id (interval: ${interval}s)..."
|
|
echo "Press Ctrl+C to stop monitoring"
|
|
|
|
while true; do
|
|
local endpoint="/repos/${owner}/${repo}/actions/runs/${workflow_run_id}"
|
|
local status=$(api_request "GET" "$endpoint" | jq -r '.status')
|
|
local conclusion=$(api_request "GET" "$endpoint" | jq -r '.conclusion')
|
|
local updated_at=$(api_request "GET" "$endpoint" | jq -r '.updated_at')
|
|
|
|
echo "[$(date +'%Y-%m-%d %H:%M:%S')] Status: $status, Conclusion: ${conclusion:-not completed}, Updated: $updated_at"
|
|
|
|
# List jobs in this workflow
|
|
local jobs_endpoint="/repos/${owner}/${repo}/actions/runs/${workflow_run_id}/jobs"
|
|
local jobs=$(api_request "GET" "$jobs_endpoint")
|
|
echo "Jobs:"
|
|
echo "$jobs" | jq -r '.jobs[] | " \(.id): \(.name) - \(.status) \(if .conclusion then "(\(.conclusion))" else "" end)"'
|
|
|
|
# Check if workflow is completed
|
|
if [[ "$status" != "queued" && "$status" != "in_progress" && "$status" != "waiting" ]]; then
|
|
echo "Workflow run $workflow_run_id has completed with status: $status and conclusion: ${conclusion:-none}"
|
|
break
|
|
fi
|
|
|
|
sleep "$interval"
|
|
done
|
|
}
|
|
|
|
# Diagnose failed job
|
|
cmd_diagnose_job() {
|
|
local owner="$1"
|
|
local repo="$2"
|
|
local job_id="$3"
|
|
|
|
if [[ -z "$owner" || -z "$repo" || -z "$job_id" ]]; then
|
|
echo "Usage: $0 diagnose-job <owner> <repo> <job_id>" >&2
|
|
exit 1
|
|
fi
|
|
|
|
echo "Diagnosing job $job_id..."
|
|
|
|
# Get job details
|
|
local job_endpoint="/repos/${owner}/${repo}/actions/jobs/${job_id}"
|
|
local job_details=$(api_request "GET" "$job_endpoint")
|
|
|
|
echo "Job Details:"
|
|
echo "$job_details" | jq '. | {id, name, status, conclusion, started_at, completed_at, runner_name}'
|
|
|
|
# Get job logs
|
|
local logs_endpoint="/repos/${owner}/${repo}/actions/jobs/${job_id}/logs"
|
|
echo -e "\nLast 50 lines of logs:"
|
|
api_request "GET" "$logs_endpoint" | tail -50
|
|
|
|
# Look for errors
|
|
echo -e "\nError analysis:"
|
|
api_request "GET" "$logs_endpoint" | grep -i "error\|fail\|panic\|exception" | tail -10
|
|
|
|
# Get workflow run details
|
|
local run_id=$(echo "$job_details" | jq -r '.run_id')
|
|
local run_endpoint="/repos/${owner}/${repo}/actions/runs/${run_id}"
|
|
local run_details=$(api_request "GET" "$run_endpoint")
|
|
|
|
echo -e "\nWorkflow Run Details:"
|
|
echo "$run_details" | jq '. | {id, display_title, status, conclusion, head_branch, head_sha}'
|
|
}
|
|
|
|
# Get recent workflow runs summary
|
|
cmd_recent_workflows() {
|
|
local owner="$1"
|
|
local repo="$2"
|
|
local limit="${3:-10}"
|
|
local status_filter="${4:-}"
|
|
|
|
if [[ -z "$owner" || -z "$repo" ]]; then
|
|
echo "Usage: $0 recent-workflows <owner> <repo> [limit] [status_filter]" >&2
|
|
echo "Status filter options: all, completed, in_progress, queued, waiting" >&2
|
|
exit 1
|
|
fi
|
|
|
|
local endpoint="/repos/${owner}/${repo}/actions/runs?limit=${limit}"
|
|
if [[ -n "$status_filter" ]]; then
|
|
endpoint="$endpoint&status=$status_filter"
|
|
fi
|
|
|
|
local workflows=$(api_request "GET" "$endpoint")
|
|
|
|
echo "Recent Workflow Runs (showing $limit most recent):"
|
|
echo "$workflows" | jq -r '.workflow_runs[] | "\(.id): \(.display_title) - \(.status) \(if .conclusion then "(\(.conclusion))" else "" end) - \(.updated_at)"'
|
|
|
|
# Show summary statistics
|
|
echo -e "\nSummary:"
|
|
echo "$workflows" | jq -r '.workflow_runs | group_by(.conclusion) | .[] | " \(.[0].conclusion // "in_progress"): \(length)"'
|
|
}
|
|
|
|
main "$@"
|