212 lines
6.1 KiB
Bash
Executable File
212 lines
6.1 KiB
Bash
Executable File
#!/bin/bash
|
|
# pr-metadata.sh - Get PR metadata as JSON on GitHub or Gitea
|
|
# Usage: pr-metadata.sh -n <pr_number> [-o <output_file>]
|
|
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
# shellcheck source=packages/mosaic/framework/tools/git/detect-platform.sh
|
|
source "$SCRIPT_DIR/detect-platform.sh"
|
|
|
|
# Parse arguments
|
|
PR_NUMBER=""
|
|
OUTPUT_FILE=""
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case $1 in
|
|
-n|--number)
|
|
PR_NUMBER="$2"
|
|
shift 2
|
|
;;
|
|
-o|--output)
|
|
OUTPUT_FILE="$2"
|
|
shift 2
|
|
;;
|
|
-h|--help)
|
|
echo "Usage: pr-metadata.sh -n <pr_number> [-o <output_file>]"
|
|
echo ""
|
|
echo "Options:"
|
|
echo " -n, --number PR number (required)"
|
|
echo " -o, --output Output file (optional, prints to stdout if omitted)"
|
|
echo " -h, --help Show this help"
|
|
exit 0
|
|
;;
|
|
*)
|
|
echo "Unknown option: $1" >&2
|
|
exit 1
|
|
;;
|
|
esac
|
|
done
|
|
|
|
if [[ -z "$PR_NUMBER" ]]; then
|
|
echo "Error: PR number is required (-n)" >&2
|
|
exit 1
|
|
fi
|
|
|
|
write_metadata() {
|
|
local metadata="$1"
|
|
if [[ -n "$OUTPUT_FILE" ]]; then
|
|
printf '%s\n' "$metadata" > "$OUTPUT_FILE"
|
|
else
|
|
printf '%s\n' "$metadata"
|
|
fi
|
|
}
|
|
|
|
curl_gitea_pull() {
|
|
local api_url="$1"
|
|
local token basic_auth raw_code body_file http_code
|
|
body_file=$(mktemp)
|
|
|
|
token=$(get_gitea_token "$HOST" || true)
|
|
if [[ -n "$token" ]]; then
|
|
raw_code=$(curl -sS -w '%{http_code}' -o "$body_file" -H "Authorization: token $token" "$api_url" || true)
|
|
if [[ "$raw_code" =~ ^2 ]]; then
|
|
cat "$body_file"
|
|
rm -f "$body_file"
|
|
return 0
|
|
fi
|
|
http_code="$raw_code"
|
|
fi
|
|
|
|
basic_auth=$(get_gitea_basic_auth "$HOST" || true)
|
|
if [[ -n "$basic_auth" ]]; then
|
|
raw_code=$(curl -sS -w '%{http_code}' -o "$body_file" -u "$basic_auth" "$api_url" || true)
|
|
if [[ "$raw_code" =~ ^2 ]]; then
|
|
cat "$body_file"
|
|
rm -f "$body_file"
|
|
return 0
|
|
fi
|
|
http_code="$raw_code"
|
|
fi
|
|
|
|
if [[ -z "${http_code:-}" ]]; then
|
|
raw_code=$(curl -sS -w '%{http_code}' -o "$body_file" "$api_url" || true)
|
|
http_code="$raw_code"
|
|
fi
|
|
|
|
python3 - "$http_code" "$body_file" <<'PY' >&2
|
|
import json
|
|
import sys
|
|
|
|
code, path = sys.argv[1], sys.argv[2]
|
|
try:
|
|
data = json.load(open(path, encoding="utf-8"))
|
|
message = data.get("message") or data.get("error") or "unknown API error"
|
|
except Exception:
|
|
message = open(path, encoding="utf-8", errors="replace").read()[:200] or "empty response"
|
|
print(f"Error: Gitea pull request API request failed with HTTP {code}: {message}")
|
|
PY
|
|
rm -f "$body_file"
|
|
return 1
|
|
}
|
|
|
|
detect_platform > /dev/null
|
|
|
|
if [[ "$PLATFORM" == "github" ]]; then
|
|
METADATA=$(gh pr view "$PR_NUMBER" --json number,title,body,state,author,headRefName,baseRefName,files,labels,assignees,milestone,createdAt,updatedAt,url,isDraft)
|
|
write_metadata "$METADATA"
|
|
elif [[ "$PLATFORM" == "gitea" ]]; then
|
|
OWNER=$(get_repo_owner)
|
|
REPO=$(get_repo_name)
|
|
HOST=$(get_remote_host) || {
|
|
echo "Error: Cannot determine host from origin remote URL" >&2
|
|
exit 1
|
|
}
|
|
|
|
API_URL="https://${HOST}/api/v1/repos/${OWNER}/${REPO}/pulls/${PR_NUMBER}"
|
|
if [[ -n "${MOSAIC_GITEA_PR_METADATA_RAW_FILE:-}" ]]; then
|
|
RAW=$(cat "$MOSAIC_GITEA_PR_METADATA_RAW_FILE")
|
|
else
|
|
RAW=$(curl_gitea_pull "$API_URL")
|
|
fi
|
|
|
|
# Normalize Gitea response to match GitHub's expected metadata schema.
|
|
METADATA=$(printf '%s' "$RAW" | python3 -c "
|
|
import json
|
|
import sys
|
|
|
|
def first_non_empty(*values):
|
|
for value in values:
|
|
if value is None:
|
|
continue
|
|
if isinstance(value, str):
|
|
value = value.strip()
|
|
if value:
|
|
return value
|
|
return ''
|
|
|
|
def nested(data, *keys):
|
|
current = data
|
|
for key in keys:
|
|
if not isinstance(current, dict):
|
|
return None
|
|
current = current.get(key)
|
|
return current
|
|
|
|
try:
|
|
data = json.load(sys.stdin)
|
|
except json.JSONDecodeError as exc:
|
|
print(f'Error: Gitea API returned non-JSON response: {exc}', file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
if not isinstance(data, dict):
|
|
print('Error: Gitea API returned an unexpected non-object response', file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
if data.get('message') and not data.get('number'):
|
|
print(f\"Error: Gitea API error: {data.get('message')}\", file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
head_ref = first_non_empty(
|
|
nested(data, 'head', 'ref'),
|
|
nested(data, 'head', 'name'),
|
|
nested(data, 'head', 'branch'),
|
|
data.get('head_branch'),
|
|
data.get('head_ref'),
|
|
data.get('head_label'),
|
|
)
|
|
base_ref = first_non_empty(
|
|
nested(data, 'base', 'ref'),
|
|
nested(data, 'base', 'name'),
|
|
nested(data, 'base', 'branch'),
|
|
data.get('base_branch'),
|
|
data.get('base_ref'),
|
|
data.get('base_label'),
|
|
)
|
|
|
|
if not head_ref or not base_ref:
|
|
available = ', '.join(sorted(data.keys()))
|
|
print(
|
|
'Error: Unable to resolve non-empty Gitea PR head/base refs '
|
|
f'(headRefName={head_ref!r}, baseRefName={base_ref!r}; keys={available})',
|
|
file=sys.stderr,
|
|
)
|
|
sys.exit(1)
|
|
|
|
normalized = {
|
|
'number': data.get('number'),
|
|
'title': data.get('title'),
|
|
'body': data.get('body', ''),
|
|
'state': data.get('state'),
|
|
'author': nested(data, 'user', 'login') or '',
|
|
'headRefName': head_ref,
|
|
'baseRefName': base_ref,
|
|
'labels': [l.get('name', '') for l in data.get('labels', []) if isinstance(l, dict)],
|
|
'assignees': [a.get('login', '') for a in data.get('assignees', []) if isinstance(a, dict)],
|
|
'milestone': nested(data, 'milestone', 'title') or '',
|
|
'createdAt': data.get('created_at', ''),
|
|
'updatedAt': data.get('updated_at', ''),
|
|
'url': data.get('html_url', ''),
|
|
'isDraft': data.get('draft', False),
|
|
'mergeable': data.get('mergeable'),
|
|
'diffUrl': data.get('diff_url', ''),
|
|
}
|
|
json.dump(normalized, sys.stdout, indent=2)
|
|
")
|
|
|
|
write_metadata "$METADATA"
|
|
else
|
|
echo "Error: Unknown platform" >&2
|
|
exit 1
|
|
fi
|