Skip to content

Commit 8d0922a

Browse files
authored
feat: Add PR Dashboard (#223)
* feat: Add PR Dashboard Signed-off-by: aceppaluni <aceppaluni@gmail.com> * fix: Apply Suggested Changes Signed-off-by: aceppaluni <aceppaluni@gmail.com> * fix: Update Workflow Signed-off-by: aceppaluni <aceppaluni@gmail.com> * fix: YML Files and Team Signed-off-by: aceppaluni <aceppaluni@gmail.com> * fix: Update Permission Signed-off-by: aceppaluni <aceppaluni@gmail.com> --------- Signed-off-by: aceppaluni <aceppaluni@gmail.com>
1 parent 4bc544f commit 8d0922a

13 files changed

Lines changed: 2514 additions & 0 deletions

.github/hiero-automation.json

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
{
2+
"maintainerTeam": "@hiero-ledger/hiero-enterprise-java-maintainers",
3+
"goodFirstIssueSupportTeam": "@hiero-ledger/hiero-sdk-good-first-issue-support",
4+
5+
"labels": {
6+
"status": {
7+
"awaitingTriage": "status: awaiting triage",
8+
"readyForDev": "status: ready for dev",
9+
"inProgress": "status: in progress",
10+
"blocked": "status: blocked",
11+
"needsReview": "status: needs review",
12+
"needsRevision": "status: needs revision"
13+
},
14+
"skill": {
15+
"goodFirstIssue": "skill: good first issue",
16+
"beginner": "skill: beginner",
17+
"intermediate": "skill: intermediate",
18+
"advanced": "skill: advanced"
19+
},
20+
"priority": {
21+
"critical": "priority: critical",
22+
"high": "priority: high",
23+
"medium": "priority: medium",
24+
"low": "priority: low"
25+
}
26+
},
27+
28+
"skillHierarchy": [
29+
"skill: good first issue",
30+
"skill: beginner",
31+
"skill: intermediate",
32+
"skill: advanced"
33+
],
34+
35+
"priorityHierarchy": [
36+
"priority: critical",
37+
"priority: high",
38+
"priority: medium",
39+
"priority: low"
40+
],
41+
42+
"skillPrerequisites": {
43+
"skill: good first issue": {
44+
"requiredLabel": null,
45+
"requiredCount": 0,
46+
"displayName": "Good First Issue"
47+
},
48+
"skill: beginner": {
49+
"requiredLabel": "skill: good first issue",
50+
"requiredCount": 2,
51+
"displayName": "Beginner",
52+
"prerequisiteDisplayName": "Good First Issues"
53+
},
54+
"skill: intermediate": {
55+
"requiredLabel": "skill: beginner",
56+
"requiredCount": 3,
57+
"displayName": "Intermediate",
58+
"prerequisiteDisplayName": "Beginner Issues"
59+
},
60+
"skill: advanced": {
61+
"requiredLabel": "skill: intermediate",
62+
"requiredCount": 3,
63+
"displayName": "Advanced",
64+
"prerequisiteDisplayName": "Intermediate Issues"
65+
}
66+
},
67+
68+
"assignmentLimits": {
69+
"maxOpenAssignments": 2,
70+
"maxGfiCompletions": 5
71+
},
72+
73+
"documentation": {
74+
"workflowGuide": "https://github.com/hiero-ledger/hiero-website/blob/main/docs/training/workflow.md"
75+
},
76+
77+
"community": {
78+
"discordChannel": "https://discord.com/channels/905194001349627914/1458790070986215567"
79+
}
80+
}

.github/scripts/bot-on-pr-open.js

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
//
3+
// bot-on-pr-open.js
4+
//
5+
// Runs when a PR is opened, reopened, or converted from draft (ready_for_review).
6+
// Performs all 4 checks (DCO, GPG, merge conflict, issue link), posts/updates
7+
// the unified dashboard comment, auto-assigns the author, and applies the
8+
// appropriate status label.
9+
10+
const {
11+
createLogger,
12+
buildBotContext,
13+
addAssignees,
14+
requireSafeUsername,
15+
runAllChecksAndComment,
16+
swapStatusLabel,
17+
} = require('./helpers');
18+
19+
const logger = createLogger('on-pr-open');
20+
21+
/**
22+
* Auto-assigns the PR author if not already assigned.
23+
* @param {object} botContext
24+
*/
25+
async function autoAssignAuthor(botContext) {
26+
const prAuthor = botContext.pr?.user?.login;
27+
if (!prAuthor) {
28+
logger.log('Exit: missing pull request author');
29+
return;
30+
}
31+
try {
32+
requireSafeUsername(prAuthor, 'pr.author');
33+
} catch (err) {
34+
logger.log('Exit: invalid pr.author', err.message);
35+
return;
36+
}
37+
38+
const currentAssignees = botContext.pr?.assignees || [];
39+
const isAlreadyAssigned = currentAssignees.some(
40+
(a) => (a?.login || '').toLowerCase() === prAuthor.toLowerCase()
41+
);
42+
if (isAlreadyAssigned) {
43+
logger.log(`Author ${prAuthor} is already assigned`);
44+
return;
45+
}
46+
await addAssignees(botContext, [prAuthor]);
47+
}
48+
49+
module.exports = async ({ github, context }) => {
50+
try {
51+
const botContext = buildBotContext({ github, context });
52+
53+
await autoAssignAuthor(botContext);
54+
55+
if (botContext.pr?.user?.type === 'Bot') {
56+
logger.log('Skipping bot-authored PR');
57+
return;
58+
}
59+
60+
const { allPassed } = await runAllChecksAndComment(botContext);
61+
const result = await swapStatusLabel(botContext, allPassed, { force: true });
62+
63+
if (!result.success) {
64+
logger.error(`Failed to swap status label: ${result.errorDetails}`);
65+
}
66+
67+
logger.log('On-PR-open bot completed');
68+
} catch (error) {
69+
logger.error('Error:', {
70+
message: error.message,
71+
number: context?.payload?.pull_request?.number,
72+
});
73+
throw error;
74+
}
75+
};
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
//
3+
// bot-on-pr-update.js
4+
//
5+
// Runs on new commits (synchronize) and PR body edits (edited). Performs all
6+
// 4 checks (DCO, GPG, merge conflict, issue link), posts/updates the unified
7+
// dashboard comment, and conditionally swaps the status label.
8+
// For edited events, exits early if only the title or base branch changed.
9+
10+
const {
11+
createLogger,
12+
buildBotContext,
13+
swapStatusLabel,
14+
runAllChecksAndComment,
15+
} = require('./helpers');
16+
17+
const logger = createLogger('on-pr-update');
18+
19+
module.exports = async ({ github, context }) => {
20+
try {
21+
const botContext = buildBotContext({ github, context });
22+
23+
if (botContext.pr?.user?.type === 'Bot') {
24+
logger.log('Skipping bot-authored PR');
25+
return;
26+
}
27+
28+
// Edits can be triggered by title changes, but we only care about body changes.
29+
if (context.payload.action === 'edited' && !context.payload.changes?.body) {
30+
logger.log('Body not changed, skipping');
31+
return;
32+
}
33+
34+
const { allPassed } = await runAllChecksAndComment(botContext);
35+
const result = await swapStatusLabel(botContext, allPassed);
36+
if (!result.success) {
37+
logger.error(`Failed to swap status label: ${result.errorDetails}`);
38+
}
39+
40+
logger.log('On-PR-update bot completed');
41+
} catch (error) {
42+
logger.error('Error:', {
43+
message: error.message,
44+
number: context?.payload?.pull_request?.number,
45+
});
46+
throw error;
47+
}
48+
};

0 commit comments

Comments
 (0)