node-gamedig/tools/find-id-changes.js

126 lines
3.6 KiB
JavaScript
Raw Normal View History

#!/usr/bin/env node
2024-06-29 22:27:19 +02:00
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
2024-06-29 22:27:19 +02:00
process.chdir(rootDir)
// Get list of commits that have modified lib/games.js
const gitLog = spawnSync(
2024-06-29 22:27:19 +02:00
'git',
[
2024-06-29 22:27:19 +02:00
'log',
'--follow',
'--format=%H',
'--diff-filter=M',
'--reverse',
'--',
'lib/games.js'
],
2024-06-29 22:27:19 +02:00
{ encoding: 'utf-8' }
)
// Make a directory to store files in
2024-06-29 22:27:19 +02:00
mkdirSync('game_changes', { recursive: true })
2024-06-29 22:27:19 +02:00
const output = []
2024-06-29 22:27:19 +02:00
for (const commitHash of gitLog.stdout.split('\n')) {
if (commitHash.length === 0) continue
// Checkout lib/games.js before the commit that changed it
assert(
2024-06-29 22:27:19 +02:00
spawnSync('git', ['checkout', `${commitHash}^1`, '--', 'lib/games.js'])
.status === 0
2024-06-29 22:27:19 +02:00
)
// We have to copy each state of the file to its own file because node caches imports
2024-06-29 22:27:19 +02:00
const beforeName = `game_changes/${commitHash}-before.js`
copyFileSync('lib/games.js', beforeName)
2024-06-29 22:27:19 +02:00
const before = await import(path.join('../', beforeName))
// Checkout lib/games.js after the commit that changed it
assert(
2024-06-29 22:27:19 +02:00
spawnSync('git', ['checkout', `${commitHash}`, '--', 'lib/games.js'])
.status === 0
2024-06-29 22:27:19 +02:00
)
2024-06-29 22:27:19 +02:00
const afterName = `game_changes/${commitHash}-after.js`
copyFileSync('lib/games.js', afterName)
2024-06-29 22:27:19 +02:00
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)
2024-06-29 22:27:19 +02:00
)
let added = Object.keys(after.games).filter(
(key) => !(key in before.games)
2024-06-29 22:27:19 +02:00
)
2024-06-29 22:27:19 +02:00
const changes = []
for (const rm of removed) {
for (const add of added) {
2024-06-29 22:27:19 +02:00
const beforeGame = before.games[rm]
const afterGame = after.games[add]
// Modify game names to ignore case, spaces, and punctuation
2024-06-29 22:27:19 +02:00
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))
) {
2024-06-29 22:27:19 +02:00
changes.push([rm, add])
removed = removed.filter((r) => r !== rm)
added = added.filter((a) => a !== add)
break
}
}
}
output.push({
hash: commitHash,
changes,
removed,
2024-06-29 22:27:19 +02:00
added
})
}
// Reset the contents of lib/games.js
2024-06-29 22:27:19 +02:00
spawnSync('git', ['checkout', '--', 'lib/games.js'])
2024-06-29 22:27:19 +02:00
return output
}
main(
// Get the root of the repo:
// dir of bin/find-id-changes.js -> /../
2024-06-29 22:27:19 +02:00
path.join(path.dirname(fileURLToPath(import.meta.url)), '..')
).then((o) => console.log(JSON.stringify(o)), console.error)