#!/bin/bash # pr-metadata.sh - Get PR metadata as JSON on GitHub or Gitea # Usage: pr-metadata.sh -n [-o ] 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 [-o ]" 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'), nested(data, 'head', 'label'), data.get('head_label'), ) if isinstance(head_ref, str) and head_ref.startswith('refs/pull/'): head_ref = first_non_empty( nested(data, 'head', 'label'), data.get('head_label'), nested(data, 'head', 'name'), nested(data, 'head', 'branch'), data.get('head_branch'), data.get('head_ref'), head_ref, ) 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