Fix comment updates for approvals #67
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: Bot Proxy Gatekeeper | |
| on: | |
| pull_request: | |
| types: [opened, synchronize, reopened, ready_for_review] | |
| pull_request_review: | |
| types: [submitted, dismissed] | |
| workflow_dispatch: | |
| permissions: | |
| contents: read | |
| pull-requests: write | |
| issues: write | |
| checks: write | |
| concurrency: | |
| group: gatekeeper-${{ github.event.pull_request.number || github.run_id }} | |
| cancel-in-progress: true | |
| jobs: | |
| enforce-human-review: | |
| runs-on: ubuntu-latest | |
| defaults: | |
| run: | |
| shell: bash | |
| steps: | |
| - name: Analyze Authorship and Approvals | |
| id: evaluate | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| PR: ${{ github.event.pull_request.number }} | |
| REPO: ${{ github.repository }} | |
| run: | | |
| set -euo pipefail | |
| if [ -z "${PR:-}" ]; then | |
| echo "conclusion=success" >> "$GITHUB_OUTPUT" | |
| printf 'summary=%s\n' "No PR context; skipping gatekeeper checks." >> "$GITHUB_OUTPUT" | |
| echo "details=" >> "$GITHUB_OUTPUT" | |
| echo "is_bot=false" >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| fi | |
| # 1. Fetch PR Data | |
| PR_DATA=$(gh pr view "$PR" --repo "$REPO" --json author,body,commits,reviews,headRefOid) | |
| HEAD_SHA=$(echo "$PR_DATA" | jq -r '.headRefOid') | |
| echo "head_sha=$HEAD_SHA" >> "$GITHUB_OUTPUT" | |
| # 2. Only enforce when Devin is the PR author | |
| IS_BOT_AUTHOR=$( | |
| echo "$PR_DATA" | jq ' | |
| (.author.login | endswith("[bot]")) | |
| or (.author.login == "app/devin-ai-integration") | |
| or (.author.is_bot == true) | |
| ' | |
| ) | |
| if [ "$IS_BOT_AUTHOR" != "true" ]; then | |
| echo "conclusion=success" >> "$GITHUB_OUTPUT" | |
| printf 'summary=%s\n' "Not a Devin-authored PR. Skipping human-review enforcement." >> "$GITHUB_OUTPUT" | |
| echo "details=" >> "$GITHUB_OUTPUT" | |
| echo "is_bot=false" >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| fi | |
| echo "is_bot=true" >> "$GITHUB_OUTPUT" | |
| # 3. Reduce to latest review per human | |
| LATEST_REVIEWS_JSON=$( | |
| echo "$PR_DATA" | jq -c ' | |
| [ | |
| .reviews[] | |
| | select(.author.login | endswith("[bot]") | not) | |
| | {login: .author.login, state: .state, submittedAt: .submittedAt} | |
| ] | |
| | sort_by(.submittedAt) | |
| | group_by(.login) | |
| | map(.[-1]) | |
| ' | |
| ) | |
| APPROVERS=$( | |
| echo "$LATEST_REVIEWS_JSON" | jq -r ' | |
| [.[] | select(.state=="APPROVED") | .login] | |
| | unique | |
| | join(", ") | |
| ' | |
| ) | |
| COUNT=$( | |
| echo "$LATEST_REVIEWS_JSON" | jq -r ' | |
| [.[] | select(.state=="APPROVED") | .login] | |
| | unique | |
| | length | |
| ' | |
| ) | |
| DETAILS=$( | |
| echo "$LATEST_REVIEWS_JSON" | jq -r ' | |
| if length == 0 then | |
| "No human reviews yet." | |
| else | |
| [.[] | "- @" + .login + ": " + .state] | join("\n") | |
| end | |
| ' | |
| ) | |
| MISSING=$((2 - COUNT)) | |
| if [ "$MISSING" -lt 0 ]; then | |
| MISSING=0 | |
| fi | |
| echo "approvals.human_unique=$COUNT" >> "$GITHUB_OUTPUT" | |
| echo "approvals_list=$APPROVERS" >> "$GITHUB_OUTPUT" | |
| echo "approvals_missing=$MISSING" >> "$GITHUB_OUTPUT" | |
| { | |
| echo "details<<EOF" | |
| echo "$DETAILS" | |
| echo "EOF" | |
| } >> "$GITHUB_OUTPUT" | |
| # 4. Enforcement Logic | |
| if [ "$COUNT" -lt 2 ]; then | |
| echo "conclusion=failure" >> "$GITHUB_OUTPUT" | |
| printf 'summary=%s\n' "2 unique human approvals required. Current: $COUNT. Missing: $MISSING." >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| fi | |
| echo "conclusion=success" >> "$GITHUB_OUTPUT" | |
| printf 'summary=%s\n' "Security requirements satisfied. Unique human approvals: $COUNT." >> "$GITHUB_OUTPUT" | |
| - name: Publish Gatekeeper Check | |
| if: steps.evaluate.outputs.head_sha != '' | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| REPO: ${{ github.repository }} | |
| HEAD_SHA: ${{ steps.evaluate.outputs.head_sha }} | |
| CONCLUSION: ${{ steps.evaluate.outputs.conclusion }} | |
| SUMMARY: ${{ steps.evaluate.outputs.summary }} | |
| DETAILS: ${{ steps.evaluate.outputs.details }} | |
| run: | | |
| set -euo pipefail | |
| gh api --method POST "repos/$REPO/check-runs" \ | |
| -f name="Bot Proxy Gatekeeper (Human Review)" \ | |
| -f head_sha="$HEAD_SHA" \ | |
| -f status="completed" \ | |
| -f conclusion="$CONCLUSION" \ | |
| -f output[title]="Bot Proxy Gatekeeper (Human Review)" \ | |
| -f output[summary]="$SUMMARY" \ | |
| -f output[text]="$DETAILS" | |
| - name: Post Security Comment | |
| if: steps.evaluate.outputs.is_bot == 'true' | |
| continue-on-error: true | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| PR: ${{ github.event.pull_request.number }} | |
| REPO: ${{ github.repository }} | |
| APPROVERS: ${{ steps.evaluate.outputs.approvals_list }} | |
| MISSING: ${{ steps.evaluate.outputs.approvals_missing }} | |
| CONCLUSION: ${{ steps.evaluate.outputs.conclusion }} | |
| run: | | |
| set -euo pipefail | |
| MARKER="<!-- bot-proxy-gatekeeper-comment -->" | |
| APPROVER_LINE="Current approvals: ${APPROVERS:-none}" | |
| if [ "$CONCLUSION" = "success" ]; then | |
| MSG=$(printf "%s\n%s\n\n**%s**" \ | |
| "$MARKER" \ | |
| "✅ **Review Requirements Satisfied**: 2 different human approvals are present." \ | |
| "$APPROVER_LINE") | |
| else | |
| MSG=$(printf "%s\n%s\n\n**%s**\n**Missing:** %s" \ | |
| "$MARKER" \ | |
| "⚠️ **Review Required**: A **human** is required to review bot PRs. 2 different human approvals are required before this PR can be merged." \ | |
| "$APPROVER_LINE" \ | |
| "${MISSING:-2}") | |
| fi | |
| COMMENT_ID=$( | |
| gh api "repos/$REPO/issues/$PR/comments" \ | |
| --jq ".[] | select(.body | contains(\"$MARKER\")) | .id" \ | |
| | head -n 1 | |
| ) | |
| if [ -n "${COMMENT_ID:-}" ]; then | |
| gh api --method PATCH "repos/$REPO/issues/comments/$COMMENT_ID" -f body="$MSG" | |
| else | |
| gh api --method POST "repos/$REPO/issues/$PR/comments" -f body="$MSG" | |
| fi |