From 0e54626bdbcc7da2a22a1c8ee9894e67717bd0cc Mon Sep 17 00:00:00 2001 From: Eric Fennis Date: Fri, 27 Jun 2025 17:11:00 +0200 Subject: [PATCH] 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 --- .../pull-request-tags-suggestions.yml | 33 +++ .gitignore | 1 + icons/trash.json | 12 +- package.json | 8 + pnpm-lock.yaml | 190 +++++++++++++++++- scripts/suggestTags.mts | 124 ++++++++++++ 6 files changed, 366 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/pull-request-tags-suggestions.yml create mode 100644 scripts/suggestTags.mts diff --git a/.github/workflows/pull-request-tags-suggestions.yml b/.github/workflows/pull-request-tags-suggestions.yml new file mode 100644 index 000000000..81d1b85d1 --- /dev/null +++ b/.github/workflows/pull-request-tags-suggestions.yml @@ -0,0 +1,33 @@ +name: Pull request tags suggestions + +on: + pull_request: + paths: + - 'icons/*.json' + +jobs: + pull-request-tags-suggestions: + name: Pull Request Tags Suggestions + runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: write + + steps: + - uses: actions/checkout@v4 + - uses: pnpm/action-setup@v4 + - uses: actions/setup-node@v4 + with: + cache: 'pnpm' + node-version-file: 'package.json' + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Generate comment markup + run: node ./scripts/suggestTags.mts + env: + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + PULL_REQUEST_NUMBER: ${{ github.event.number }} + COMMIT_SHA: ${{ github.sha }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index e2098e23e..41cb26c26 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ .obsidian .now .idea +.env node_modules dist build diff --git a/icons/trash.json b/icons/trash.json index f38133f82..6803cf3de 100644 --- a/icons/trash.json +++ b/icons/trash.json @@ -7,10 +7,20 @@ "karsa-mistmere" ], "tags": [ + "empty", + "deletion", + "cleanup", + "junk", + "clear", "garbage", "delete", "remove", - "bin" + "bin", + "waste", + "recycle", + "discard", + "binoculars", + "rubbish" ], "categories": [ "files", diff --git a/package.json b/package.json index fd5a84d67..1aa654cff 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,8 @@ "generate:changelog": "node ./scripts/generateChangelog.mts", "generate:contributors": "node ./scripts/updateContributors.mts icons/*.svg", "generate:nextJSAliases": "node ./scripts/generateNextJSAliases.mts", + "suggest:tags": "node ./scripts/suggestTags.mts", + "suggest:tags:watch": "node --env-file .env --watch ./scripts/suggestTags.mts ", "postinstall": "husky install", "lint:es": "eslint .", "lint:format": "prettier \"**/*.{js,mjs,ts,jsx,tsx,html,css,scss,json,yml,yaml}\" --check", @@ -38,6 +40,8 @@ "renamePattern": "node ./scripts/rename/renamePattern.mts" }, "devDependencies": { + "@actions/core": "^1.11.1", + "@actions/github": "^6.0.1", "@html-eslint/eslint-plugin": "^0.19.1", "@html-eslint/parser": "^0.19.1", "@octokit/rest": "^19.0.13", @@ -56,6 +60,7 @@ "husky": "^8.0.3", "lint-staged": "^13.3.0", "minimist": "^1.2.8", + "openai": "^5.8.1", "p-memoize": "^7.1.1", "prettier": "3.2.4", "prettier-plugin-astro": "^0.14.1", @@ -80,5 +85,8 @@ "overrides": { "cross-spawn": "7.0.5" } + }, + "dependencies": { + "zod": "^3.25.67" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 035478de5..9258db9bb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -12,7 +12,17 @@ packageExtensionsChecksum: sha256-iadh7fHFnueQwqCQdCI9Kni0+s5mxkL9biStuN8E4yQ= importers: .: + dependencies: + zod: + specifier: ^3.25.67 + version: 3.25.67 devDependencies: + '@actions/core': + specifier: ^1.11.1 + version: 1.11.1 + '@actions/github': + specifier: ^6.0.1 + version: 6.0.1 '@html-eslint/eslint-plugin': specifier: ^0.19.1 version: 0.19.1 @@ -67,6 +77,9 @@ importers: minimist: specifier: ^1.2.8 version: 1.2.8 + openai: + specifier: ^5.8.1 + version: 5.8.1(ws@8.18.0)(zod@3.25.67) p-memoize: specifier: ^7.1.1 version: 7.1.1 @@ -870,6 +883,21 @@ importers: packages: + '@actions/core@1.11.1': + resolution: {integrity: sha512-hXJCSrkwfA46Vd9Z3q4cpEpHB1rL5NG04+/rbqW9d3+CSvtB1tYe8UTpAlixa1vj0m/ULglfEK2UKxMGxCxv5A==} + + '@actions/exec@1.1.1': + resolution: {integrity: sha512-+sCcHHbVdk93a0XT19ECtO/gIXoxvdsgQLzb2fE2/5sIZmWQuluYyjPQtrtTHdU1YzTZ7bAPN4sITq2xi1679w==} + + '@actions/github@6.0.1': + resolution: {integrity: sha512-xbZVcaqD4XnQAe35qSQqskb3SqIAfRyLBrHMd/8TuL7hJSz2QtbDwnNM8zWx4zO5l2fnGtseNE3MbEvD7BxVMw==} + + '@actions/http-client@2.2.3': + resolution: {integrity: sha512-mx8hyJi/hjFvbPokCg4uRd4ZX78t+YyRPtnKWwIl+RzNaVuFpQHfmlGVfsKEJN8LwTCvL+DfVgAM04XaHkm6bA==} + + '@actions/io@1.1.3': + resolution: {integrity: sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q==} + '@adobe/css-tools@4.4.2': resolution: {integrity: sha512-baYZExFpsdkBNuvGKTKWCwKH57HRZLVtycZS05WTQNVOiXVSeAki3nU35zlRbToeMW8aHlJfyS+1C4BOv27q0A==} @@ -3979,32 +4007,66 @@ packages: resolution: {integrity: sha512-TWFX7cZF2LXoCvdmJWY7XVPi74aSY0+FfBZNSXEXFkMpjcqsQwDSYVv5FhRFaI0V1ECnwbz4j59T/G+rXNWaIQ==} engines: {node: '>= 14'} + '@octokit/auth-token@4.0.0': + resolution: {integrity: sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA==} + engines: {node: '>= 18'} + '@octokit/core@4.2.4': resolution: {integrity: sha512-rYKilwgzQ7/imScn3M9/pFfUf4I1AZEH3KhyJmtPdE2zfaXAn2mFfUy4FbKewzc2We5y/LlKLj36fWJLKC2SIQ==} engines: {node: '>= 14'} + '@octokit/core@5.2.1': + resolution: {integrity: sha512-dKYCMuPO1bmrpuogcjQ8z7ICCH3FP6WmxpwC03yjzGfZhj9fTJg6+bS1+UAplekbN2C+M61UNllGOOoAfGCrdQ==} + engines: {node: '>= 18'} + '@octokit/endpoint@7.0.6': resolution: {integrity: sha512-5L4fseVRUsDFGR00tMWD/Trdeeihn999rTMGRMC1G/Ldi1uWlWJzI98H4Iak5DB/RVvQuyMYKqSK/R6mbSOQyg==} engines: {node: '>= 14'} + '@octokit/endpoint@9.0.6': + resolution: {integrity: sha512-H1fNTMA57HbkFESSt3Y9+FBICv+0jFceJFPWDePYlR/iMGrwM5ph+Dd4XRQs+8X+PUFURLQgX9ChPfhJ/1uNQw==} + engines: {node: '>= 18'} + '@octokit/graphql@5.0.6': resolution: {integrity: sha512-Fxyxdy/JH0MnIB5h+UQ3yCoh1FG4kWXfFKkpWqjZHw/p+Kc8Y44Hu/kCgNBT6nU1shNumEchmW/sUO1JuQnPcw==} engines: {node: '>= 14'} + '@octokit/graphql@7.1.1': + resolution: {integrity: sha512-3mkDltSfcDUoa176nlGoA32RGjeWjl3K7F/BwHwRMJUW/IteSa4bnSV8p2ThNkcIcZU2umkZWxwETSSCJf2Q7g==} + engines: {node: '>= 18'} + '@octokit/openapi-types@18.1.1': resolution: {integrity: sha512-VRaeH8nCDtF5aXWnjPuEMIYf1itK/s3JYyJcWFJT8X9pSNnBtriDf7wlEWsGuhPLl4QIH4xM8fqTXDwJ3Mu6sw==} + '@octokit/openapi-types@20.0.0': + resolution: {integrity: sha512-EtqRBEjp1dL/15V7WiX5LJMIxxkdiGJnabzYx5Apx4FkQIFgAfKumXeYAqqJCj1s+BMX4cPFIFC4OLCR6stlnA==} + + '@octokit/openapi-types@24.2.0': + resolution: {integrity: sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==} + '@octokit/plugin-paginate-rest@6.1.2': resolution: {integrity: sha512-qhrmtQeHU/IivxucOV1bbI/xZyC/iOBhclokv7Sut5vnejAIAEXVcGQeRpQlU39E0WwK9lNvJHphHri/DB6lbQ==} engines: {node: '>= 14'} peerDependencies: '@octokit/core': '>=4' + '@octokit/plugin-paginate-rest@9.2.2': + resolution: {integrity: sha512-u3KYkGF7GcZnSD/3UP0S7K5XUFT2FkOQdcfXZGZQPGv3lm4F2Xbf71lvjldr8c1H3nNbF+33cLEkWYbokGWqiQ==} + engines: {node: '>= 18'} + peerDependencies: + '@octokit/core': '5' + '@octokit/plugin-request-log@1.0.4': resolution: {integrity: sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA==} peerDependencies: '@octokit/core': '>=3' + '@octokit/plugin-rest-endpoint-methods@10.4.1': + resolution: {integrity: sha512-xV1b+ceKV9KytQe3zCVqjg+8GTGfDYwaT1ATU5isiUyVtlVAO3HNdzpS4sr4GBx4hxQ46s7ITtZrAsxG22+rVg==} + engines: {node: '>= 18'} + peerDependencies: + '@octokit/core': '5' + '@octokit/plugin-rest-endpoint-methods@7.2.3': resolution: {integrity: sha512-I5Gml6kTAkzVlN7KCtjOM+Ruwe/rQppp0QU372K1GP7kNOYEKe8Xn5BW4sE62JAHdwpq95OQK/qGNyKQMUzVgA==} engines: {node: '>= 14'} @@ -4015,10 +4077,18 @@ packages: resolution: {integrity: sha512-crqw3V5Iy2uOU5Np+8M/YexTlT8zxCfI+qu+LxUB7SZpje4Qmx3mub5DfEKSO8Ylyk0aogi6TYdf6kxzh2BguQ==} engines: {node: '>= 14'} + '@octokit/request-error@5.1.1': + resolution: {integrity: sha512-v9iyEQJH6ZntoENr9/yXxjuezh4My67CBSu9r6Ve/05Iu5gNgnisNWOsoJHTP6k0Rr0+HQIpnH+kyammu90q/g==} + engines: {node: '>= 18'} + '@octokit/request@6.2.8': resolution: {integrity: sha512-ow4+pkVQ+6XVVsekSYBzJC0VTVvh/FCTUUgTsboGq+DTeWdyIFV8WSCdo0RIxk6wSkBTHqIK1mYuY7nOBXOchw==} engines: {node: '>= 14'} + '@octokit/request@8.4.1': + resolution: {integrity: sha512-qnB2+SY3hkCmBxZsR/MPCybNmbJe4KAlfWErXq+rBKkQJlbjdJeS85VI9r8UqeLYLvnAenU8Q1okM/0MBsAGXw==} + engines: {node: '>= 18'} + '@octokit/rest@19.0.13': resolution: {integrity: sha512-/EzVox5V9gYGdbAI+ovYj3nXQT1TtTHRT+0eZPcuC05UFSWO3mdO9UY1C0i2eLF9Un1ONJkAk+IEtYGAC+TahA==} engines: {node: '>= 14'} @@ -4029,6 +4099,12 @@ packages: '@octokit/types@10.0.0': resolution: {integrity: sha512-Vm8IddVmhCgU1fxC1eyinpwqzXPEYu0NrYzD3YZjlGjyftdLBTeqNblRC0jmJmgxbJIsQlyogVeGnrNaaMVzIg==} + '@octokit/types@12.6.0': + resolution: {integrity: sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw==} + + '@octokit/types@13.10.0': + resolution: {integrity: sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==} + '@octokit/types@9.3.2': resolution: {integrity: sha512-D4iHGTdAnEEVsB8fl95m1hiz7D5YiRdQ9b/OEb3BYRVwbLsGHcRVPz+u+BgRLNk0Q0/4iZCBqDN96j2XNxfXrA==} @@ -10293,6 +10369,18 @@ packages: resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==} engines: {node: '>=12'} + openai@5.8.1: + resolution: {integrity: sha512-+qp4vQjJs43pzMSb6quTYslOhVE0c0c7j4YMoEks83BnusG23UrsWn3Hey6/8mwYadY05KipLvbp+PTO4jxO9w==} + hasBin: true + peerDependencies: + ws: ^8.18.0 + zod: ^3.23.8 + peerDependenciesMeta: + ws: + optional: true + zod: + optional: true + openapi-typescript@6.7.6: resolution: {integrity: sha512-c/hfooPx+RBIOPM09GSxABOZhYPblDoyaGhqBkD/59vtpN21jEuWKDlM0KYTvqJVlSYjKs0tBcIdeXKChlSPtw==} hasBin: true @@ -12402,6 +12490,10 @@ packages: resolution: {integrity: sha512-OvmFcj70PhmAsVQKfC15XoKH55cRWuaRzvr2fpTNhTNer6JBpG8n6vOhRrIgxMjcikyYt88xqYXMMVapJ4Rjvg==} hasBin: true + tunnel@0.0.6: + resolution: {integrity: sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==} + engines: {node: '>=0.6.11 <=0.7.0 || >=0.7.3'} + type-check@0.4.0: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} @@ -13456,6 +13548,9 @@ packages: zod@3.24.2: resolution: {integrity: sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ==} + zod@3.25.67: + resolution: {integrity: sha512-idA2YXwpCdqUSKRCACDE6ItZD9TZzy3OZMtpfLoh6oPR47lipysRrJfjzMqFxQ3uJuUPyUeWe1r9vLH33xO/Qw==} + zone.js@0.11.8: resolution: {integrity: sha512-82bctBg2hKcEJ21humWIkXRlLBBmrc3nN7DFh5LGGhcyycO2S7FN8NmdvlcKaGFDNVL4/9kFLmwmInTavdJERA==} @@ -13464,6 +13559,32 @@ packages: snapshots: + '@actions/core@1.11.1': + dependencies: + '@actions/exec': 1.1.1 + '@actions/http-client': 2.2.3 + + '@actions/exec@1.1.1': + dependencies: + '@actions/io': 1.1.3 + + '@actions/github@6.0.1': + dependencies: + '@actions/http-client': 2.2.3 + '@octokit/core': 5.2.1 + '@octokit/plugin-paginate-rest': 9.2.2(@octokit/core@5.2.1) + '@octokit/plugin-rest-endpoint-methods': 10.4.1(@octokit/core@5.2.1) + '@octokit/request': 8.4.1 + '@octokit/request-error': 5.1.1 + undici: 5.28.5 + + '@actions/http-client@2.2.3': + dependencies: + tunnel: 0.0.6 + undici: 5.28.5 + + '@actions/io@1.1.3': {} + '@adobe/css-tools@4.4.2': {} '@algolia/autocomplete-core@1.17.7(@algolia/client-search@5.25.0)(algoliasearch@5.25.0)(search-insights@2.8.2)': @@ -16974,6 +17095,8 @@ snapshots: '@octokit/auth-token@3.0.4': {} + '@octokit/auth-token@4.0.0': {} + '@octokit/core@4.2.4(encoding@0.1.13)': dependencies: '@octokit/auth-token': 3.0.4 @@ -16986,12 +17109,27 @@ snapshots: transitivePeerDependencies: - encoding + '@octokit/core@5.2.1': + dependencies: + '@octokit/auth-token': 4.0.0 + '@octokit/graphql': 7.1.1 + '@octokit/request': 8.4.1 + '@octokit/request-error': 5.1.1 + '@octokit/types': 13.10.0 + before-after-hook: 2.2.3 + universal-user-agent: 6.0.1 + '@octokit/endpoint@7.0.6': dependencies: '@octokit/types': 9.3.2 is-plain-object: 5.0.0 universal-user-agent: 6.0.1 + '@octokit/endpoint@9.0.6': + dependencies: + '@octokit/types': 13.10.0 + universal-user-agent: 6.0.1 + '@octokit/graphql@5.0.6(encoding@0.1.13)': dependencies: '@octokit/request': 6.2.8(encoding@0.1.13) @@ -17000,18 +17138,38 @@ snapshots: transitivePeerDependencies: - encoding + '@octokit/graphql@7.1.1': + dependencies: + '@octokit/request': 8.4.1 + '@octokit/types': 13.10.0 + universal-user-agent: 6.0.1 + '@octokit/openapi-types@18.1.1': {} + '@octokit/openapi-types@20.0.0': {} + + '@octokit/openapi-types@24.2.0': {} + '@octokit/plugin-paginate-rest@6.1.2(@octokit/core@4.2.4(encoding@0.1.13))': dependencies: '@octokit/core': 4.2.4(encoding@0.1.13) '@octokit/tsconfig': 1.0.2 '@octokit/types': 9.3.2 + '@octokit/plugin-paginate-rest@9.2.2(@octokit/core@5.2.1)': + dependencies: + '@octokit/core': 5.2.1 + '@octokit/types': 12.6.0 + '@octokit/plugin-request-log@1.0.4(@octokit/core@4.2.4(encoding@0.1.13))': dependencies: '@octokit/core': 4.2.4(encoding@0.1.13) + '@octokit/plugin-rest-endpoint-methods@10.4.1(@octokit/core@5.2.1)': + dependencies: + '@octokit/core': 5.2.1 + '@octokit/types': 12.6.0 + '@octokit/plugin-rest-endpoint-methods@7.2.3(@octokit/core@4.2.4(encoding@0.1.13))': dependencies: '@octokit/core': 4.2.4(encoding@0.1.13) @@ -17023,6 +17181,12 @@ snapshots: deprecation: 2.3.1 once: 1.4.0 + '@octokit/request-error@5.1.1': + dependencies: + '@octokit/types': 13.10.0 + deprecation: 2.3.1 + once: 1.4.0 + '@octokit/request@6.2.8(encoding@0.1.13)': dependencies: '@octokit/endpoint': 7.0.6 @@ -17034,6 +17198,13 @@ snapshots: transitivePeerDependencies: - encoding + '@octokit/request@8.4.1': + dependencies: + '@octokit/endpoint': 9.0.6 + '@octokit/request-error': 5.1.1 + '@octokit/types': 13.10.0 + universal-user-agent: 6.0.1 + '@octokit/rest@19.0.13(encoding@0.1.13)': dependencies: '@octokit/core': 4.2.4(encoding@0.1.13) @@ -17049,6 +17220,14 @@ snapshots: dependencies: '@octokit/openapi-types': 18.1.1 + '@octokit/types@12.6.0': + dependencies: + '@octokit/openapi-types': 20.0.0 + + '@octokit/types@13.10.0': + dependencies: + '@octokit/openapi-types': 24.2.0 + '@octokit/types@9.3.2': dependencies: '@octokit/openapi-types': 18.1.1 @@ -18978,7 +19157,7 @@ snapshots: acorn-walk@8.3.3: dependencies: - acorn: 8.14.0 + acorn: 8.14.1 acorn@8.12.1: {} @@ -24715,6 +24894,11 @@ snapshots: is-docker: 2.2.1 is-wsl: 2.2.0 + openai@5.8.1(ws@8.18.0)(zod@3.25.67): + optionalDependencies: + ws: 8.18.0 + zod: 3.25.67 + openapi-typescript@6.7.6: dependencies: ansi-colors: 4.1.3 @@ -27409,6 +27593,8 @@ snapshots: argparse: 2.0.1 pako: 1.0.11 + tunnel@0.0.6: {} + type-check@0.4.0: dependencies: prelude-ls: 1.2.1 @@ -28430,6 +28616,8 @@ snapshots: zod@3.24.2: {} + zod@3.25.67: {} + zone.js@0.11.8: dependencies: tslib: 2.8.1 diff --git a/scripts/suggestTags.mts b/scripts/suggestTags.mts new file mode 100644 index 000000000..451af2705 --- /dev/null +++ b/scripts/suggestTags.mts @@ -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, +});