From e53edf23c3bb549bd08a30ec0d9df65a29299730 Mon Sep 17 00:00:00 2001 From: Douile Date: Tue, 12 Dec 2023 21:06:10 +0000 Subject: [PATCH] tools: Add find ID changes to list changes to game IDs This tool iterates through the commit history finding commits where lib/games.js has been modified. Whenever it has it loads the before/after state and compares them to generate a list of IDs that were removed/added/changed (changed is based on comparing the name of the game). The output of this script can then be converted into an object mapping between old IDs and new IDs using a jq one-liner. --- tools/find-id-changes.js | 125 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 tools/find-id-changes.js diff --git a/tools/find-id-changes.js b/tools/find-id-changes.js new file mode 100644 index 0000000..aec3459 --- /dev/null +++ b/tools/find-id-changes.js @@ -0,0 +1,125 @@ +#!/usr/bin/env node + +import { spawnSync } from "node:child_process"; +import assert from "node:assert"; +import { mkdirSync, copyFileSync } from "node:fs"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; +import process from "node:process"; + +// Generate a list of changes to "lib/games.js" where game IDs have been changed via the git history. +// Requires git to be installed. +// Make sure you don't have any local un-committed changes to lib/games.js + +// Usage: node tools/find-id-changes.js > id-changes.json +// Output is an array of +// { +// "hash": "git commit hash", +// "changes": [ ["oldid", "newid"], ... ], +// "removed": [ "removedid", ... ], +// "added": [ "addedid", ... ] +// } + +// The output can be converted to a map of { "oldid": "newid" } using a jq command: +// cat id-changes.json | jq ".[].changes | map({ (.[0]): .[1] } ) | add" | jq -s "add" + +const main = async (rootDir) => { + // Make sure CWD is the root of the repo + process.chdir(rootDir); + + // Get list of commits that have modified lib/games.js + const gitLog = spawnSync( + "git", + [ + "log", + "--follow", + "--format=%H", + "--diff-filter=M", + "--reverse", + "--", + "lib/games.js", + ], + { encoding: "utf-8" } + ); + + // Make a directory to store files in + mkdirSync("game_changes", { recursive: true }); + + const output = []; + + for (const commitHash of gitLog.stdout.split("\n")) { + if (commitHash.length === 0) continue; + + // Checkout lib/games.js before the commit that changed it + assert( + spawnSync("git", ["checkout", `${commitHash}^1`, "--", "lib/games.js"]) + .status === 0 + ); + + // We have to copy each state of the file to its own file because node caches imports + const beforeName = `game_changes/${commitHash}-before.js`; + copyFileSync("lib/games.js", beforeName); + + const before = await import(path.join("../", beforeName)); + + // Checkout lib/games.js after the commit that changed it + assert( + spawnSync("git", ["checkout", `${commitHash}`, "--", "lib/games.js"]) + .status === 0 + ); + + const afterName = `game_changes/${commitHash}-after.js`; + copyFileSync("lib/games.js", afterName); + + const after = await import(path.join("../", afterName)); + + // Find game IDs that were removed and added + let removed = Object.keys(before.games).filter( + (key) => !(key in after.games) + ); + let added = Object.keys(after.games).filter( + (key) => !(key in before.games) + ); + + const changes = []; + + for (const rm of removed) { + for (const add of added) { + const beforeGame = before.games[rm]; + const afterGame = after.games[add]; + + // Modify game names to ignore case, spaces, and punctuation + const beforeName = beforeGame.name.toLowerCase().replace(/[^a-z]/g, ""); + const afterName = afterGame.name.toLowerCase().replace(/[^a-z]/g, ""); + + if ( + beforeGame.options.protocol === afterGame.options.protocol && + (beforeName.includes(afterName) || afterName.includes(beforeName)) + ) { + changes.push([rm, add]); + removed = removed.filter((r) => r !== rm); + added = added.filter((a) => a !== add); + break; + } + } + } + + output.push({ + hash: commitHash, + changes, + removed, + added, + }); + } + + // Reset the contents of lib/games.js + spawnSync("git", ["checkout", "--", "lib/games.js"]); + + return output; +}; + +main( + // Get the root of the repo: + // dir of bin/find-id-changes.js -> /../ + path.join(path.dirname(fileURLToPath(import.meta.url)), "..") +).then((o) => console.log(JSON.stringify(o)), console.error);