Skip to content

Commit ac7fb4b

Browse files
authored
Merge branch 'hiero-ledger:main' into main
2 parents 579a173 + 390f5e0 commit ac7fb4b

13 files changed

Lines changed: 295 additions & 66 deletions

.coderabbit.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1249,12 +1249,12 @@ reviews:
12491249
2. Extract the module name before `_pb2`
12501250
(e.g. `transaction_record` from `transaction_record_pb2`)
12511251
3. Build the canonical URL:
1252-
`https://github.com/hashgraph/hedera-protobufs/blob/v0.66.0/<path>/<module>.proto`
1252+
`https://github.com/hashgraph/hedera-protobufs/blob/v0.72.0-rc.2/<path>/<module>.proto`
12531253

12541254
**Example:**
12551255
```
12561256
Import: from hiero_sdk_python.hapi.services import transaction_record_pb2
1257-
Schema: https://github.com/hashgraph/hedera-protobufs/blob/v0.66.0/services/transaction_record.proto
1257+
Schema: https://github.com/hashgraph/hedera-protobufs/blob/v0.72.0-rc.2/services/transaction_record.proto
12581258
```
12591259

12601260
### Step 3 — Compare the SDK class against the proto schema
Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
/**
2+
* PR Draft Explainer Bot
3+
*
4+
* Triggers when a pull request is converted to draft.
5+
*
6+
* Safety:
7+
* - Prevents duplicate comments using a unique HTML marker.
8+
* - Only posts if a CHANGES_REQUESTED review exists.
9+
* - Fails safely if review lookup fails.
10+
* - Uses pagination to scan existing comments safely.
11+
*/
12+
13+
const COMMENT_MARKER = "<!-- pr-draft-explainer -->";
14+
const DRY_RUN = process.env.DRY_RUN === "true";
15+
const manualPRNumber = process.env.PR_NUMBER;
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 explanation 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 explanation 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 explainer comment found (scanned ${scanned} comments).`);
58+
return false;
59+
}
60+
61+
/**
62+
* Builds the draft explainer comment body.
63+
*
64+
* @param {string} greetingTarget - Formatted username to greet (e.g., "@username").
65+
* @returns {string} - Formatted reminder message.
66+
*/
67+
68+
function buildExplainerComment(greetingTarget) {
69+
return `
70+
${COMMENT_MARKER}
71+
Hi ${greetingTarget}!
72+
73+
We suggested a few updates and moved this PR to **draft** while you apply the feedback. This keeps it out of the review queue until it is ready again.
74+
75+
### What happens next?
76+
- Make the requested changes.
77+
- When you are ready, click **“Ready for review”** (recommended) or use the \`/review\` command.
78+
79+
Thanks again for your contribution!
80+
`.trim();
81+
}
82+
83+
/**
84+
* Main entry point.
85+
*
86+
* Execution Flow:
87+
* 1. Ensure PR exists in event payload.
88+
* 2. Prevent duplicate bot comments.
89+
* 3. Confirm at least one CHANGES_REQUESTED review exists.
90+
* 4. Post explanation comment.
91+
*/
92+
93+
module.exports = async ({ github, context }) => {
94+
let pr = context.payload.pull_request;
95+
let prNumber = pr?.number || manualPRNumber;
96+
const { owner, repo } = context.repo;
97+
98+
if (!prNumber) {
99+
console.log("No PR number found in payload or environment. Exiting.");
100+
return;
101+
}
102+
if (!pr) {
103+
console.log(`Fetching PR #${prNumber} (manual workflow_dispatch run)...`);
104+
105+
try {
106+
const prResponse = await github.rest.pulls.get({
107+
owner,
108+
repo,
109+
pull_number: prNumber,
110+
});
111+
112+
pr = prResponse.data;
113+
} catch (error) {
114+
console.log(`Failed to fetch PR #${prNumber}: ${error.message}`);
115+
return;
116+
}
117+
}
118+
119+
const authorLogin = pr.user?.login;
120+
const greetingTarget = authorLogin ? `@${authorLogin}` : "there";
121+
122+
if (!pr.draft) {
123+
console.log(`PR #${prNumber} is not draft. Skipping.`);
124+
return;
125+
}
126+
127+
console.log(`PR #${prNumber} was converted to draft. Checking if explanation is needed.`);
128+
129+
// Prevent duplicate comments
130+
let alreadyCommented = false;
131+
try {
132+
alreadyCommented = await commentExists({
133+
github,
134+
owner,
135+
repo,
136+
issueNumber: prNumber,
137+
marker: COMMENT_MARKER,
138+
});
139+
} catch (err) {
140+
console.log(`Failed to check existing comments on PR #${prNumber} in ${owner}/${repo}: ${err.message}`);
141+
console.log("Skipping explanation to avoid potential duplicate.");
142+
return;
143+
}
144+
145+
if (alreadyCommented) {
146+
console.log("Explanation already exists — skipping.");
147+
return;
148+
}
149+
150+
// Only proceed if changes were previously requested on this PR
151+
try {
152+
const reviews = await github.rest.pulls.listReviews({
153+
owner,
154+
repo,
155+
pull_number: prNumber,
156+
per_page: 100,
157+
});
158+
159+
// Track the latest review from each reviewer
160+
const latestReviews = new Map();
161+
162+
for (const review of reviews.data) {
163+
const reviewer = review.user?.login;
164+
if (!reviewer) continue;
165+
166+
const previous = latestReviews.get(reviewer);
167+
168+
if (!previous || new Date(review.submitted_at) > new Date(previous.submitted_at)) {
169+
latestReviews.set(reviewer, review);
170+
}
171+
}
172+
173+
const hasChangeRequest = [...latestReviews.values()].some(
174+
(review) => review.state === "CHANGES_REQUESTED"
175+
);
176+
177+
if (!hasChangeRequest) {
178+
console.log("No CHANGES_REQUESTED review found. Skipping explanation comment.");
179+
return;
180+
}
181+
182+
} catch (error) {
183+
console.log(`Review lookup failed for PR #${prNumber}: ${error.message}. Skipping to avoid a false explanation.`);
184+
return;
185+
}
186+
187+
// Post explanation comment
188+
try {
189+
if (DRY_RUN) {
190+
console.log(`[DRY RUN] Explanation comment would be posted on PR #${prNumber}.`);
191+
return;
192+
}
193+
await github.rest.issues.createComment({
194+
owner,
195+
repo,
196+
issue_number: prNumber,
197+
body: buildExplainerComment(greetingTarget),
198+
});
199+
console.log(`Posted draft explanation comment on PR #${prNumber}.`);
200+
} catch (error) {
201+
console.log(`Failed to post draft explanation on PR #${prNumber}: ${error.message}`);
202+
}
203+
};

