# Checking CI Quality Gates for a PR

Complete workflow for determining whether a PR passes all CI gates, finding failing jobs, and accessing logs.

## 1. Get PR Details

```bash
PR=$(curl -s "${FORGEJO_URL}/api/v1/repos/{owner}/{repo}/pulls/{index}" \
  -H "Authorization: token ${FORGEJO_PAT}")
HEAD_SHA=$(echo "$PR" | jq -r '.head.sha')
echo "PR head SHA: $HEAD_SHA"
echo "Mergeable: $(echo "$PR" | jq -r '.mergeable')"
```

## 2. Get Combined Commit Status

The combined status aggregates all individual check statuses into a single state:

```bash
COMBINED=$(curl -s "${FORGEJO_URL}/api/v1/repos/{owner}/{repo}/commits/${HEAD_SHA}/status" \
  -H "Authorization: token ${FORGEJO_PAT}")

echo "Combined state: $(echo "$COMBINED" | jq -r '.state')"
echo "Total checks: $(echo "$COMBINED" | jq -r '.total_count')"
echo "Statuses: $(echo "$COMBINED" | jq -r '.statuses | length')"
```

Combined state values:
- `pending` — one or more checks are still running
- `success` — all checks passed
- `failure` — one or more checks failed
- `error` — one or more checks errored
- `""` (empty) — no status checks have been reported

## 3. Get Individual Statuses

```bash
STATUSES=$(curl -s "${FORGEJO_URL}/api/v1/repos/{owner}/{repo}/commits/${HEAD_SHA}/statuses" \
  -H "Authorization: token ${FORGEJO_PAT}")

# Show all statuses
echo "$STATUSES" | jq '.[] | {context, state, description, target_url, created_at}'

# Filter to only failing statuses
echo "$STATUSES" | jq '[.[] | select(.state == "failure" or .state == "error")] | .[] | {context, state, description, target_url}'
```

## 4. Parse target_url to Get Run and Job Info

Forgejo Actions sets `target_url` in the format:
`${FORGEJO_URL}/{owner}/{repo}/actions/runs/{run_id}/jobs/{job_index}`

```bash
# Extract run ID and job index from target_url
echo "$STATUSES" | jq -r '.[] | select(.state == "failure") | .target_url' | while read -r url; do
  echo "Failing job URL: $url"
  # Parse run_id and job_index
  RUN_ID=$(echo "$url" | grep -oP 'runs/\K[0-9]+')
  JOB_INDEX=$(echo "$url" | grep -oP 'jobs/\K[0-9]+')
  echo "  Run ID: $RUN_ID, Job Index: $JOB_INDEX"
done
```

## 5. Get Action Run Details

```bash
RUN_ID="{run_id}"  # from step 4

# Get run details
RUN=$(curl -s "${FORGEJO_URL}/api/v1/repos/{owner}/{repo}/actions/runs/${RUN_ID}" \
  -H "Authorization: token ${FORGEJO_PAT}")
echo "$RUN" | jq '{id, status, conclusion, event, workflow_id, started_at, completed_at}'
```

## 6. Access Logs

Forgejo Actions logs are accessible via the web UI. The API does not currently expose raw log content directly, but you can construct the URL:

```bash
# Construct log URL for viewing in browser
echo "${FORGEJO_URL}/{owner}/{repo}/actions/runs/${RUN_ID}/jobs/${JOB_INDEX}"

# Alternatively, list workflow runs to find runs by event or status
curl -s "${FORGEJO_URL}/api/v1/repos/{owner}/{repo}/actions/runs?status={status}" \
  -H "Authorization: token ${FORGEJO_PAT}" | jq '.workflow_runs[] | {id, name, status, conclusion, html_url}'
```

## 7. Check Branch Protection Rules

Branch protection defines which status checks must pass before merging:

```bash
PROTECTION=$(curl -s "${FORGEJO_URL}/api/v1/repos/{owner}/{repo}/branch_protections" \
  -H "Authorization: token ${FORGEJO_PAT}")

echo "$PROTECTION" | jq '.[] | {
  branch_name,
  enable_status_check,
  status_check_contexts,
  required_approvals,
  enable_merge_whitelist,
  block_on_rejected_reviews
}'
```

To check if a PR meets all requirements:

