fix(ci): restrict BM Bossbot to trusted PR authors

Signed-off-by: phernandez <paul@basicmachines.co>
This commit is contained in:
phernandez
2026-06-09 17:16:49 -05:00
parent 3d22ba3004
commit 79fcfbce6a
2 changed files with 88 additions and 3 deletions
+59 -2
View File
@@ -65,6 +65,24 @@ jobs:
echo "pr_number=$(jq -r '.pull_request.number' "${event_file}")" >> "${GITHUB_OUTPUT}"
echo "head_sha=$(jq -r '.pull_request.head.sha' "${event_file}")" >> "${GITHUB_OUTPUT}"
echo "head_ref=$(jq -r '.pull_request.head.ref' "${event_file}")" >> "${GITHUB_OUTPUT}"
echo "author_association=$(jq -r '.pull_request.author_association // ""' "${event_file}")" >> "${GITHUB_OUTPUT}"
- name: Classify PR author
id: trust
env:
AUTHOR_ASSOCIATION: ${{ steps.pr.outputs.author_association }}
run: |
set -euo pipefail
case "${AUTHOR_ASSOCIATION}" in
OWNER|MEMBER|COLLABORATOR)
trusted_author=true
;;
*)
trusted_author=false
;;
esac
echo "trusted_author=${trusted_author}" >> "${GITHUB_OUTPUT}"
echo "author_association=${AUTHOR_ASSOCIATION}" >> "${GITHUB_OUTPUT}"
- name: Mark BM Bossbot approval pending
env:
@@ -75,8 +93,36 @@ jobs:
--repo "${GITHUB_REPOSITORY}" \
--run-url "${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}"
- name: Decline outside contributor PRs
id: outside
if: steps.trust.outputs.trusted_author != 'true'
env:
HEAD_SHA: ${{ steps.pr.outputs.head_sha }}
AUTHOR_ASSOCIATION: ${{ steps.trust.outputs.author_association }}
run: |
set -euo pipefail
review_file="${RUNNER_TEMP}/bm-bossbot-review.json"
jq -n \
--arg sha "${HEAD_SHA}" \
--arg association "${AUTHOR_ASSOCIATION}" \
'{
reviewed_head_sha: $sha,
review_complete: false,
verdict: "needs_human",
blocking_findings: [
{
title: "BM Bossbot does not run for outside contributors",
body: "This PR author association is \($association). BM Bossbot only runs for OWNER, MEMBER, and COLLABORATOR pull requests, so this PR requires a maintainer path outside the automatic merge gate."
}
],
nonblocking_findings: [],
summary: "BM Bossbot intentionally did not run Codex because this PR was not opened by an owner, member, or collaborator."
}' > "${review_file}"
echo "review_file=${review_file}" >> "${GITHUB_OUTPUT}"
- name: Collect sanitized PR context
id: context
if: steps.trust.outputs.trusted_author == 'true'
env:
GH_TOKEN: ${{ github.token }}
PR_NUMBER: ${{ steps.pr.outputs.pr_number }}
@@ -149,7 +195,7 @@ jobs:
- name: Run BM Bossbot review with Codex
id: codex
if: steps.context.outputs.diff_truncated != 'true'
if: steps.trust.outputs.trusted_author == 'true' && steps.context.outputs.diff_truncated != 'true'
uses: openai/codex-action@v1
with:
openai-api-key: ${{ secrets.OPENAI_API_KEY }}
@@ -159,6 +205,17 @@ jobs:
sandbox: read-only
safety-strategy: drop-sudo
- name: Select BM Bossbot review output
id: review_output
if: always()
env:
OUTSIDE_REVIEW_FILE: ${{ steps.outside.outputs.review_file }}
CONTEXT_REVIEW_FILE: ${{ steps.context.outputs.review_file }}
run: |
set -euo pipefail
review_file="${OUTSIDE_REVIEW_FILE:-${CONTEXT_REVIEW_FILE:-${RUNNER_TEMP}/missing-bm-bossbot-review.json}}"
echo "review_file=${review_file}" >> "${GITHUB_OUTPUT}"
- name: Finalize BM Bossbot approval
if: always()
env:
@@ -166,7 +223,7 @@ jobs:
run: |
uv run --script scripts/bm_bossbot_status.py finalize \
--event "${{ steps.pr.outputs.event_file }}" \
--review "${{ steps.context.outputs.review_file }}" \
--review "${{ steps.review_output.outputs.review_file }}" \
--repo "${GITHUB_REPOSITORY}" \
--run-url "${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}"
+29 -1
View File
@@ -106,10 +106,38 @@ def test_bm_bossbot_rejects_oversized_diffs_without_partial_approval() -> None:
assert "review_complete: false" in workflow_text
assert 'verdict: "needs_human"' in workflow_text
assert "Diff exceeds BM Bossbot review limit" in workflow_text
assert run_codex["if"] == "steps.context.outputs.diff_truncated != 'true'"
assert (
run_codex["if"]
== "steps.trust.outputs.trusted_author == 'true' && steps.context.outputs.diff_truncated != 'true'"
)
assert "head -c 120000" not in workflow_text
def test_bm_bossbot_does_not_run_codex_for_outside_contributors() -> None:
workflow_text = WORKFLOW_PATH.read_text(encoding="utf-8")
workflow = _workflow()
steps = workflow["jobs"]["review"]["steps"]
classify = next(step for step in steps if step["name"] == "Classify PR author")
outside = next(step for step in steps if step["name"] == "Decline outside contributor PRs")
collect = next(step for step in steps if step["name"] == "Collect sanitized PR context")
run_codex = next(step for step in steps if step["name"] == "Run BM Bossbot review with Codex")
select_review = next(step for step in steps if step["name"] == "Select BM Bossbot review output")
finalize = next(step for step in steps if step["name"] == "Finalize BM Bossbot approval")
assert "OWNER|MEMBER|COLLABORATOR" in classify["run"]
assert outside["if"] == "steps.trust.outputs.trusted_author != 'true'"
assert collect["if"] == "steps.trust.outputs.trusted_author == 'true'"
assert (
run_codex["if"]
== "steps.trust.outputs.trusted_author == 'true' && steps.context.outputs.diff_truncated != 'true'"
)
assert select_review["if"] == "always()"
assert "BM Bossbot does not run for outside contributors" in workflow_text
assert "missing-bm-bossbot-review.json" in workflow_text
assert '--review "${{ steps.review_output.outputs.review_file }}"' in finalize["run"]
def test_bm_bossbot_prompt_references_engineering_style_and_json_bullets() -> None:
prompt = PROMPT_PATH.read_text(encoding="utf-8")