mirror of
https://github.com/lucide-icons/lucide.git
synced 2025-12-18 05:47:42 +01:00
ci(icons): Add ChatGPT tags suggestions on icon PRs (#3372)
* Add gpt tags * Add github actions flow * Add link so people can use the chat * Fix workflow * Add openai dep * Add actions core * Try gh pr review in actions * Try with octokit * Remove changed files part * Try with createReview function * Try this * fix broken json file * Turn on review by gh action * Try this * Update icons/trash.json * Update the runner * Remove added tags * Add more checks
This commit is contained in:
124
scripts/suggestTags.mts
Normal file
124
scripts/suggestTags.mts
Normal file
@@ -0,0 +1,124 @@
|
||||
import OpenAI from "openai";
|
||||
import { Octokit } from "@octokit/rest";
|
||||
import { zodTextFormat } from "openai/helpers/zod";
|
||||
|
||||
import path from "node:path";
|
||||
import fs from "node:fs/promises";
|
||||
import z from "zod";
|
||||
|
||||
const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN });
|
||||
const pullRequestNumber = Number(process.env.PULL_REQUEST_NUMBER);
|
||||
const username = process.env.REVIEWER ?? 'github-actions[bot]';
|
||||
const commitSha = process.env.COMMIT_SHA ?? "HEAD";
|
||||
|
||||
const owner = 'lucide-icons';
|
||||
const repo = 'lucide';
|
||||
|
||||
const tagsSchema = z.object({
|
||||
tags: z.array(z.string()),
|
||||
})
|
||||
|
||||
const { data: files } = await octokit.pulls.listFiles({
|
||||
owner,
|
||||
repo,
|
||||
pull_number: pullRequestNumber,
|
||||
});
|
||||
|
||||
const { data: reviews } = await octokit.pulls.listReviews({
|
||||
owner,
|
||||
repo,
|
||||
pull_number: pullRequestNumber,
|
||||
query: `in:body author:github-actions[bot]`,
|
||||
});
|
||||
|
||||
const hasUserReviews = reviews.some(review => review.user?.login === username);
|
||||
|
||||
// TODO: Find a better way to check if the PR has been updated since the last review
|
||||
if(hasUserReviews) {
|
||||
console.log(`Pull request #${pullRequestNumber} already has reviews from ${username}. Skipping...`);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
const changedFiles = files
|
||||
.map((file) => file.filename)
|
||||
.filter((file) => file.startsWith('icons/') && file.includes('.json'))
|
||||
.filter((file, idx, arr) => arr.indexOf(file) === idx);;
|
||||
|
||||
if (changedFiles.length === 0) {
|
||||
console.log('No changed icons found');
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
const client = new OpenAI({
|
||||
apiKey: process.env.OPENAI_API_KEY,
|
||||
});
|
||||
|
||||
const suggestionsByFile = changedFiles.map(async (file) => {
|
||||
const filePath = file.replace('.json', '');
|
||||
const iconName = filePath.split('/').pop();
|
||||
|
||||
const input = `Create a list of tags for a \`${iconName}\` icon. Don't include words like: 'icon' and preferably use single words.`;
|
||||
|
||||
const response = await client.responses.create({
|
||||
model: "gpt-4.1-nano",
|
||||
input,
|
||||
text: {
|
||||
format: zodTextFormat(tagsSchema, "tags"),
|
||||
},
|
||||
});
|
||||
|
||||
const { tags: suggestedTags } = JSON.parse(response.output_text);
|
||||
|
||||
console.log(`Suggesting tags for ${iconName}:`, suggestedTags);
|
||||
|
||||
// const currentContent = require(`../${filePath}`);
|
||||
const jsonFile = path.join(process.cwd(), file);
|
||||
const currentFileContent = await fs.readFile(jsonFile, 'utf-8') as unknown as string;
|
||||
const metaData = JSON.parse(currentFileContent);
|
||||
|
||||
console.log(`Current tags for ${iconName}:`, metaData.tags || []);
|
||||
|
||||
const tagSuggestionsWithoutDuplicates = suggestedTags.filter((tag) => {
|
||||
return !metaData.tags?.includes(tag) && tag !== iconName;
|
||||
});
|
||||
|
||||
console.log(`Tag suggestions for ${iconName} without duplicates:`, tagSuggestionsWithoutDuplicates);
|
||||
|
||||
if (tagSuggestionsWithoutDuplicates.length === 0) {
|
||||
console.log(`No new tags to suggest for ${iconName}. Skipping...`);
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
// Find the startLine in the json file
|
||||
const startLine = currentFileContent.split('\n').findIndex((line) => line.includes('"tags":')) + 1;
|
||||
|
||||
const codeSuggestion = tagSuggestionsWithoutDuplicates.map((tag) => ` "${tag}"`).join(',\n');
|
||||
|
||||
const message = `Suggestions for the \`${iconName}\` icon.
|
||||
Try asking it your self if you want more suggestions. [Open ChatGPT](https://chatgpt.com/?q=${encodeURIComponent(input)})
|
||||
Here are the suggestions:
|
||||
\`\`\`suggestion\n "tags": [\n${codeSuggestion},`;
|
||||
|
||||
return {
|
||||
path: file,
|
||||
line: startLine,
|
||||
body: message,
|
||||
}
|
||||
})
|
||||
|
||||
const comments = (await Promise.all(suggestionsByFile)).filter(comment => comment !== null)
|
||||
|
||||
if (comments.length === 0) {
|
||||
console.log('No new tags to suggest for any icons.');
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
await octokit.pulls.createReview({
|
||||
owner,
|
||||
repo,
|
||||
pull_number: pullRequestNumber,
|
||||
body: `### 🤖 ChatGPT Tags suggestions ✨
|
||||
I've asked ChatGPT for some suggestions for tags.`,
|
||||
event: "COMMENT",
|
||||
comments,
|
||||
commit_id: commitSha,
|
||||
});
|
||||
Reference in New Issue
Block a user