.github/spam-list.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ Halbot100
66
roberthallers
77
SergioChan
88
ndpvt-web
9+
OnlyTerp

.github/workflows/bot-gfi-candidate-notification.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ permissions:
1313

1414
jobs:
1515
gfi_candidate_notify_team:
16-
runs-on: ubuntu-latest
16+
runs-on: hl-sdk-py-lin-md
1717
if: contains(github.event.issue.labels.*.name, 'good first issue candidate')
1818
concurrency:
1919
group: gfi-candidate-${{ github.event.issue.number }}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# This workflow posts a friendly explanation when a PR is moved to draft status after changes are requested
2+
3+
name: PR Draft Explainer
4+
on:
5+
pull_request:
6+
types: [converted_to_draft]
7+
workflow_dispatch:
8+
inputs:
9+
pr_number:
10+
description: "PR number to test"
11+
required: true
12+
type: number
13+
dry_run:
14+
description: "Run without posting comment"
15+
required: false
16+
type: boolean
17+
default: true
18+
19+
permissions:
20+
pull-requests: read
21+
issues: write
22+
contents: read
23+
24+
jobs:
25+
pr-draft-explainer:
26+
runs-on: ubuntu-latest
27+
concurrency:
28+
group: pr-draft-explainer-${{ github.event.pull_request.number || github.event.inputs.pr_number }}
29+
cancel-in-progress: true
30+
steps:
31+
- name: Harden the runner
32+
uses: step-security/harden-runner@5ef0c079ce82195b2a36a210272d6b661572d83e # v2.14.2
33+
with:
34+
egress-policy: audit
35+
36+
- name: Checkout repository
37+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
38+
with:
39+
ref: main
40+
41+
- name: Run draft explainer bot
42+
env:
43+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
44+
DRY_RUN: ${{ github.event.inputs.dry_run || 'false' }}
45+
PR_NUMBER: ${{ github.event.pull_request.number || github.event.inputs.pr_number }}
46+
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd #v8.0.0
47+
with:
48+
script: |
49+
const script = require('./.github/scripts/bot-pr-draft-explainer.js');
50+
await script({ github, context });

.github/workflows/deps-check.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ jobs:
3737
cache: "pip"
3838

3939
- name: Install uv
40-
uses: astral-sh/setup-uv@5a095e7a2014a4212f075830d4f7277575a9d098 # v7.3.1
40+
uses: astral-sh/setup-uv@e06108dd0aef18192324c70427afc47652e63a82 # v7.5.0
4141

