118 lines
4.6 KiB
Bash
Executable file
118 lines
4.6 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
# =============================================================================
|
|
# skills/ingest/scripts/open-pr.sh
|
|
# Branch, commit (conventional), push, and open a Forgejo PR for the wiki/ changes.
|
|
# Mirrors the API conventions of providers/forgejo.sh (token auth + http_code).
|
|
# Runs inside the genome checkout (cwd = genome root). Never touches main.
|
|
#
|
|
# open-pr.sh --slug <slug> --title "feat: ingest <slug>" --body-file <path> \
|
|
# [--base main] [--label CONFLICT]
|
|
#
|
|
# Requires env: FORGEJO_URL, FORGEJO_USER, FORGEJO_TOKEN.
|
|
# =============================================================================
|
|
set -euo pipefail
|
|
|
|
: "${FORGEJO_URL:?missing FORGEJO_URL}"
|
|
: "${FORGEJO_USER:?missing FORGEJO_USER}"
|
|
: "${FORGEJO_TOKEN:?missing FORGEJO_TOKEN}"
|
|
|
|
slug="" title="" body_file="" base="main" label=""
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--slug) slug="$2"; shift 2 ;;
|
|
--title) title="$2"; shift 2 ;;
|
|
--body-file) body_file="$2"; shift 2 ;;
|
|
--base) base="$2"; shift 2 ;;
|
|
--label) label="$2"; shift 2 ;;
|
|
*) echo "open-pr: unknown arg: $1" >&2; exit 1 ;;
|
|
esac
|
|
done
|
|
|
|
: "${slug:?--slug required}"
|
|
: "${title:?--title required}"
|
|
: "${body_file:?--body-file required}"
|
|
[[ -f "$body_file" ]] || { echo "open-pr: body file not found: $body_file" >&2; exit 1; }
|
|
|
|
branch="feat/ai-ingest-${slug}"
|
|
repo="$(basename -s .git "$(git config --get remote.origin.url)")"
|
|
|
|
# 1. Branch + commit + push (AGENTS.md rule 5: never commit to main)
|
|
git switch -c "$branch" 2>/dev/null || git switch "$branch"
|
|
git add wiki/
|
|
# Scope BOTH the emptiness check and the commit to wiki/ — never commit anything that
|
|
# happened to be staged outside wiki/ (a stray hook, an aborted prior run, etc.).
|
|
if git diff --cached --quiet -- wiki/; then
|
|
echo "open-pr: nothing staged under wiki/ — aborting" >&2
|
|
exit 1
|
|
fi
|
|
git commit -m "$title" -- wiki/
|
|
git push -u origin "$branch"
|
|
|
|
# DRY_RUN: local git work done; skip the Forgejo API (offline tests).
|
|
if [[ -n "${DRY_RUN:-}" ]]; then
|
|
echo "PR opened: DRY-RUN ${branch} -> ${base}"
|
|
exit 0
|
|
fi
|
|
|
|
# 2. Open the PR via Forgejo API (jq builds the JSON safely)
|
|
# TODO: Forgejo-only. When registry.sh/globals.env sets PROVIDER=github, branch on
|
|
# $PROVIDER here and delegate to providers/github.sh (same token + http_code contract).
|
|
body="$(cat "$body_file")"
|
|
payload="$(jq -n --arg head "$branch" --arg base "$base" \
|
|
--arg title "$title" --arg body "$body" \
|
|
'{head:$head, base:$base, title:$title, body:$body}')"
|
|
|
|
resp="$(curl --max-time 30 -s -w '\n%{http_code}' \
|
|
-H "Authorization: token ${FORGEJO_TOKEN}" \
|
|
-H "Content-Type: application/json" \
|
|
-X POST "${FORGEJO_URL}/api/v1/repos/${FORGEJO_USER}/${repo}/pulls" \
|
|
-d "$payload")"
|
|
|
|
# curl -w appends '\n<code>' AFTER the body, so the code is always the final line and the
|
|
# body is everything before it. Parameter expansion (no subshells), robust to multi-line JSON.
|
|
code="${resp##*$'\n'}"
|
|
json="${resp%$'\n'*}"
|
|
|
|
case "$code" in
|
|
201)
|
|
url="$(printf '%s' "$json" | jq -r '.html_url')"
|
|
number="$(printf '%s' "$json" | jq -r '.number')"
|
|
echo "PR opened: ${url}"
|
|
;;
|
|
409)
|
|
# PR already exists — fetch it so the orchestrator still gets the URL.
|
|
existing="$(curl --max-time 15 -s -H "Authorization: token ${FORGEJO_TOKEN}" \
|
|
"${FORGEJO_URL}/api/v1/repos/${FORGEJO_USER}/${repo}/pulls?state=open" \
|
|
| jq -r --arg b "$branch" '.[] | select(.head.ref==$b) | .html_url' | head -n1)"
|
|
if [[ -n "$existing" && "$existing" != "null" ]]; then
|
|
echo "PR opened: ${existing}"
|
|
else
|
|
echo "open-pr: a PR for '${branch}' already exists (push updated the branch)." >&2
|
|
fi
|
|
exit 0
|
|
;;
|
|
401)
|
|
echo "open-pr: unauthorized — check FORGEJO_TOKEN (n8n-bot)." >&2
|
|
exit 1
|
|
;;
|
|
*)
|
|
echo "open-pr: Forgejo API HTTP ${code}: ${json}" >&2
|
|
exit 1
|
|
;;
|
|
esac
|
|
|
|
# 3. Optional label (e.g. CONFLICT). Best-effort; non-fatal.
|
|
if [[ -n "$label" && -n "${number:-}" ]]; then
|
|
label_id="$(curl --max-time 15 -s -H "Authorization: token ${FORGEJO_TOKEN}" \
|
|
"${FORGEJO_URL}/api/v1/repos/${FORGEJO_USER}/${repo}/labels" \
|
|
| jq -r --arg n "$label" '.[] | select(.name==$n) | .id' | head -n1)"
|
|
if [[ -n "$label_id" && "$label_id" != "null" ]]; then
|
|
curl --max-time 15 -s -o /dev/null \
|
|
-H "Authorization: token ${FORGEJO_TOKEN}" -H "Content-Type: application/json" \
|
|
-X POST "${FORGEJO_URL}/api/v1/repos/${FORGEJO_USER}/${repo}/issues/${number}/labels" \
|
|
-d "{\"labels\":[${label_id}]}" \
|
|
&& echo "label '${label}' applied" >&2
|
|
else
|
|
echo "open-pr: label '${label}' not found in repo — skipped." >&2
|
|
fi
|
|
fi
|