Login
4 branches 0 tags
Ben (U939/Arch Linux) SSH aa92047 1 month ago 12 Commits
rubhub / scripts / migrate.ts
#!/usr/bin/env bun

import { readdirSync } from "node:fs";
import { join } from "node:path";
import { SQL } from "bun";

const MIGRATIONS_DIR = "./migrations";

// Hardcode connection string or read from env
const pg = new SQL("postgresql://postgres:postgres@localhost:5432/rubhub");

async function ensureMigrationsTable() {
	await pg`
    CREATE TABLE IF NOT EXISTS migrations (
      name TEXT PRIMARY KEY,
      applied_at TIMESTAMPTZ NOT NULL DEFAULT now()
    );
  `;
}

async function getAppliedMigrations(): Promise<Set<string>> {
	const res = await pg`SELECT name FROM migrations;`;
	const set = new Set<string>();
	for (const row of res) {
		set.add(row.name);
	}
	return set;
}

async function applyMigration(name: string, sqlFilePath: string) {
	console.log(`→ Applying ${name} ...`);
	pg.begin(async (tx) => {
		await tx.file(sqlFilePath);
		await tx`INSERT INTO migrations (name) VALUES (${name})`;
	})
		.catch((err) => {
			console.error(`✗ Failed ${name}`, err);
			process.exit(1);
		})
		.then(() => {
			console.log(`✓ Done ${name}`);
		});
}

async function main() {
	await ensureMigrationsTable();
	const applied = await getAppliedMigrations();
	const files = readdirSync(MIGRATIONS_DIR)
		.filter((f) => f.endsWith(".sql"))
		.sort();

	for (const file of files) {
		if (applied.has(file)) {
			console.log(`→ Skipping ${file} (already applied)`);
		} else {
			await applyMigration(file, join(MIGRATIONS_DIR, file));
		}
	}
	console.log("All migrations are up to date.");
}

main().catch((err) => {
	console.error(err);
	process.exit(1);
});