@sebspark/spanner-migrate 0.1.1 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +155 -49
- package/dist/{chunk-K5WX6ESL.mjs → chunk-SZDH364K.mjs} +94 -60
- package/dist/cli.js +254 -98
- package/dist/cli.mjs +160 -38
- package/dist/index.d.mts +13 -7
- package/dist/index.d.ts +13 -7
- package/dist/index.js +93 -59
- package/dist/index.mjs +1 -1
- package/package.json +14 -9
package/dist/cli.mjs
CHANGED
|
@@ -5,55 +5,53 @@ import {
|
|
|
5
5
|
init,
|
|
6
6
|
status,
|
|
7
7
|
up
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-SZDH364K.mjs";
|
|
9
9
|
|
|
10
10
|
// src/cli.ts
|
|
11
11
|
import fs from "node:fs/promises";
|
|
12
12
|
import { join } from "node:path";
|
|
13
|
-
import input from "@inquirer/
|
|
13
|
+
import { input, select } from "@inquirer/prompts";
|
|
14
14
|
import yargs from "yargs";
|
|
15
15
|
import { hideBin } from "yargs/helpers";
|
|
16
16
|
var CONFIG_FILE = "./.spanner-migrate.config.json";
|
|
17
|
-
async function loadConfig() {
|
|
18
|
-
try {
|
|
19
|
-
const configContent = await fs.readFile(CONFIG_FILE, "utf8");
|
|
20
|
-
return JSON.parse(configContent);
|
|
21
|
-
} catch {
|
|
22
|
-
console.error('Config file not found. Run "spanner-migrate init" first.');
|
|
23
|
-
process.exit(1);
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
17
|
yargs(hideBin(process.argv)).scriptName("spanner-migrate").usage("$0 <command>").command(
|
|
27
18
|
"init",
|
|
28
19
|
"Initialize a .spanner-migrate.config.json file",
|
|
29
20
|
async () => {
|
|
30
|
-
const migrationsPath = await input({
|
|
31
|
-
message: "Enter the path for your migrations",
|
|
32
|
-
required: true,
|
|
33
|
-
default: "./migrations"
|
|
34
|
-
});
|
|
35
21
|
const instanceName = await input({
|
|
36
22
|
message: "Enter Spanner instance name",
|
|
37
23
|
required: true
|
|
38
24
|
});
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
25
|
+
const databases = [
|
|
26
|
+
await getDatabaseConfig(true)
|
|
27
|
+
];
|
|
28
|
+
while (true) {
|
|
29
|
+
const dbConfig = await getDatabaseConfig(false);
|
|
30
|
+
if (!dbConfig) break;
|
|
31
|
+
databases.push(dbConfig);
|
|
32
|
+
}
|
|
43
33
|
const projectId = await input({
|
|
44
34
|
message: "Enter Google Cloud project name",
|
|
45
35
|
required: false
|
|
46
36
|
});
|
|
47
|
-
const config = {
|
|
37
|
+
const config = {
|
|
38
|
+
instance: {
|
|
39
|
+
name: instanceName,
|
|
40
|
+
databases
|
|
41
|
+
}
|
|
42
|
+
};
|
|
48
43
|
if (projectId) config.projectId = projectId;
|
|
49
44
|
await init(config, CONFIG_FILE);
|
|
50
|
-
console.log(`Configuration written to ${CONFIG_FILE}`);
|
|
51
45
|
}
|
|
52
46
|
).command(
|
|
53
47
|
"create <description ...>",
|
|
54
48
|
"Create a new migration file",
|
|
55
49
|
(yargs2) => {
|
|
56
|
-
yargs2.
|
|
50
|
+
yargs2.option("database", {
|
|
51
|
+
alias: "d",
|
|
52
|
+
type: "string",
|
|
53
|
+
describe: "Database name"
|
|
54
|
+
}).positional("description", {
|
|
57
55
|
type: "string",
|
|
58
56
|
describe: "Description of the migration",
|
|
59
57
|
demandOption: true
|
|
@@ -62,33 +60,157 @@ yargs(hideBin(process.argv)).scriptName("spanner-migrate").usage("$0 <command>")
|
|
|
62
60
|
async (args) => {
|
|
63
61
|
const config = await loadConfig();
|
|
64
62
|
const fullDescription = args.description.join(" ");
|
|
65
|
-
|
|
63
|
+
let databaseConfig;
|
|
64
|
+
if (args.database) {
|
|
65
|
+
databaseConfig = config.instance.databases.find(
|
|
66
|
+
(db) => db.name === args.database
|
|
67
|
+
);
|
|
68
|
+
if (!databaseConfig) {
|
|
69
|
+
throw new Error(`Unknown database name "${args.database}"`);
|
|
70
|
+
}
|
|
71
|
+
} else {
|
|
72
|
+
if (config.instance.databases.length === 1) {
|
|
73
|
+
databaseConfig = config.instance.databases[0];
|
|
74
|
+
} else {
|
|
75
|
+
databaseConfig = await select({
|
|
76
|
+
message: "Select database",
|
|
77
|
+
choices: config.instance.databases.map((dbConfig) => ({
|
|
78
|
+
name: dbConfig.name,
|
|
79
|
+
value: dbConfig
|
|
80
|
+
}))
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
if (!databaseConfig) throw new Error("No database config found");
|
|
85
|
+
await create(databaseConfig, fullDescription);
|
|
66
86
|
console.log(
|
|
67
|
-
`Migration file created: '${join(
|
|
87
|
+
`Migration file created: '${join(databaseConfig.migrationsPath, args.description.join("_"))}.sql'`
|
|
68
88
|
);
|
|
69
89
|
}
|
|
70
90
|
).command(
|
|
71
91
|
"up",
|
|
72
92
|
"Apply migrations",
|
|
73
93
|
(yargs2) => {
|
|
74
|
-
yargs2.option("
|
|
94
|
+
yargs2.option("database", {
|
|
95
|
+
alias: "d",
|
|
96
|
+
type: "string",
|
|
97
|
+
describe: "Database name",
|
|
98
|
+
requiresArg: false
|
|
99
|
+
}).option("max", {
|
|
75
100
|
alias: "m",
|
|
76
101
|
type: "number",
|
|
77
|
-
describe: "Maximum number of migrations to apply",
|
|
78
|
-
|
|
102
|
+
describe: "Maximum number of migrations to apply (requires --database)",
|
|
103
|
+
requiresArg: false
|
|
79
104
|
});
|
|
80
105
|
},
|
|
81
106
|
async (args) => {
|
|
82
107
|
const config = await loadConfig();
|
|
83
|
-
|
|
108
|
+
if (args.max !== void 0) {
|
|
109
|
+
if (!args.database) {
|
|
110
|
+
throw new Error("The --max option requires a specified --database");
|
|
111
|
+
}
|
|
112
|
+
if (!Number.isInteger(args.max) || args.max <= 0) {
|
|
113
|
+
throw new Error("The --max option must be an integer greater than 0");
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
if (args.database) {
|
|
117
|
+
const databaseConfig = config.instance.databases.find(
|
|
118
|
+
(db) => db.name === args.database
|
|
119
|
+
);
|
|
120
|
+
if (!databaseConfig) {
|
|
121
|
+
throw new Error(`Unknown database name "${args.database}"`);
|
|
122
|
+
}
|
|
123
|
+
if (args.max !== void 0) {
|
|
124
|
+
await up(config, databaseConfig, args.max);
|
|
125
|
+
} else {
|
|
126
|
+
await up(config, databaseConfig);
|
|
127
|
+
}
|
|
128
|
+
} else {
|
|
129
|
+
await up(config);
|
|
130
|
+
}
|
|
84
131
|
console.log("Migrations applied successfully.");
|
|
85
132
|
}
|
|
86
|
-
).command(
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
})
|
|
133
|
+
).command(
|
|
134
|
+
"down",
|
|
135
|
+
"Roll back the last applied migration",
|
|
136
|
+
(yargs2) => {
|
|
137
|
+
yargs2.option("database", {
|
|
138
|
+
alias: "d",
|
|
139
|
+
type: "string",
|
|
140
|
+
describe: "Specify the database to roll back (required if multiple databases exist)"
|
|
141
|
+
});
|
|
142
|
+
},
|
|
143
|
+
async (args) => {
|
|
144
|
+
const config = await loadConfig();
|
|
145
|
+
let databaseConfig;
|
|
146
|
+
if (args.database) {
|
|
147
|
+
databaseConfig = config.instance.databases.find(
|
|
148
|
+
(dbConfig) => dbConfig.name === args.database
|
|
149
|
+
);
|
|
150
|
+
if (!databaseConfig) {
|
|
151
|
+
throw new Error(`Unknown database name "${args.database}"`);
|
|
152
|
+
}
|
|
153
|
+
} else if (config.instance.databases.length === 1) {
|
|
154
|
+
databaseConfig = config.instance.databases[0];
|
|
155
|
+
} else {
|
|
156
|
+
throw new Error(
|
|
157
|
+
"Multiple databases detected. Use --database to specify which one to roll back."
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
if (!databaseConfig) throw new Error("No database config found");
|
|
161
|
+
await down(config, databaseConfig);
|
|
162
|
+
console.log("Last migration rolled back successfully.");
|
|
163
|
+
}
|
|
164
|
+
).command(
|
|
165
|
+
"status",
|
|
166
|
+
"Show the migration status",
|
|
167
|
+
(yargs2) => {
|
|
168
|
+
yargs2.option("database", {
|
|
169
|
+
alias: "d",
|
|
170
|
+
type: "string",
|
|
171
|
+
describe: "Specify a database to check status (optional, runs on all databases if omitted)"
|
|
172
|
+
});
|
|
173
|
+
},
|
|
174
|
+
async (args) => {
|
|
175
|
+
const config = await loadConfig();
|
|
176
|
+
let migrationStatus;
|
|
177
|
+
if (args.database) {
|
|
178
|
+
const databaseConfig = config.instance.databases.find(
|
|
179
|
+
(db) => db.name === args.database
|
|
180
|
+
);
|
|
181
|
+
if (!databaseConfig) {
|
|
182
|
+
throw new Error(`Unknown database name "${args.database}"`);
|
|
183
|
+
}
|
|
184
|
+
migrationStatus = await status(config, [databaseConfig]);
|
|
185
|
+
} else {
|
|
186
|
+
migrationStatus = await status(config);
|
|
187
|
+
}
|
|
188
|
+
console.log(migrationStatus);
|
|
189
|
+
}
|
|
190
|
+
).demandCommand().help().parse();
|
|
191
|
+
async function loadConfig() {
|
|
192
|
+
try {
|
|
193
|
+
const configContent = await fs.readFile(CONFIG_FILE, "utf8");
|
|
194
|
+
return JSON.parse(configContent);
|
|
195
|
+
} catch {
|
|
196
|
+
console.error('Config file not found. Run "spanner-migrate init" first.');
|
|
197
|
+
process.exit(1);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
var getDatabaseConfig = async (required) => {
|
|
201
|
+
const message = required ? "Enter Spanner database name" : "Enter another Spanner database name [Enter to continue]";
|
|
202
|
+
const name = await input({
|
|
203
|
+
message,
|
|
204
|
+
required
|
|
205
|
+
});
|
|
206
|
+
if (!name) return;
|
|
207
|
+
const migrationsPath = await input({
|
|
208
|
+
message: "Enter the path for your migrations",
|
|
209
|
+
required: true,
|
|
210
|
+
default: `./migrations/${name}`
|
|
211
|
+
});
|
|
212
|
+
return {
|
|
213
|
+
name,
|
|
214
|
+
migrationsPath
|
|
215
|
+
};
|
|
216
|
+
};
|
package/dist/index.d.mts
CHANGED
|
@@ -1,14 +1,20 @@
|
|
|
1
|
-
type
|
|
1
|
+
type DatabaseConfig = {
|
|
2
|
+
name: string;
|
|
2
3
|
migrationsPath: string;
|
|
3
|
-
|
|
4
|
-
|
|
4
|
+
};
|
|
5
|
+
type InstanceConfig = {
|
|
6
|
+
name: string;
|
|
7
|
+
databases: DatabaseConfig[];
|
|
8
|
+
};
|
|
9
|
+
type Config = {
|
|
10
|
+
instance: InstanceConfig;
|
|
5
11
|
projectId?: string;
|
|
6
12
|
};
|
|
7
13
|
|
|
8
14
|
declare const init: (config: Config, configPath: string) => Promise<void>;
|
|
9
|
-
declare const create: (config:
|
|
10
|
-
declare const up: (config: Config, max?: number) => Promise<void>;
|
|
11
|
-
declare const down: (config: Config) => Promise<void>;
|
|
12
|
-
declare const status: (config: Config) => Promise<string>;
|
|
15
|
+
declare const create: (config: DatabaseConfig, description: string) => Promise<void>;
|
|
16
|
+
declare const up: (config: Config, database?: DatabaseConfig, max?: number) => Promise<void>;
|
|
17
|
+
declare const down: (config: Config, database: DatabaseConfig) => Promise<void>;
|
|
18
|
+
declare const status: (config: Config, databases?: DatabaseConfig[]) => Promise<string>;
|
|
13
19
|
|
|
14
20
|
export { create, down, init, status, up };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,14 +1,20 @@
|
|
|
1
|
-
type
|
|
1
|
+
type DatabaseConfig = {
|
|
2
|
+
name: string;
|
|
2
3
|
migrationsPath: string;
|
|
3
|
-
|
|
4
|
-
|
|
4
|
+
};
|
|
5
|
+
type InstanceConfig = {
|
|
6
|
+
name: string;
|
|
7
|
+
databases: DatabaseConfig[];
|
|
8
|
+
};
|
|
9
|
+
type Config = {
|
|
10
|
+
instance: InstanceConfig;
|
|
5
11
|
projectId?: string;
|
|
6
12
|
};
|
|
7
13
|
|
|
8
14
|
declare const init: (config: Config, configPath: string) => Promise<void>;
|
|
9
|
-
declare const create: (config:
|
|
10
|
-
declare const up: (config: Config, max?: number) => Promise<void>;
|
|
11
|
-
declare const down: (config: Config) => Promise<void>;
|
|
12
|
-
declare const status: (config: Config) => Promise<string>;
|
|
15
|
+
declare const create: (config: DatabaseConfig, description: string) => Promise<void>;
|
|
16
|
+
declare const up: (config: Config, database?: DatabaseConfig, max?: number) => Promise<void>;
|
|
17
|
+
declare const down: (config: Config, database: DatabaseConfig) => Promise<void>;
|
|
18
|
+
declare const status: (config: Config, databases?: DatabaseConfig[]) => Promise<string>;
|
|
13
19
|
|
|
14
20
|
export { create, down, init, status, up };
|
package/dist/index.js
CHANGED
|
@@ -66,7 +66,7 @@ var applyDown = async (db) => {
|
|
|
66
66
|
json: true
|
|
67
67
|
};
|
|
68
68
|
const [rows] = await db.run(req);
|
|
69
|
-
const lastMigration = rows
|
|
69
|
+
const lastMigration = rows?.[0];
|
|
70
70
|
if (!lastMigration) {
|
|
71
71
|
throw new Error("No migrations found to roll back.");
|
|
72
72
|
}
|
|
@@ -98,12 +98,11 @@ var runScript = async (db, script) => {
|
|
|
98
98
|
}
|
|
99
99
|
for (const statement of statements) {
|
|
100
100
|
console.log(`Executing statement: ${statement}`);
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
await db.updateSchema(sql);
|
|
101
|
+
if (isSchemaChange(statement)) {
|
|
102
|
+
await db.updateSchema(statement);
|
|
104
103
|
} else {
|
|
105
104
|
await db.runTransactionAsync(async (transaction) => {
|
|
106
|
-
await transaction.runUpdate(
|
|
105
|
+
await transaction.runUpdate(statement);
|
|
107
106
|
await transaction.commit();
|
|
108
107
|
});
|
|
109
108
|
}
|
|
@@ -129,12 +128,12 @@ var SQL_CREATE_TABLE_MIGRATIONS = `
|
|
|
129
128
|
down STRING(1024)
|
|
130
129
|
) PRIMARY KEY (id)
|
|
131
130
|
`;
|
|
132
|
-
var ensureMigrationTable = async (
|
|
133
|
-
const [rows] = await
|
|
131
|
+
var ensureMigrationTable = async (db) => {
|
|
132
|
+
const [rows] = await db.run(SQL_SELECT_TABLE_MIGRATIONS);
|
|
134
133
|
if (rows.length) return;
|
|
135
134
|
console.log("Creating migration table");
|
|
136
135
|
try {
|
|
137
|
-
await
|
|
136
|
+
await db.updateSchema(SQL_CREATE_TABLE_MIGRATIONS);
|
|
138
137
|
} catch (err) {
|
|
139
138
|
console.error("Failed to create migrations table");
|
|
140
139
|
throw err;
|
|
@@ -165,7 +164,7 @@ var import_node_path = require("path");
|
|
|
165
164
|
var getMigrationFiles = async (path) => {
|
|
166
165
|
try {
|
|
167
166
|
const files = await (0, import_promises.readdir)(path);
|
|
168
|
-
const migrationFileIds = files.filter((file) => file.endsWith(".
|
|
167
|
+
const migrationFileIds = files.filter((file) => file.endsWith(".sql")).map((file) => file.replace(/\.sql$/, ""));
|
|
169
168
|
return migrationFileIds;
|
|
170
169
|
} catch (error) {
|
|
171
170
|
throw new Error(
|
|
@@ -175,35 +174,39 @@ var getMigrationFiles = async (path) => {
|
|
|
175
174
|
};
|
|
176
175
|
var getMigration = async (path, id) => {
|
|
177
176
|
try {
|
|
178
|
-
const filePath = (0, import_node_path.resolve)(process.cwd(), (0, import_node_path.join)(path, `${id}.
|
|
177
|
+
const filePath = (0, import_node_path.resolve)(process.cwd(), (0, import_node_path.join)(path, `${id}.sql`));
|
|
179
178
|
try {
|
|
180
179
|
await (0, import_promises.access)(filePath);
|
|
181
180
|
} catch (err) {
|
|
182
181
|
throw new Error(`Migration file not found: ${filePath}`);
|
|
183
182
|
}
|
|
184
|
-
const
|
|
185
|
-
|
|
183
|
+
const migrationText = await (0, import_promises.readFile)(filePath, "utf8");
|
|
184
|
+
const up2 = getSql(migrationText, "up");
|
|
185
|
+
const down2 = getSql(migrationText, "down");
|
|
186
|
+
const description = getDescription(migrationText);
|
|
187
|
+
if (!up2 || !down2) {
|
|
186
188
|
throw new Error(
|
|
187
189
|
`Migration file ${filePath} does not export required scripts (up, down).`
|
|
188
190
|
);
|
|
189
191
|
}
|
|
190
|
-
return {
|
|
191
|
-
id,
|
|
192
|
-
description: id.split("_").slice(1).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" "),
|
|
193
|
-
// Generate a human-readable description
|
|
194
|
-
up: migrationModule.up,
|
|
195
|
-
down: migrationModule.down
|
|
196
|
-
};
|
|
192
|
+
return { id, description, up: up2, down: down2 };
|
|
197
193
|
} catch (error) {
|
|
198
194
|
throw new Error(
|
|
199
195
|
`Failed to get migration ${id}: ${error.message}`
|
|
200
196
|
);
|
|
201
197
|
}
|
|
202
198
|
};
|
|
199
|
+
var getDescription = (text) => text?.match(/^--\s*Description:\s*(.+)$/m)?.[1]?.trim() || "";
|
|
200
|
+
var getSql = (text, direction) => {
|
|
201
|
+
const rx = {
|
|
202
|
+
up: /---- UP ----\n([\s\S]*?)\n---- DOWN ----/,
|
|
203
|
+
down: /---- DOWN ----\n([\s\S]*)$/
|
|
204
|
+
};
|
|
205
|
+
return text?.match(rx[direction])?.[1]?.replace(/--.*$/gm, "").trim();
|
|
206
|
+
};
|
|
203
207
|
var getNewMigrations = (applied, files) => {
|
|
204
208
|
const sortedFiles = files.sort();
|
|
205
209
|
for (let ix = 0; ix < applied.length; ix++) {
|
|
206
|
-
console.log(sortedFiles[ix], applied[ix].id);
|
|
207
210
|
if (sortedFiles[ix] !== applied[ix].id) {
|
|
208
211
|
throw new Error(
|
|
209
212
|
`Mismatch between applied migrations and files. Found '${sortedFiles[ix]}' but expected '${applied[ix].id}' at position ${ix}.`
|
|
@@ -211,25 +214,24 @@ var getNewMigrations = (applied, files) => {
|
|
|
211
214
|
}
|
|
212
215
|
}
|
|
213
216
|
const newMigrations = sortedFiles.slice(applied.length);
|
|
214
|
-
console.log(`Found ${newMigrations.length} new migrations.`);
|
|
215
217
|
return newMigrations;
|
|
216
218
|
};
|
|
217
219
|
var createMigration = async (path, description) => {
|
|
218
220
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
219
221
|
const compactTimestamp = timestamp.replace(/[-:.TZ]/g, "");
|
|
220
222
|
const parsedDescription = description.replace(/\s+/g, "_").toLowerCase();
|
|
221
|
-
const filename = `${compactTimestamp}_${parsedDescription}.
|
|
223
|
+
const filename = `${compactTimestamp}_${parsedDescription}.sql`;
|
|
222
224
|
const filePath = (0, import_node_path.join)(path, filename);
|
|
223
|
-
const template =
|
|
224
|
-
|
|
225
|
+
const template = `-- Created: ${timestamp}
|
|
226
|
+
-- Description: ${description}
|
|
227
|
+
|
|
228
|
+
---- UP ----
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
---- DOWN ----
|
|
225
233
|
|
|
226
|
-
export const up = \`
|
|
227
|
-
-- SQL for migrate up
|
|
228
|
-
\`
|
|
229
234
|
|
|
230
|
-
export const down = \`
|
|
231
|
-
-- SQL for migrate down
|
|
232
|
-
\`
|
|
233
235
|
`;
|
|
234
236
|
try {
|
|
235
237
|
await (0, import_promises.mkdir)(path, { recursive: true });
|
|
@@ -262,42 +264,74 @@ var init = async (config, configPath) => {
|
|
|
262
264
|
var create = async (config, description) => {
|
|
263
265
|
await createMigration(config.migrationsPath, description);
|
|
264
266
|
};
|
|
265
|
-
var up = async (config, max
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
const
|
|
270
|
-
const
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
267
|
+
var up = async (config, database, max) => {
|
|
268
|
+
if (max && !database) {
|
|
269
|
+
throw new Error("Max number of migrations requires specifying a database");
|
|
270
|
+
}
|
|
271
|
+
const databases = database ? [database] : config.instance.databases;
|
|
272
|
+
for (const databaseConfig of databases) {
|
|
273
|
+
const path = {
|
|
274
|
+
projectId: config.projectId,
|
|
275
|
+
instanceName: config.instance.name,
|
|
276
|
+
databaseName: databaseConfig.name
|
|
277
|
+
};
|
|
278
|
+
const db = getDb(path);
|
|
279
|
+
await ensureMigrationTable(db);
|
|
280
|
+
const appliedMigrations = await getAppliedMigrations(db);
|
|
281
|
+
const migrationFiles = await getMigrationFiles(
|
|
282
|
+
databaseConfig.migrationsPath
|
|
283
|
+
);
|
|
284
|
+
const newMigrations = getNewMigrations(appliedMigrations, migrationFiles);
|
|
285
|
+
console.log(`Found ${newMigrations.length} new migrations.`);
|
|
286
|
+
console.log(newMigrations.map((mig) => ` ${mig}`).join("\n"));
|
|
287
|
+
const newMigrationsToApply = max ? newMigrations.slice(0, max) : newMigrations;
|
|
288
|
+
for (const id of newMigrationsToApply) {
|
|
289
|
+
const migration = await getMigration(databaseConfig.migrationsPath, id);
|
|
290
|
+
await applyUp(db, migration);
|
|
291
|
+
}
|
|
276
292
|
}
|
|
277
293
|
};
|
|
278
|
-
var down = async (config) => {
|
|
279
|
-
const
|
|
294
|
+
var down = async (config, database) => {
|
|
295
|
+
const path = {
|
|
296
|
+
projectId: config.projectId,
|
|
297
|
+
instanceName: config.instance.name,
|
|
298
|
+
databaseName: database.name
|
|
299
|
+
};
|
|
300
|
+
const db = getDb(path);
|
|
280
301
|
await ensureMigrationTable(db);
|
|
281
302
|
await applyDown(db);
|
|
282
303
|
};
|
|
283
|
-
var status = async (config) => {
|
|
284
|
-
const
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
304
|
+
var status = async (config, databases) => {
|
|
305
|
+
const statuses = [];
|
|
306
|
+
for (const databaseConfig of databases || config.instance.databases) {
|
|
307
|
+
const path = {
|
|
308
|
+
projectId: config.projectId,
|
|
309
|
+
instanceName: config.instance.name,
|
|
310
|
+
databaseName: databaseConfig.name
|
|
311
|
+
};
|
|
312
|
+
const db = getDb(path);
|
|
313
|
+
await ensureMigrationTable(db);
|
|
314
|
+
const appliedMigrations = await getAppliedMigrations(db);
|
|
315
|
+
const migrationFiles = await getMigrationFiles(
|
|
316
|
+
databaseConfig.migrationsPath
|
|
317
|
+
);
|
|
318
|
+
const newMigrations = getNewMigrations(appliedMigrations, migrationFiles);
|
|
319
|
+
statuses.push(
|
|
320
|
+
[
|
|
321
|
+
`Migrations [${databaseConfig.name}]`,
|
|
322
|
+
"",
|
|
323
|
+
"Applied",
|
|
324
|
+
"--------------------------------------------------------------------------------",
|
|
325
|
+
`${appliedMigrations.map((m) => m.id).join("\n")}
|
|
295
326
|
`,
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
327
|
+
"New",
|
|
328
|
+
"--------------------------------------------------------------------------------",
|
|
329
|
+
`${newMigrations.join("\n")}
|
|
299
330
|
`
|
|
300
|
-
|
|
331
|
+
].join("\n")
|
|
332
|
+
);
|
|
333
|
+
}
|
|
334
|
+
return statuses.join("\n\n");
|
|
301
335
|
};
|
|
302
336
|
// Annotate the CommonJS export names for ESM import in node:
|
|
303
337
|
0 && (module.exports = {
|
package/dist/index.mjs
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sebspark/spanner-migrate",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -14,24 +14,29 @@
|
|
|
14
14
|
"dev": "tsc --watch --noEmit",
|
|
15
15
|
"lint": "biome check .",
|
|
16
16
|
"test": "jest --config jest.config.ts --passWithNoTests --coverage",
|
|
17
|
-
"test:e2e": "jest --config jest.e2e.config.ts",
|
|
17
|
+
"test:e2e": "jest --config jest.e2e.config.ts --runInBand",
|
|
18
18
|
"typecheck": "tsc --noEmit "
|
|
19
19
|
},
|
|
20
20
|
"devDependencies": {
|
|
21
|
-
"@google-cloud/spanner": "
|
|
21
|
+
"@google-cloud/spanner": "7.18.1",
|
|
22
|
+
"@sebspark/cli-tester": "*",
|
|
22
23
|
"@sebspark/spanner-mock": "*",
|
|
23
|
-
"@types/jest": "
|
|
24
|
-
"@types/yargs": "
|
|
25
|
-
"jest": "
|
|
26
|
-
"testcontainers": "
|
|
27
|
-
"ts-jest": "
|
|
24
|
+
"@types/jest": "29.5.14",
|
|
25
|
+
"@types/yargs": "17.0.33",
|
|
26
|
+
"jest": "29.7.0",
|
|
27
|
+
"testcontainers": "10.18.0",
|
|
28
|
+
"ts-jest": "29.2.6",
|
|
28
29
|
"tsconfig": "*"
|
|
29
30
|
},
|
|
30
31
|
"peerDependencies": {
|
|
31
32
|
"@google-cloud/spanner": "*"
|
|
32
33
|
},
|
|
33
34
|
"dependencies": {
|
|
34
|
-
"@inquirer/
|
|
35
|
+
"@inquirer/prompts": "7.3.2",
|
|
36
|
+
"@jest/globals": "29.7.0",
|
|
37
|
+
"@types/node": "20.17.17",
|
|
38
|
+
"typescript": "5.7.3",
|
|
39
|
+
"vitest": "3.0.7",
|
|
35
40
|
"yargs": "17.7.2"
|
|
36
41
|
}
|
|
37
42
|
}
|