fix: skip comment-only migration SQL
Quality / quality (ubuntu-latest) (push) Failing after 9m47s
Quality / quality (windows-latest) (push) Canceled after 0s

This commit is contained in:
2026-08-12 19:30:30 +05:30
parent 7a968977f6
commit d47c57a953
6 changed files with 69 additions and 8 deletions
+3 -3
View File
@@ -268,7 +268,7 @@
},
"packages/cli": {
"name": "@wrnexus/cli",
"version": "0.8.21",
"version": "0.8.22",
"bin": {
"wrnexus": "src/index.ts",
},
@@ -324,7 +324,7 @@
},
"packages/db": {
"name": "@wrnexus/db",
"version": "0.8.9",
"version": "0.8.10",
"devDependencies": {
"@types/bun": "^1.3.14",
"typescript": "^5.9.2",
@@ -332,7 +332,7 @@
},
"packages/dev-server": {
"name": "@wrnexus/dev-server",
"version": "0.8.20",
"version": "0.8.21",
"dependencies": {
"@wrnexus/authz": "workspace:*",
"@wrnexus/cache": "workspace:*",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/cli",
"version": "0.8.21",
"version": "0.8.22",
"type": "module",
"main": "src/index.ts",
"exports": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/db",
"version": "0.8.9",
"version": "0.8.10",
"private": true,
"type": "module",
"main": "./src/index.ts",
+46 -2
View File
@@ -51,6 +51,50 @@ function section(content: string, which: "up" | "down"): string {
return (next ? rest.slice(0, next.index) : rest).trim();
}
function hasExecutableSql(sql: string): boolean {
let quote = "";
let lineComment = false;
let blockComment = false;
for (let index = 0; index < sql.length; index++) {
const char = sql[index]!;
const next = sql[index + 1] ?? "";
if (lineComment) {
if (char === "\n" || char === "\r") lineComment = false;
continue;
}
if (blockComment) {
if (char === "*" && next === "/") {
blockComment = false;
index++;
}
continue;
}
if (quote) {
if (char === quote) {
if (next === quote) index++;
else quote = "";
}
continue;
}
if (char === "-" && next === "-") {
lineComment = true;
index++;
continue;
}
if (char === "/" && next === "*") {
blockComment = true;
index++;
continue;
}
if (char === "'" || char === '"' || char === "`") {
quote = char;
return true;
}
if (!/\s|;/.test(char)) return true;
}
return false;
}
/** Load and parse all migration files in a directory, sorted by filename. */
export function loadMigrations(dir: string): Migration[] {
if (!existsSync(dir)) return [];
@@ -136,7 +180,7 @@ export async function applyMigrations(
for (const migration of pending.filter(({ name }) => !current.has(name))) {
throwIfAborted(options.signal);
await db.tx(async (tx) => {
if (migration.up) await tx.exec(migration.up);
if (hasExecutableSql(migration.up)) await tx.exec(migration.up);
await tx.exec(`INSERT INTO ${MIGRATIONS_TABLE} (name) VALUES (?)`, [migration.name]);
});
done.push(migration.name);
@@ -175,7 +219,7 @@ export async function rollback(
try {
throwIfAborted(options.signal);
await db.tx(async (tx) => {
if (migration?.down) await tx.exec(migration.down);
if (migration && hasExecutableSql(migration.down)) await tx.exec(migration.down);
await tx.exec(`DELETE FROM ${MIGRATIONS_TABLE} WHERE name = ?`, [last]);
});
return last;
+17
View File
@@ -9,6 +9,7 @@ import {
createTableSql,
parseMigration,
applyMigrations,
appliedMigrations,
migrate,
status,
rollback,
@@ -144,6 +145,22 @@ test("an empty migration set does not touch the database", async () => {
expect(await applyMigrations(db, [])).toEqual([]);
});
test("comment-only migrations are recorded without executing empty SQL", async () => {
const db = createDb(sqlite());
const migrations = [
{
name: "0001_placeholder",
up: "-- Create application tables here.\n/* No schema yet. */\n;",
down: "-- Nothing to roll back.",
},
];
expect(await applyMigrations(db, migrations)).toEqual(["0001_placeholder"]);
expect(await appliedMigrations(db)).toEqual(["0001_placeholder"]);
expect(await rollback(db, ".", { lock: false })).toBe("0001_placeholder");
await db.close();
});
test("migration dry-run plans changes without applying schema and honors cancellation", async () => {
const db = createDb(sqlite());
const migrations = [
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/dev-server",
"version": "0.8.20",
"version": "0.8.21",
"type": "module",
"main": "src/index.ts",
"exports": {