Skip to content

Commit 789492f

Browse files
committed
fix: bot bash script API
Signed-off-by: aceppaluni <aceppaluni@gmail.com>
1 parent e1fd8e5 commit 789492f

File tree

3 files changed

+185
-36
lines changed

3 files changed

+185
-36
lines changed
Lines changed: 161 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,61 +1,194 @@
1-
#!/bin/bash
1+
#!/usr/bin/env bash
22
set -euo pipefail
33

4+
#######################################
5+
# Logging helper
6+
#######################################
47
log() {
58
echo "[advanced-check] $1"
69
}
710

8-
# Required env vars
9-
if [[ -z "${REPO:-}" ]] || [[ -z "${ISSUE_NUMBER:-}" ]] || [[ -z "${GH_TOKEN:-}" ]]; then
10-
log "ERROR: Missing required environment variables"
11+
#######################################
12+
# Validate required environment variables
13+
#######################################
14+
if [[ -z "${REPO:-}" ]]; then
15+
log "ERROR: REPO must be set (e.g. owner/name)"
1116
exit 1
1217
fi
1318

14-
# IMPORTANT SAFETY:
15-
# If no trigger assignee exists (i.e. label event), do nothing
16-
if [[ -z "${TRIGGER_ASSIGNEE:-}" ]]; then
17-
log "No trigger assignee detected (label-only event). Skipping enforcement."
19+
OWNER="${REPO%%/*}"
20+
NAME="${REPO##*/}"
21+
22+
#######################################
23+
# GraphQL count helper (INTERMEDIATE ONLY)
24+
#######################################
25+
get_intermediate_count() {
26+
local user=$1
27+
28+
gh api graphql -f query="
29+
{
30+
repository(owner: \"$OWNER\", name: \"$NAME\") {
31+
intermediate: issues(
32+
first: 1
33+
states: CLOSED
34+
filterBy: {
35+
assignee: \"$user\"
36+
labels: [\"intermediate\"]
37+
}
38+
) {
39+
totalCount
40+
}
41+
}
42+
}"
43+
}
44+
45+
#######################################
46+
# Helper: has bot already commented for user?
47+
#######################################
48+
already_commented() {
49+
local user=$1
50+
51+
gh issue view "$ISSUE_NUMBER" --repo "$REPO" \
52+
--json comments \
53+
--jq --arg user "@$user" '
54+
.comments[].body
55+
| select(test("Hi " + $user + ", I cannot assign you to this issue yet."))
56+
' | grep -q .
57+
}
58+
59+
#######################################
60+
# Helper: is user currently assigned?
61+
#######################################
62+
is_assigned() {
63+
local user=$1
64+
65+
gh issue view "$ISSUE_NUMBER" --repo "$REPO" \
66+
--json assignees \
67+
--jq --arg user "$user" '
68+
.assignees[].login | select(. == $user)
69+
' | grep -q .
70+
}
71+
72+
#######################################
73+
# DRY RUN MODE
74+
#######################################
75+
if [[ "${DRY_RUN:-false}" == "true" ]]; then
76+
if [[ -z "${DRY_RUN_USER:-}" ]]; then
77+
log "ERROR: DRY_RUN_USER must be set when DRY_RUN=true"
78+
exit 1
79+
fi
80+
81+
USER="$DRY_RUN_USER"
82+
log "DRY RUN MODE ENABLED"
83+
log "Repository: $REPO"
84+
log "User: @$USER"
85+
86+
COUNTS=$(get_intermediate_count "$USER")
87+
INT_COUNT=$(jq '.data.repository.intermediate.totalCount' <<<"$COUNTS")
88+
89+
echo
90+
log "Intermediate Issues (closed): $INT_COUNT"
91+
92+
if (( INT_COUNT >= 1 )); then
93+
log "Result: USER QUALIFIED"
94+
else
95+
log "Result: USER NOT QUALIFIED"
96+
fi
97+
1898
exit 0
1999
fi
20100

101+
#######################################
102+
# NORMAL MODE (ENFORCEMENT)
103+
#######################################
104+
if [[ -z "${ISSUE_NUMBER:-}" ]]; then
105+
log "ERROR: ISSUE_NUMBER must be set in normal mode"
106+
exit 1
107+
fi
108+
109+
#######################################
110+
# Check a single user
111+
#######################################
21112
check_user() {
22113
local user=$1
23-
log "Checking @$user..."
114+
log "Checking qualification for @$user..."
24115

25-
# Skip core contributors
26-
PERMISSION=$(gh api "repos/$REPO/collaborators/$user/permission" \
27-
--jq '.permission' 2>/dev/null || echo "none")
116+
# Permission exemption
117+
PERMISSION=$(
118+
gh api "repos/$REPO/collaborators/$user/permission" \
119+
--jq '.permission // "none"' 2>/dev/null || echo "none"
120+
)
28121

29122
if [[ "$PERMISSION" =~ ^(admin|write|triage)$ ]]; then
30-
log "@$user is core ($PERMISSION). Skipping."
123+
log "User @$user is core member ($PERMISSION). Skipping."
31124
return 0
32125
fi
33126

34-
# Count completed intermediate ISSUES or PRs
35-
INT_QUERY="repo:$REPO is:closed (is:issue OR is:pr) label:intermediate -reason:\"not planned\" (author:$user OR assignee:$user)"
36-
INT_COUNT=$(gh api search/issues -f q="$INT_QUERY" --jq '.total_count' || echo "0")
127+
COUNTS=$(get_intermediate_count "$user")
128+
INT_COUNT=$(jq '.data.repository.intermediate.totalCount' <<<"$COUNTS")
37129

38-
[[ "$INT_COUNT" =~ ^[0-9]+$ ]] || INT_COUNT=0
130+
log "Counts → Intermediate: $INT_COUNT"
39131

40132
if (( INT_COUNT >= 1 )); then
41-
log "@$user qualified."
133+
log "User @$user qualified."
42134
return 0
43135
fi
44136

45-
log "@$user not qualified. Unassigning."
137+
###################################
138+
# Failure path (duplicate-safe)
139+
###################################
140+
log "User @$user NOT qualified."
46141

47-
MSG="Hi @$user, I can’t assign you to this issue yet.
142+
SUGGESTION="[intermediate issues](https://github.com/$REPO/labels/intermediate)"
48143

49-
**Why?**
50-
Advanced issues involve high-risk changes to core systems and require prior experience.
144+
MSG="Hi @$user, I cannot assign you to this issue yet.
51145
52-
**Requirement:**
53-
- Complete at least **1 intermediate issue** (You have: **$INT_COUNT**)
146+
**Why?**
147+
Advanced issues involve high-risk changes to the core codebase and require prior experience in this repository.
54148
55-
Please check out available [intermediate issues](https://github.com/$REPO/labels/intermediate) to build your experience first."
149+
**Requirement:**
150+
- Complete at least **1** 'intermediate' issue (You have: **$INT_COUNT**)
56151
57-
gh issue comment "$ISSUE_NUMBER" --repo "$REPO" --body "$MSG"
58-
gh issue edit "$ISSUE_NUMBER" --repo "$REPO" --remove-assignee "$user"
152+
Please check out our **$SUGGESTION** to build your experience first!"
153+
154+
if already_commented "$user"; then
155+
log "Comment already exists for @$user. Skipping comment."
156+
else
157+
log "Posting comment for @$user."
158+
gh issue comment "$ISSUE_NUMBER" --repo "$REPO" --body "$MSG"
159+
fi
160+
161+
if is_assigned "$user"; then
162+
log "Unassigning @$user."
163+
gh issue edit "$ISSUE_NUMBER" --repo "$REPO" --remove-assignee "$user"
164+
else
165+
log "User @$user already unassigned. Skipping."
166+
fi
59167
}
60168

61-
check_user "$TRIGGER_ASSIGNEE"
169+
#######################################
170+
# Main execution
171+
#######################################
172+
log "Normal enforcement mode enabled"
173+
log "Repository: $REPO"
174+
log "Issue: #$ISSUE_NUMBER"
175+
176+
if [[ -n "${TRIGGER_ASSIGNEE:-}" ]]; then
177+
check_user "$TRIGGER_ASSIGNEE"
178+
else
179+
log "Checking all assignees..."
180+
181+
ASSIGNEES=$(
182+
gh issue view "$ISSUE_NUMBER" --repo "$REPO" \
183+
--json assignees --jq '.assignees[].login'
184+
)
185+
186+
if [[ -z "$ASSIGNEES" ]]; then
187+
log "No assignees found."
188+
exit 0
189+
fi
190+
191+
while read -r user; do
192+
[[ -n "$user" ]] && check_user "$user"
193+
done <<< "$ASSIGNEES"
194+
fi

.github/workflows/bot-advanced-check.yml

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,25 +4,40 @@ on:
44
issues:
55
types: [assigned, labeled]
66

7+
workflow_dispatch:
8+
inputs:
9+
username:
10+
description: "GitHub username to dry-run qualification check"
11+
required: true
12+
type: string
13+
714
permissions:
815
issues: write
916

1017
concurrency:
11-
group: ${{ github.workflow }}-${{ github.event.issue.number }}
18+
group: ${{ github.workflow }}-${{ github.event.issue.number || github.run_id }}
1219
cancel-in-progress: true
1320

1421
jobs:
22+
#######################################
23+
# Automatic enforcement (issues)
24+
#######################################
1525
check-advanced-qualification:
16-
# Run only for issues labeled 'advanced', triggered by assignment or by adding
17-
# the label to an issue that already has assignees
1826
if: >
19-
contains(github.event.issue.labels.*.name, 'advanced') &&
20-
(github.event.action == 'assigned' || (github.event.action == 'labeled' && github.event.issue.assignees[0] != null))
27+
github.event_name == 'issues' &&
28+
contains(github.event.issue.labels.*.name, 'advanced') &&
29+
(
30+
github.event.action == 'assigned' ||
31+
(
32+
github.event.action == 'labeled' &&
33+
github.event.label.name == 'advanced' &&
34+
github.event.issue.assignees[0] != null
35+
)
36+
)
37+
2138
runs-on: ubuntu-latest
2239
steps:
2340
- name: Checkout scripts
24-
# Pinned to v6.0.1 (8e8c483db84b4bee98b60c0593521ed34d9990e8)
25-
# Followed guide: https://github.com/actions/checkout/releases
2641
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
2742
with:
2843
sparse-checkout: .github/scripts
@@ -35,4 +50,4 @@ jobs:
3550
REPO: ${{ github.repository }}
3651
run: |
3752
chmod +x .github/scripts/bot-advanced-check.sh
38-
./.github/scripts/bot-advanced-check.sh
53+
./.github/scripts/bot-advanced-check.sh

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ This changelog is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.
9595
- Added technical docstrings and hardening (set -euo pipefail) to the pr-check-test-files.sh script (#1336)
9696

9797
### Changed
98+
- Improved reliability of the advanced issue assignment bot by correcting qualification checks and preventing erroneous contributor unassignments. (#1400)
9899
- Renamed `.github/scripts/check_advanced_requirement.sh` to `bot-advanced-check.sh` for workflow consistency (#1341)
99100
- Added global review instructions to CodeRabbit configuration to limit reviews to issue/PR scope and prevent scope creep [#1373]
100101
- Archived unused auto draft GitHub workflow to prevent it from running (#1371)

0 commit comments

Comments
 (0)