Skip to content

Commit e5dadde

Browse files
authored
Merge branch 'hiero-ledger:main' into main
2 parents a8f95fa + 946fec3 commit e5dadde

189 files changed

Lines changed: 3080 additions & 1530 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.coderabbit.yaml

Lines changed: 401 additions & 0 deletions
Large diffs are not rendered by default.

.github/ISSUE_TEMPLATE/03_beginner_issue.yml

Lines changed: 212 additions & 129 deletions
Large diffs are not rendered by default.

.github/scripts/bot-assignment-check.sh

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,7 @@ issue_has_gfi() {
3636
}
3737

3838
assignments_count() {
39-
# Count only issues (not PRs) assigned to the user, using structured isPullRequest field
40-
gh issue list --repo "${REPO}" --assignee "${ASSIGNEE}" --state open --limit 100 --json number,isPullRequest --jq '[.[] | select(.isPullRequest | not)] | length'
39+
gh issue list --repo "${REPO}" --assignee "${ASSIGNEE}" --state open --limit 100 --json number --jq 'length'
4140
}
4241

4342
remove_assignee() {
@@ -76,7 +75,7 @@ Hi @$ASSIGNEE, this is the Assignment Bot.
7675
7776
:warning: **Assignment Limit Exceeded**
7877
79-
Your account currently has limited assignment privileges with a maximum of **1 open issue assignment** at a time.
78+
Your account currently has limited assignment privileges with a maximum of **1 open assignment** at a time.
8079
8180
You currently have $count open issue(s) assigned. Please complete and merge your existing assignment before requesting a new one.
8281
@@ -97,7 +96,7 @@ msg_normal_limit_exceeded() {
9796
cat <<EOF
9897
Hi @$ASSIGNEE, this is the Assignment Bot.
9998
100-
Assigning you to this issue would exceed the limit of 2 open issue assignments.
99+
Assigning you to this issue would exceed the limit of 2 open assignments.
101100
102101
Please resolve and merge your existing assigned issues before requesting new ones.
103102
EOF
Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
/**
2+
* PR Draft Ready Reminder Bot
3+
*
4+
* Triggers when commits are pushed to a draft PR that has
5+
* CHANGES_REQUESTED reviews, and posts a reminder comment.
6+
*
7+
* Safety:
8+
* - Runs on pull_request_target (secure pattern)
9+
* - Skips non-draft PRs
10+
* - Skips bot-authored PRs
11+
* - Skips if reviewDecision !== CHANGES_REQUESTED
12+
* - Prevents duplicate comments via marker
13+
*/
14+
15+
const COMMENT_MARKER = "<!-- draft-ready-reminder-bot -->";
16+
17+
/**
18+
* Checks if the reminder comment already exists.
19+
* Uses GitHub pagination to safely scan all comments.
20+
*
21+
* @param {import("@actions/github").GitHub} params.github - Authenticated GitHub client.
22+
* @param {string} params.owner - Repository owner.
23+
* @param {string} params.repo - Repository name.
24+
* @param {number} params.issueNumber - Pull request number.
25+
* @param {string} params.marker - Unique marker string to detect duplicate comments.
26+
* @returns {Promise<boolean>} - True if a comment with the marker exists.
27+
*/
28+
29+
async function commentExists({ github, owner, repo, issueNumber, marker }) {
30+
console.log("Checking for existing reminder comments...");
31+
32+
let scanned = 0;
33+
const MAX_COMMENTS = 500;
34+
35+
for await (const response of github.paginate.iterator(
36+
github.rest.issues.listComments,
37+
{
38+
owner,
39+
repo,
40+
issue_number: issueNumber,
41+
per_page: 100,
42+
}
43+
)) {
44+
for (const comment of response.data) {
45+
scanned++;
46+
if (comment.body?.includes(marker)) {
47+
console.log(`Found existing reminder comment (scanned ${scanned} comments).`);
48+
return true;
49+
}
50+
if (scanned >= MAX_COMMENTS) {
51+
console.log(`Reached scan limit (${MAX_COMMENTS} comments) — assuming no duplicate.`);
52+
return false;
53+
}
54+
}
55+
}
56+
57+
console.log(`No existing reminder comment found (scanned ${scanned} comments).`);
58+
return false;
59+
}
60+
61+
/**
62+
* Builds the draft-ready reminder comment body.
63+
*
64+
* @param {string} username - PR author username.
65+
* @returns {string} - Formatted reminder message.
66+
*/
67+
function buildReminderComment(username) {
68+
return `
69+
${COMMENT_MARKER}
70+
👋 Hi @${username},
71+
72+
We noticed your pull request has had *recent changes pushed* after *changes were requested*.
73+
74+
If these updates address the feedback, you can:
75+
- resolve any open review conversations (reply if clarification is needed, or mark them as resolved),
76+
- click **“Ready for review”** (recommended), or
77+
- use the \`/review\` command.
78+
79+
Thanks for keeping things moving! 🙌
80+
— Hiero SDK Automation Team
81+
`.trim();
82+
}
83+
84+
/**
85+
* Main entry point for the PR Draft Ready Reminder Bot.
86+
*
87+
* This function:
88+
* 1. Resolves the PR number from the event context or environment.
89+
* 2. Ensures the PR is a draft and not bot-authored.
90+
* 3. Checks whether the review decision is CHANGES_REQUESTED.
91+
* 4. Prevents duplicate reminder comments.
92+
* 5. Posts a reminder comment.
93+
*/
94+
95+
module.exports = async function ({ github, context }) {
96+
97+
// Resolve PR number from event or workflow_dispatch
98+
const prNumber =
99+
context.payload?.pull_request?.number ||
100+
Number(process.env.PR_NUMBER);
101+
102+
if (!prNumber) {
103+
console.log("No PR number found in context or environment — exiting.");
104+
return;
105+
}
106+
107+
const { owner, repo } = context.repo;
108+
109+
console.log(`Processing PR #${prNumber} in ${owner}/${repo}`);
110+
111+
// Fetch PR details
112+
let pr;
113+
try {
114+
console.log("Fetching PR details...");
115+
({ data: pr } = await github.rest.pulls.get({
116+
owner,
117+
repo,
118+
pull_number: prNumber,
119+
}));
120+
} catch (err) {
121+
console.log(`Failed to fetch PR #${prNumber} in ${owner}/${repo}: ${err.message}`);
122+
return;
123+
}
124+
125+
console.log(`PR state → draft=${pr.draft}, author=${pr.user.login}, type=${pr.user?.type}`);
126+
127+
// Early exit: only draft PRs
128+
if (!pr.draft) {
129+
console.log("PR is not a draft — skipping reminder.");
130+
return;
131+
}
132+
133+
// Early exit: Bot authored PRs
134+
if (pr.user?.type === "Bot") {
135+
console.log("PR authored by bot — skipping reminder.");
136+
return;
137+
}
138+
139+
// Fetch reviewDecision via GraphQL
140+
console.log("Fetching reviewDecision via GraphQL...");
141+
142+
const query = `
143+
query ($owner: String!, $repo: String!, $number: Int!) {
144+
repository(owner: $owner, name: $repo) {
145+
pullRequest(number: $number) {
146+
reviewDecision
147+
}
148+
}
149+
}
150+
`;
151+
152+
let reviewDecision;
153+
154+
try {
155+
const result = await github.graphql(query, {
156+
owner,
157+
repo,
158+
number: prNumber,
159+
});
160+
161+
reviewDecision = result?.repository?.pullRequest?.reviewDecision;
162+
163+
if (!reviewDecision) {
164+
console.log("No reviewDecision returned — skipping.");
165+
return;
166+
}
167+
168+
console.log(`reviewDecision = ${reviewDecision}`);
169+
} catch (err) {
170+
console.log(`Failed to fetch reviewDecision for PR #${prNumber} in ${owner}/${repo} — skipping reminder.`);
171+
console.log(`Error: ${err.message}`);
172+
return;
173+
}
174+
175+
// Only trigger when changes were requested
176+
if (reviewDecision !== "CHANGES_REQUESTED") {
177+
console.log("reviewDecision is not CHANGES_REQUESTED — no reminder needed.");
178+
return;
179+
}
180+
181+
// Prevent duplicate comments
182+
let alreadyCommented = false;
183+
try {
184+
alreadyCommented = await commentExists({
185+
github,
186+
owner,
187+
repo,
188+
issueNumber: prNumber,
189+
marker: COMMENT_MARKER,
190+
});
191+
} catch (err) {
192+
console.log(`Failed to check existing comments on PR #${prNumber} in ${owner}/${repo}: ${err.message}`);
193+
console.log("Skipping reminder to avoid potential duplicate.");
194+
return;
195+
}
196+
197+
if (alreadyCommented) {
198+
console.log("Reminder already exists — skipping.");
199+
return;
200+
}
201+
try {
202+
await github.rest.issues.createComment({
203+
owner,
204+
repo,
205+
issue_number: prNumber,
206+
body: buildReminderComment(pr.user.login),
207+
});
208+
209+
console.log(`Reminder successfully posted on PR #${prNumber}`);
210+
} catch (error) {
211+
// Permission handling
212+
console.log("Failed to create reminder comment.");
213+
console.log(`PR #${prNumber} in ${owner}/${repo}`);
214+
console.log(`Error status: ${error.status}`);
215+
console.log(`Error message: ${error.message}`);
216+
}
217+
};

.github/scripts/bot-pr-missing-linked-issue.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,9 @@ module.exports = async ({ github, context }) => {
6262
`🚨 **This pull request does not have an issue linked.**`,
6363
``,
6464
`Please link an issue using the following format:`,
65-
`- Fixes #123`,
65+
'```',
66+
'Fixes #123',
67+
'```',
6668
``,
6769
`📖 Guide:`,
6870
`[docs/sdk_developers/how_to_link_issues.md](https://github.com/${context.repo.owner}/${context.repo.repo}/blob/main/docs/sdk_developers/how_to_link_issues.md)`,

.github/scripts/bot-workflows.js

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -112,12 +112,6 @@ const REPO = process.env.REPO || process.env.GITHUB_REPOSITORY || '';
112112
let DRY_RUN = normaliseDryRun(process.env.DRY_RUN || '1');
113113
let PR_NUMBER = process.env.PR_NUMBER || '';
114114

115-
// Validate workflow name contains only safe characters
116-
if (FAILED_WORKFLOW_NAME && !/^[\w\s\-\.]+$/.test(FAILED_WORKFLOW_NAME)) {
117-
console.error(`ERROR: FAILED_WORKFLOW_NAME contains invalid characters: ${FAILED_WORKFLOW_NAME}`);
118-
process.exit(1);
119-
}
120-
121115
// Set GH_TOKEN environment variable for gh CLI
122116
if (GH_TOKEN) {
123117
process.env.GH_TOKEN = GH_TOKEN;

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ jobs:
1212
runs-on: ubuntu-latest
1313
steps:
1414
- name: Harden the runner
15-
uses: step-security/harden-runner@e3f713f2d8f53843e71c69a996d56f51aa9adfb9 # v2.14.1
15+
uses: step-security/harden-runner@5ef0c079ce82195b2a36a210272d6b661572d83e # v2.14.2
1616
with:
1717
egress-policy: audit
1818

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ jobs:
2323

2424
steps:
2525
- name: Harden runner
26-
uses: step-security/harden-runner@e3f713f2d8f53843e71c69a996d56f51aa9adfb9 # v2.14.1
26+
uses: step-security/harden-runner@5ef0c079ce82195b2a36a210272d6b661572d83e # v2.14.2
2727
with:
2828
egress-policy: audit
2929

.github/workflows/bot-coderabbit-plan-trigger.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ jobs:
3434
3535
steps:
3636
- name: Harden the runner
37-
uses: step-security/harden-runner@e3f713f2d8f53843e71c69a996d56f51aa9adfb9 # v2.14.1
37+
uses: step-security/harden-runner@5ef0c079ce82195b2a36a210272d6b661572d83e # v2.14.2
3838
with:
3939
egress-policy: audit
4040

.github/workflows/bot-community-calls.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ jobs:
2727
runs-on: ubuntu-latest
2828
steps:
2929
- name: Harden the runner
30-
uses: step-security/harden-runner@e3f713f2d8f53843e71c69a996d56f51aa9adfb9 #2.14.1
30+
uses: step-security/harden-runner@5ef0c079ce82195b2a36a210272d6b661572d83e #2.14.2
3131
with:
3232
egress-policy: audit
3333

0 commit comments

Comments
 (0)