Skip to content

Commit 79aae64

Browse files
authored
feat: route bot-authored PRs to per-repo owner instead of skipping (#8)
Adds bot_pr_owners map keyed by repo name. When a PR author is in bot_authors (dependabot, renovate, claude, copilot review bot, listed external contributors), the workflow now requests the mapped owner as reviewer instead of exiting 0. Mapping: lisk-backend -> ishantiw lisk-web -> mmarinovic lisk-mobile -> 5heri lisk-infra -> Nazgolze lisk-contracts-> matjazv Repos without a bot_pr_owners entry retain the old skip behavior, so this is opt-in per repo. Implementation: - review-map.yml: add bot_pr_owners section + clarify bot_authors comment - auto-assign-reviewer.yml: hoist EXISTING fetch above bot path so the bot path can short-circuit on idempotency; replace skip with route + request + comment + soft-fail (mirrors the squad cascade's failure semantics). - validate-review-map.yml: assert every bot_pr_owners key is in enabled_repos; include bot_pr_owners values in the GitHub-user existence check. - docs/auto-reviewer.md: document the bot short-circuit, add the current owner table, update kill-switch list.
1 parent a1ade8c commit 79aae64

4 files changed

Lines changed: 83 additions & 8 deletions

File tree

.github/workflows/auto-assign-reviewer.yml

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -96,9 +96,36 @@ jobs:
9696
exit 0
9797
fi
9898
99-
# ── Bot-author skip ──────────────────────────────────────────────
99+
# ── Already-requested reviewers (idempotency) ────────────────────
100+
# Fetched up-front so both the bot-author path and the squad
101+
# cascade can short-circuit if the chosen reviewer is already
102+
# requested.
103+
EXISTING=$(gh api "repos/$REPO/pulls/$PR_NUMBER/requested_reviewers" --jq '.users[].login' 2>/dev/null || true)
104+
105+
# ── Bot-author / external-author routing ─────────────────────────
106+
# Bots and listed external contributors bypass the squad cascade
107+
# because they have no squad. Route them to the repo's bot_pr_owner
108+
# if one is mapped; otherwise skip.
100109
if jq -r '.bot_authors[]' "$MAP_JSON" | grep -Fxq "$PR_AUTHOR"; then
101-
log "auto-reviewer: skipping bot/external author '$PR_AUTHOR'."
110+
BOT_OWNER=$(jq -r --arg r "$REPO_NAME" '.bot_pr_owners[$r] // ""' "$MAP_JSON")
111+
if [ -z "$BOT_OWNER" ] || [ "$BOT_OWNER" = "null" ]; then
112+
log "auto-reviewer: bot/external author '$PR_AUTHOR' on $REPO_NAME has no bot_pr_owners mapping. Skipping."
113+
exit 0
114+
fi
115+
if printf '%s\n' "$EXISTING" | grep -Fxq "$BOT_OWNER"; then
116+
log "auto-reviewer: $BOT_OWNER already requested on PR #$PR_NUMBER (bot path). Skipping."
117+
exit 0
118+
fi
119+
log "auto-reviewer: routing bot/external author '$PR_AUTHOR' → $BOT_OWNER (repo owner for $REPO_NAME)"
120+
if ! gh api -X POST "repos/$REPO/pulls/$PR_NUMBER/requested_reviewers" \
121+
-f "reviewers[]=$BOT_OWNER" >/dev/null 2>err.log; then
122+
ERR=$(cat err.log || true)
123+
warn "auto-reviewer: failed to request $BOT_OWNER for bot PR: $ERR"
124+
comment "🤖 **Auto-reviewer:** tried to request review from \`@$BOT_OWNER\` for bot/external author \`$PR_AUTHOR\` but the API rejected it (likely not a collaborator on this repo). Please assign a reviewer manually."
125+
exit 0
126+
fi
127+
comment "🤖 **Auto-reviewer:** requested review from @$BOT_OWNER — repo owner for bot/external author \`$PR_AUTHOR\`."
128+
log "auto-reviewer: bot-path success."
102129
exit 0
103130
fi
104131
@@ -115,9 +142,6 @@ jobs:
115142
116143
log "auto-reviewer: author=$PR_AUTHOR squad=$AUTHOR_SQUAD domain=$DOMAIN repo=$REPO_NAME pr=$PR_NUMBER"
117144
118-
# ── Already-requested reviewers (idempotency) ────────────────────
119-
EXISTING=$(gh api "repos/$REPO/pulls/$PR_NUMBER/requested_reviewers" --jq '.users[].login' 2>/dev/null || true)
120-
121145
# ── Cascade resolution ───────────────────────────────────────────
122146
SELECTED=""
123147
REASON=""

.github/workflows/validate-review-map.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ name: Validate review-map.yml
77
# - Every domain key in specialists is one of: backend|web|infra|mobile|contracts
88
# - Every squad name in sibling_fallback_order exists under .squads
99
# - Each squad's TL is also in members (when not null)
10+
# - Every bot_pr_owners key matches an entry in enabled_repos
1011

1112
on:
1213
pull_request:
@@ -86,6 +87,14 @@ jobs:
8687
fi
8788
done < <(jq -r '.squads | to_entries[] | [.key, (.value.tl // "")] | @tsv' "$MAP_JSON")
8889
90+
# bot_pr_owners keys must be in enabled_repos
91+
while IFS=$'\t' read -r repo owner; do
92+
[ -z "$repo" ] && continue
93+
if ! jq -r '.enabled_repos[]' "$MAP_JSON" | grep -Fxq "$repo"; then
94+
err "bot_pr_owners key '$repo' is not in enabled_repos"
95+
fi
96+
done < <(jq -r '.bot_pr_owners // {} | to_entries[] | [.key, .value] | @tsv' "$MAP_JSON")
97+
8998
# fallback_to_squad_members_for entries must be valid domains
9099
while IFS=$'\t' read -r squad domain; do
91100
[ -z "$domain" ] && continue
@@ -141,6 +150,7 @@ jobs:
141150
jq -r '
142151
[
143152
(.bot_authors // [])[],
153+
(.bot_pr_owners // {} | to_entries[].value),
144154
(.squads[].members // [])[],
145155
(.squads[].tl // empty),
146156
(.squads[].specialists // {} | to_entries[].value[])

docs/auto-reviewer.md

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,13 @@ specialist." This workflow does that with one shared map.
2424

2525
## Cascade
2626

27-
Given PR author `A`, repo domain `D`, author squad `S`:
27+
**Bot / external authors short-circuit before the cascade.** If the PR author
28+
is in `bot_authors`, the workflow requests the repo's `bot_pr_owners[<repo>]`
29+
(if mapped) and exits. Bots have no squad and don't need round-robin
30+
distribution; a single per-repo owner is enough. If no owner is mapped for
31+
the repo, the workflow exits 0 silently.
32+
33+
Given a human PR author `A`, repo domain `D`, author squad `S`:
2834

2935
1. **Own-squad specialists.** If `S.specialists[D]` minus `A` is non-empty,
3036
pick `eligible[PR# % len]`.
@@ -84,10 +90,33 @@ siblings.
8490

8591
- **All assignment off:** set `enabled: false` in `review-map.yml`.
8692
- **One repo off:** remove it from `enabled_repos`.
87-
- **Skip a specific author:** add login to `bot_authors`.
93+
- **Bypass squad cascade for an author:** add login to `bot_authors`. They get
94+
routed to `bot_pr_owners[<repo>]` if mapped, otherwise skipped.
95+
- **Stop bot routing in a repo:** remove the repo from `bot_pr_owners` (bot
96+
PRs in that repo will then be skipped).
8897

8998
All take effect on the next PR open with no other action.
9099

100+
## Bot / external author routing
101+
102+
`bot_pr_owners` maps repo name → single GitHub login. Designed for PRs where
103+
squad-based routing doesn't apply: dependabot bumps, renovate bumps, claude
104+
PR commits, copilot review bot, plus listed external contributors.
105+
106+
Current mapping:
107+
108+
| Repo | Owner |
109+
|-----------------|-------------|
110+
| `lisk-backend` | `ishantiw` |
111+
| `lisk-web` | `mmarinovic`|
112+
| `lisk-mobile` | `5heri` |
113+
| `lisk-infra` | `Nazgolze` |
114+
| `lisk-contracts`| `matjazv` |
115+
116+
The owner is requested verbatim — no round-robin, no domain check, no
117+
fallback. If they're unavailable, the comment trail surfaces who the API
118+
rejected so a human can re-assign.
119+
91120
## What does and doesn't fail the workflow
92121

93122
The workflow is best-effort. It always exits 0 (so it never blocks merges)

review-map.yml

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ enabled_repos:
1717
- lisk-mobile
1818
- lisk-contracts
1919

20-
# Authors to skip entirely (no assignment, exit 0).
20+
# Authors that bypass the squad cascade. They get routed to the repo's
21+
# bot_pr_owner (see below) if one is set, otherwise the workflow exits 0.
2122
bot_authors:
2223
- dependabot[bot]
2324
- renovate[bot]
@@ -26,6 +27,17 @@ bot_authors:
2627
- copilot-pull-request-reviewer[bot]
2728
- bmijac # external contributor on lisk-web
2829

30+
# Per-repo owner for bot-authored / external-author PRs. Keyed by repo
31+
# name (must match an entry in enabled_repos). When a PR author is in
32+
# bot_authors, the mapped owner is requested instead of running the
33+
# squad cascade. If a repo has no entry here, bot PRs are skipped.
34+
bot_pr_owners:
35+
lisk-backend: ishantiw
36+
lisk-web: mmarinovic
37+
lisk-mobile: 5heri
38+
lisk-infra: Nazgolze
39+
lisk-contracts: matjazv
40+
2941
# Domain equivalence — within a squad, specialists for an equivalent domain
3042
# may review when no specialist exists for the requested domain. This stays
3143
# the review inside the squad. Equivalence does NOT apply to sibling cascade

0 commit comments

Comments
 (0)