Skip to content

Commit c9acb5c

Browse files
authored
feat: add CodeRabbit release gate workflow and prompt for PR audits (hiero-ledger#2049)
Signed-off-by: MonaaEid <monaa_eid@hotmail.com> Signed-off-by: MontyPokemon <59332150+MonaaEid@users.noreply.github.com>
1 parent 33b8730 commit c9acb5c

4 files changed

Lines changed: 220 additions & 0 deletions

File tree

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
## 🕵️ Release-Gate Audit: Breaking Changes & Architecture
2+
3+
You are performing a **Senior Release Engineer** audit on this diff. Your goal is to identify risks that could impact downstream users or system stability.
4+
5+
### 🚨 Critical Instructions
6+
- **Ignore** linting, variable naming, or stylistic preferences.
7+
- **Focus** on public API surface, data schemas, and logic flow.
8+
- **Be Concise**: Use bullet points. If no risks are found, state "No breaking changes identified."
9+
10+
---
11+
12+
### 1. 🛠️ Public API & Contract Changes
13+
Identify any modifications to the public-facing interface.
14+
- **Removals/Renames**: Are any classes, methods, or variables renamed or removed?
15+
- **Signature Changes**: Have parameter types or return types changed in a way that breaks existing calls?
16+
- **Defaults**: Have default values for arguments changed?
17+
18+
### 2. 🏗️ Architectural Integrity
19+
- **Dependency Changes**: Highlight any new external libraries or significant version bumps.
20+
- **Side Effects**: Are there new global states, singletons, or changes to how the application initializes?
21+
- **Resource Usage**: Does this diff introduce patterns that might impact memory or CPU (e.g., new loops, heavy recursive calls)?
22+
23+
### 3. 📉 Backward Compatibility & Data
24+
- **Persistence**: If applicable, do database schema changes or file format changes require a migration?
25+
- **Protocol**: If this communicates via API/WebSockets, is the payload structure still compatible with the previous version?
26+
27+
### 4. 🧪 Testing & Validation
28+
- **Coverage Gap**: Are there major logic additions that lack corresponding test files in the diff?
29+
- **Edge Cases**: Identify at least one "what-if" scenario that might cause a failure (e.g., "What if the network is down during this new initialization step?").
30+
31+
---
32+
33+
### 🏁 Summary Verdict
34+
Provide a 1-sentence summary of the "Safety" of this release candidate.
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
/**
2+
* Posts a single "@coderabbit review" comment on release PRs, embedding the
3+
* release review prompt. Designed to run with:
4+
* - permissions: contents: read, pull-requests: write
5+
*
6+
* Safety:
7+
* - Only runs for maintainer-authored PRs (MEMBER/OWNER)
8+
* - Dedupe via hidden marker comment
9+
*/
10+
11+
const fs = require("fs");
12+
const path = require("path");
13+
14+
const MARKER = "<!-- coderabbit-release-gate: v1 -->";
15+
16+
17+
function loadPrompt() {
18+
const promptPath = path.join(
19+
process.env.GITHUB_WORKSPACE || ".",
20+
".github/coderabbit/release-pr-prompt.md"
21+
);
22+
try {
23+
const content = fs.readFileSync(promptPath, "utf8").trim();
24+
if (!content) {
25+
throw new Error("Release prompt file is empty");
26+
}
27+
return content;
28+
} catch (error) {
29+
throw new Error(`Failed to load release prompt from ${promptPath}: ${error.message}`);
30+
}
31+
}
32+
33+
34+
async function commentAlreadyExists({ github, owner, repo, issue_number }) {
35+
try {
36+
const data = await github.paginate(github.rest.issues.listComments, {
37+
owner,
38+
repo,
39+
issue_number,
40+
per_page: 100,
41+
});
42+
data.forEach(c => {
43+
if (c.body && c.body.includes(MARKER)) {
44+
console.log(`FOUND MATCH in comment by ${c.user.login}: ${c.html_url}`);
45+
}
46+
});
47+
return data.some((c) => typeof c.body === "string" && c.body.includes(MARKER));
48+
}
49+
catch (error) {
50+
console.error(`Error checking for existing comments: ${error.message}`);
51+
return true; // Fail closed to avoid duplicates if we can't verify
52+
}
53+
}
54+
55+
56+
function buildBody({ prompt, headRef }) {
57+
return [
58+
"@coderabbit review",
59+
"",
60+
MARKER,
61+
"",
62+
"## 🚀 Release Gate: Cumulative Audit",
63+
"> **Notice to Reviewer**: This is a checkpoint PR for a new release. While the diff here primarily updates versioning and the Changelog, your audit must cover the **entire cumulative scope** of changes since the last version tag.",
64+
"",
65+
"### 🎯 Audit Objectives",
66+
"- Analyze all features merged into `main` since the previous release.",
67+
"- Identify breaking changes in the public API or protocol.",
68+
"- Verify architectural integrity across the Hiero SDK Python modules.",
69+
"",
70+
"<details>",
71+
"<summary><b>View Senior Audit Constraints</b></summary>",
72+
"",
73+
prompt,
74+
"",
75+
"</details>",
76+
"",
77+
"---",
78+
`*Auditing Branch: \`${headRef}\` against the project history.*`,
79+
].join("\n");
80+
}
81+
82+
function getSkipReason(pr) {
83+
if (!pr) {
84+
return "No pull_request payload; exiting.";
85+
}
86+
87+
if (!["MEMBER", "OWNER"].includes(pr.author_association)) {
88+
return `author_association=${pr.author_association}; skipping.`;
89+
}
90+
91+
const title = (pr.title || "").toLowerCase();
92+
const isReleaseTitle =
93+
title.startsWith("chore: release v") || title.startsWith("release v");
94+
95+
if (!isReleaseTitle) {
96+
return "Not a release PR title; skipping.";
97+
}
98+
99+
return null;
100+
}
101+
102+
module.exports = async ({ github, context }) => {
103+
let issue_number = "unknown";
104+
let headRef = "?";
105+
let baseRef = "?";
106+
107+
try {
108+
const owner = context.repo.owner;
109+
const repo = context.repo.repo;
110+
const pr = context.payload.pull_request;
111+
112+
const skipReason = getSkipReason(pr);
113+
if (skipReason) {
114+
console.log(skipReason);
115+
return;
116+
}
117+
118+
issue_number = pr.number;
119+
if (await commentAlreadyExists({ github, owner, repo, issue_number })) {
120+
console.log("Release gate comment already exists. Skipping.");
121+
return;
122+
}
123+
124+
headRef = pr.head?.ref || "unknown";
125+
baseRef = pr.base?.ref || "unknown";
126+
127+
const prompt = loadPrompt();
128+
const body = buildBody({ prompt, headRef });
129+
130+
await github.rest.issues.createComment({
131+
owner,
132+
repo,
133+
issue_number,
134+
body,
135+
});
136+
137+
console.log("Posted CodeRabbit release-gate comment.");
138+
console.log(`PR #${issue_number} (${headRef}${baseRef})`);
139+
} catch (error) {
140+
console.error(`Error in release PR coderabbit gate: ${error.message}`);
141+
console.log(`PR #${issue_number || 'unknown'} (${headRef || '?'}${baseRef || '?'})`);
142+
}
143+
};
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
name: CodeRabbit Release Gate Comment
2+
3+
on:
4+
pull_request:
5+
types: [opened, reopened, synchronize, edited]
6+
7+
permissions:
8+
contents: read
9+
pull-requests: write
10+
11+
concurrency:
12+
group: coderabbit-release-gate-${{ github.event.pull_request.number }}
13+
cancel-in-progress: true
14+
15+
jobs:
16+
coderabbit-release-gate:
17+
runs-on: ubuntu-latest
18+
# Only run for release PRs /title check as initial filter
19+
if: |
20+
github.event.pull_request &&
21+
(github.event.pull_request.author_association == 'MEMBER' ||
22+
github.event.pull_request.author_association == 'OWNER') &&
23+
(startsWith(github.event.pull_request.title, 'chore: release v') ||
24+
startsWith(github.event.pull_request.title, 'release v'))
25+
26+
steps:
27+
- name: Harden the runner
28+
uses: step-security/harden-runner@e3f713f2d8f53843e71c69a996d56f51aa9adfb9 # v2.14.1
29+
with:
30+
egress-policy: audit
31+
32+
- name: Checkout repository
33+
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 #v6.0.1
34+
35+
- name: Post CodeRabbit release-gate prompt comment
36+
env:
37+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
38+
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd #v8.0.0
39+
with:
40+
script: |
41+
const script = require('./.github/scripts/release-pr-coderabbit-gate.js');
42+
await script({ github, context});

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ This changelog is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.
2727
- Refactored the Advanced Issue Template to ensure PR-level quality checklists do not block maintainers during issue creation (#2036)
2828
- Add automated label sync workflow to propagate labels from linked issues to pull requests (#1716)
2929
- chore: update spam list (#2035)
30+
- Add CodeRabbit release gate workflow and prompt for PR audits `release-pr-prompt.md`, `release-pr-coderabbit-gate.yml` and `release-pr-coderabbit-gate.js`
3031

3132
## [0.2.3] - 2026-03-26
3233

0 commit comments

Comments
 (0)