Skip to content
Merged

Test #515

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions .coderabbit.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# CodeRabbit configuration — https://docs.coderabbit.ai/guides/configure-coderabbit
# CodeRabbit is the required review gate for vouch (free for this public repo). it
# reviews every non-draft PR automatically. request_changes_workflow is on, so it
# submits a formal approve / request-changes review; the coderabbit-gate workflow
# turns that verdict into the required `coderabbit-approved` status check, so a pr
# only auto-merges once CodeRabbit approves (on top of ci + trust-gate + CODEOWNERS,
# with the owner's auto-merge label as the go signal). a pr CodeRabbit requests
# changes on 3 times is auto-closed (the owner and bots are exempt).
language: "en-US"
early_access: false
reviews:
profile: "chill"
request_changes_workflow: true
high_level_summary: true
poem: false
review_status: true
collapse_walkthrough: false
auto_review:
enabled: true
drafts: false
base_branches:
- "test"
- "main"
path_instructions:
- path: "src/vouch/**"
instructions: >-
Follow CLAUDE.md and AGENTS.md. Use lowercase prose in comments and
review notes. Vouch's load-bearing invariant is the review gate: every
write goes through proposals.approve(). Flag any change that adds a
parallel data path bypassing the review gate. storage.py is pure I/O —
flag business logic added there.
- path: ".github/workflows/**"
instructions: >-
These are core, security-sensitive workflows. Flag any pull_request_target
or workflow_run that checks out or runs untrusted head code, any unpinned
third-party action, and any untrusted event field interpolated into a run
block instead of passed through env.
chat:
auto_reply: true
29 changes: 16 additions & 13 deletions .github/workflows/auto-merge.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: auto-merge
on:
pull_request_target: # zizmor: ignore[dangerous-triggers] no untrusted code runs here; verification is delegated to pr-verify.yml behind an environment approval gate
pull_request_target: # zizmor: ignore[dangerous-triggers] no untrusted code runs here; only metadata is read and native auto-merge is armed. review is done by CodeRabbit + ci.
types: [labeled, synchronize]
permissions: {}
# a newer event for the same PR supersedes an in-flight run.
Expand All @@ -10,7 +10,7 @@ concurrency:
jobs:
# any push VOIDS prior authorization: disable auto-merge and drop the label.
# never checks out or runs head code. re-authorize (label or /auto-merge) to
# re-verify the new head. closes the label-then-swap TOCTOU.
# re-arm on the new head. closes the label-then-swap TOCTOU.
deauthorize-on-push:
if: github.event.action == 'synchronize'
runs-on: ubuntu-latest
Expand All @@ -29,11 +29,11 @@ jobs:
if [ "$had_label" = "true" ]; then
gh pr edit "$PR" --repo "$REPO" --remove-label auto-merge || true
gh pr comment "$PR" --repo "$REPO" --body \
"new commits were pushed after authorization, so the prior verification no longer matches the head. auto-merge is disarmed and the label removed — re-authorize (add the auto-merge label or comment /auto-merge) to re-verify the new commits."
"new commits were pushed after authorization. auto-merge is disarmed and the label removed — re-add the auto-merge label (or comment /auto-merge) to re-arm on the new head."
fi

guard:
# only a FRESH label starts verification, and only from the trusted owner.
# only a FRESH label arms auto-merge, and only from the trusted owner.
if: github.event.action == 'labeled' && github.event.label.name == 'auto-merge'
runs-on: ubuntu-latest
permissions:
Expand Down Expand Up @@ -72,16 +72,19 @@ jobs:
klass=$(PYTHONPATH=src python -m vouch.pr_bot classify --files-file changed.txt --print-klass)
echo "klass=$klass" >> "$GITHUB_OUTPUT"

