fix: cascade workflow — use PR-based merge for Gitea #10

Merged
jmiller merged 1 commits from fix/cascade-dev-gitea-api into main 2026-05-05 20:43:58 +00:00
+80 -48
View File
@@ -17,8 +17,8 @@
# | Triggers on every push to main (PR merges, bot commits, etc.) |
# | |
# | 1. Check if a 'dev' branch exists |
# | 2. Attempt API merge (main → dev) |
# | 3. On conflict: create a PR for manual resolution |
# | 2. Create a PR (main → dev) via Gitea API |
# | 3. Auto-merge if clean; leave open for manual resolution on conflict |
# | |
# +========================================================================+
@@ -68,85 +68,117 @@ jobs:
echo "️ No dev branch found (HTTP ${STATUS}) — skipping cascade"
fi
- name: Merge main → dev via API
- name: Check if dev is already up to date
if: steps.check.outputs.exists == 'true'
id: merge
id: diff
env:
GA_TOKEN: ${{ secrets.GA_TOKEN }}
run: |
API="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
COMMIT_MSG="chore: cascade main → dev [skip ci]"
RESPONSE=$(curl -sS -w "\n%{http_code}" \
-X POST \
# Compare main..dev — if ahead_by is 0 there's nothing to cascade
RESPONSE=$(curl -sS \
-H "Authorization: token ${GA_TOKEN}" \
-H "Content-Type: application/json" \
-d "{
\"base\": \"dev\",
\"head\": \"main\",
\"merge_message_field\": \"${COMMIT_MSG}\"
}" \
"${API}/merges")
"${API}/compare/dev...main")
HTTP_CODE=$(echo "$RESPONSE" | tail -1)
BODY=$(echo "$RESPONSE" | sed '$d')
AHEAD=$(echo "$RESPONSE" | jq '.total_commits // 0')
echo "HTTP ${HTTP_CODE}"
if [ "$HTTP_CODE" = "200" ] || [ "$HTTP_CODE" = "201" ]; then
echo "result=success" >> "$GITHUB_OUTPUT"
echo "✅ main merged into dev successfully"
elif [ "$HTTP_CODE" = "204" ]; then
echo "result=noop" >> "$GITHUB_OUTPUT"
if [ "$AHEAD" -eq 0 ]; then
echo "needs_merge=false" >> "$GITHUB_OUTPUT"
echo "✅ dev is already up to date with main"
elif [ "$HTTP_CODE" = "409" ]; then
echo "result=conflict" >> "$GITHUB_OUTPUT"
echo "⚠️ Merge conflict detected — will create PR"
else
echo "result=error" >> "$GITHUB_OUTPUT"
echo "❌ Unexpected response: ${BODY}"
exit 1
echo "needs_merge=true" >> "$GITHUB_OUTPUT"
echo "️ main is ${AHEAD} commit(s) ahead of dev"
fi
- name: Create conflict-resolution PR
if: steps.merge.outputs.result == 'conflict'
- name: Create cascade PR
if: steps.diff.outputs.needs_merge == 'true'
id: pr
env:
GA_TOKEN: ${{ secrets.GA_TOKEN }}
run: |
API="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
SHORT_SHA="${GITHUB_SHA:0:7}"
# Check if a cascade PR already exists
# Check if a cascade PR already exists (main → dev)
EXISTING=$(curl -sS \
-H "Authorization: token ${GA_TOKEN}" \
"${API}/pulls?state=open&labels=cascade&head=${GITEA_ORG}:main&base=dev&limit=1" \
| jq 'length')
"${API}/pulls?state=open&head=${GITEA_ORG}:main&base=dev&limit=1")
if [ "$EXISTING" -gt 0 ]; then
echo "️ Cascade PR already exists — skipping duplicate"
EXISTING_COUNT=$(echo "$EXISTING" | jq 'length')
if [ "$EXISTING_COUNT" -gt 0 ]; then
PR_NUMBER=$(echo "$EXISTING" | jq -r '.[0].number')
PR_URL=$(echo "$EXISTING" | jq -r '.[0].html_url')
echo "pr_number=${PR_NUMBER}" >> "$GITHUB_OUTPUT"
echo "pr_exists=true" >> "$GITHUB_OUTPUT"
echo "️ Cascade PR already exists: ${PR_URL}"
else
RESPONSE=$(curl -sS -w "\n%{http_code}" \
-X POST \
-H "Authorization: token ${GA_TOKEN}" \
-H "Content-Type: application/json" \
-d "{
\"title\": \"chore: cascade main → dev (${SHORT_SHA}) [skip ci]\",
\"body\": \"## Automatic cascade\\n\\nForward-merging \`main\` (${SHORT_SHA}) into \`dev\` to keep branches in sync.\\n\\nIf this PR has conflicts, please resolve them manually and merge.\\n\\n> Auto-created by the **Cascade Main → Dev** workflow.\",
\"head\": \"main\",
\"base\": \"dev\"
}" \
"${API}/pulls")
HTTP_CODE=$(echo "$RESPONSE" | tail -1)
BODY=$(echo "$RESPONSE" | sed '$d')
PR_NUMBER=$(echo "$BODY" | jq -r '.number // empty')
PR_URL=$(echo "$BODY" | jq -r '.html_url // empty')
if [ "$HTTP_CODE" = "201" ] && [ -n "$PR_NUMBER" ]; then
echo "pr_number=${PR_NUMBER}" >> "$GITHUB_OUTPUT"
echo "pr_exists=false" >> "$GITHUB_OUTPUT"
echo "✅ Created cascade PR #${PR_NUMBER}: ${PR_URL}"
else
echo "❌ Failed to create PR (HTTP ${HTTP_CODE}): ${BODY}"
exit 1
fi
fi
- name: Auto-merge cascade PR
if: steps.pr.outputs.pr_number != ''
env:
GA_TOKEN: ${{ secrets.GA_TOKEN }}
run: |
API="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
PR_NUMBER="${{ steps.pr.outputs.pr_number }}"
# Check if PR is mergeable
PR_DATA=$(curl -sS \
-H "Authorization: token ${GA_TOKEN}" \
"${API}/pulls/${PR_NUMBER}")
MERGEABLE=$(echo "$PR_DATA" | jq -r '.mergeable // false')
if [ "$MERGEABLE" != "true" ]; then
echo "⚠️ PR #${PR_NUMBER} has conflicts — leaving open for manual resolution"
exit 0
fi
# Create the PR
# Merge the PR
RESPONSE=$(curl -sS -w "\n%{http_code}" \
-X POST \
-H "Authorization: token ${GA_TOKEN}" \
-H "Content-Type: application/json" \
-d "{
\"title\": \"chore: cascade main → dev (conflicts)\",
\"body\": \"## Automatic cascade\\n\\nMerging \`main\` (${SHORT_SHA}) into \`dev\` hit merge conflicts.\\n\\nPlease resolve conflicts and merge this PR to keep \`dev\` in sync with \`main\`.\\n\\n> Auto-created by the **Cascade Main → Dev** workflow.\",
\"head\": \"main\",
\"base\": \"dev\"
\"Do\": \"merge\",
\"merge_message_field\": \"chore: cascade main → dev [skip ci]\",
\"delete_branch_after_merge\": false
}" \
"${API}/pulls")
"${API}/pulls/${PR_NUMBER}/merge")
HTTP_CODE=$(echo "$RESPONSE" | tail -1)
BODY=$(echo "$RESPONSE" | sed '$d')
PR_URL=$(echo "$BODY" | jq -r '.html_url // empty')
if [ "$HTTP_CODE" = "201" ] && [ -n "$PR_URL" ]; then
echo "✅ Created conflict-resolution PR: ${PR_URL}"
if [ "$HTTP_CODE" = "200" ] || [ "$HTTP_CODE" = "204" ]; then
echo "✅ Cascade PR #${PR_NUMBER} merged — dev is now in sync with main"
else
echo "❌ Failed to create PR (HTTP ${HTTP_CODE}): ${BODY}"
exit 1
BODY=$(echo "$RESPONSE" | sed '$d')
echo "⚠️ Merge failed (HTTP ${HTTP_CODE}): ${BODY}"
echo "PR #${PR_NUMBER} left open for manual resolution"
fi