Reusable GitHub Actions workflows shared across Bike4Mind repos. Each one is called with workflow_call from a small stub in the calling repo. Each workflow's header comment documents its inputs.
The PR review bot. A caller adds the stub below as .github/workflows/pr-bot-review.yml, then creates the labels bot-review and bot-review-fold.
bot-reviewreviews the PR and posts one review.bot-review-foldalso applies the findings that meet the skill's fold bar, as one non-force fixup commit on the PR head.
The label is removed when the run finishes, so re-applying it starts a fresh run. On a draft, a fork or a protected ref the run is skipped and the label stays. Remove it and add it again once the PR qualifies.
name: pr-bot-review
on:
pull_request:
types: [labeled]
permissions:
contents: read
pull-requests: write
issues: write
actions: read
id-token: write # required by claude-code-action even when using anthropic_api_key
concurrency:
group: pr-bot-review-${{ github.event.pull_request.number }}-${{ (github.event.label.name == 'bot-review' || github.event.label.name == 'bot-review-fold') && 'bot' || github.run_id }}
cancel-in-progress: ${{ github.event.label.name != 'bot-review-fold' }}
jobs:
review:
# Do NOT switch this to pull_request_target: it would check out the untrusted
# head and run an agent over it. The fork gate lives inside the reusable.
if: github.event.label.name == 'bot-review' || github.event.label.name == 'bot-review-fold'
# workflow-templates main, <date>
uses: Bike4Mind/workflow-templates/.github/workflows/pr-bot-review.yml@<40-char sha>
secrets: inherit
with:
fold_mode: ${{ github.event.label.name == 'bot-review-fold' }}
# b4m-devtools main, <date>
skill_ref: <40-char sha>permissions:The reusable's token can only narrow what the caller grants. Without this block the job gets the repo's default read-only token, and every review write fails with 403.concurrency:A group inside a called workflow does not reliably supersede the caller's run, so it lives here. Thecancel-in-progresstest must namebot-review-fold: a fold run never cancels a run already in flight. Workflow-level concurrency applies before the jobif:, so every label added to the PR joins a group. Therun_idfallback in the group key gives any other label a group of its own, so it cancels nothing.secrets: inherit. The reusable declares nosecrets:block. It reads the secretsANTHROPIC_API_KEY,PREMIUM_OVERLAY_APP_PRIVATE_KEYandBOT_REVIEW_APP_PRIVATE_KEY, and the variablesPREMIUM_OVERLAY_CLIENT_IDandBOT_REVIEW_CLIENT_ID, all from the calling repo or its org. ThePREMIUM_OVERLAY_*App reads the skill repo. TheBOT_REVIEW_*App pushes fold commits, so it must be installed on the calling repo withcontents: write.- Both SHAs. Pin
uses:andskill_refto full 40-character commit SHAs.skill_refhas no default, and the run refuses anything but a SHA. - The label names, in all three places. The
if:,fold_modeandcancel-in-progresslines each namebot-review-fold. Miss theif:and no fold run starts. Misscancel-in-progressand an incoming fold run cancels whatever is already in flight.
verdict_on_cleandefaults toCOMMENT. SetAPPROVEif a clean review should count as an approval.changeset_guarddefaults tofalse. Set it totruein a repo whose release bot pushes changeset commits, so a re-review is skipped when those are the only new commits.changeset_bot_loginnames that bot.runner_labeldefaults toubuntu-latest. A fold run refuses anything but a GitHub-hosted Linux runner, because its write fences name that image's paths. Review runs work on any runner. Do not passvars.RUNNER_LABELhere: repos point it at their build tier, which would fail every fold run.anthropic_federation_rule_id,anthropic_organization_id,anthropic_service_account_idandanthropic_workspace_idswitch the review from theANTHROPIC_API_KEYsecret to workload identity federation. They are identifiers, not secrets, and the stub still needsid-token: write. Setting the rule id stops the secret reaching the action, so a failed exchange fails the review: it is not a runtime fallback, and rolling back means dropping thesewith:lines from the stub. Two things to know before the first federated run:- Make the federation rule require both the calling repository (
repository, or thesubfor it) and the workflow, by matching thejob_workflow_refclaim againstBike4Mind/workflow-templates/.github/workflows/pr-bot-review.yml@. Callers pin by SHA, so the claim ends in@<sha>and needs a prefix match. Neither condition is enough alone:repositoryalone lets any other workflow in the repo withid-token: writeexchange a token, and thejob_workflow_refprefix alone accepts tokens from any other owner's repo that calls this public reusable. - The action's inline-comment classification step runs only with an API key, so on a federated run every buffered inline comment that is not confirmed is posted unclassified.
- Make the federation rule require both the calling repository (
max_changed_files,protected_base_refs,protected_head_refsandskip_head_ref_prefixgate which PRs are reviewed at all. The defaults suit most repos.
Every input is described in the header of .github/workflows/pr-bot-review.yml.