2020 - name : Setup Zig
2121 uses : mlugg/setup-zig@v2
2222 with :
23- version : 0.14 .1
23+ version : 0.15 .1
2424
2525 - name : Build linter
2626 working-directory : tools/linter
4545 echo "Lint results:"
4646 cat lint_results.json
4747
48-
49- - name : Post comments with metadata
48+ - name : Post review with grouped comments
5049 uses : actions/github-script@v7
5150 with :
5251 github-token : ${{ secrets.GITHUB_TOKEN }}
@@ -62,58 +61,126 @@ jobs:
6261 const content = fs.readFileSync('lint_results.json', 'utf8').trim();
6362 if (!content || content === '[]') {
6463 console.log('No lint issues found');
64+
65+ // Check if there's an existing review to dismiss
66+ const existingReviews = await github.rest.pulls.listReviews({
67+ owner: context.repo.owner,
68+ repo: context.repo.repo,
69+ pull_number: context.issue.number
70+ });
71+
72+ const botReview = existingReviews.data.find(review =>
73+ review.user.login === 'github-actions[bot]' &&
74+ review.body && review.body.includes('<!-- lint-review -->')
75+ );
76+
77+ if (botReview) {
78+ await github.rest.pulls.dismissReview({
79+ owner: context.repo.owner,
80+ repo: context.repo.repo,
81+ pull_number: context.issue.number,
82+ review_id: botReview.id,
83+ message: 'All lint issues have been resolved'
84+ });
85+ console.log('Dismissed previous lint review - no issues found');
86+ }
6587 return;
6688 }
6789
6890 const issues = JSON.parse(content);
6991
70- const existingComments = await github.rest.pulls.listReviewComments({
92+ // Check for existing bot review
93+ const existingReviews = await github.rest.pulls.listReviews({
7194 owner: context.repo.owner,
7295 repo: context.repo.repo,
7396 pull_number: context.issue.number
7497 });
7598
76- const botComments = existingComments .data.filter(comment =>
77- comment .user.login === 'github-actions[bot]' &&
78- comment .body. includes('<!-- lint-comment ')
99+ const botReview = existingReviews .data.find(review =>
100+ review .user.login === 'github-actions[bot]' &&
101+ review .body && review.body. includes('<!-- lint-review --> ')
79102 );
80103
81- const existingHashes = new Set();
82- botComments.forEach(comment => {
83- const match = comment.body.match(/<!-- lint-comment:(\w+) -->/);
84- if (match) existingHashes.add(match[1]);
85- } );
104+ // Create hash of current issues to check if review needs updating
105+ const issuesHash = crypto.createHash('md5')
106+ .update(JSON.stringify(issues.map(i => `${i.file}:${i.line}:${i.message}`)))
107+ .digest('hex')
108+ .substring(0, 8 );
86109
87- let postedCount = 0;
88- let skippedCount = 0;
110+ // Check if existing review has the same issues
111+ if (botReview && botReview.body.includes(`<!-- lint-hash:${issuesHash} -->`)) {
112+ console.log('Review already exists with same issues, skipping');
113+ return;
114+ }
89115
90- for (const issue of issues) {
91- const issueData = `${issue.file}:${issue.line}:${issue.message}`;
92- const issueHash = crypto.createHash('md5').update(issueData).digest('hex').substring(0, 8);
116+ // Dismiss existing review if it exists
117+ if (botReview) {
118+ await github.rest.pulls.dismissReview({
119+ owner: context.repo.owner,
120+ repo: context.repo.repo,
121+ pull_number: context.issue.number,
122+ review_id: botReview.id,
123+ message: 'Updating with new lint results'
124+ });
125+ }
126+
127+ // Prepare review comments
128+ const reviewComments = [];
129+ const issuesByFile = {};
93130
94- if (existingHashes.has(issueHash)) {
95- console.log(`Skipping duplicate issue: ${issueHash}`);
96- skippedCount++;
97- continue;
131+ for (const issue of issues) {
132+ if (!issuesByFile[issue.file]) {
133+ issuesByFile[issue.file] = [];
98134 }
135+ issuesByFile[issue.file].push(issue);
136+
137+ reviewComments.push({
138+ path: issue.file,
139+ line: issue.line,
140+ body: issue.message
141+ });
142+ }
143+
144+ // Create review body with summary
145+ const totalIssues = issues.length;
146+ const fileCount = Object.keys(issuesByFile).length;
99147
100- const commentBody = `${issue.message}\n\n<!-- lint-comment:${issueHash} --> \n`;
101- console.log(`comment body:`, commentBody) ;
148+ let reviewBody = `## 🔍 Lint Results\n \n`;
149+ reviewBody += `Found **${totalIssues}** issue${totalIssues !== 1 ? 's' : ''} in **${fileCount}** file${fileCount !== 1 ? 's' : ''}:\n\n` ;
102150
151+ for (const [file, fileIssues] of Object.entries(issuesByFile)) {
152+ reviewBody += `- **${file}**: ${fileIssues.length} issue${fileIssues.length !== 1 ? 's' : ''}\n`;
153+ }
154+
155+ reviewBody += `\n<!-- lint-review -->\n<!-- lint-hash:${issuesHash} -->`;
156+
157+ try {
158+ const review = await github.rest.pulls.createReview({
159+ owner: context.repo.owner,
160+ repo: context.repo.repo,
161+ pull_number: context.issue.number,
162+ commit_id: context.payload.pull_request.head.sha,
163+ body: reviewBody,
164+ event: 'REQUEST_CHANGES',
165+ comments: reviewComments
166+ });
167+
168+ console.log(`Created review with ${reviewComments.length} comments`);
169+ } catch (error) {
170+ console.error(`Failed to create review:`, error.message);
171+
172+ // Fallback: try to create review without comments if there's an error
103173 try {
104- await github.rest.pulls.createReviewComment ({
174+ await github.rest.pulls.createReview ({
105175 owner: context.repo.owner,
106176 repo: context.repo.repo,
107177 pull_number: context.issue.number,
108178 commit_id: context.payload.pull_request.head.sha,
109- path: issue.file,
110- line: issue.line,
111- body: commentBody
179+ body: reviewBody + '\n\n⚠️ Could not attach inline comments due to an error.',
180+ event: 'REQUEST_CHANGES'
112181 });
113- postedCount++ ;
114- } catch (error ) {
115- console.error(`Failed to post comment:`, error .message);
182+ console.log('Created review without inline comments as fallback') ;
183+ } catch (fallbackError ) {
184+ console.error('Fallback review creation also failed:', fallbackError .message);
116185 }
117186 }
118-
119- console.log(`Posted ${postedCount} new comments, skipped ${skippedCount} duplicates`);
0 commit comments