Skip to content

Commit f090551

Browse files
authored
Merge pull request #148 from exploreriii/shared-libraries-guards
chore: added eligibility flexible guards, routing and logging extensive
2 parents b3b8b34 + 38d7ab7 commit f090551

6 files changed

Lines changed: 331 additions & 70 deletions

File tree

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

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,29 @@ const REJECTION_REASONS =
1212

1313
const rejectionRouter = ({ reason, context = {}, username, urls = {} }) => {
1414
switch (reason) {
15-
// ───── Beginner → Intermediate ladder
15+
// ─────────────────────────────────────────
16+
// Beginner eligibility failures
17+
// ─────────────────────────────────────────
1618
case REJECTION_REASONS.MISSING_GFI:
19+
return beginnerRejection({
20+
username,
21+
completedGfiCount: context.completedGfiCount ?? 0,
22+
browseGfiUrl: urls.gfi,
23+
});
24+
25+
// ─────────────────────────────────────────
26+
// Intermediate eligibility failures
27+
// ─────────────────────────────────────────
1728
case REJECTION_REASONS.MISSING_BEGINNER:
1829
return intermediateRejection({
1930
username,
20-
requiredGfiCount: context.requiredGfiCount ?? 1,
2131
requiredBeginnerCount: context.requiredBeginnerCount ?? 1,
22-
browseGfiUrl: urls.gfi,
2332
browseBeginnerUrl: urls.beginner,
2433
});
2534

26-
// ───── Intermediate → Advanced ladder
35+
// ─────────────────────────────────────────
36+
// Advanced eligibility failures
37+
// ─────────────────────────────────────────
2738
case REJECTION_REASONS.MISSING_INTERMEDIATE:
2839
return advancedRejection({
2940
username,
@@ -49,7 +60,7 @@ const rejectionRouter = ({ reason, context = {}, username, urls = {} }) => {
4960
});
5061

5162
// ─────────────────────────────────────────
52-
// Spam attempting non-GFI assignment
63+
// Spam attempting disallowed assignment
5364
// ─────────────────────────────────────────
5465
case REJECTION_REASONS.SPAM:
5566
return spamNonGfiAssignment(username);

.github/scripts/lib/eligibility/has-eligibility-01-gfi.js

Lines changed: 71 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,41 @@
1+
/**
2+
* Determines whether a contributor is eligible to be assigned
3+
* a Good First Issue (GFI).
4+
*
5+
* ELIGIBILITY RULES:
6+
* - Repository team members (triage / write / maintain / admin)
7+
* always bypass GFI eligibility checks.
8+
* - Spam-listed contributors may have at most
9+
* `MAX_OPEN_ISSUES_SPAM_LIST` open GFI assignments.
10+
* - All other contributors may have at most
11+
* `MAX_OPEN_ISSUES_NORMAL` open GFI assignments.
12+
*
13+
* IMPORTANT:
14+
* - This helper is policy-only.
15+
* - It does NOT assign issues.
16+
* - It does NOT post comments.
17+
* - Callers must route rejections via `rejectionRouter`.
18+
*
19+
* @param {Object} params
20+
* @param {import('@actions/github').GitHub} params.github
21+
* @param {string} params.owner
22+
* @param {string} params.repo
23+
* @param {string} params.username
24+
* @returns {Promise<{
25+
* eligible: boolean,
26+
* reason?: string,
27+
* context?: Object
28+
* }>}
29+
*/
130
const { isTeam } = require('../team/has-team');
231
const { isOnSpamList } = require('../counts/is-on-spam-list');
3-
const { countOpenAssignedIssues } = require('../counts/count-opened-assigned-issues');
32+
const { countOpenAssignedIssues } =
33+
require('../counts/count-opened-assigned-issues');
434
const REJECTION_REASONS = require('./rejection-reasons');
535

36+
// ─────────────────────────────────────────────
37+
// Policy configuration
38+
// ─────────────────────────────────────────────
639
const MAX_OPEN_ISSUES_NORMAL = 2;
740
const MAX_OPEN_ISSUES_SPAM_LIST = 1;
841

@@ -12,10 +45,26 @@ const hasGfiEligibility = async ({
1245
repo,
1346
username,
1447
}) => {
48+
console.log('[has-gfi-eligibility] Start', {
49+
owner,
50+
repo,
51+
username,
52+
});
53+
54+
// ─────────────────────────────────────────────
55+
// Team members bypass all GFI restrictions
56+
// ─────────────────────────────────────────────
1557
if (await isTeam({ github, owner, repo, username })) {
58+
console.log('[has-gfi-eligibility] Bypass: team member', {
59+
username,
60+
});
61+
1662
return { eligible: true };
1763
}
1864

65+
// ─────────────────────────────────────────────
66+
// Spam list evaluation
67+
// ─────────────────────────────────────────────
1968
const isSpamListed = await isOnSpamList({
2069
github,
2170
owner,
@@ -27,14 +76,28 @@ const hasGfiEligibility = async ({
2776
? MAX_OPEN_ISSUES_SPAM_LIST
2877
: MAX_OPEN_ISSUES_NORMAL;
2978

79+
// ─────────────────────────────────────────────
80+
// Capacity check
81+
// ─────────────────────────────────────────────
3082
const openAssignedCount = await countOpenAssignedIssues({
3183
github,
3284
owner,
3385
repo,
3486
username,
3587
});
3688

89+
console.log('[has-gfi-eligibility] Capacity evaluation', {
90+
username,
91+
isSpamListed,
92+
openAssignedCount,
93+
maxAllowed,
94+
});
95+
3796
if (openAssignedCount >= maxAllowed) {
97+
console.log('[has-gfi-eligibility] Rejected: capacity exceeded', {
98+
username,
99+
});
100+
38101
return {
39102
eligible: false,
40103
reason: REJECTION_REASONS.CAPACITY,
@@ -46,6 +109,13 @@ const hasGfiEligibility = async ({
46109
};
47110
}
48111

112+
// ─────────────────────────────────────────────
113+
// Eligible
114+
// ─────────────────────────────────────────────
115+
console.log('[has-gfi-eligibility] Eligible', {
116+
username,
117+
});
118+
49119
return { eligible: true };
50120
};
51121

.github/scripts/lib/eligibility/has-eligibility-02-beginner.js

Lines changed: 81 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,35 @@
1+
/**
2+
* Determines whether a contributor is eligible to be assigned
3+
* a Beginner issue.
4+
*
5+
* ELIGIBILITY RULES:
6+
* - Team members bypass all checks
7+
* - Spam-listed users are never eligible
8+
* - Max open assignments enforced
9+
* - Must have completed at least N Good First Issues
10+
*
11+
* @param {Object} params
12+
* @param {import('@actions/github').GitHub} params.github
13+
* @param {string} params.owner
14+
* @param {string} params.repo
15+
* @param {string} params.username
16+
* @returns {Promise<{
17+
* eligible: boolean,
18+
* reason?: string,
19+
* context?: Object
20+
* }>}
21+
*/
122
const { isTeam } = require('../team/has-team');
223
const { isOnSpamList } = require('../counts/is-on-spam-list');
3-
const { hasCompletedGfi } = require('../counts/has-completed-n-01-gfi');
4-
const { countOpenAssignedIssues } = require('../counts/count-opened-assigned-issues');
24+
const { hasCompletedGfi } =
25+
require('../counts/has-completed-n-01-gfi');
26+
const { countOpenAssignedIssues } =
27+
require('../counts/count-opened-assigned-issues');
528
const REJECTION_REASONS = require('./rejection-reasons');
629

30+
// ─────────────────────────────────────────────
31+
// Policy configuration
32+
// ─────────────────────────────────────────────
733
const MAX_OPEN_ASSIGNED_ISSUES = 2;
834
const REQUIRED_GFI_COUNT = 1;
935

@@ -19,39 +45,73 @@ const hasBeginnerEligibility = async ({
1945
username,
2046
});
2147

48+
// ─────────────────────────────────────────────
2249
// Team members bypass everything
50+
// ─────────────────────────────────────────────
2351
if (await isTeam({ github, owner, repo, username })) {
52+
console.log('[has-beginner-eligibility] Bypass: team member', {
53+
username,
54+
});
55+
2456
return { eligible: true };
2557
}
2658

59+
// ─────────────────────────────────────────────
2760
// Spam-listed users are never eligible
28-
if (await isOnSpamList({ github, owner, repo, username })) {
61+
// ─────────────────────────────────────────────
62+
const isSpamListed = await isOnSpamList({
63+
github,
64+
owner,
65+
repo,
66+
username,
67+
});
68+
69+
if (isSpamListed) {
70+
console.log('[has-beginner-eligibility] Rejected: spam listed', {
71+
username,
72+
});
73+
2974
return {
3075
eligible: false,
3176
reason: REJECTION_REASONS.SPAM,
3277
};
3378
}
3479

80+
// ─────────────────────────────────────────────
3581
// Capacity check
82+
// ─────────────────────────────────────────────
3683
const openAssignedCount = await countOpenAssignedIssues({
3784
github,
3885
owner,
3986
repo,
4087
username,
4188
});
4289

90+
console.log('[has-beginner-eligibility] Capacity evaluation', {
91+
username,
92+
openAssignedCount,
93+
maxAllowed: MAX_OPEN_ASSIGNED_ISSUES,
94+
});
95+
4396
if (openAssignedCount >= MAX_OPEN_ASSIGNED_ISSUES) {
97+
console.log('[has-beginner-eligibility] Rejected: capacity exceeded', {
98+
username,
99+
});
100+
44101
return {
45102
eligible: false,
46103
reason: REJECTION_REASONS.CAPACITY,
47104
context: {
48105
openAssignedCount,
49106
maxAllowed: MAX_OPEN_ASSIGNED_ISSUES,
107+
isSpamListed: false,
50108
},
51109
};
52110
}
53111

112+
// ─────────────────────────────────────────────
54113
// GFI prerequisite
114+
// ─────────────────────────────────────────────
55115
const hasRequiredGfi = await hasCompletedGfi({
56116
github,
57117
owner,
@@ -60,18 +120,34 @@ const hasBeginnerEligibility = async ({
60120
requiredCount: REQUIRED_GFI_COUNT,
61121
});
62122

123+
console.log('[has-beginner-eligibility] GFI prerequisite', {
124+
username,
125+
requiredGfiCount: REQUIRED_GFI_COUNT,
126+
satisfied: hasRequiredGfi,
127+
});
128+
63129
if (!hasRequiredGfi) {
130+
console.log('[has-beginner-eligibility] Rejected: missing GFI', {
131+
username,
132+
});
133+
64134
return {
65135
eligible: false,
66136
reason: REJECTION_REASONS.MISSING_GFI,
67137
context: {
68-
completedGfiCount: 0, // safe fallback
138+
requiredGfiCount: REQUIRED_GFI_COUNT,
69139
},
70140
};
71141
}
72142

143+
// ─────────────────────────────────────────────
144+
// Eligible
145+
// ─────────────────────────────────────────────
146+
console.log('[has-beginner-eligibility] Eligible', {
147+
username,
148+
});
149+
73150
return { eligible: true };
74151
};
75152

76153
module.exports = { hasBeginnerEligibility };
77-

0 commit comments

Comments
 (0)