Skip to content

Commit ee717b9

Browse files
committed
idk but the robot says it works even more now
1 parent ff20039 commit ee717b9

File tree

9 files changed

+2079
-453
lines changed

9 files changed

+2079
-453
lines changed

.github/workflows/auto-pr-resource.yml

Lines changed: 147 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,14 @@ jobs:
2222
- name: Checkout repository
2323
uses: actions/checkout@v4
2424

25-
- name: Parse issue and create/update PR
25+
- name: Parse issue fields
26+
id: parse
2627
uses: actions/github-script@v7
2728
with:
2829
script: |
2930
const issue = context.payload.issue;
3031
const body = issue.body;
31-
const action = context.payload.action;
32-
32+
3333
// Parse the issue form fields
3434
function parseField(body, fieldName) {
3535
const regex = new RegExp(`### ${fieldName}\\s*\\n\\s*([\\s\\S]*?)(?=\\n### |$)`, 'i');
@@ -39,12 +39,12 @@ jobs:
3939
}
4040
return '';
4141
}
42-
42+
4343
function parseCheckboxes(body, fieldName) {
4444
const regex = new RegExp(`### ${fieldName}\\s*\\n([\\s\\S]*?)(?=\\n### |$)`, 'i');
4545
const match = body.match(regex);
4646
if (!match) return [];
47-
47+
4848
const checkboxSection = match[1];
4949
const checked = [];
5050
const checkboxRegex = /- \[x\] (.+)/gi;
@@ -54,13 +54,13 @@ jobs:
5454
}
5555
return checked;
5656
}
57-
57+
5858
const title = parseField(body, 'Resource Title');
5959
const url = parseField(body, 'URL');
6060
const description = parseField(body, 'Description');
6161
const dialect = parseField(body, 'Dialect / Section');
6262
const tags = parseCheckboxes(body, 'Tags');
63-
63+
6464
if (!title || !url || !description || !dialect) {
6565
console.log('Missing required fields, skipping PR creation');
6666
await github.rest.issues.createComment({
@@ -69,99 +69,151 @@ jobs:
6969
issue_number: issue.number,
7070
body: '⚠️ Could not parse all required fields from this submission. Please ensure Title, URL, Description, and Dialect are filled out.'
7171
});
72+
core.setOutput('skip', 'true');
7273
return;
7374
}
74-
75-
// Map dialect to section function name and insertion marker
75+
76+
// Map dialect to section ID
7677
const dialectMap = {
77-
'General Lisp Resources': { section: 'general-section', id: 'general' },
78-
'Common Lisp': { section: 'common-lisp-section', id: 'common-lisp' },
79-
'Scheme': { section: 'scheme-section', id: 'scheme' },
80-
'Racket': { section: 'racket-section', id: 'racket' },
81-
'Clojure': { section: 'clojure-section', id: 'clojure' },
82-
'Emacs Lisp': { section: 'emacs-lisp-section', id: 'emacs-lisp' },
83-
'Janet': { section: 'janet-section', id: 'janet' },
84-
'Other Lisps & Inspired Languages': { section: 'others-section', id: 'others' }
78+
'General Lisp Resources': 'general',
79+
'Common Lisp': 'common-lisp',
80+
'Scheme': 'scheme',
81+
'Racket': 'racket',
82+
'Clojure': 'clojure',
83+
'Emacs Lisp': 'emacs-lisp',
84+
'Janet': 'janet',
85+
'Other Lisps & Inspired Languages': 'others'
8586
};
86-
87-
const sectionInfo = dialectMap[dialect];
88-
if (!sectionInfo) {
87+
88+
const sectionId = dialectMap[dialect];
89+
if (!sectionId) {
8990
console.log('Unknown dialect:', dialect);
91+
await github.rest.issues.createComment({
92+
owner: context.repo.owner,
93+
repo: context.repo.repo,
94+
issue_number: issue.number,
95+
body: `⚠️ Unknown dialect: "${dialect}". Please select a valid option.`
96+
});
97+
core.setOutput('skip', 'true');
9098
return;
9199
}
92-
93-
// Format tags for Scheme
94-
const tagsScheme = tags.length > 0
95-
? `'(${tags.map(t => `"${t}"`).join(' ')})`
96-
: "'()";
97-
98-
// Escape special characters for Scheme strings
99-
const escapeScheme = (str) => str.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
100-
101-
// Create the resource-card s-expression
102-
const resourceCard = [
103-
' ,(resource-card',
104-
` "${escapeScheme(title)}"`,
105-
` "${escapeScheme(url)}"`,
106-
` "${escapeScheme(description)}"`,
107-
` ${tagsScheme})`
108-
].join('\n');
109-
110-
// Consistent branch name based on issue number only
100+
101+
// Output parsed values
102+
core.setOutput('skip', 'false');
103+
core.setOutput('title', title);
104+
core.setOutput('url', url);
105+
core.setOutput('description', description);
106+
core.setOutput('dialect', dialect);
107+
core.setOutput('section_id', sectionId);
108+
core.setOutput('tags', JSON.stringify(tags));
109+
core.setOutput('tags_json', JSON.stringify(tags));
110+
111+
- name: Modify index.scm with Scheme script
112+
if: steps.parse.outputs.skip != 'true'
113+
id: modify
114+
run: |
115+
# Escape single quotes for shell by replacing ' with '\''
116+
escape_shell() {
117+
echo "$1" | sed "s/'/'\\\\''/g"
118+
}
119+
120+
TITLE='${{ steps.parse.outputs.title }}'
121+
URL='${{ steps.parse.outputs.url }}'
122+
DESC='${{ steps.parse.outputs.description }}'
123+
SECTION_ID='${{ steps.parse.outputs.section_id }}'
124+
TAGS_JSON='${{ steps.parse.outputs.tags_json }}'
125+
126+
echo "Running Scheme script to add resource..."
127+
echo "Section ID: $SECTION_ID"
128+
echo "Title: $TITLE"
129+
echo "URL: $URL"
130+
131+
# Run the docker compose command
132+
if docker compose run --rm build scripts/add-resource.scm --in-place "$SECTION_ID" "$TITLE" "$URL" "$DESC" "$TAGS_JSON"; then
133+
echo "success=true" >> $GITHUB_OUTPUT
134+
echo "Resource added successfully"
135+
else
136+
EXIT_CODE=$?
137+
echo "success=false" >> $GITHUB_OUTPUT
138+
echo "exit_code=$EXIT_CODE" >> $GITHUB_OUTPUT
139+
140+
# Map exit code to error message
141+
case $EXIT_CODE in
142+
1)
143+
ERROR_MSG="Invalid arguments provided to the script."
144+
;;
145+
2)
146+
ERROR_MSG="Could not find pages/index.scm file."
147+
;;
148+
3)
149+
ERROR_MSG="Parse/syntax error in the Scheme file."
150+
;;
151+
4)
152+
ERROR_MSG="Could not find the section in index.scm."
153+
;;
154+
5)
155+
ERROR_MSG="Validation failed. The generated content is invalid."
156+
;;
157+
*)
158+
ERROR_MSG="Unknown error occurred (exit code: $EXIT_CODE)."
159+
;;
160+
esac
161+
162+
echo "error_message=$ERROR_MSG" >> $GITHUB_OUTPUT
163+
echo "Script failed with exit code $EXIT_CODE: $ERROR_MSG"
164+
exit 1
165+
fi
166+
167+
- name: Handle script failure
168+
if: failure() && steps.modify.outputs.success == 'false'
169+
uses: actions/github-script@v7
170+
with:
171+
script: |
172+
const errorMessage = '${{ steps.modify.outputs.error_message }}';
173+
const dialect = '${{ steps.parse.outputs.dialect }}';
174+
175+
await github.rest.issues.createComment({
176+
owner: context.repo.owner,
177+
repo: context.repo.repo,
178+
issue_number: context.payload.issue.number,
179+
body: `⚠️ Failed to add resource: ${errorMessage}\n\nPlease check the workflow logs for more details, or contact a maintainer if the issue persists.`
180+
});
181+
182+
- name: Create or update PR
183+
if: steps.parse.outputs.skip != 'true' && steps.modify.outputs.success == 'true'
184+
uses: actions/github-script@v7
185+
with:
186+
script: |
187+
const issue = context.payload.issue;
188+
const title = '${{ steps.parse.outputs.title }}';
189+
const url = '${{ steps.parse.outputs.url }}';
190+
const description = '${{ steps.parse.outputs.description }}';
191+
const dialect = '${{ steps.parse.outputs.dialect }}';
192+
const tags = JSON.parse('${{ steps.parse.outputs.tags }}');
193+
111194
const branchName = `resource/issue-${issue.number}`;
112-
195+
113196
// Check if a PR already exists for this issue
114197
const { data: pulls } = await github.rest.pulls.list({
115198
owner: context.repo.owner,
116199
repo: context.repo.repo,
117200
head: `${context.repo.owner}:${branchName}`,
118201
state: 'open'
119202
});
120-
203+
121204
const existingPR = pulls.length > 0 ? pulls[0] : null;
122-
123-
// Get the default branch SHA (latest main)
205+
206+
// Get the current commit SHA (the modified index.scm is in the working directory)
124207
const { data: mainRef } = await github.rest.git.getRef({
125208
owner: context.repo.owner,
126209
repo: context.repo.repo,
127210
ref: 'heads/main'
128211
});
129-
130-
// Get the latest index.scm content from main
131-
const { data: mainFileData } = await github.rest.repos.getContent({
132-
owner: context.repo.owner,
133-
repo: context.repo.repo,
134-
path: 'pages/index.scm',
135-
ref: 'main'
136-
});
137-
138-
const content = Buffer.from(mainFileData.content, 'base64').toString('utf8');
139-
140-
// Find the section and insert before the closing ))) of the div/section/quasiquote
141-
// Match up to and including all complete resource-cards, then capture just the 3 structural closing parens
142-
const sectionRegex = new RegExp(
143-
`(\\(section \\(@ \\(id "${sectionInfo.id}"\\)[\\s\\S]*?\\(div \\(@ \\(class "resources-grid"\\)\\)[\\s\\S]*?)(\\)\\)\\))`,
144-
'm'
145-
);
146-
147-
const sectionMatch = content.match(sectionRegex);
148-
if (!sectionMatch) {
149-
console.log('Could not find section:', sectionInfo.id);
150-
await github.rest.issues.createComment({
151-
owner: context.repo.owner,
152-
repo: context.repo.repo,
153-
issue_number: issue.number,
154-
body: `⚠️ Could not find the "${dialect}" section in index.scm. Manual intervention required.`
155-
});
156-
return;
157-
}
158-
159-
// Insert the new resource card before the closing )))))
160-
const newContent = content.replace(
161-
sectionRegex,
162-
`$1\n${resourceCard}$2`
163-
);
164-
212+
213+
// Read the modified index.scm file
214+
const fs = require('fs');
215+
const modifiedContent = fs.readFileSync('pages/index.scm', 'utf8');
216+
165217
const prBody = [
166218
'## New Resource Submission',
167219
'',
@@ -180,11 +232,11 @@ jobs:
180232
'',
181233
`Closes #${issue.number}`
182234
].join('\n');
183-
235+
184236
if (existingPR) {
185237
// UPDATE existing PR: reset branch to main and apply new changes
186238
console.log(`Updating existing PR #${existingPR.number}`);
187-
239+
188240
// Force update the branch ref to point to main
189241
await github.rest.git.updateRef({
190242
owner: context.repo.owner,
@@ -193,26 +245,26 @@ jobs:
193245
sha: mainRef.object.sha,
194246
force: true
195247
});
196-
248+
197249
// Get the file SHA from the reset branch (now same as main)
198250
const { data: branchFileData } = await github.rest.repos.getContent({
199251
owner: context.repo.owner,
200252
repo: context.repo.repo,
201253
path: 'pages/index.scm',
202254
ref: branchName
203255
});
204-
256+
205257
// Update the file with new content
206258
await github.rest.repos.createOrUpdateFileContents({
207259
owner: context.repo.owner,
208260
repo: context.repo.repo,
209261
path: 'pages/index.scm',
210262
message: `Update resource: ${title}`,
211-
content: Buffer.from(newContent).toString('base64'),
263+
content: Buffer.from(modifiedContent).toString('base64'),
212264
sha: branchFileData.sha,
213265
branch: branchName
214266
});
215-
267+
216268
// Update PR title and body
217269
await github.rest.pulls.update({
218270
owner: context.repo.owner,
@@ -221,21 +273,21 @@ jobs:
221273
title: `Add resource: ${title}`,
222274
body: prBody
223275
});
224-
276+
225277
// Comment on the issue about the update
226278
await github.rest.issues.createComment({
227279
owner: context.repo.owner,
228280
repo: context.repo.repo,
229281
issue_number: issue.number,
230282
body: `🔄 Pull request #${existingPR.number} has been updated with your changes.`
231283
});
232-
284+
233285
console.log(`Updated PR #${existingPR.number} for issue #${issue.number}`);
234-
286+
235287
} else {
236288
// CREATE new PR
237289
console.log('Creating new PR');
238-
290+
239291
// Create new branch from main
240292
try {
241293
await github.rest.git.createRef({
@@ -258,26 +310,26 @@ jobs:
258310
throw e;
259311
}
260312
}
261-
313+
262314
// Get the file SHA from the new branch
263315
const { data: branchFileData } = await github.rest.repos.getContent({
264316
owner: context.repo.owner,
265317
repo: context.repo.repo,
266318
path: 'pages/index.scm',
267319
ref: branchName
268320
});
269-
321+
270322
// Update the file
271323
await github.rest.repos.createOrUpdateFileContents({
272324
owner: context.repo.owner,
273325
repo: context.repo.repo,
274326
path: 'pages/index.scm',
275327
message: `Add resource: ${title}`,
276-
content: Buffer.from(newContent).toString('base64'),
328+
content: Buffer.from(modifiedContent).toString('base64'),
277329
sha: branchFileData.sha,
278330
branch: branchName
279331
});
280-
332+
281333
// Create the pull request
282334
const { data: pr } = await github.rest.pulls.create({
283335
owner: context.repo.owner,
@@ -287,14 +339,14 @@ jobs:
287339
base: 'main',
288340
body: prBody
289341
});
290-
342+
291343
// Comment on the issue with the PR link
292344
await github.rest.issues.createComment({
293345
owner: context.repo.owner,
294346
repo: context.repo.repo,
295347
issue_number: issue.number,
296348
body: `🎉 Thanks for your submission! A pull request has been automatically created: #${pr.number}\n\nA maintainer will review it shortly.`
297349
});
298-
350+
299351
console.log(`Created PR #${pr.number} for issue #${issue.number}`);
300352
}

0 commit comments

Comments
 (0)