[BOUNTY #476] Button component with loading spinners - Resubmit (scope fix) #1666
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: AI Code Review | |
| # This is a thin trigger shim — the real review logic runs in the private repo. | |
| # Contributors cannot break the review system by modifying this file. | |
| # Any changes to this file will only affect the trigger mechanism, | |
| # not the scoring, LLM pipeline, or review logic. | |
| on: | |
| pull_request_target: | |
| types: [opened, synchronize] | |
| workflow_dispatch: | |
| inputs: | |
| pr_number: | |
| description: 'PR number to review' | |
| required: true | |
| type: string | |
| permissions: | |
| contents: read | |
| jobs: | |
| trigger-review: | |
| runs-on: ubuntu-latest | |
| concurrency: | |
| group: pr-review-${{ inputs.pr_number || github.event.pull_request.number }} | |
| cancel-in-progress: true | |
| steps: | |
| - name: Check if bounty is still open | |
| id: bounty_check | |
| env: | |
| GH_TOKEN: ${{ secrets.SOLFOUNDRY_GITHUB_PAT }} | |
| run: | | |
| if [ -n "${{ inputs.pr_number }}" ]; then | |
| PR_NUM="${{ inputs.pr_number }}" | |
| else | |
| PR_NUM="${{ github.event.pull_request.number }}" | |
| fi | |
| echo "pr_num=$PR_NUM" >> $GITHUB_OUTPUT | |
| # Extract linked issue from PR title + body | |
| PR_JSON=$(curl -sf -H "Authorization: token $GH_TOKEN" \ | |
| "https://api.github.com/repos/${{ github.repository }}/pulls/$PR_NUM") | |
| PR_TITLE=$(echo "$PR_JSON" | python3 -c "import sys,json; print(json.loads(sys.stdin.read()).get('title',''))" 2>/dev/null) | |
| PR_AUTHOR=$(echo "$PR_JSON" | python3 -c "import sys,json; print(json.loads(sys.stdin.read()).get('user',{}).get('login',''))" 2>/dev/null) | |
| PR_BODY=$(echo "$PR_JSON" | python3 -c "import sys,json; print(json.loads(sys.stdin.read()).get('body','') or '')" 2>/dev/null) | |
| ISSUE_NUM=$(python3 -c " | |
| import re | |
| text = '''$PR_TITLE''' + ' ' + '''$PR_BODY''' | |
| m = re.search(r'(?:closes|fixes|resolves)\s+(?:#(?:\S+#)?)(\d+)', text, re.IGNORECASE) | |
| print(m.group(1) if m else '') | |
| " 2>/dev/null) | |
| if [ -z "$ISSUE_NUM" ]; then | |
| echo "No linked issue — proceeding to dispatch" | |
| echo "skip=false" >> $GITHUB_OUTPUT | |
| exit 0 | |
| fi | |
| # Check if the bounty issue is closed | |
| ISSUE_STATE=$(curl -sf -H "Authorization: token $GH_TOKEN" \ | |
| "https://api.github.com/repos/${{ github.repository }}/issues/$ISSUE_NUM" \ | |
| | python3 -c "import sys,json; print(json.loads(sys.stdin.read()).get('state','open'))" 2>/dev/null) | |
| if [ "$ISSUE_STATE" = "closed" ]; then | |
| echo "BLOCKED: Bounty #$ISSUE_NUM is closed — closing PR #$PR_NUM" | |
| # Post comment | |
| curl -sf -X POST \ | |
| -H "Authorization: token $GH_TOKEN" \ | |
| -H "Content-Type: application/json" \ | |
| "https://api.github.com/repos/${{ github.repository }}/issues/$PR_NUM/comments" \ | |
| -d "{\"body\":\"❌ **Bounty Closed**\n\n@$PR_AUTHOR, bounty #$ISSUE_NUM has already been completed and closed.\n\nThis PR cannot be reviewed because the bounty is no longer active. Please check the [open bounties](https://github.com/${{ github.repository }}/issues?q=is%3Aissue+is%3Aopen+label%3Abounty) for available work.\n\n---\n*SolFoundry Review Bot*\"}" | |
| # Close PR | |
| curl -sf -X PATCH \ | |
| -H "Authorization: token $GH_TOKEN" \ | |
| -H "Content-Type: application/json" \ | |
| "https://api.github.com/repos/${{ github.repository }}/pulls/$PR_NUM" \ | |
| -d '{"state":"closed"}' | |
| echo "skip=true" >> $GITHUB_OUTPUT | |
| else | |
| echo "Bounty #$ISSUE_NUM is open — proceeding" | |
| echo "skip=false" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Check tier eligibility | |
| if: steps.bounty_check.outputs.skip != 'true' | |
| id: tier_check | |
| env: | |
| GH_TOKEN: ${{ secrets.SOLFOUNDRY_GITHUB_PAT }} | |
| run: | | |
| PR_NUM="${{ steps.bounty_check.outputs.pr_num }}" | |
| REPO="${{ github.repository }}" | |
| # Get PR info | |
| PR_JSON=$(curl -sf -H "Authorization: token $GH_TOKEN" \ | |
| "https://api.github.com/repos/$REPO/pulls/$PR_NUM") | |
| PR_AUTHOR=$(echo "$PR_JSON" | python3 -c "import sys,json; print(json.loads(sys.stdin.read()).get('user',{}).get('login',''))" 2>/dev/null) | |
| PR_TITLE=$(echo "$PR_JSON" | python3 -c "import sys,json; print(json.loads(sys.stdin.read()).get('title',''))" 2>/dev/null) | |
| PR_BODY=$(echo "$PR_JSON" | python3 -c "import sys,json; print(json.loads(sys.stdin.read()).get('body','') or '')" 2>/dev/null) | |
| # Extract linked issue number | |
| ISSUE_NUM=$(python3 -c " | |
| import re | |
| text = '''$PR_TITLE''' + ' ' + '''$PR_BODY''' | |
| m = re.search(r'(?:closes|fixes|resolves)\s+(?:\S+#)?(\d+)', text, re.IGNORECASE) | |
| print(m.group(1) if m else '') | |
| " 2>/dev/null) | |
| if [ -z "$ISSUE_NUM" ]; then | |
| echo "No linked issue — skipping tier check" | |
| echo "skip=false" >> $GITHUB_OUTPUT | |
| exit 0 | |
| fi | |
| # Get issue labels to determine tier | |
| ISSUE_JSON=$(curl -sf -H "Authorization: token $GH_TOKEN" \ | |
| "https://api.github.com/repos/$REPO/issues/$ISSUE_NUM") | |
| LABELS=$(echo "$ISSUE_JSON" | python3 -c "import sys,json; print(','.join(l['name'] for l in json.loads(sys.stdin.read()).get('labels',[])))" 2>/dev/null) | |
| echo "Issue #$ISSUE_NUM labels: $LABELS" | |
| # T1 bounties — open to everyone, no check needed | |
| if echo "$LABELS" | grep -q "tier-1"; then | |
| echo "T1 bounty — open race, no tier gate" | |
| echo "skip=false" >> $GITHUB_OUTPUT | |
| exit 0 | |
| fi | |
| # Skip non-bounty issues | |
| if ! echo "$LABELS" | grep -q "bounty"; then | |
| echo "Not a bounty issue — no tier gate" | |
| echo "skip=false" >> $GITHUB_OUTPUT | |
| exit 0 | |
| fi | |
| # For T2/T3: check contributor's merged bounty PR history | |
| python3 << PYEOF | |
| import json, re, urllib.request, os, sys | |
| token = os.environ["GH_TOKEN"] | |
| repo = "$REPO" | |
| user = "$PR_AUTHOR" | |
| issue_num = "$ISSUE_NUM" | |
| labels = "$LABELS" | |
| pr_num = "$PR_NUM" | |
| def gh_get(path): | |
| req = urllib.request.Request(f"https://api.github.com/{path}") | |
| req.add_header("Authorization", f"token {token}") | |
| req.add_header("Accept", "application/vnd.github.v3+json") | |
| return json.loads(urllib.request.urlopen(req).read().decode()) | |
| def gh_post(path, data): | |
| req = urllib.request.Request(f"https://api.github.com/{path}", json.dumps(data).encode(), method="POST") | |
| req.add_header("Authorization", f"token {token}") | |
| req.add_header("Accept", "application/vnd.github.v3+json") | |
| req.add_header("Content-Type", "application/json") | |
| urllib.request.urlopen(req) | |
| def gh_patch(path, data): | |
| req = urllib.request.Request(f"https://api.github.com/{path}", json.dumps(data).encode(), method="PATCH") | |
| req.add_header("Authorization", f"token {token}") | |
| req.add_header("Accept", "application/vnd.github.v3+json") | |
| req.add_header("Content-Type", "application/json") | |
| urllib.request.urlopen(req) | |
| def get_issue_tier(inum): | |
| try: | |
| issue = gh_get(f"repos/{repo}/issues/{inum}") | |
| ilabels = [l["name"] for l in issue.get("labels", [])] | |
| if "bounty" not in ilabels: | |
| return None | |
| for t in ("tier-3", "tier-2", "tier-1"): | |
| if t in ilabels: | |
| return t | |
| return "tier-1" | |
| except: | |
| return None | |
| # Count merged bounty PRs by tier | |
| t1_count = t2_count = 0 | |
| page = 1 | |
| while True: | |
| prs = gh_get(f"repos/{repo}/pulls?state=closed&per_page=100&page={page}") | |
| for pr in prs: | |
| if pr["user"]["login"] != user or not pr.get("merged_at"): | |
| continue | |
| body = pr.get("body") or "" | |
| linked = re.findall(r"(?:closes|fixes|resolves)\s*#(\d+)", body, re.IGNORECASE) | |
| for ln in linked: | |
| tier = get_issue_tier(ln) | |
| if tier == "tier-1": t1_count += 1 | |
| elif tier == "tier-2": t2_count += 1 | |
| if len(prs) < 100: break | |
| page += 1 | |
| print(f"{user}: T1={t1_count}, T2={t2_count}") | |
| is_t2 = "tier-2" in labels | |
| is_t3 = "tier-3" in labels | |
| blocked = False | |
| if is_t2 and t1_count < 4: | |
| remaining = 4 - t1_count | |
| msg = ( | |
| f"⚠️ @{user} — **Tier 2 bounties require 4+ completed Tier 1 bounties.**\n\n" | |
| f"You have **{t1_count}** merged T1 PR(s) — **{remaining} more** to unlock T2.\n\n" | |
| f"This PR cannot proceed to review. Please build your reputation with " | |
| f"[Tier 1 bounties](https://github.com/{repo}/labels/tier-1) first.\n\n" | |
| f"*— SolFoundry Bot 🏭*" | |
| ) | |
| blocked = True | |
| elif is_t3: | |
| t3_std = t2_count >= 3 | |
| t3_alt = t1_count >= 5 and t2_count >= 1 | |
| if not t3_std and not t3_alt: | |
| msg = ( | |
| f"⚠️ @{user} — **Tier 3 bounties are reputation-gated.**\n\n" | |
| f"Requirements (one of):\n" | |
| f"- **Standard:** 3+ completed Tier 2 bounties (you have {t2_count})\n" | |
| f"- **Alternative:** 5+ T1 AND 1+ T2 (you have {t1_count} T1, {t2_count} T2)\n\n" | |
| f"This PR cannot proceed to review until you meet the requirements.\n\n" | |
| f"*— SolFoundry Bot 🏭*" | |
| ) | |
| blocked = True | |
| if blocked: | |
| gh_post(f"repos/{repo}/issues/{pr_num}/comments", {"body": msg}) | |
| gh_patch(f"repos/{repo}/pulls/{pr_num}", {"state": "closed"}) | |
| print(f"BLOCKED: {user} not eligible — PR #{pr_num} closed") | |
| with open(os.environ["GITHUB_OUTPUT"], "a") as f: | |
| f.write("skip=true\n") | |
| else: | |
| print(f"ELIGIBLE: {user} passes tier gate") | |
| with open(os.environ["GITHUB_OUTPUT"], "a") as f: | |
| f.write("skip=false\n") | |
| PYEOF | |
| - name: Trigger private review pipeline | |
| if: steps.bounty_check.outputs.skip != 'true' && steps.tier_check.outputs.skip != 'true' | |
| env: | |
| REVIEW_PAT: ${{ secrets.SOLFOUNDRY_GITHUB_PAT }} | |
| run: | | |
| PR_NUM="${{ steps.bounty_check.outputs.pr_num }}" | |
| # Determine action type for smart dedup in private pipeline | |
| if [ -n "${{ github.event.action }}" ]; then | |
| ACTION="${{ github.event.action }}" | |
| else | |
| ACTION="manual" | |
| fi | |
| echo "Dispatching review for PR #${PR_NUM} (action: ${ACTION}) to private pipeline..." | |
| # Fire repository_dispatch to the private review repo | |
| curl -sf -X POST \ | |
| -H "Authorization: token $REVIEW_PAT" \ | |
| -H "Accept: application/vnd.github.v3+json" \ | |
| "https://api.github.com/repos/SolFoundry/solfoundry-review/dispatches" \ | |
| -d "{\"event_type\": \"review-pr\", \"client_payload\": {\"pr_number\": \"${PR_NUM}\", \"action\": \"${ACTION}\"}}" | |
| echo "✅ Review dispatched for PR #${PR_NUM}" |