4242
- name: Create virtual environment
4343
run: uv venv
@@ -69,8 +69,8 @@ jobs:
6969
7070
- name: Generate Proto Files
7171
run: |
72-
uv run python generate_proto.py
72+
uv run --resolution=lowest-direct python generate_proto.py
7373
7474
- name: Run unit tests (min deps)
7575
run: |
76-
uv run pytest tests/unit -v
76+
uv run --resolution=lowest-direct pytest tests/unit -v

.github/workflows/pr-check-codecov.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ jobs:
2929
python-version: "3.11"
3030

3131
- name: Install uv
32-
uses: astral-sh/setup-uv@5a095e7a2014a4212f075830d4f7277575a9d098 # v7.3.1
32+
uses: astral-sh/setup-uv@e06108dd0aef18192324c70427afc47652e63a82 # v7.5.0
3333

3434
- name: Install dependencies
3535
run: |

.github/workflows/pr-check-examples.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ jobs:
2424
fetch-depth: 0
2525

2626
- name: Install uv
27-
uses: astral-sh/setup-uv@5a095e7a2014a4212f075830d4f7277575a9d098 # v7.3.1
27+
uses: astral-sh/setup-uv@e06108dd0aef18192324c70427afc47652e63a82 # v7.5.0
2828

2929
- name: Install dependencies
3030
run: uv sync --all-extras

.github/workflows/pr-check-test.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ jobs:
4949
python-version: ${{ matrix.python-version }}
5050

5151
- name: Install uv
52-
uses: astral-sh/setup-uv@5a095e7a2014a4212f075830d4f7277575a9d098 # v7.3.1
52+
uses: astral-sh/setup-uv@e06108dd0aef18192324c70427afc47652e63a82 # v7.5.0
5353
with:
5454
enable-cache: true
5555

@@ -112,7 +112,7 @@ jobs:
112112
python-version: ${{ matrix.python-version }}
113113

114114
- name: Install uv
115-
uses: astral-sh/setup-uv@5a095e7a2014a4212f075830d4f7277575a9d098 # v7.3.1
115+
uses: astral-sh/setup-uv@e06108dd0aef18192324c70427afc47652e63a82 # v7.5.0
116116
with:
117117
enable-cache: true
118118

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,17 @@ This changelog is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.
1212

1313
### Changed
1414
- Changed pytest version to "pytest>=8.3.4,<10" (#1917)
15+
- Update protobuf schema version to v0.72.0-rc.2 in `.coderabbit.yaml`
1516

1617
### Src
1718
- Updated `generated_proto.py` file to work with new proto version
19+
- fix: Ensure UTF-8 encoding when reading and writing proto files in `generate_proto.py` to prevent encoding issues on Windows (`#1963`)
20+
1821

1922
### Examples
23+
- Updated the `examples/consensus/topic_create_transaction_revenue_generating.py` example to use `Client.from_env()` for simpler client setup. (#1964)
24+
25+
- Refactored `examples/consensus/topic_delete_transaction.py` to use Client.from_env() for simplified client initialization, removed manual setup code, and cleaned up unused imports (`os`, `AccountId`, `PrivateKey`). (`#1971`)
2026

2127
### Tests
2228

@@ -28,6 +34,8 @@ This changelog is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.
2834

2935

3036
### .github
37+
- chore: ensure uv run uses lowest-direct resolution in deps-check workflow (#1919)
38+
- Added PR draft explainer workflow to comment when PRs are converted to draft after changes are requested. (#1723)
3139
- changed `pr-check-test` to run unit matrix first, run integration matrix only after unit success, skip docs/examples/.github-only changes, and parallelize integration tests with xdist (`#1878`)
3240
- archived workflows relating to PR reminders
3341
- chore: switch workflow runner from ubuntu-latest to hl-sdk-py-lin-md for bot-assignment-check.yml workflow
@@ -36,9 +44,11 @@ This changelog is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.
3644
- chore: update bot-coderabbit-plan-trigger workflow to use self-hosted runner (`#1925`)
3745
- Require contributors to complete 1 beginner issue before they can be assigned an intermediate issue (#1939)
3846
- Expand spam list (#1933)
47+
- Expand spam list (#1972)
3948
- chore: add ndpvt-web to spam list (#1945)
4049
- chore: update bot-community-calls workflow to use self hosted runner (#1942)
4150
- chore(ci): update bot-inactivity-unassign workflow to use hl-sdk-py-lin-md runner
51+
- chore: update bot-gfi-candidate-notification workflow to use hl-sdk-py-lin-md runner (`#1966`)
4252
## [0.2.1] - 2026-03-05
4353

4454
### Added

0 commit comments

Comments
 (0)