name: "Create Gitea Pull Request" description: "Creates a pull request on a Gitea repository via the REST API" inputs: gitea_url: description: "Base URL of the Gitea instance" required: false default: "" repository: description: "Target repository as owner/repo" required: false default: "" token: description: "Gitea token with pull-request write access" required: true title: description: "Pull request title" required: true body: description: "Pull request description" required: false default: "" head: description: "Source branch. Same-repo: branch name. Fork: owner:branch" required: true base: description: "Target branch" required: false default: "main" allow_existing: description: "If a matching open PR already exists, succeed and return it instead of failing" required: false default: "true" outputs: number: description: "Pull request number" value: ${{ steps.create_pr.outputs.number }} url: description: "API URL of the pull request" value: ${{ steps.create_pr.outputs.url }} html_url: description: "Web URL of the pull request" value: ${{ steps.create_pr.outputs.html_url }} runs: using: "composite" steps: - id: create_pr name: Create Pull Request shell: bash run: | set -euo pipefail GITEA_URL="${{ inputs.gitea_url }}" GITEA_URL="${GITEA_URL:-${{ github.server_url }}}" REPOSITORY="${{ inputs.repository }}" REPOSITORY="${REPOSITORY:-${{ github.repository }}}" TOKEN="${{ inputs.token }}" TITLE="${{ inputs.title }}" BODY="${{ inputs.body }}" HEAD="${{ inputs.head }}" BASE="${{ inputs.base }}" BASE="${BASE:-main}" ALLOW_EXISTING="${{ inputs.allow_existing }}" ALLOW_EXISTING="${ALLOW_EXISTING:-true}" if [[ ! "${REPOSITORY}" =~ ^[^/]+/[^/]+$ ]]; then echo "::error::repository must be owner/repo, got: ${REPOSITORY}" exit 1 fi API_BASE="${GITEA_URL}/api/v1/repos/${REPOSITORY}" AUTH_HEADER="Authorization: token ${TOKEN}" echo "::notice::Creating pull request ${REPOSITORY}: ${HEAD} -> ${BASE}" PAYLOAD="$( jq -n \ --arg title "${TITLE}" \ --arg body "${BODY}" \ --arg head "${HEAD}" \ --arg base "${BASE}" \ '{title: $title, body: $body, head: $head, base: $base}' )" HTTP_CODE="$( curl \ --silent \ --show-error \ --output /tmp/gitea-pr-response \ --write-out "%{http_code}" \ -X POST \ -H "${AUTH_HEADER}" \ -H "Content-Type: application/json" \ --data "${PAYLOAD}" \ "${API_BASE}/pulls" )" echo "::group::Gitea pull request API response" echo "HTTP ${HTTP_CODE}" cat /tmp/gitea-pr-response echo echo "::endgroup::" write_outputs() { local response_file="$1" local number url html_url number="$(jq -r '.number // empty' "${response_file}")" url="$(jq -r '.url // empty' "${response_file}")" html_url="$(jq -r '.html_url // empty' "${response_file}")" if [ -z "${number}" ]; then echo "::error::Could not parse pull request number from API response" exit 1 fi echo "number=${number}" >> "${GITHUB_OUTPUT}" echo "url=${url}" >> "${GITHUB_OUTPUT}" echo "html_url=${html_url}" >> "${GITHUB_OUTPUT}" echo "::notice::Pull request #${number}: ${html_url}" } case "${HTTP_CODE}" in 201) write_outputs /tmp/gitea-pr-response ;; *) if [ "${ALLOW_EXISTING}" = "true" ]; then echo "::notice::Create failed with HTTP ${HTTP_CODE}. Checking for an existing open pull request." EXISTING_CODE="$( curl \ --silent \ --show-error \ --output /tmp/gitea-pr-existing \ --write-out "%{http_code}" \ -H "${AUTH_HEADER}" \ "${API_BASE}/pulls?state=open&head=${HEAD}" )" if [ "${EXISTING_CODE}" -eq 200 ]; then MATCH="$( jq \ --arg head "${HEAD}" \ --arg base "${BASE}" \ '[.[] | select((.head.ref == $head or .head.label == $head) and .base.ref == $base)] | .[0] // empty' \ /tmp/gitea-pr-existing )" if [ -n "${MATCH}" ]; then echo "${MATCH}" > /tmp/gitea-pr-response echo "::notice::Open pull request already exists. Reusing it." write_outputs /tmp/gitea-pr-response exit 0 fi fi fi echo "::error::Failed to create pull request with HTTP ${HTTP_CODE}" exit 1 ;; esac