call-verify:
arm:
needs: guard
# core PRs are never armed — CODEOWNERS requires the owner's approval.
if: needs.guard.outputs.klass != 'core'
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
checks: write
uses: ./.github/workflows/pr-verify.yml
with:
pr_number: ${{ github.event.pull_request.number }}
head_sha: ${{ github.event.pull_request.head.sha }}
klass: ${{ needs.guard.outputs.klass }}
secrets:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
steps:
- name: arm native auto-merge (non-core)
env:
GH_TOKEN: ${{ github.token }}
REPO: ${{ github.repository }}
PR: ${{ github.event.pull_request.number }}
run: |
gh pr merge "$PR" --repo "$REPO" --auto --squash
72 changes: 72 additions & 0 deletions .github/workflows/coderabbit-gate.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
name: coderabbit-gate
# CodeRabbit is the required review gate. this workflow turns CodeRabbit's review
# verdict into the `coderabbit-approved` commit status the branch ruleset
# requires (so native auto-merge waits for its approval), and auto-closes a
# contributor pr CodeRabbit has requested changes on 3 times. it reads only
# review metadata via the api and checks out the trusted base ref — no untrusted
# head code is ever checked out or run.
on:
pull_request_review:
types: [submitted, dismissed, edited]
pull_request_target: # zizmor: ignore[dangerous-triggers] no untrusted code runs here; only review metadata is read, a commit status is set, and a stale pr may be closed.
types: [opened, reopened, synchronize]
permissions: {}
# a newer event for the same pr supersedes an in-flight run (latest verdict wins).
concurrency:
group: coderabbit-gate-${{ github.event.pull_request.number }}
cancel-in-progress: true
jobs:
gate:
runs-on: ubuntu-latest
permissions:
contents: read # checkout base ref + run pr_bot
statuses: write # publish the coderabbit-approved commit status
pull-requests: write # comment + close after 3 rejected rounds
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
ref: ${{ github.event.pull_request.base.sha }}
persist-credentials: false
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
with:
python-version: "3.12"
- name: evaluate coderabbit verdict
id: gate
env:
GH_TOKEN: ${{ github.token }}
REPO: ${{ github.repository }}
PR: ${{ github.event.pull_request.number }}
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
AUTHOR: ${{ github.event.pull_request.user.login }}
run: |
gh api "repos/$REPO/pulls/$PR/reviews?per_page=100" > reviews.json
PYTHONPATH=src python -m vouch.pr_bot coderabbit-gate \
--reviews-file reviews.json --head-sha "$HEAD_SHA" --author "$AUTHOR" \
>> "$GITHUB_OUTPUT"
- name: publish coderabbit-approved status
env:
GH_TOKEN: ${{ github.token }}
REPO: ${{ github.repository }}
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
STATE: ${{ steps.gate.outputs.state }}
VERDICT: ${{ steps.gate.outputs.verdict }}
run: |
case "$VERDICT" in
approved) desc="CodeRabbit approved this commit." ;;
changes) desc="CodeRabbit requested changes — resolve them to merge." ;;
*) desc="Waiting for CodeRabbit to review this commit." ;;
esac
gh api --method POST "repos/$REPO/statuses/$HEAD_SHA" \
-f state="$STATE" -f context="coderabbit-approved" -f description="$desc"
- name: auto-close after 3 rejected rounds
if: steps.gate.outputs.close == 'true'
env:
GH_TOKEN: ${{ github.token }}
REPO: ${{ github.repository }}
PR: ${{ github.event.pull_request.number }}
STRIKES: ${{ steps.gate.outputs.strikes }}
run: |
gh pr merge "$PR" --repo "$REPO" --disable-auto || true
gh pr edit "$PR" --repo "$REPO" --remove-label auto-merge || true
gh pr close "$PR" --repo "$REPO" --comment \
"CodeRabbit requested changes on this pr $STRIKES times without an approval, so it is being closed automatically. the feedback still stands — address it and reopen this pr (or open a fresh one) and it will be reviewed again."
40 changes: 20 additions & 20 deletions .github/workflows/comment-command.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
name: comment-command
# slash-command trigger: the owner comments `/auto-merge` or `/verify` on a PR
# to authorize verification, as an alternative to applying the auto-merge label.
# slash-command trigger: the owner comments `/auto-merge` on a PR to arm
# auto-merge, as an alternative to applying the auto-merge label. review is done
# by CodeRabbit + ci; this only arms native auto-merge for non-core PRs.
on:
issue_comment:
types: [created]
Expand All @@ -10,7 +11,7 @@ concurrency:
cancel-in-progress: false
jobs:
parse:
# only PR comments, only the trusted owner, only a recognized command.
# only PR comments, only the trusted owner, only the /auto-merge command.
if: >
github.event.issue.pull_request &&
github.event.comment.author_association == 'OWNER' &&
Expand All @@ -21,15 +22,14 @@ jobs:
pull-requests: write
outputs:
is_command: ${{ steps.cmd.outputs.is_command }}
head_sha: ${{ steps.meta.outputs.head_sha }}
klass: ${{ steps.meta.outputs.klass }}
steps:
- name: detect /auto-merge or /verify
- name: detect /auto-merge
id: cmd
env:
BODY: ${{ github.event.comment.body }}
run: |
if printf '%s' "$BODY" | grep -Eiq '(^|[[:space:]])/(auto-merge|verify)([[:space:]]|$)'; then
if printf '%s' "$BODY" | grep -Eiq '(^|[[:space:]])/auto-merge([[:space:]]|$)'; then
echo "is_command=true" >> "$GITHUB_OUTPUT"
else
echo "is_command=false" >> "$GITHUB_OUTPUT"
Expand All @@ -42,35 +42,35 @@ jobs:
if: steps.cmd.outputs.is_command == 'true'
with:
python-version: "3.12"
- name: resolve head, classify, and mark authorized
- name: classify and mark authorized
id: meta
if: steps.cmd.outputs.is_command == 'true'
env:
GH_TOKEN: ${{ github.token }}
REPO: ${{ github.repository }}
PR: ${{ github.event.issue.number }}
run: |
head_sha="$(gh pr view "$PR" --repo "$REPO" --json headRefOid --jq .headRefOid)"
gh pr view "$PR" --repo "$REPO" --json files --jq '.files[].path' > changed.txt
klass="$(PYTHONPATH=src python -m vouch.pr_bot classify --files-file changed.txt --print-klass)"
# mark authorized (visible, and enables deauthorize-on-push). a label
# added via GITHUB_TOKEN does not re-trigger auto-merge.yml (GitHub's
# token-recursion guard), so this does not double-run verification.
# token-recursion guard), so this does not double-arm.
gh pr edit "$PR" --repo "$REPO" --add-label auto-merge || true
echo "head_sha=$head_sha" >> "$GITHUB_OUTPUT"
echo "klass=$klass" >> "$GITHUB_OUTPUT"

