diff --git a/.github/workflows/pr-title-check.yml b/.github/workflows/pr-title-check.yml index a51cc4b327c..9eef239007c 100644 --- a/.github/workflows/pr-title-check.yml +++ b/.github/workflows/pr-title-check.yml @@ -14,15 +14,67 @@ jobs: timeout-minutes: 10 steps: - name: Verify Title Format + id: pr_title env: PR_TITLE: ${{ github.event.pull_request.title }} run: | - title="$PR_TITLE" - regex="^FINERACT-[0-9]+: " + set -euo pipefail - if [[ ! "$title" =~ $regex ]]; then - echo "::error::PR title '$title' is invalid." + regex="^(FINERACT-[0-9]+): " + + if [[ ! "$PR_TITLE" =~ $regex ]]; then + echo "::error::PR title '$PR_TITLE' is invalid." echo "::error::It must start with a Jira Ticket ID (e.g., 'FINERACT-123: Description...')." exit 1 fi - echo "Success: PR title matches the required format." + echo "jira_id=${BASH_REMATCH[1]}" >> "$GITHUB_OUTPUT" + echo "Success: PR title references ${BASH_REMATCH[1]}." + + - name: Verify Jira Story Status + env: + JIRA_ID: ${{ steps.pr_title.outputs.jira_id }} + run: | + set -euo pipefail + + python3 <<'PY' + import json + import os + import sys + import urllib.error + import urllib.request + + jira_id = os.environ["JIRA_ID"] + browse_url = f"https://issues.apache.org/jira/browse/{jira_id}" + api_url = f"https://issues.apache.org/jira/rest/api/2/issue/{jira_id}?fields=status" + request = urllib.request.Request( + api_url, + headers={ + "Accept": "application/json", + "User-Agent": "apache-fineract-pr-compliance-check", + }, + ) + + try: + with urllib.request.urlopen(request, timeout=30) as response: + issue = json.load(response) + except urllib.error.HTTPError as error: + if error.code == 404: + print(f"::error title=Jira issue not found::{jira_id} does not exist in Apache Jira: {browse_url}") + else: + print(f"::error title=Jira lookup failed::Apache Jira returned HTTP {error.code} for {jira_id}: {browse_url}") + sys.exit(1) + except (TimeoutError, urllib.error.URLError) as error: + print(f"::error title=Jira lookup failed::Could not reach Apache Jira for {jira_id}: {error}") + sys.exit(1) + + status = issue.get("fields", {}).get("status", {}).get("name", "") + normalized_status = status.strip().upper() + if normalized_status not in {"OPEN", "IN PROGRESS"}: + print( + f"::error title=Jira issue status not allowed::{jira_id} has status '{status}'. " + f"Expected 'Open' or 'In Progress': {browse_url}" + ) + sys.exit(1) + + print(f"Success: {jira_id} exists and has status '{status}': {browse_url}") + PY