mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-22 08:02:19 +00:00
feat(cli): push local datatable migrations before running on migrate up
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -11,6 +11,7 @@ import { psql as psqlDatatable } from "./psql.ts";
|
||||
import { serve as serveDatatable } from "./serve.ts";
|
||||
import {
|
||||
createMigration,
|
||||
pushLocalMigrations,
|
||||
rollbackMigrations,
|
||||
runMigrations,
|
||||
} from "../datatable_migrations.ts";
|
||||
@@ -73,6 +74,9 @@ async function migrateUp(opts: GlobalOptions & { datatable?: string }) {
|
||||
return;
|
||||
}
|
||||
for (const dt of targets) {
|
||||
// Push any locally-created/edited migration files first (without running
|
||||
// them), so `migrate up` works even before a `wmill sync push`.
|
||||
await pushLocalMigrations(workspace.workspaceId, dt);
|
||||
await runMigrations(workspace.workspaceId, dt);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,6 +213,66 @@ export async function pushMigrationFromDisk(
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Upsert the on-disk migrations of a data table to the workspace, so a freshly
|
||||
* created migration file works with `wmill datatable migrate up` even without a
|
||||
* prior `wmill sync push`. Pushes only migrations that are new or edited
|
||||
* (compared against the workspace's current definitions); it never deletes
|
||||
* remote migrations absent on disk and never touches other item kinds.
|
||||
*/
|
||||
export async function pushLocalMigrations(
|
||||
workspace: string,
|
||||
datatableName: string,
|
||||
): Promise<void> {
|
||||
const dir = path.join(process.cwd(), MIGRATIONS_DIR, datatableName);
|
||||
if (!fs.existsSync(dir)) return;
|
||||
|
||||
// Local migrations are identified by their `.up.sql` file (the up file is
|
||||
// mandatory); this deliberately ignores files that were only deleted locally.
|
||||
const local: { timestamp: number; name: string }[] = [];
|
||||
for (const file of fs.readdirSync(dir)) {
|
||||
const m = file.match(/^(\d+)_(.*)\.up\.sql$/);
|
||||
if (m) local.push({ timestamp: Number(m[1]), name: m[2] });
|
||||
}
|
||||
if (local.length === 0) return;
|
||||
|
||||
const remote = await wmill.listDatatableMigrations({ workspace });
|
||||
const remoteByTs = new Map(
|
||||
remote
|
||||
.filter((r) => r.datatable === datatableName)
|
||||
.map((r) => [r.timestamp, r] as const),
|
||||
);
|
||||
|
||||
for (const { timestamp, name } of local) {
|
||||
const base = `${timestamp}_${name}`;
|
||||
const code_up = await readTextFile(path.join(dir, `${base}.up.sql`));
|
||||
const downPath = path.join(dir, `${base}.down.sql`);
|
||||
const code_down = fs.existsSync(downPath)
|
||||
? await readTextFile(downPath)
|
||||
: undefined;
|
||||
|
||||
const r = remoteByTs.get(timestamp);
|
||||
const unchanged =
|
||||
r !== undefined &&
|
||||
r.name === name &&
|
||||
r.code_up === code_up &&
|
||||
(r.code_down ?? undefined) === code_down;
|
||||
if (unchanged) continue;
|
||||
|
||||
log.info(colors.green(`Pushing datatable_migration ${datatableName}/${base}`));
|
||||
await wmill.upsertDatatableMigration({
|
||||
workspace,
|
||||
datatableName,
|
||||
requestBody: {
|
||||
timestamp,
|
||||
name,
|
||||
code_up,
|
||||
...(code_down !== undefined ? { code_down } : {}),
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* After a push that introduced new migrations, list them and (interactively)
|
||||
* offer to run them, equivalent to `wmill datatable migrate up` on each affected
|
||||
|
||||
Reference in New Issue
Block a user