#!/bin/bash # pr-create.sh - Create pull requests on Gitea or GitHub # Usage: pr-create.sh -t "Title" [-b "Body"] [-B base] [-H head] [-l "labels"] [-m "milestone"] set -e SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" source "$SCRIPT_DIR/detect-platform.sh" # Default values TITLE="" BODY="" BASE_BRANCH="" HEAD_BRANCH="" LABELS="" MILESTONE="" DRAFT=false ISSUE="" # get_remote_host, get_gitea_token, get_repo_info, and get_gitea_repo_args are provided by detect-platform.sh gitea_pr_create_api() { local host repo token url payload host=$(get_remote_host) || { echo "Error: could not determine remote host for API fallback" >&2 return 1 } repo=$(get_repo_info) || { echo "Error: could not determine repo owner/name for API fallback" >&2 return 1 } token=$(get_gitea_token "$host") || { echo "Error: Gitea token not found for API fallback (set GITEA_TOKEN or configure ~/.git-credentials)" >&2 return 1 } if [[ -n "$LABELS" || -n "$MILESTONE" || "$DRAFT" == true ]]; then echo "Warning: API fallback applies title/body/head/base only; labels/milestone/draft require authenticated tea setup." >&2 fi payload=$(TITLE="$TITLE" BODY="$BODY" HEAD_BRANCH="$HEAD_BRANCH" BASE_BRANCH="$BASE_BRANCH" python3 - <<'PY' import json import os payload = { "title": os.environ["TITLE"], "head": os.environ["HEAD_BRANCH"], "base": os.environ["BASE_BRANCH"] or "main", } body = os.environ.get("BODY", "") if body: payload["body"] = body print(json.dumps(payload)) PY ) url="https://${host}/api/v1/repos/${repo}/pulls" curl -fsS -X POST \ -H "Authorization: token ${token}" \ -H "Content-Type: application/json" \ -d "$payload" \ "$url" } usage() { cat <&2 usage ;; esac done # If no title but issue provided, generate title if [[ -z "$TITLE" ]] && [[ -n "$ISSUE" ]]; then TITLE="Fixes #$ISSUE" fi if [[ -z "$TITLE" ]]; then echo "Error: Title is required (-t) or provide an issue (-i)" >&2 usage fi # Default head branch to current branch if [[ -z "$HEAD_BRANCH" ]]; then HEAD_BRANCH=$(git branch --show-current) fi # Add issue reference to body if provided if [[ -n "$ISSUE" ]]; then if [[ -n "$BODY" ]]; then BODY="$BODY Fixes #$ISSUE" else BODY="Fixes #$ISSUE" fi fi PLATFORM=$(detect_platform) case "$PLATFORM" in github) CMD=(gh pr create --title "$TITLE") [[ -n "$BODY" ]] && CMD+=(--body "$BODY") [[ -n "$BASE_BRANCH" ]] && CMD+=(--base "$BASE_BRANCH") [[ -n "$HEAD_BRANCH" ]] && CMD+=(--head "$HEAD_BRANCH") [[ -n "$LABELS" ]] && CMD+=(--label "$LABELS") [[ -n "$MILESTONE" ]] && CMD+=(--milestone "$MILESTONE") [[ "$DRAFT" == true ]] && CMD+=(--draft) "${CMD[@]}" ;; gitea) # tea pull create syntax. Always pass --repo because tea repo inference # is unreliable in Mosaic worktrees/profile shells. Use arrays instead # of eval so markdown backticks/body content are not shell-executed. REPO_SLUG=$(get_repo_slug) REPO_ARGS=(--repo "$REPO_SLUG" --login "${GITEA_LOGIN:-mosaicstack}") CMD=(tea pr create "${REPO_ARGS[@]}" --title "$TITLE") [[ -n "$BODY" ]] && CMD+=(--description "$BODY") [[ -n "$BASE_BRANCH" ]] && CMD+=(--base "$BASE_BRANCH") [[ -n "$HEAD_BRANCH" ]] && CMD+=(--head "$HEAD_BRANCH") # Handle labels for tea if [[ -n "$LABELS" ]]; then # tea may use --labels flag CMD+=(--labels "$LABELS") fi # Handle milestone for tea if [[ -n "$MILESTONE" ]]; then MILESTONE_ID=$(tea milestones list "${REPO_ARGS[@]}" 2>/dev/null | grep -E "^\s*[0-9]+" | grep "$MILESTONE" | awk '{print $1}' | head -1) if [[ -n "$MILESTONE_ID" ]]; then CMD+=(--milestone "$MILESTONE_ID") else echo "Warning: Could not find milestone '$MILESTONE', creating without milestone" >&2 fi fi # Note: tea may not support --draft flag in all versions if [[ "$DRAFT" == true ]]; then echo "Note: Draft PR may not be supported by your tea version" >&2 fi if "${CMD[@]}"; then exit 0 fi echo "Warning: tea pr create failed, trying Gitea API fallback..." >&2 gitea_pr_create_api ;; *) echo "Error: Could not detect git platform" >&2 exit 1 ;; esac