diff --git a/package.json b/package.json index 821019d..ee9c521 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "db:generate": "dotenv -- drizzle-kit generate", "db:migrate": "dotenv -- drizzle-kit migrate", "db:sync-prod": "bash scripts/sync-prod-db.sh", + "backfill:canonical": "dotenv -- tsx scripts/backfill-cli.ts", "dev": "NODE_OPTIONS='--import ./instrument.server.mjs' dotenv -- tsx watch server.ts", "start": "NODE_ENV=production NODE_OPTIONS='--import ./instrument.server.mjs' node dist/server.js", "start:production": "NODE_ENV=production NODE_OPTIONS='--import ./instrument.server.mjs' node dist/server.js", diff --git a/scripts/backfill-cli.ts b/scripts/backfill-cli.ts new file mode 100644 index 0000000..4a6d3d0 --- /dev/null +++ b/scripts/backfill-cli.ts @@ -0,0 +1,78 @@ +/** + * CLI entry point for the Phase 2 canonical backfill. + * + * Usage: + * npm run backfill:canonical -- [--dry-run | --apply] [--sport=] + * + * Defaults to --dry-run for safety. No writes will be issued unless + * --apply is passed explicitly. + */ + +import { runBackfill } from "./backfill-canonical-layer"; +import type { BackfillOptions } from "./backfill-canonical-layer"; + +function printHelp(): void { + console.log( + [ + "Usage: backfill-cli [options]", + "", + "Options:", + " --dry-run Run without writing (default).", + " --apply Actually write to the database.", + " --sport= Only backfill for the given sport id.", + " --help Show this message.", + ].join("\n"), + ); +} + +function parseArgs(): BackfillOptions { + const args = process.argv.slice(2); + const opts: BackfillOptions = { dryRun: true }; // dry-run by default for safety + for (const a of args) { + if (a === "--apply") { + opts.dryRun = false; + } else if (a === "--dry-run") { + opts.dryRun = true; + } else if (a.startsWith("--sport=")) { + opts.sportId = a.slice("--sport=".length); + } else if (a === "--help" || a === "-h") { + printHelp(); + process.exit(0); + } else { + console.error(`unknown arg: ${a}`); + process.exit(1); + } + } + return opts; +} + +async function main() { + const opts = parseArgs(); + console.log( + `Running backfill (dryRun=${opts.dryRun}, sportId=${opts.sportId ?? "all"})`, + ); + const report = await runBackfill(opts); + + console.log("---"); + console.log(`tournamentsCreated: ${report.tournamentsCreated}`); + console.log(`tournamentsLinked: ${report.tournamentsLinked}`); + console.log(`participantsCreated: ${report.participantsCreated}`); + console.log(`participantsLinked: ${report.participantsLinked}`); + console.log(`tournamentResultsCreated: ${report.tournamentResultsCreated}`); + console.log(`surfaceElosCreated: ${report.surfaceElosCreated}`); + + if (report.warnings.length) { + console.log("\nWARNINGS:"); + for (const w of report.warnings) console.log(` ${w}`); + } + if (report.errors.length) { + console.log("\nERRORS:"); + for (const e of report.errors) console.log(` ${e}`); + process.exit(2); + } +} + +main().catch((e) => { + console.error(e); + process.exit(1); +});