call-verify:
arm:
needs: parse
if: needs.parse.outputs.is_command == 'true'
# core PRs are never armed — CODEOWNERS requires the owner's approval.
if: needs.parse.outputs.is_command == 'true' && needs.parse.outputs.klass != 'core'
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
checks: write
uses: ./.github/workflows/pr-verify.yml
with:
pr_number: ${{ github.event.issue.number }}
head_sha: ${{ needs.parse.outputs.head_sha }}
klass: ${{ needs.parse.outputs.klass }}
secrets:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
steps:
- name: arm native auto-merge (non-core)
env:
GH_TOKEN: ${{ github.token }}
REPO: ${{ github.repository }}
PR: ${{ github.event.issue.number }}
run: |
gh pr merge "$PR" --repo "$REPO" --auto --squash
87 changes: 0 additions & 87 deletions .github/workflows/pr-verify.yml

This file was deleted.

51 changes: 51 additions & 0 deletions .github/workflows/stale-pr-reaper.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
name: stale-pr-reaper
# daily reaper: closes a pr whose author left CodeRabbit's change request
# unaddressed (no new commit) for 2 days. complements the 3-strikes close in
# coderabbit-gate.yml — that fires when the author keeps pushing failing
# commits, this fires when they go silent. scheduled workflows only run from the
# default branch, so this activates once it is on `main`. it reads only api
# metadata and runs from the trusted default branch — no pr head code is run.
on:
schedule:
- cron: "17 6 * * *" # daily ~06:17 UTC
workflow_dispatch: {}
permissions: {}
concurrency:
group: stale-pr-reaper
cancel-in-progress: false
jobs:
reap:
runs-on: ubuntu-latest
permissions:
contents: read # checkout + run pr_bot
pull-requests: write # comment + close
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
persist-credentials: false
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
with:
python-version: "3.12"
- name: close prs stale for 2 days after a change request
env:
GH_TOKEN: ${{ github.token }}
REPO: ${{ github.repository }}
run: |
now=$(date -u +%s)
gh pr list --repo "$REPO" --state open --limit 100 \
--json number,headRefOid,author,isDraft \
--jq '.[] | select(.isDraft|not) | [.number, .headRefOid, .author.login] | @tsv' \
> prs.tsv
while IFS=$'\t' read -r num sha author; do
[ -z "$num" ] && continue
gh api "repos/$REPO/pulls/$num/reviews?per_page=100" > reviews.json
if PYTHONPATH=src python -m vouch.pr_bot stale-check \
--reviews-file reviews.json --head-sha "$sha" \
--author "$author" --now-epoch "$now"; then
echo "reaping #$num (stale after change request)"
gh pr merge "$num" --repo "$REPO" --disable-auto || true
gh pr edit "$num" --repo "$REPO" --remove-label auto-merge || true
gh pr close "$num" --repo "$REPO" --comment \
"closing automatically: CodeRabbit requested changes and this pr has had no new commits for 2 days. the feedback still stands — push a fix and reopen this pr (or open a fresh one) and it will be reviewed again."
fi
done < prs.tsv
Loading
Loading