Skip to content

Commit cbaa031

Browse files
committed
feat: Add scripts to auto-review PRs
1 parent ecf7302 commit cbaa031

File tree

7 files changed

+1617
-12
lines changed

7 files changed

+1617
-12
lines changed

.eslintrc.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ module.exports = {
33
browser: true,
44
commonjs: true,
55
es2021: true,
6+
node: true,
67
},
78
extends: 'eslint:recommended',
89
parserOptions: {
Lines changed: 366 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,366 @@
1+
name: Copilot PR Review & Auto-Fix
2+
3+
on:
4+
pull_request_target:
5+
paths:
6+
- resources/**
7+
- db/**
8+
- README.md
9+
types: [opened, synchronize]
10+
11+
permissions:
12+
contents: write
13+
pull-requests: write
14+
issues: write
15+
16+
jobs:
17+
# Job to check for changes to auto-generated files (db/ and README.md)
18+
check-auto-generated:
19+
runs-on: ubuntu-latest
20+
steps:
21+
- name: Check for auto-generated file changes
22+
id: check
23+
uses: actions/github-script@v7
24+
with:
25+
github-token: ${{ secrets.GITHUB_TOKEN }}
26+
script: |
27+
const pr = context.payload.pull_request;
28+
29+
// Get list of changed files
30+
const { data: files } = await github.rest.pulls.listFiles({
31+
owner: context.repo.owner,
32+
repo: context.repo.repo,
33+
pull_number: pr.number
34+
});
35+
36+
// Check for db folder changes
37+
const dbChanges = files.some(f => f.filename.startsWith('db/'));
38+
core.setOutput('has_db_changes', dbChanges.toString());
39+
40+
// Check for README changes
41+
const readmeChanges = files.some(f => f.filename.toLowerCase() === 'readme.md');
42+
core.setOutput('has_readme_changes', readmeChanges.toString());
43+
44+
if (dbChanges) {
45+
console.log('db/ folder was modified');
46+
}
47+
if (readmeChanges) {
48+
console.log('README.md was modified');
49+
}
50+
51+
- name: Post comment about db folder changes
52+
if: steps.check.outputs.has_db_changes == 'true'
53+
uses: actions/github-script@v7
54+
with:
55+
github-token: ${{ secrets.GITHUB_TOKEN }}
56+
script: |
57+
const pr = context.payload.pull_request;
58+
59+
const commentBody = `Hi, and thank you for the contribution.
60+
61+
The \`/db\` folder is auto-generated, and changes must be made in the \`/resources\` folder.
62+
Could you please amend this PR?
63+
Our [CONTRIBUTING](https://github.com/marcelscruz/dev-resources/blob/main/CONTRIBUTING.md) guide has instructions that might help you.
64+
65+
Thanks!`;
66+
67+
await github.rest.issues.createComment({
68+
owner: context.repo.owner,
69+
repo: context.repo.repo,
70+
issue_number: pr.number,
71+
body: commentBody
72+
});
73+
74+
- name: Post comment about README changes
75+
if: steps.check.outputs.has_readme_changes == 'true'
76+
uses: actions/github-script@v7
77+
with:
78+
github-token: ${{ secrets.GITHUB_TOKEN }}
79+
script: |
80+
const pr = context.payload.pull_request;
81+
82+
const commentBody = `Hi, and thank you for the contribution.
83+
84+
The README file is auto-generated, and changes must be made in the \`/resources\` folder.
85+
Could you please amend this PR?
86+
Our [CONTRIBUTING](https://github.com/marcelscruz/dev-resources/blob/main/CONTRIBUTING.md) guide has instructions that might help you.
87+
88+
Thanks!`;
89+
90+
await github.rest.issues.createComment({
91+
owner: context.repo.owner,
92+
repo: context.repo.repo,
93+
issue_number: pr.number,
94+
body: commentBody
95+
});
96+
97+
validate-and-review:
98+
runs-on: ubuntu-latest
99+
needs: check-auto-generated
100+
steps:
101+
- name: Checkout base branch
102+
uses: actions/checkout@v4
103+
with:
104+
ref: ${{ github.event.pull_request.base.ref }}
105+
106+
- name: Checkout PR head
107+
uses: actions/checkout@v4
108+
with:
109+
repository: ${{ github.event.pull_request.head.repo.full_name }}
110+
ref: ${{ github.event.pull_request.head.ref }}
111+
fetch-depth: 0
112+
113+
- name: Setup Node
114+
uses: actions/setup-node@v4
115+
with:
116+
node-version: 20
117+
118+
- name: Install dependencies
119+
run: npm install @actions/github
120+
121+
- name: Run resource validation
122+
id: validate
123+
run: |
124+
node scripts/validate-resources.js github 2>&1 | tee validation-output.txt
125+
EXIT_CODE=${PIPESTATUS[0]}
126+
echo "exit_code=$EXIT_CODE" >> $GITHUB_OUTPUT
127+
# Store multiline output
128+
{
129+
echo 'output<<EOF'
130+
cat validation-output.txt
131+
echo 'EOF'
132+
} >> $GITHUB_OUTPUT
133+
continue-on-error: true
134+
135+
- name: Run TypeScript check
136+
id: typescript
137+
run: |
138+
npx tsc --noEmit 2>&1 | tee typescript-output.txt || true
139+
{
140+
echo 'output<<EOF'
141+
cat typescript-output.txt
142+
echo 'EOF'
143+
} >> $GITHUB_OUTPUT
144+
continue-on-error: true
145+
146+
- name: Post validation results
147+
uses: actions/github-script@v7
148+
with:
149+
github-token: ${{ secrets.GITHUB_TOKEN }}
150+
script: |
151+
const validationOutput = `${{ steps.validate.outputs.output }}`;
152+
const typescriptOutput = `${{ steps.typescript.outputs.output }}`;
153+
const exitCode = '${{ steps.validate.outputs.exit_code }}';
154+
155+
let commentBody = '## 🤖 Automated PR Review\n\n';
156+
157+
// Add validation results
158+
if (exitCode !== '0') {
159+
commentBody += '### ❌ Resource Validation Issues Found\n\n';
160+
commentBody += '```\n' + validationOutput + '\n```\n\n';
161+
} else {
162+
commentBody += '### ✅ Resource Validation Passed\n\n';
163+
}
164+
165+
// Add TypeScript results if there are errors
166+
if (typescriptOutput && typescriptOutput.includes('error')) {
167+
commentBody += '### ❌ TypeScript Errors\n\n';
168+
commentBody += '```\n' + typescriptOutput + '\n```\n\n';
169+
}
170+
171+
// Add helpful links
172+
commentBody += '---\n\n';
173+
commentBody += '📚 **Resources:**\n';
174+
commentBody += '- [Contribution Guidelines](https://github.com/marcelscruz/dev-resources/blob/main/CONTRIBUTING.md)\n';
175+
commentBody += '- [Valid Categories](https://github.com/marcelscruz/dev-resources/blob/main/types/category.ts)\n';
176+
177+
await github.rest.issues.createComment({
178+
owner: context.repo.owner,
179+
repo: context.repo.repo,
180+
issue_number: context.payload.pull_request.number,
181+
body: commentBody
182+
});
183+
184+
copilot-review:
185+
runs-on: ubuntu-latest
186+
needs: [check-auto-generated, validate-and-review]
187+
steps:
188+
- name: Checkout PR
189+
uses: actions/checkout@v4
190+
with:
191+
repository: ${{ github.event.pull_request.head.repo.full_name }}
192+
ref: ${{ github.event.pull_request.head.ref }}
193+
194+
- name: Request Copilot Code Review
195+
uses: actions/github-script@v7
196+
with:
197+
github-token: ${{ secrets.GITHUB_TOKEN }}
198+
script: |
199+
// Request a review from GitHub Copilot (if enabled in repo settings)
200+
// This will trigger Copilot's code review feature
201+
try {
202+
// Add a label to indicate Copilot review requested
203+
await github.rest.issues.addLabels({
204+
owner: context.repo.owner,
205+
repo: context.repo.repo,
206+
issue_number: context.payload.pull_request.number,
207+
labels: ['copilot-review']
208+
});
209+
} catch (error) {
210+
console.log('Could not add label:', error.message);
211+
}
212+
213+
// Log that Copilot review was triggered
214+
console.log('Copilot review triggered for PR #' + context.payload.pull_request.number);
215+
216+
auto-fix:
217+
runs-on: ubuntu-latest
218+
needs: validate-and-review
219+
if: failure() && github.event.pull_request.head.repo.full_name == github.event.pull_request.base.repo.full_name
220+
steps:
221+
- name: Checkout PR
222+
uses: actions/checkout@v4
223+
with:
224+
ref: ${{ github.event.pull_request.head.ref }}
225+
token: ${{ secrets.GITHUB_TOKEN }}
226+
227+
- name: Setup Node
228+
uses: actions/setup-node@v4
229+
with:
230+
node-version: 20
231+
232+
- name: Configure Git
233+
run: |
234+
git config user.name "github-actions[bot]"
235+
git config user.email "github-actions[bot]@users.noreply.github.com"
236+
237+
- name: Apply automatic fixes
238+
id: auto-fix
239+
run: |
240+
node scripts/auto-fix-resources.js 2>&1 | tee auto-fix-output.txt
241+
EXIT_CODE=${PIPESTATUS[0]}
242+
echo "exit_code=$EXIT_CODE" >> $GITHUB_OUTPUT
243+
{
244+
echo 'output<<EOF'
245+
cat auto-fix-output.txt
246+
echo 'EOF'
247+
} >> $GITHUB_OUTPUT
248+
continue-on-error: true
249+
250+
- name: Commit and push fixes
251+
if: steps.auto-fix.outputs.exit_code == '0' && steps.auto-fix.outputs.output != ''
252+
run: |
253+
# Check if there are changes to commit
254+
if git diff --quiet && git diff --cached --quiet; then
255+
echo "No changes to commit"
256+
else
257+
git add resources/
258+
git commit -m "🤖 Auto-fix: Apply automatic fixes for validation errors
259+
260+
Applied fixes based on validation errors:
261+
- Fixed invalid category names with suggested replacements
262+
- Fixed URL protocol issues (added https:// where missing)
263+
- Fixed alphabetical ordering of resources
264+
- Fixed missing commas between resource objects
265+
266+
These fixes were automatically applied by the Copilot PR Review workflow."
267+
git push
268+
echo "✅ Automatic fixes have been applied and pushed to the PR"
269+
fi
270+
271+
- name: Post auto-fix results
272+
if: steps.auto-fix.outputs.output != ''
273+
uses: actions/github-script@v7
274+
with:
275+
github-token: ${{ secrets.GITHUB_TOKEN }}
276+
script: |
277+
const fixOutput = `${{ steps.auto-fix.outputs.output }}`;
278+
let fixData;
279+
try {
280+
fixData = JSON.parse(fixOutput);
281+
} catch (e) {
282+
fixData = null;
283+
}
284+
285+
let commentBody = '## 🤖 Automatic Fixes Applied\n\n';
286+
287+
if (fixData && fixData.totalFixes > 0) {
288+
commentBody += `✅ **${fixData.totalFixes} fix(es) applied automatically:**\n\n`;
289+
290+
// Group fixes by file
291+
const byFile = {};
292+
for (const fix of fixData.fixesApplied) {
293+
if (!byFile[fix.file]) byFile[fix.file] = [];
294+
byFile[fix.file].push(fix);
295+
}
296+
297+
for (const [file, fixes] of Object.entries(byFile)) {
298+
commentBody += `### ${file}\n`;
299+
for (const fix of fixes) {
300+
commentBody += `- Line ${fix.line}: ${fix.change}\n`;
301+
}
302+
commentBody += '\n';
303+
}
304+
305+
commentBody += '---\n\n';
306+
commentBody += '✨ These fixes have been automatically applied and committed to your PR branch.\n';
307+
commentBody += 'Please review the changes and ensure they look correct.';
308+
} else {
309+
commentBody += 'No automatic fixes were available for the issues found.\n';
310+
commentBody += 'Please review the validation errors and apply fixes manually.';
311+
}
312+
313+
await github.rest.issues.createComment({
314+
owner: context.repo.owner,
315+
repo: context.repo.repo,
316+
issue_number: context.payload.pull_request.number,
317+
body: commentBody
318+
});
319+
320+
auto-fix-suggestions:
321+
runs-on: ubuntu-latest
322+
needs: [check-auto-generated, validate-and-review]
323+
if: failure() && github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
324+
steps:
325+
- name: Checkout PR
326+
uses: actions/checkout@v4
327+
with:
328+
repository: ${{ github.event.pull_request.head.repo.full_name }}
329+
ref: ${{ github.event.pull_request.head.ref }}
330+
331+
- name: Setup Node
332+
uses: actions/setup-node@v4
333+
with:
334+
node-version: 20
335+
336+
- name: Generate fix suggestions
337+
id: fixes
338+
run: |
339+
node scripts/generate-fix-suggestions.js 2>&1 | tee fixes-output.txt || true
340+
{
341+
echo 'output<<EOF'
342+
cat fixes-output.txt
343+
echo 'EOF'
344+
} >> $GITHUB_OUTPUT
345+
346+
- name: Post fix suggestions
347+
uses: actions/github-script@v7
348+
with:
349+
github-token: ${{ secrets.GITHUB_TOKEN }}
350+
script: |
351+
const fixesOutput = `${{ steps.fixes.outputs.output }}`;
352+
353+
if (fixesOutput && fixesOutput.trim()) {
354+
const commentBody = '## 💡 Suggested Fixes\n\n' +
355+
'Based on the validation errors, here are suggested fixes:\n\n' +
356+
'```diff\n' + fixesOutput + '\n```\n\n' +
357+
'---\n\n' +
358+
'🤖 *These suggestions were generated automatically. Please review them before applying.*';
359+
360+
await github.rest.issues.createComment({
361+
owner: context.repo.owner,
362+
repo: context.repo.repo,
363+
issue_number: context.payload.pull_request.number,
364+
body: commentBody
365+
});
366+
}

package.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,11 @@
77
"update-db": "ts-node utils/db/update-db.js",
88
"update-readme": "ts-node utils/readme/update-readme.js",
99
"dev-order": "node utils/order/resources-extractor.js",
10-
"prettier:format": "prettier --write \"**/*.{ts,js}\""
10+
"prettier:format": "prettier --write \"**/*.{ts,js}\"",
11+
"validate": "node scripts/validate-resources.js",
12+
"validate:github": "node scripts/validate-resources.js github",
13+
"suggest-fixes": "node scripts/generate-fix-suggestions.js",
14+
"auto-fix": "node scripts/auto-fix-resources.js"
1115
},
1216
"dependencies": {
1317
"eslint": "^7.18.0",

0 commit comments

Comments
 (0)