mirror of
https://github.com/tcgdex/cards-database.git
synced 2025-05-07 17:57:51 +00:00
chore: Add ability to comment on Prs to simplify checking cards (#729)
This commit is contained in:
parent
b26236e78c
commit
1eb4c677a7
34
.github/scripts/.gitignore
vendored
Normal file
34
.github/scripts/.gitignore
vendored
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
# dependencies (bun install)
|
||||||
|
node_modules
|
||||||
|
|
||||||
|
# output
|
||||||
|
out
|
||||||
|
dist
|
||||||
|
*.tgz
|
||||||
|
|
||||||
|
# code coverage
|
||||||
|
coverage
|
||||||
|
*.lcov
|
||||||
|
|
||||||
|
# logs
|
||||||
|
logs
|
||||||
|
_.log
|
||||||
|
report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
|
||||||
|
|
||||||
|
# dotenv environment variable files
|
||||||
|
.env
|
||||||
|
.env.development.local
|
||||||
|
.env.test.local
|
||||||
|
.env.production.local
|
||||||
|
.env.local
|
||||||
|
|
||||||
|
# caches
|
||||||
|
.eslintcache
|
||||||
|
.cache
|
||||||
|
*.tsbuildinfo
|
||||||
|
|
||||||
|
# IntelliJ based IDEs
|
||||||
|
.idea
|
||||||
|
|
||||||
|
# Finder (MacOS) folder config
|
||||||
|
.DS_Store
|
15
.github/scripts/README.md
vendored
Normal file
15
.github/scripts/README.md
vendored
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
# scripts
|
||||||
|
|
||||||
|
To install dependencies:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bun install
|
||||||
|
```
|
||||||
|
|
||||||
|
To run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bun run
|
||||||
|
```
|
||||||
|
|
||||||
|
This project was created using `bun init` in bun v1.2.11. [Bun](https://bun.sh) is a fast all-in-one JavaScript runtime.
|
106
.github/scripts/bun.lock
vendored
Normal file
106
.github/scripts/bun.lock
vendored
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
{
|
||||||
|
"lockfileVersion": 1,
|
||||||
|
"workspaces": {
|
||||||
|
"": {
|
||||||
|
"name": "scripts",
|
||||||
|
"dependencies": {
|
||||||
|
"@actions/core": "^1.11.1",
|
||||||
|
"@actions/github": "^6.0.0",
|
||||||
|
"@tcgdex/sdk": "^2.6.0",
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/bun": "latest",
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"typescript": "^5",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"packages": {
|
||||||
|
"@actions/core": ["@actions/core@1.11.1", "", { "dependencies": { "@actions/exec": "^1.1.1", "@actions/http-client": "^2.0.1" } }, "sha512-hXJCSrkwfA46Vd9Z3q4cpEpHB1rL5NG04+/rbqW9d3+CSvtB1tYe8UTpAlixa1vj0m/ULglfEK2UKxMGxCxv5A=="],
|
||||||
|
|
||||||
|
"@actions/exec": ["@actions/exec@1.1.1", "", { "dependencies": { "@actions/io": "^1.0.1" } }, "sha512-+sCcHHbVdk93a0XT19ECtO/gIXoxvdsgQLzb2fE2/5sIZmWQuluYyjPQtrtTHdU1YzTZ7bAPN4sITq2xi1679w=="],
|
||||||
|
|
||||||
|
"@actions/github": ["@actions/github@6.0.0", "", { "dependencies": { "@actions/http-client": "^2.2.0", "@octokit/core": "^5.0.1", "@octokit/plugin-paginate-rest": "^9.0.0", "@octokit/plugin-rest-endpoint-methods": "^10.0.0" } }, "sha512-alScpSVnYmjNEXboZjarjukQEzgCRmjMv6Xj47fsdnqGS73bjJNDpiiXmp8jr0UZLdUB6d9jW63IcmddUP+l0g=="],
|
||||||
|
|
||||||
|
"@actions/http-client": ["@actions/http-client@2.2.3", "", { "dependencies": { "tunnel": "^0.0.6", "undici": "^5.25.4" } }, "sha512-mx8hyJi/hjFvbPokCg4uRd4ZX78t+YyRPtnKWwIl+RzNaVuFpQHfmlGVfsKEJN8LwTCvL+DfVgAM04XaHkm6bA=="],
|
||||||
|
|
||||||
|
"@actions/io": ["@actions/io@1.1.3", "", {}, "sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q=="],
|
||||||
|
|
||||||
|
"@cachex/core": ["@cachex/core@1.0.1", "", { "dependencies": { "@dzeio/object-util": "^1.8.3" } }, "sha512-sHynwjF9hKIvwg8rTUdvA7MOUMcUC5Mq0dpynPBILRS+IPvsHcE4Cb2uRSs0/I2nxO7NQp9p+xHYistdfJJSwg=="],
|
||||||
|
|
||||||
|
"@cachex/memory": ["@cachex/memory@1.0.1", "", { "dependencies": { "@cachex/core": "^1" } }, "sha512-KWUTdCCXhIlAkJaVZMhUh9kD0uq8PxC3Z34Q3lMGZOAV6FXP/cOMW89ALrWX3VkoRrrM4R6MIMO+amZNOvEqgw=="],
|
||||||
|
|
||||||
|
"@cachex/web-storage": ["@cachex/web-storage@1.0.1", "", { "dependencies": { "@cachex/core": "^1" } }, "sha512-E8Xa9qDZgNgr+lcj3eixowg7PH2CVZbp3huuoc5xVVTtwYrZi5YqbHBG12yG3r6C6Fts/2Yoq6cbVBSm6c8VRA=="],
|
||||||
|
|
||||||
|
"@dzeio/object-util": ["@dzeio/object-util@1.9.1", "", {}, "sha512-cLGsjAc7hzSadS57jcMxSPidYabyZXJOFnasScSrE/V5yflhze6T7L5/98josWYrXMvoKu7N+Ivk6vGkIj72UQ=="],
|
||||||
|
|
||||||
|
"@fastify/busboy": ["@fastify/busboy@2.1.1", "", {}, "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA=="],
|
||||||
|
|
||||||
|
"@octokit/auth-token": ["@octokit/auth-token@4.0.0", "", {}, "sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA=="],
|
||||||
|
|
||||||
|
"@octokit/core": ["@octokit/core@5.2.1", "", { "dependencies": { "@octokit/auth-token": "^4.0.0", "@octokit/graphql": "^7.1.0", "@octokit/request": "^8.4.1", "@octokit/request-error": "^5.1.1", "@octokit/types": "^13.0.0", "before-after-hook": "^2.2.0", "universal-user-agent": "^6.0.0" } }, "sha512-dKYCMuPO1bmrpuogcjQ8z7ICCH3FP6WmxpwC03yjzGfZhj9fTJg6+bS1+UAplekbN2C+M61UNllGOOoAfGCrdQ=="],
|
||||||
|
|
||||||
|
"@octokit/endpoint": ["@octokit/endpoint@9.0.6", "", { "dependencies": { "@octokit/types": "^13.1.0", "universal-user-agent": "^6.0.0" } }, "sha512-H1fNTMA57HbkFESSt3Y9+FBICv+0jFceJFPWDePYlR/iMGrwM5ph+Dd4XRQs+8X+PUFURLQgX9ChPfhJ/1uNQw=="],
|
||||||
|
|
||||||
|
"@octokit/graphql": ["@octokit/graphql@7.1.1", "", { "dependencies": { "@octokit/request": "^8.4.1", "@octokit/types": "^13.0.0", "universal-user-agent": "^6.0.0" } }, "sha512-3mkDltSfcDUoa176nlGoA32RGjeWjl3K7F/BwHwRMJUW/IteSa4bnSV8p2ThNkcIcZU2umkZWxwETSSCJf2Q7g=="],
|
||||||
|
|
||||||
|
"@octokit/openapi-types": ["@octokit/openapi-types@24.2.0", "", {}, "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg=="],
|
||||||
|
|
||||||
|
"@octokit/plugin-paginate-rest": ["@octokit/plugin-paginate-rest@9.2.2", "", { "dependencies": { "@octokit/types": "^12.6.0" }, "peerDependencies": { "@octokit/core": "5" } }, "sha512-u3KYkGF7GcZnSD/3UP0S7K5XUFT2FkOQdcfXZGZQPGv3lm4F2Xbf71lvjldr8c1H3nNbF+33cLEkWYbokGWqiQ=="],
|
||||||
|
|
||||||
|
"@octokit/plugin-rest-endpoint-methods": ["@octokit/plugin-rest-endpoint-methods@10.4.1", "", { "dependencies": { "@octokit/types": "^12.6.0" }, "peerDependencies": { "@octokit/core": "5" } }, "sha512-xV1b+ceKV9KytQe3zCVqjg+8GTGfDYwaT1ATU5isiUyVtlVAO3HNdzpS4sr4GBx4hxQ46s7ITtZrAsxG22+rVg=="],
|
||||||
|
|
||||||
|
"@octokit/request": ["@octokit/request@8.4.1", "", { "dependencies": { "@octokit/endpoint": "^9.0.6", "@octokit/request-error": "^5.1.1", "@octokit/types": "^13.1.0", "universal-user-agent": "^6.0.0" } }, "sha512-qnB2+SY3hkCmBxZsR/MPCybNmbJe4KAlfWErXq+rBKkQJlbjdJeS85VI9r8UqeLYLvnAenU8Q1okM/0MBsAGXw=="],
|
||||||
|
|
||||||
|
"@octokit/request-error": ["@octokit/request-error@5.1.1", "", { "dependencies": { "@octokit/types": "^13.1.0", "deprecation": "^2.0.0", "once": "^1.4.0" } }, "sha512-v9iyEQJH6ZntoENr9/yXxjuezh4My67CBSu9r6Ve/05Iu5gNgnisNWOsoJHTP6k0Rr0+HQIpnH+kyammu90q/g=="],
|
||||||
|
|
||||||
|
"@octokit/types": ["@octokit/types@13.10.0", "", { "dependencies": { "@octokit/openapi-types": "^24.2.0" } }, "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA=="],
|
||||||
|
|
||||||
|
"@tcgdex/sdk": ["@tcgdex/sdk@2.6.0", "", { "dependencies": { "@cachex/memory": "^1", "@cachex/web-storage": "^1", "@dzeio/object-util": "^1", "isomorphic-unfetch": "^3" } }, "sha512-q0O7dNzRRLq38XwqUoHFKo78/NCQW5pEIZ+JnlrVOxp9r9R/LTIQGAMNR96Il0hb1uIm4j5o7WBueBCpuDyomQ=="],
|
||||||
|
|
||||||
|
"@types/bun": ["@types/bun@1.2.11", "", { "dependencies": { "bun-types": "1.2.11" } }, "sha512-ZLbbI91EmmGwlWTRWuV6J19IUiUC5YQ3TCEuSHI3usIP75kuoA8/0PVF+LTrbEnVc8JIhpElWOxv1ocI1fJBbw=="],
|
||||||
|
|
||||||
|
"@types/node": ["@types/node@22.15.3", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-lX7HFZeHf4QG/J7tBZqrCAXwz9J5RD56Y6MpP0eJkka8p+K0RY/yBTW7CYFJ4VGCclxqOLKmiGP5juQc6MKgcw=="],
|
||||||
|
|
||||||
|
"before-after-hook": ["before-after-hook@2.2.3", "", {}, "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ=="],
|
||||||
|
|
||||||
|
"bun-types": ["bun-types@1.2.11", "", { "dependencies": { "@types/node": "*" } }, "sha512-dbkp5Lo8HDrXkLrONm6bk+yiiYQSntvFUzQp0v3pzTAsXk6FtgVMjdQ+lzFNVAmQFUkPQZ3WMZqH5tTo+Dp/IA=="],
|
||||||
|
|
||||||
|
"deprecation": ["deprecation@2.3.1", "", {}, "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ=="],
|
||||||
|
|
||||||
|
"isomorphic-unfetch": ["isomorphic-unfetch@3.1.0", "", { "dependencies": { "node-fetch": "^2.6.1", "unfetch": "^4.2.0" } }, "sha512-geDJjpoZ8N0kWexiwkX8F9NkTsXhetLPVbZFQ+JTW239QNOwvB0gniuR1Wc6f0AMTn7/mFGyXvHTifrCp/GH8Q=="],
|
||||||
|
|
||||||
|
"node-fetch": ["node-fetch@2.7.0", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="],
|
||||||
|
|
||||||
|
"once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="],
|
||||||
|
|
||||||
|
"tr46": ["tr46@0.0.3", "", {}, "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="],
|
||||||
|
|
||||||
|
"tunnel": ["tunnel@0.0.6", "", {}, "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg=="],
|
||||||
|
|
||||||
|
"typescript": ["typescript@5.8.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ=="],
|
||||||
|
|
||||||
|
"undici": ["undici@5.29.0", "", { "dependencies": { "@fastify/busboy": "^2.0.0" } }, "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg=="],
|
||||||
|
|
||||||
|
"undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="],
|
||||||
|
|
||||||
|
"unfetch": ["unfetch@4.2.0", "", {}, "sha512-F9p7yYCn6cIW9El1zi0HI6vqpeIvBsr3dSuRO6Xuppb1u5rXpCPmMvLSyECLhybr9isec8Ohl0hPekMVrEinDA=="],
|
||||||
|
|
||||||
|
"universal-user-agent": ["universal-user-agent@6.0.1", "", {}, "sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ=="],
|
||||||
|
|
||||||
|
"webidl-conversions": ["webidl-conversions@3.0.1", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="],
|
||||||
|
|
||||||
|
"whatwg-url": ["whatwg-url@5.0.0", "", { "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" } }, "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw=="],
|
||||||
|
|
||||||
|
"wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="],
|
||||||
|
|
||||||
|
"@octokit/plugin-paginate-rest/@octokit/types": ["@octokit/types@12.6.0", "", { "dependencies": { "@octokit/openapi-types": "^20.0.0" } }, "sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw=="],
|
||||||
|
|
||||||
|
"@octokit/plugin-rest-endpoint-methods/@octokit/types": ["@octokit/types@12.6.0", "", { "dependencies": { "@octokit/openapi-types": "^20.0.0" } }, "sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw=="],
|
||||||
|
|
||||||
|
"@octokit/plugin-paginate-rest/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@20.0.0", "", {}, "sha512-EtqRBEjp1dL/15V7WiX5LJMIxxkdiGJnabzYx5Apx4FkQIFgAfKumXeYAqqJCj1s+BMX4cPFIFC4OLCR6stlnA=="],
|
||||||
|
|
||||||
|
"@octokit/plugin-rest-endpoint-methods/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@20.0.0", "", {}, "sha512-EtqRBEjp1dL/15V7WiX5LJMIxxkdiGJnabzYx5Apx4FkQIFgAfKumXeYAqqJCj1s+BMX4cPFIFC4OLCR6stlnA=="],
|
||||||
|
}
|
||||||
|
}
|
371
.github/scripts/load-cards.ts
vendored
Normal file
371
.github/scripts/load-cards.ts
vendored
Normal file
@ -0,0 +1,371 @@
|
|||||||
|
import core from "@actions/core";
|
||||||
|
import github from "@actions/github";
|
||||||
|
import TCGdex from "@tcgdex/sdk";
|
||||||
|
|
||||||
|
// Types
|
||||||
|
type CardData = {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
image?: string;
|
||||||
|
rarity?: string;
|
||||||
|
set: { name: string };
|
||||||
|
hasImage: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
type CardResult = {
|
||||||
|
file: string;
|
||||||
|
card?: CardData;
|
||||||
|
error?: string;
|
||||||
|
isAsian?: boolean;
|
||||||
|
usedLanguage?: string;
|
||||||
|
hasImage?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
type CardFetchResult = {
|
||||||
|
card: any;
|
||||||
|
usedLanguage: string;
|
||||||
|
hasImage: boolean;
|
||||||
|
} | null;
|
||||||
|
|
||||||
|
// Constants
|
||||||
|
const DATA_REGEX = /^data\/([^\/]+)\/([^\/]+)\/([^\/]+)\.ts$/;
|
||||||
|
const DATA_ASIA_REGEX = /^data-asia\/([^\/]+)\/([^\/]+)\/([^\/]+)\.ts$/;
|
||||||
|
|
||||||
|
const INTERNATIONAL_LANGUAGES = ["en", "fr", "es", "es-mx", "it", "pt", "pt-br", "pt-pt", "de", "nl", "pl", "ru"];
|
||||||
|
const ASIAN_LANGUAGES = ["ja", "ko", "zh-tw", "id", "th", "zh-cn"];
|
||||||
|
|
||||||
|
const LANGUAGE_NAMES: Record<string, string> = {
|
||||||
|
en: "English",
|
||||||
|
fr: "French",
|
||||||
|
es: "Spanish",
|
||||||
|
"es-mx": "Spanish (Mexico)",
|
||||||
|
it: "Italian",
|
||||||
|
pt: "Portuguese",
|
||||||
|
"pt-br": "Portuguese (Brazil)",
|
||||||
|
"pt-pt": "Portuguese (Portugal)",
|
||||||
|
de: "German",
|
||||||
|
nl: "Dutch",
|
||||||
|
pl: "Polish",
|
||||||
|
ru: "Russian",
|
||||||
|
ja: "Japanese",
|
||||||
|
ko: "Korean",
|
||||||
|
"zh-tw": "Chinese (Taiwan)",
|
||||||
|
id: "Indonesian",
|
||||||
|
th: "Thai",
|
||||||
|
"zh-cn": "Chinese (China)",
|
||||||
|
};
|
||||||
|
|
||||||
|
// Helper function to sanitize card data
|
||||||
|
function sanitizeCardData(card: any): CardData | undefined {
|
||||||
|
if (!card) return undefined;
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: card.id,
|
||||||
|
name: card.name,
|
||||||
|
image: card.image,
|
||||||
|
rarity: card.rarity,
|
||||||
|
set: card.set ? { name: card.set.name } : { name: "Unknown" },
|
||||||
|
hasImage: !!card.image,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to try fetching a card with fallback to other languages
|
||||||
|
async function tryFetchCardWithFallback(
|
||||||
|
setIdentifier: string,
|
||||||
|
cardLocalId: string,
|
||||||
|
primaryLanguage: string,
|
||||||
|
allLanguages: string[],
|
||||||
|
isAsianRegion: boolean,
|
||||||
|
): Promise<CardFetchResult> {
|
||||||
|
let lastError: Error | unknown = null;
|
||||||
|
const languagesToTry = [primaryLanguage, ...allLanguages.filter((lang) => lang !== primaryLanguage)];
|
||||||
|
let foundWithoutImage: { lang: string; card: any } | undefined;
|
||||||
|
|
||||||
|
for (const lang of languagesToTry) {
|
||||||
|
try {
|
||||||
|
console.log(` Trying language: ${lang}`);
|
||||||
|
const tcgdex = new TCGdex(lang as any);
|
||||||
|
let card;
|
||||||
|
|
||||||
|
if (!isAsianRegion) {
|
||||||
|
const set = await tcgdex.set.get(setIdentifier);
|
||||||
|
card = await tcgdex.card.get(`${set!.id}-${cardLocalId}`);
|
||||||
|
} else {
|
||||||
|
card = await tcgdex.card.get(`${setIdentifier}-${cardLocalId}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (card && !card.image) {
|
||||||
|
foundWithoutImage = { lang, card };
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (card && card.image) {
|
||||||
|
console.log(` Card: ${card.name} (${card.id}) - Found using language: ${lang}`);
|
||||||
|
return { card, usedLanguage: lang, hasImage: true };
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
lastError = error;
|
||||||
|
console.log(` Failed with language ${lang}: ${error instanceof Error ? error.message : "Unknown error"}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (foundWithoutImage) {
|
||||||
|
console.log(` Card: ${foundWithoutImage.card.name} (${foundWithoutImage.card.id}) - Found without image`);
|
||||||
|
return { card: foundWithoutImage.card, usedLanguage: foundWithoutImage.lang, hasImage: false };
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
` All languages failed. Last error: ${lastError instanceof Error ? lastError.message : "Unknown error"}`,
|
||||||
|
);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get changed files from GitHub
|
||||||
|
async function getChangedFiles(
|
||||||
|
context: typeof github.context,
|
||||||
|
octokit: ReturnType<typeof github.getOctokit>,
|
||||||
|
): Promise<string[]> {
|
||||||
|
if (context.payload.pull_request) {
|
||||||
|
const { owner, repo } = context.repo;
|
||||||
|
const prNumber = context.payload.pull_request.number;
|
||||||
|
const response = await octokit.rest.pulls.listFiles({
|
||||||
|
owner,
|
||||||
|
repo,
|
||||||
|
pull_number: prNumber,
|
||||||
|
});
|
||||||
|
return response.data.map((file) => file.filename);
|
||||||
|
} else if (context.payload.commits) {
|
||||||
|
const filesSet = new Set<string>();
|
||||||
|
for (const commit of context.payload.commits) {
|
||||||
|
["added", "modified", "removed"].forEach((type) => {
|
||||||
|
if (commit[type]) commit[type].forEach((file: string) => filesSet.add(file));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return Array.from(filesSet);
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process a single card file
|
||||||
|
async function processCardFile(file: string): Promise<CardResult | null> {
|
||||||
|
console.log(` - ${file}`);
|
||||||
|
let match = file.match(DATA_REGEX);
|
||||||
|
|
||||||
|
if (match) {
|
||||||
|
const [_, , setName, cardLocalId] = match;
|
||||||
|
const result = await tryFetchCardWithFallback(setName!, cardLocalId!, "en", INTERNATIONAL_LANGUAGES, false);
|
||||||
|
|
||||||
|
if (result) {
|
||||||
|
return {
|
||||||
|
file,
|
||||||
|
card: sanitizeCardData(result.card),
|
||||||
|
isAsian: false,
|
||||||
|
usedLanguage: result.usedLanguage,
|
||||||
|
hasImage: result.hasImage,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
file,
|
||||||
|
error: "Failed to fetch card information in all available languages",
|
||||||
|
isAsian: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match = file.match(DATA_ASIA_REGEX);
|
||||||
|
if (match) {
|
||||||
|
const [_, , setId, cardLocalId] = match;
|
||||||
|
const result = await tryFetchCardWithFallback(setId!, cardLocalId!, "ja", ASIAN_LANGUAGES, true);
|
||||||
|
|
||||||
|
if (result) {
|
||||||
|
return {
|
||||||
|
file,
|
||||||
|
card: sanitizeCardData(result.card),
|
||||||
|
isAsian: true,
|
||||||
|
usedLanguage: result.usedLanguage,
|
||||||
|
hasImage: result.hasImage,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
file,
|
||||||
|
error: "Failed to fetch card information in all available languages",
|
||||||
|
isAsian: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate comment body for PR
|
||||||
|
function generateCommentBody(
|
||||||
|
cardResults: CardResult[],
|
||||||
|
changedFiles: string[],
|
||||||
|
repoFullName: string,
|
||||||
|
contextSha: string,
|
||||||
|
): string {
|
||||||
|
const successfulCards = cardResults.filter((r) => r.card).length;
|
||||||
|
const errorCards = cardResults.filter((r) => r.error).length;
|
||||||
|
const cardsWithoutImages = cardResults.filter((r) => r.card && !r.hasImage).length;
|
||||||
|
|
||||||
|
let commentBody = `## 🃏 ${successfulCards + errorCards} Card${successfulCards + errorCards !== 1 ? "s" : ""} Changed\n\n`;
|
||||||
|
|
||||||
|
if (cardResults.length === 0) {
|
||||||
|
commentBody +=
|
||||||
|
changedFiles.length > 0
|
||||||
|
? "No recognized card files were changed in this PR.\n"
|
||||||
|
: "No files were changed in this PR.\n";
|
||||||
|
return commentBody;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add summary if there are errors or cards without images
|
||||||
|
if (errorCards > 0 || cardsWithoutImages > 0) {
|
||||||
|
commentBody += `**Details:** ${successfulCards} processed successfully`;
|
||||||
|
if (cardsWithoutImages > 0) {
|
||||||
|
commentBody += ` (${cardsWithoutImages} without images)`;
|
||||||
|
}
|
||||||
|
if (errorCards > 0) {
|
||||||
|
commentBody += `, ${errorCards} with errors`;
|
||||||
|
}
|
||||||
|
commentBody += `\n\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate detailed card information
|
||||||
|
for (const item of cardResults) {
|
||||||
|
const fileUrl = `https://github.com/${repoFullName}/blob/${contextSha}/${item.file}`;
|
||||||
|
|
||||||
|
if (item.card) {
|
||||||
|
const langInfo = item.usedLanguage ? ` (found using ${item.usedLanguage})` : "";
|
||||||
|
const imageStatus = !item.hasImage ? ` <em>(no images)</em>` : "";
|
||||||
|
|
||||||
|
commentBody += `<details><summary><strong>${item.card.name}</strong> (${item.card.id})${langInfo}${imageStatus}</summary>\n\n`;
|
||||||
|
|
||||||
|
if (item.card.image) {
|
||||||
|
const languages = item.isAsian ? ASIAN_LANGUAGES : INTERNATIONAL_LANGUAGES;
|
||||||
|
commentBody += renderCardImageTable(item.card, languages);
|
||||||
|
} else {
|
||||||
|
commentBody += `<p align="center"><em>No images available for this card</em></p>\n\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
commentBody += `**File:** [${item.file}](${fileUrl}) \n`;
|
||||||
|
commentBody += `**Set:** ${item.card.set?.name || "Unknown"} \n`;
|
||||||
|
commentBody += `**Rarity:** ${item.card.rarity || "Unknown"}\n\n`;
|
||||||
|
commentBody += "</details>\n\n";
|
||||||
|
} else if (item.error) {
|
||||||
|
commentBody += `<details><summary>⚠️ <strong>Error processing ${item.file.split("/").pop()}</strong></summary>\n\n`;
|
||||||
|
commentBody += `**File:** [${item.file}](${fileUrl}) \n`;
|
||||||
|
commentBody += `**Error:** ${item.error}\n\n`;
|
||||||
|
commentBody += "</details>\n\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return commentBody;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper to render the card image table
|
||||||
|
function renderCardImageTable(card: CardData, languages: string[]): string {
|
||||||
|
let tableMarkdown = `<div align="center">\n\n`;
|
||||||
|
tableMarkdown += `| Language | Language | Language |\n`;
|
||||||
|
tableMarkdown += `|:-------:|:-------:|:-------:|\n`;
|
||||||
|
|
||||||
|
for (let i = 0; i < languages.length; i += 3) {
|
||||||
|
tableMarkdown += `|`;
|
||||||
|
|
||||||
|
for (let j = 0; j < 3; j++) {
|
||||||
|
const langIndex = i + j;
|
||||||
|
if (langIndex < languages.length) {
|
||||||
|
const lang = languages[langIndex];
|
||||||
|
const langName = LANGUAGE_NAMES[lang as keyof typeof LANGUAGE_NAMES] || lang;
|
||||||
|
const localizedImageUrl = card.image!.replace(/\/[a-z]{2}(-[a-z]{2})?\//, `/${lang}/`);
|
||||||
|
tableMarkdown += ` <strong>${langName} (${lang})</strong><br><img src="${localizedImageUrl}/high.webp" alt="${card.name} (${langName})" width="200"/> |`;
|
||||||
|
} else {
|
||||||
|
tableMarkdown += ` |`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tableMarkdown += `\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
tableMarkdown += `\n</div>\n\n`;
|
||||||
|
return tableMarkdown;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Post or update PR comment
|
||||||
|
async function postOrUpdatePRComment(
|
||||||
|
octokit: ReturnType<typeof github.getOctokit>,
|
||||||
|
owner: string,
|
||||||
|
repo: string,
|
||||||
|
prNumber: number,
|
||||||
|
commentBody: string,
|
||||||
|
): Promise<void> {
|
||||||
|
const commentsResponse = await octokit.rest.issues.listComments({
|
||||||
|
owner,
|
||||||
|
repo,
|
||||||
|
issue_number: prNumber,
|
||||||
|
});
|
||||||
|
|
||||||
|
const botLogin = "github-actions[bot]";
|
||||||
|
const existingComment = commentsResponse.data.find(
|
||||||
|
(comment) => comment.user?.login === botLogin && comment.body?.includes("## 🃏"),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (existingComment) {
|
||||||
|
await octokit.rest.issues.updateComment({
|
||||||
|
owner,
|
||||||
|
repo,
|
||||||
|
comment_id: existingComment.id,
|
||||||
|
body: commentBody,
|
||||||
|
});
|
||||||
|
console.log(`Updated existing comment #${existingComment.id} on PR #${prNumber}`);
|
||||||
|
} else {
|
||||||
|
await octokit.rest.issues.createComment({
|
||||||
|
owner,
|
||||||
|
repo,
|
||||||
|
issue_number: prNumber,
|
||||||
|
body: commentBody,
|
||||||
|
});
|
||||||
|
console.log(`Posted new comment to PR #${prNumber}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function run() {
|
||||||
|
try {
|
||||||
|
// Initialize GitHub client
|
||||||
|
const token = core.getInput("github-token", { required: true });
|
||||||
|
const octokit = github.getOctokit(token);
|
||||||
|
const context = github.context;
|
||||||
|
const { owner, repo } = context.repo;
|
||||||
|
const repoFullName = `${owner}/${repo}`;
|
||||||
|
|
||||||
|
// Get changed files
|
||||||
|
const changedFiles = await getChangedFiles(context, octokit);
|
||||||
|
|
||||||
|
// Process card files
|
||||||
|
const cardResults: CardResult[] = [];
|
||||||
|
for (const file of changedFiles) {
|
||||||
|
const result = await processCardFile(file);
|
||||||
|
if (result) cardResults.push(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate comment body
|
||||||
|
const commentBody = generateCommentBody(cardResults, changedFiles, repoFullName, context.sha);
|
||||||
|
|
||||||
|
// Post or update the comment in the PR
|
||||||
|
if (context.payload.pull_request) {
|
||||||
|
const prNumber = context.payload.pull_request.number;
|
||||||
|
await postOrUpdatePRComment(octokit, owner, repo, prNumber, commentBody);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store the generated comment for the workflow
|
||||||
|
core.setOutput("pr_comment", commentBody);
|
||||||
|
core.setOutput("files", changedFiles.join(","));
|
||||||
|
core.setOutput("cardFiles", JSON.stringify(cardResults));
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof Error) {
|
||||||
|
core.setFailed(error.message);
|
||||||
|
} else {
|
||||||
|
core.setFailed("An unknown error occurred");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
run();
|
15
.github/scripts/package.json
vendored
Normal file
15
.github/scripts/package.json
vendored
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"name": "scripts",
|
||||||
|
"private": true,
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/bun": "latest"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"typescript": "^5"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@actions/core": "^1.11.1",
|
||||||
|
"@actions/github": "^6.0.0",
|
||||||
|
"@tcgdex/sdk": "^2.6.0"
|
||||||
|
}
|
||||||
|
}
|
28
.github/scripts/tsconfig.json
vendored
Normal file
28
.github/scripts/tsconfig.json
vendored
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
// Environment setup & latest features
|
||||||
|
"lib": ["ESNext"],
|
||||||
|
"target": "ESNext",
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleDetection": "force",
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
"allowJs": true,
|
||||||
|
|
||||||
|
// Bundler mode
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"allowImportingTsExtensions": true,
|
||||||
|
"verbatimModuleSyntax": true,
|
||||||
|
"noEmit": true,
|
||||||
|
|
||||||
|
// Best practices
|
||||||
|
"strict": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
"noUncheckedIndexedAccess": true,
|
||||||
|
|
||||||
|
// Some stricter flags (disabled by default)
|
||||||
|
"noUnusedLocals": false,
|
||||||
|
"noUnusedParameters": false,
|
||||||
|
"noPropertyAccessFromIndexSignature": false
|
||||||
|
}
|
||||||
|
}
|
11
.github/workflows/build.yml
vendored
11
.github/workflows/build.yml
vendored
@ -3,7 +3,7 @@ name: Build Docker image
|
|||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- '*'
|
- master
|
||||||
pull_request:
|
pull_request:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
@ -45,7 +45,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKERHUB_USER }}
|
username: ${{ secrets.DOCKERHUB_USER }}
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
if: ${{ !github.event.pull_request.head.repo.fork && github.actor != 'dependabot[bot]' }}
|
if: ${{ secrets.DOCKERHUB_TOKEN && !github.event.pull_request.head.repo.fork && github.actor != 'dependabot[bot]' }}
|
||||||
|
|
||||||
- name: Login to Github Packages
|
- name: Login to Github Packages
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v3
|
||||||
@ -53,7 +53,7 @@ jobs:
|
|||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
password: ${{ secrets.DOCKER_TOKEN }}
|
password: ${{ secrets.DOCKER_TOKEN }}
|
||||||
if: ${{ !github.event.pull_request.head.repo.fork && github.actor != 'dependabot[bot]' }}
|
if: ${{ secrets.DOCKER_TOKEN && !github.event.pull_request.head.repo.fork && github.actor != 'dependabot[bot]' }}
|
||||||
|
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v3
|
uses: docker/setup-qemu-action@v3
|
||||||
@ -65,7 +65,7 @@ jobs:
|
|||||||
uses: oven-sh/setup-bun@v2
|
uses: oven-sh/setup-bun@v2
|
||||||
with:
|
with:
|
||||||
bun-version: latest
|
bun-version: latest
|
||||||
|
|
||||||
- name: Pre build server
|
- name: Pre build server
|
||||||
run: |
|
run: |
|
||||||
bun install --frozen-lockfile
|
bun install --frozen-lockfile
|
||||||
@ -86,6 +86,5 @@ jobs:
|
|||||||
tags: ${{ steps.meta.outputs.tags }}
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
cache-from: type=gha
|
cache-from: type=gha
|
||||||
push: ${{ !github.event.pull_request.head.repo.fork && github.actor != 'dependabot[bot]' }}
|
push: ${{ (secrets.DOCKERHUB_TOKEN || secrets.DOCKER_TOKEN) && !github.event.pull_request.head.repo.fork && github.actor != 'dependabot[bot]' }}
|
||||||
cache-to: type=gha,mode=max
|
cache-to: type=gha,mode=max
|
||||||
|
|
||||||
|
36
.github/workflows/comment-pr.yml
vendored
Normal file
36
.github/workflows/comment-pr.yml
vendored
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
name: Card Info Comment
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
types: [opened, synchronize]
|
||||||
|
paths:
|
||||||
|
- 'data/**/*.ts'
|
||||||
|
- 'data-asia/**/*.ts'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
card-info-comment:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
continue-on-error: true
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
working-directory: .github/scripts
|
||||||
|
permissions:
|
||||||
|
pull-requests: write
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Setup Bun
|
||||||
|
uses: oven-sh/setup-bun@v2
|
||||||
|
with:
|
||||||
|
bun-version: latest
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: bun i --frozen-lockfile
|
||||||
|
|
||||||
|
- name: Run load-cards script
|
||||||
|
id: load-cards
|
||||||
|
run: bun load-cards.js
|
||||||
|
env:
|
||||||
|
INPUT_GITHUB-TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
Loading…
x
Reference in New Issue
Block a user