feat: add datatable migrate new command to scaffold migrations

This commit is contained in:
Diego Imbert
2026-06-17 11:47:47 +02:00
parent b3002f5e24
commit 6e1ce7fd4e
2 changed files with 57 additions and 1 deletions
+16 -1
View File
@@ -10,6 +10,7 @@ import { runCatalogQuery } from "../../utils/catalog.ts";
import { psql as psqlDatatable } from "./psql.ts";
import { serve as serveDatatable } from "./serve.ts";
import {
createMigration,
rollbackMigrations,
runMigrations,
} from "../datatable_migrations.ts";
@@ -45,6 +46,13 @@ async function run(
await runCatalogQuery(opts, "datatable", name, sql);
}
function migrateNew(
opts: GlobalOptions & { datatable?: string },
name: string,
) {
createMigration(opts.datatable ?? DEFAULT_DATATABLE_NAME, name);
}
async function migrateUp(opts: GlobalOptions, name?: string) {
const workspace = await resolveWorkspace(opts);
await requireLogin(opts);
@@ -58,7 +66,14 @@ async function migrateDown(opts: GlobalOptions, name?: string) {
}
const migrateCommand = new Command()
.description("apply or roll back datatable migrations")
.description("manage datatable migrations")
.command("new", "scaffold a new migration (.up.sql / .down.sql files)")
.arguments("<name:string>")
.option(
"-d --datatable <datatable:string>",
"Target datatable (default: main)",
)
.action(migrateNew as any)
.command("up", "apply all pending migrations to a datatable")
.arguments("[name:string]")
.action(migrateUp as any)
+41
View File
@@ -11,6 +11,47 @@ import { Confirm } from "@cliffy/prompt/confirm";
// target data table, as `<timestamp>_<name>.up.sql` (and optional `.down.sql`).
const MIGRATIONS_DIR = "datatable_migrations";
// Migration names map directly onto file names and the DB `name` column.
const MIGRATION_NAME_RE = /^[a-zA-Z0-9_-]+$/;
/** Current UTC time as a YYYYMMDDHHMMSS migration version. */
function migrationTimestamp(): string {
const d = new Date();
const p = (n: number) => String(n).padStart(2, "0");
return (
`${d.getUTCFullYear()}${p(d.getUTCMonth() + 1)}${p(d.getUTCDate())}` +
`${p(d.getUTCHours())}${p(d.getUTCMinutes())}${p(d.getUTCSeconds())}`
);
}
/**
* Scaffold a new migration under datatable_migrations/<datatable>/ as empty
* `<timestamp>_<name>.up.sql` and `.down.sql` files. Purely local — no network.
*/
export function createMigration(datatable: string, name: string): void {
if (!MIGRATION_NAME_RE.test(name)) {
throw new Error(
`Invalid migration name '${name}': use only letters, digits, '_' and '-'`,
);
}
const dir = path.join(process.cwd(), MIGRATIONS_DIR, datatable);
fs.mkdirSync(dir, { recursive: true });
const timestamp = migrationTimestamp();
const base = `${timestamp}_${name}`;
const up = path.join(dir, `${base}.up.sql`);
const down = path.join(dir, `${base}.down.sql`);
fs.writeFileSync(up, `-- up migration: ${name}\n`, "utf-8");
fs.writeFileSync(down, `-- down migration: ${name}\n`, "utf-8");
log.info(
colors.green(`Created migration ${base} in ${MIGRATIONS_DIR}/${datatable}/`),
);
for (const f of [up, down]) {
log.info(colors.gray(` ${path.relative(process.cwd(), f)}`));
}
}
/**
* Apply the workspace's pending migrations to a data table (forwards migrations
* recorded in `_wm_migrations`). Mirrors `wmill datatable migrate up`.