Skip to content

Commit c041cdb

Browse files
authored
Merge pull request #95 from exploreriii/shared-libraries-guards
feat: enforce intermediate and advanced
2 parents 3f117df + f14bd73 commit c041cdb

File tree

6 files changed

+209
-5
lines changed

6 files changed

+209
-5
lines changed
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
const { hasIntermediateEligibility } = require('../lib/eligibility/has-eligibility-03-intermediate');
2+
const { rejectionRouter } = require('../lib/comments/rejection-router');
3+
4+
const INTERMEDIATE_LABEL = 'Intermediate';
5+
6+
function isIntermediate(issue) {
7+
return (issue.labels ?? []).some(l => l.name === INTERMEDIATE_LABEL);
8+
}
9+
10+
module.exports = async ({ github, context }) => {
11+
const { issue, assignee } = context.payload;
12+
const { owner, repo } = context.repo;
13+
14+
if (!issue || !assignee) return;
15+
if (!isIntermediate(issue)) return;
16+
17+
const username = assignee.login;
18+
19+
const result = await hasIntermediateEligibility({
20+
github,
21+
owner,
22+
repo,
23+
username,
24+
});
25+
26+
if (result.eligible) {
27+
console.log('[intermediate-enforce] Assignment allowed', {
28+
issue: issue.number,
29+
username,
30+
});
31+
return;
32+
}
33+
34+
// ❌ Not eligible → unassign
35+
await github.rest.issues.removeAssignees({
36+
owner,
37+
repo,
38+
issue_number: issue.number,
39+
assignees: [username],
40+
});
41+
42+
const body = rejectionRouter({
43+
reason: result.reason,
44+
context: result.context,
45+
username,
46+
});
47+
48+
if (body) {
49+
await github.rest.issues.createComment({
50+
owner,
51+
repo,
52+
issue_number: issue.number,
53+
body,
54+
});
55+
}
56+
57+
console.log('[intermediate-enforce] Assignment reverted', {
58+
issue: issue.number,
59+
username,
60+
reason: result.reason,
61+
});
62+
};
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
const { hasAdvancedEligibility } =
2+
require('../lib/eligibility/has-advanced-eligibility');
3+
const { rejectionRouter } =
4+
require('../lib/comments/rejection-router');
5+
6+
const ADVANCED_LABEL = 'Advanced';
7+
8+
function isAdvanced(issue) {
9+
return (issue.labels ?? []).some(l => l.name === ADVANCED_LABEL);
10+
}
11+
12+
module.exports = async ({ github, context }) => {
13+
const { issue, assignee } = context.payload;
14+
const { owner, repo } = context.repo;
15+
16+
if (!issue || !assignee) return;
17+
if (!isAdvanced(issue)) return;
18+
19+
const username = assignee.login;
20+
21+
console.log('[advanced-enforce] Checking eligibility', {
22+
issue: issue.number,
23+
username,
24+
});
25+
26+
const result = await hasAdvancedEligibility({
27+
github,
28+
owner,
29+
repo,
30+
username,
31+
});
32+
33+
if (result.eligible) {
34+
console.log('[advanced-enforce] Assignment allowed', {
35+
issue: issue.number,
36+
username,
37+
});
38+
return;
39+
}
40+
41+
// ❌ Not eligible → revert assignment
42+
await github.rest.issues.removeAssignees({
43+
owner,
44+
repo,
45+
issue_number: issue.number,
46+
assignees: [username],
47+
});
48+
49+
const body = rejectionRouter({
50+
reason: result.reason,
51+
context: result.context,
52+
username,
53+
});
54+
55+
if (body) {
56+
await github.rest.issues.createComment({
57+
owner,
58+
repo,
59+
issue_number: issue.number,
60+
body,
61+
});
62+
}
63+
64+
console.log('[advanced-enforce] Assignment reverted', {
65+
issue: issue.number,
66+
username,
67+
reason: result.reason,
68+
});
69+
};