```bash
# Get required contexts
REQUIRED_CONTEXTS=$(echo "$PROTECTION" | jq -r '.[] | select(.branch_name == "${BASE_BRANCH}") | .status_check_contexts[]')

# Get actual statuses
ACTUAL=$(curl -s "${FORGEJO_URL}/api/v1/repos/{owner}/{repo}/commits/${HEAD_SHA}/statuses" \
  -H "Authorization: token ${FORGEJO_PAT}")

# Check each required context
echo "$REQUIRED_CONTEXTS" | while read -r ctx; do
  STATE=$(echo "$ACTUAL" | jq -r --arg ctx "$ctx" '[.[] | select(.context == $ctx)] | first | .state // "missing"')
  echo "  $ctx: $STATE"
done
```

## Complete Script

```bash
#!/bin/bash
set -euo pipefail

OWNER="{owner}"
REPO="{repo}"
PR_INDEX="{index}"

echo "=== Checking CI gates for PR #${PR_INDEX} ==="

# Get PR head SHA
PR=$(curl -s "${FORGEJO_URL}/api/v1/repos/${OWNER}/${REPO}/pulls/${PR_INDEX}" \
  -H "Authorization: token ${FORGEJO_PAT}")
HEAD_SHA=$(echo "$PR" | jq -r '.head.sha')
echo "Head SHA: ${HEAD_SHA}"

# Get combined status
COMBINED=$(curl -s "${FORGEJO_URL}/api/v1/repos/${OWNER}/${REPO}/commits/${HEAD_SHA}/status" \
  -H "Authorization: token ${FORGEJO_PAT}")
STATE=$(echo "$COMBINED" | jq -r '.state')
TOTAL=$(echo "$COMBINED" | jq -r '.total_count')
echo "Combined state: ${STATE} (${TOTAL} checks)"

if [ "$STATE" = "success" ]; then
  echo "All CI checks passed."
  exit 0
fi

if [ "$STATE" = "pending" ]; then
  echo "CI is still running. Waiting..."
  while true; do
    sleep 30
    STATE=$(curl -s "${FORGEJO_URL}/api/v1/repos/${OWNER}/${REPO}/commits/${HEAD_SHA}/status" \
      -H "Authorization: token ${FORGEJO_PAT}" | jq -r '.state')
    echo "  Status: $STATE"
    [ "$STATE" != "pending" ] && break
  done
fi

# Show individual statuses
echo ""
echo "=== Individual check results ==="
STATUSES=$(curl -s "${FORGEJO_URL}/api/v1/repos/${OWNER}/${REPO}/commits/${HEAD_SHA}/statuses" \
  -H "Authorization: token ${FORGEJO_PAT}")
echo "$STATUSES" | jq -r '.[] | "\(.state)\t\(.context)\t\(.description // "")"' | column -t -s $'\t'

# Show failing jobs
echo ""
echo "=== Failing jobs ==="
echo "$STATUSES" | jq -r '.[] | select(.state == "failure" or .state == "error") | "FAIL: \(.context) — \(.description // "no description")\n  URL: \(.target_url)"'

# Check branch protection requirements
echo ""
echo "=== Branch protection requirements ==="
PROTECTION=$(curl -s "${FORGEJO_URL}/api/v1/repos/${OWNER}/${REPO}/branch_protections" \
  -H "Authorization: token ${FORGEJO_PAT}")
BASE_BRANCH=$(echo "$PR" | jq -r '.base.ref')
RULE=$(echo "$PROTECTION" | jq --arg branch "$BASE_BRANCH" '.[] | select(.branch_name == $branch)')

if [ -n "$RULE" ]; then
  echo "Required approvals: $(echo "$RULE" | jq -r '.required_approvals')"
  echo "Status checks enabled: $(echo "$RULE" | jq -r '.enable_status_check')"
  echo "Required contexts:"
  echo "$RULE" | jq -r '.status_check_contexts[]? // empty' | while read -r ctx; do
    CTX_STATE=$(echo "$STATUSES" | jq -r --arg ctx "$ctx" '[.[] | select(.context == $ctx)] | first | .state // "missing"')
    if [ "$CTX_STATE" = "success" ]; then
      echo "  PASS: $ctx"
    else
      echo "  FAIL: $ctx ($CTX_STATE)"
    fi
  done
else
  echo "No branch protection rule found for '${BASE_BRANCH}'"
fi
```
