158 lines
4.3 KiB
JavaScript
158 lines
4.3 KiB
JavaScript
import { execFileSync } from "node:child_process";
|
|
import fs from "node:fs";
|
|
import path from "node:path";
|
|
|
|
const root = process.cwd();
|
|
const decisionsPath = path.join(root, "assets", "cards", "worker-review-decisions.json");
|
|
|
|
function git(args) {
|
|
return execFileSync("git", ["-c", "safe.directory=C:/Users/MD/source/repos/card_game", ...args], {
|
|
cwd: root,
|
|
encoding: "utf8",
|
|
}).trim();
|
|
}
|
|
|
|
function lines(output) {
|
|
return output ? output.split(/\r?\n/).filter(Boolean) : [];
|
|
}
|
|
|
|
function listRefs() {
|
|
const raw = git([
|
|
"for-each-ref",
|
|
"--format=%(refname:short)",
|
|
"refs/heads/codex/cards-*",
|
|
"refs/remotes/origin/codex/cards-*",
|
|
]);
|
|
const refs = lines(raw);
|
|
const shortNames = [...new Set(refs.map((ref) => ref.replace(/^origin\//, "")))].sort();
|
|
return shortNames.map((shortName) => (refs.includes(shortName) ? shortName : `origin/${shortName}`));
|
|
}
|
|
|
|
function listPngs(ref) {
|
|
try {
|
|
return lines(git(["ls-tree", "-r", "--name-only", ref, "assets/cards"])).filter((file) =>
|
|
/^assets\/cards\/C\d{4}\/damage-[0-4]\.png$/.test(file),
|
|
);
|
|
} catch {
|
|
return [];
|
|
}
|
|
}
|
|
|
|
function completeCharacters(files) {
|
|
const stagesByCharacter = new Map();
|
|
for (const file of files) {
|
|
const match = file.match(/^assets\/cards\/(C\d{4})\/damage-([0-4])\.png$/);
|
|
if (!match) continue;
|
|
if (!stagesByCharacter.has(match[1])) stagesByCharacter.set(match[1], new Set());
|
|
stagesByCharacter.get(match[1]).add(match[2]);
|
|
}
|
|
|
|
return [...stagesByCharacter]
|
|
.filter(([, stages]) => [0, 1, 2, 3, 4].every((stage) => stages.has(String(stage))))
|
|
.map(([characterId]) => characterId)
|
|
.sort();
|
|
}
|
|
|
|
function loadDecisions() {
|
|
if (!fs.existsSync(decisionsPath)) return [];
|
|
const payload = JSON.parse(fs.readFileSync(decisionsPath, "utf8"));
|
|
return Array.isArray(payload.decisions) ? payload.decisions : [];
|
|
}
|
|
|
|
function blobRows(ref, characterId) {
|
|
const prefix = `assets/cards/${characterId}/`;
|
|
const output = git(["ls-tree", "-r", ref, prefix]);
|
|
return lines(output)
|
|
.map((row) => {
|
|
const match = row.match(/^\d+\s+blob\s+([0-9a-f]+)\s+(.+)$/);
|
|
return match ? { blob: match[1], path: match[2] } : null;
|
|
})
|
|
.filter(Boolean);
|
|
}
|
|
|
|
const mainFiles = listPngs("main");
|
|
const mainComplete = completeCharacters(mainFiles);
|
|
const mainCompleteSet = new Set(mainComplete);
|
|
const mainBlobToPath = new Map();
|
|
|
|
for (const file of mainFiles) {
|
|
const row = blobRows("main", file.match(/^assets\/cards\/(C\d{4})\//)[1]).find((item) => item.path === file);
|
|
if (row && !mainBlobToPath.has(row.blob)) mainBlobToPath.set(row.blob, row.path);
|
|
}
|
|
|
|
const decisions = loadDecisions();
|
|
const rejectedByRefCommitCharacter = new Map(
|
|
decisions
|
|
.filter((decision) => decision.decision === "rejected")
|
|
.map((decision) => [
|
|
`${decision.sourceRef}@${decision.sourceCommit}:${decision.characterId}`,
|
|
decision,
|
|
]),
|
|
);
|
|
|
|
const branches = [];
|
|
const candidates = [];
|
|
const excluded = [];
|
|
|
|
for (const ref of listRefs()) {
|
|
const commit = git(["rev-parse", ref]);
|
|
const pngs = listPngs(ref);
|
|
const complete = completeCharacters(pngs);
|
|
const newComplete = complete.filter((characterId) => !mainCompleteSet.has(characterId));
|
|
const branch = {
|
|
ref,
|
|
commit,
|
|
pngCount: pngs.length,
|
|
completeCharacterCount: complete.length,
|
|
newCompleteCharacterCount: newComplete.length,
|
|
newCompleteCharacters: newComplete,
|
|
};
|
|
branches.push(branch);
|
|
|
|
for (const characterId of newComplete) {
|
|
const rejection = rejectedByRefCommitCharacter.get(`${ref}@${commit}:${characterId}`);
|
|
if (rejection) {
|
|
excluded.push({
|
|
ref,
|
|
commit,
|
|
characterId,
|
|
reasonCode: rejection.reasonCode,
|
|
notes: rejection.notes,
|
|
});
|
|
continue;
|
|
}
|
|
|
|
const duplicateBlobs = blobRows(ref, characterId)
|
|
.filter((row) => mainBlobToPath.has(row.blob))
|
|
.map((row) => ({
|
|
candidatePath: row.path,
|
|
existingPath: mainBlobToPath.get(row.blob),
|
|
}));
|
|
|
|
candidates.push({
|
|
ref,
|
|
commit,
|
|
characterId,
|
|
duplicateBlobs,
|
|
needsReview: duplicateBlobs.length > 0 ? "duplicate-check" : "visual-review",
|
|
});
|
|
}
|
|
}
|
|
|
|
console.log(
|
|
JSON.stringify(
|
|
{
|
|
schemaVersion: 1,
|
|
main: {
|
|
pngCount: mainFiles.length,
|
|
completeCharacterCount: mainComplete.length,
|
|
},
|
|
candidates,
|
|
excluded,
|
|
branches,
|
|
},
|
|
null,
|
|
2,
|
|
),
|
|
);
|