.github/scripts/lib/comments/rejection-router.js

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,34 @@
11
const { beginnerRejection } = require('./difficulty-02-beginner');
2+
const { intermediateRejection } = require('./difficulty-03-intermediate');
3+
const { advancedRejection } = require('./difficulty-04-advanced');
24
const { capacityLimitReached } = require('./max-assignments-reached');
35
const { spamNonGfiAssignment } = require('./spam-restrictions');
46

57
const REJECTION_REASONS =
68
require('../eligibility/rejection-reasons');
79

8-
const rejectionRouter = ({ reason, context, username, urls }) => {
10+
const rejectionRouter = ({ reason, context = {}, username, urls = {} }) => {
911
switch (reason) {
12+
// ───── Beginner → Intermediate ladder
1013
case REJECTION_REASONS.MISSING_GFI:
11-
return beginnerRejection({
14+
case REJECTION_REASONS.MISSING_BEGINNER:
15+
return intermediateRejection({
1216
username,
13-
completedGfiCount: context.completedGfiCount,
17+
requiredGfiCount: context.requiredGfiCount ?? 1,
18+
requiredBeginnerCount: context.requiredBeginnerCount ?? 1,
1419
browseGfiUrl: urls.gfi,
20+
browseBeginnerUrl: urls.beginner,
1521
});
1622

23+
// ───── Intermediate → Advanced ladder
24+
case REJECTION_REASONS.MISSING_INTERMEDIATE:
25+
return advancedRejection({
26+
username,
27+
requiredIntermediateCount: context.requiredCount ?? 1,
28+
browseIntermediateUrl: urls.intermediate,
29+
});
30+
31+
// ───── Shared rules
1732
case REJECTION_REASONS.CAPACITY:
1833
return capacityLimitReached({
1934
username,
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
name: Enforce Advanced Assignment Eligibility
2+
3+
on:
4+
issues:
5+
types:
6+
- assigned
7+
8+
permissions:
9+
issues: write
10+
contents: read
11+
12+
jobs:
13+
enforce-advanced:
14+
runs-on: ubuntu-latest
15+
16+
concurrency:
17+
group: advanced-assign-${{ github.event.issue.number }}
18+
cancel-in-progress: false
19+
20+
steps:
21+
- name: Checkout repository
22+
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
23+
24+
- name: Enforce Advanced eligibility
25+
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd #v8.0.0
26+
with:
27+
script: |
28+
const script = require('./.github/scripts/bots/assign-04-advanced-check.js');
29+
await script({ github, context });

.github/workflows/bot-beginner-assign-on-comment.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,15 @@ permissions:
1010
contents: read
1111

1212
jobs:
13-
gfi-assign:
13+
beginner-assign:
1414
# Only run on issue comments (not PR comments)
1515
if: github.event.issue.pull_request == null
1616

1717
runs-on: ubuntu-latest
1818

1919
# Prevent race conditions: always wait for the first assignment request to finish processing
2020
concurrency:
21-
group: gfi-assign-${{ github.event.issue.number }}
21+
group: beginner-assign-${{ github.event.issue.number }}
2222
cancel-in-progress: false
2323

2424
steps:
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
name: Enforce Intermediate Assignment Eligibility
2+
3+
on:
4+
issues:
5+
types:
6+
- assigned
7+
8+
permissions:
9+
issues: write
10+
contents: read
11+
12+
jobs:
13+
enforce-intermediate:
14+
runs-on: ubuntu-latest
15+
16+
concurrency:
17+
group: intermediate-assign-${{ github.event.issue.number }}
18+
cancel-in-progress: false
19+
20+
steps:
21+
- name: Checkout repository
22+
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
23+
24+
- name: Enforce Advanced eligibility
25+
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd #v8.0.0
26+
with:
27+
script: |
28+
const script = require('./.github/scripts/bots/assign-03-intermediate-check.js');
29+
await script({ github, context });

0 commit comments

Comments
 (0)