stepwise-migrations 1.0.8 → 1.0.10
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 +177 -28
- package/dist/db.js +28 -8
- package/dist/index.js +45 -11
- package/dist/migrate.js +17 -11
- package/dist/utils.js +29 -19
- package/package.json +3 -1
- package/src/db.ts +37 -8
- package/src/index.ts +68 -15
- package/src/migrate.ts +28 -15
- package/src/types.ts +3 -1
- package/src/utils.ts +39 -23
package/README.md
CHANGED
@@ -16,7 +16,7 @@ No subdirectories are read below the migration directory.
|
|
16
16
|
|
17
17
|
## Usage
|
18
18
|
|
19
|
-
```
|
19
|
+
```text
|
20
20
|
Usage: stepwise-migrations [command] [options]
|
21
21
|
|
22
22
|
Commands:
|
@@ -24,10 +24,16 @@ Commands:
|
|
24
24
|
Migrate the database to the latest version
|
25
25
|
down
|
26
26
|
Rollback the database to the previous version
|
27
|
+
validate
|
28
|
+
Validate the migration files and the migration history table
|
29
|
+
audit
|
30
|
+
Show the audit history for the migrations in the database
|
27
31
|
info
|
28
32
|
Show information about the current state of the migrations in the database
|
29
33
|
drop
|
30
34
|
Drop all tables, schema and migration history table
|
35
|
+
get-script
|
36
|
+
Get the script for the last applied migration
|
31
37
|
|
32
38
|
Options:
|
33
39
|
--connection <connection> The connection string to use to connect to the database
|
@@ -36,6 +42,7 @@ Options:
|
|
36
42
|
--ssl true/false Whether to use SSL for the connection (default: false)
|
37
43
|
--nup Number of up migrations to apply (default: all)
|
38
44
|
--ndown Number of down migrations to apply (default: 1)
|
45
|
+
--filename The filename to get the script for (default: last applied migration)
|
39
46
|
|
40
47
|
Example:
|
41
48
|
npx stepwise-migrations migrate \
|
@@ -48,7 +55,7 @@ Example:
|
|
48
55
|
|
49
56
|
### Migrate
|
50
57
|
|
51
|
-
|
58
|
+
If all files are in a valid state, runs all the "up" migrations that have not been applied yet.
|
52
59
|
|
53
60
|
```bash
|
54
61
|
npx stepwise-migrations migrate \
|
@@ -57,25 +64,36 @@ npx stepwise-migrations migrate \
|
|
57
64
|
--path=./db/migration/
|
58
65
|
```
|
59
66
|
|
60
|
-
|
67
|
+
<details>
|
61
68
|
|
62
|
-
|
69
|
+
<summary>Example output</summary>
|
70
|
+
|
71
|
+
```text
|
63
72
|
Creating schema myschema... done!
|
64
73
|
Creating migration history table... done!
|
65
74
|
Applying migration v1_connect_session_table.sql... done!
|
66
75
|
Applying migration v2_auth.sql... done!
|
67
76
|
All done! Applied 2 migrations
|
68
|
-
|
69
|
-
|
70
|
-
│ (index) │ id │ name │
|
71
|
-
|
72
|
-
│ 0 │ 1 │ 'v1_connect_session_table.sql' │ '
|
73
|
-
│ 1 │ 2 │ 'v2_auth.sql' │ '
|
74
|
-
|
77
|
+
Migration history:
|
78
|
+
┌─────────┬────┬────────────────────────────────┬────────────┬──────────────────────────────┐
|
79
|
+
│ (index) │ id │ name │ applied_by │ applied_at │
|
80
|
+
├─────────┼────┼────────────────────────────────┼────────────┼──────────────────────────────┤
|
81
|
+
│ 0 │ 1 │ 'v1_connect_session_table.sql' │ 'postgres' │ '2024-11-24 05:40:41.211617' │
|
82
|
+
│ 1 │ 2 │ 'v2_auth.sql' │ 'postgres' │ '2024-11-24 05:40:41.214732' │
|
83
|
+
└─────────┴────┴────────────────────────────────┴────────────┴──────────────────────────────┘
|
84
|
+
Unapplied migrations:
|
85
|
+
┌─────────┐
|
86
|
+
│ (index) │
|
87
|
+
├─────────┤
|
75
88
|
```
|
76
89
|
|
90
|
+
</details>
|
91
|
+
|
77
92
|
### Down
|
78
93
|
|
94
|
+
Runs a single down migration for the last applied migration.
|
95
|
+
Can run multiple down migrations if the `--ndown` option is provided.
|
96
|
+
|
79
97
|
Command:
|
80
98
|
|
81
99
|
```bash
|
@@ -85,21 +103,112 @@ npx stepwise-migrations down \
|
|
85
103
|
--path=./db/migration/
|
86
104
|
```
|
87
105
|
|
88
|
-
|
106
|
+
<details>
|
89
107
|
|
90
|
-
|
108
|
+
<summary>Example output</summary>
|
109
|
+
|
110
|
+
```text
|
91
111
|
Applying down migration v2_auth.down.sql... done!
|
92
112
|
All done! Applied 1 down migration
|
93
|
-
|
94
|
-
|
95
|
-
│ (index) │ id │ name │
|
96
|
-
|
97
|
-
│ 0 │ 1 │ 'v1_connect_session_table.sql' │ '
|
98
|
-
|
113
|
+
Migration history:
|
114
|
+
┌─────────┬────┬────────────────────────────────┬────────────┬──────────────────────────────┐
|
115
|
+
│ (index) │ id │ name │ applied_by │ applied_at │
|
116
|
+
├─────────┼────┼────────────────────────────────┼────────────┼──────────────────────────────┤
|
117
|
+
│ 0 │ 1 │ 'v1_connect_session_table.sql' │ 'postgres' │ '2024-11-24 05:40:41.211617' │
|
118
|
+
└─────────┴────┴────────────────────────────────┴────────────┴──────────────────────────────┘
|
119
|
+
Unapplied migrations:
|
120
|
+
┌─────────┬───────────────┐
|
121
|
+
│ (index) │ filename │
|
122
|
+
├─────────┼───────────────┤
|
123
|
+
│ 0 │ 'v2_auth.sql' │
|
124
|
+
└─────────┴───────────────┘
|
125
|
+
```
|
126
|
+
|
127
|
+
</details>
|
128
|
+
|
129
|
+
### Validate
|
130
|
+
|
131
|
+
Validates the migration files and the migration history table.
|
132
|
+
|
133
|
+
```bash
|
134
|
+
npx stepwise-migrations validate \
|
135
|
+
--connection=postgresql://postgres:postgres@127.0.0.1:5432/mydb \
|
136
|
+
--schema=myschema \
|
137
|
+
--path=./db/migration/
|
138
|
+
```
|
139
|
+
|
140
|
+
<details>
|
141
|
+
|
142
|
+
<summary>Example output - validation passed</summary>
|
143
|
+
|
144
|
+
```text
|
145
|
+
Validation passed
|
146
|
+
Migration history:
|
147
|
+
┌─────────┬────┬────────────────────────────────┬────────────┬──────────────────────────────┐
|
148
|
+
│ (index) │ id │ name │ applied_by │ applied_at │
|
149
|
+
├─────────┼────┼────────────────────────────────┼────────────┼──────────────────────────────┤
|
150
|
+
│ 0 │ 1 │ 'v1_connect_session_table.sql' │ 'postgres' │ '2024-11-24 05:40:41.211617' │
|
151
|
+
└─────────┴────┴────────────────────────────────┴────────────┴──────────────────────────────┘
|
152
|
+
Unapplied migrations:
|
153
|
+
┌─────────┬───────────────┐
|
154
|
+
│ (index) │ filename │
|
155
|
+
├─────────┼───────────────┤
|
156
|
+
│ 0 │ 'v2_auth.sql' │
|
157
|
+
└─────────┴───────────────┘
|
158
|
+
```
|
159
|
+
|
160
|
+
</details>
|
161
|
+
|
162
|
+
<details>
|
163
|
+
|
164
|
+
<summary>Example output - script changed error</summary>
|
165
|
+
|
166
|
+
```sql
|
167
|
+
Error: migration v1_connect_session_table.sql has been modified, aborting.
|
168
|
+
"expire" timestamp(6) NOT NULL
|
169
|
+
)
|
170
|
+
WITH (OIDS=FALSE);
|
171
|
+
-ALTER TABLE "session" ADD CONSTRAINT "session_pkey" PRIMARY KEY ("sid") NOT DEFERRABLE INITIALLY IMMEDIATE;
|
172
|
+
|
173
|
+
+ALTER TABLE "session" ADD CONSTRAINT "session_pkey" PRIMARY KEY ("sid") NOT DEFERRABLE INITIALLY IMMEDIATE;
|
174
|
+
+
|
175
|
+
+ALTER TABLE "session" ADD INDEX "session_sid" ON "session" (sid);
|
176
|
+
```
|
177
|
+
|
178
|
+
</details>
|
179
|
+
|
180
|
+
### Audit
|
181
|
+
|
182
|
+
Shows the audit history for the migrations in the database.
|
183
|
+
|
184
|
+
```bash
|
185
|
+
npx stepwise-migrations audit \
|
186
|
+
--connection=postgresql://postgres:postgres@127.0.0.1:5432/mydb \
|
187
|
+
--schema=myschema \
|
188
|
+
--path=./db/migration/
|
189
|
+
```
|
190
|
+
|
191
|
+
<details>
|
192
|
+
|
193
|
+
<summary>Example output</summary>
|
194
|
+
|
195
|
+
```text
|
196
|
+
Audit history:
|
197
|
+
┌─────────┬────┬────────┬────────────────────────────────┬────────────┬──────────────────────────────┐
|
198
|
+
│ (index) │ id │ type │ name │ applied_by │ applied_at │
|
199
|
+
├─────────┼────┼────────┼────────────────────────────────┼────────────┼──────────────────────────────┤
|
200
|
+
│ 0 │ 1 │ 'up' │ 'v1_connect_session_table.sql' │ 'postgres' │ '2024-11-24 05:40:41.211617' │
|
201
|
+
│ 1 │ 2 │ 'up' │ 'v2_auth.sql' │ 'postgres' │ '2024-11-24 05:40:41.214732' │
|
202
|
+
│ 2 │ 3 │ 'down' │ 'v2_auth.down.sql' │ 'postgres' │ '2024-11-24 05:41:34.541462' │
|
203
|
+
└─────────┴────┴────────┴────────────────────────────────┴────────────┴──────────────────────────────┘
|
99
204
|
```
|
100
205
|
|
206
|
+
</details>
|
207
|
+
|
101
208
|
### Info
|
102
209
|
|
210
|
+
Shows the current state of the migrations in the database.
|
211
|
+
|
103
212
|
Command:
|
104
213
|
|
105
214
|
```bash
|
@@ -109,19 +218,55 @@ npx stepwise-migrations info \
|
|
109
218
|
--path=./db/migration/
|
110
219
|
```
|
111
220
|
|
112
|
-
|
221
|
+
<details>
|
113
222
|
|
114
|
-
|
223
|
+
<summary>Example output</summary>
|
224
|
+
|
225
|
+
```text
|
115
226
|
Migration history:
|
116
|
-
|
117
|
-
│ (index) │ id │ name │
|
118
|
-
|
119
|
-
│ 0 │ 1 │ 'v1_connect_session_table.sql' │ '
|
120
|
-
|
227
|
+
┌─────────┬────┬────────────────────────────────┬────────────┬──────────────────────────────┐
|
228
|
+
│ (index) │ id │ name │ applied_by │ applied_at │
|
229
|
+
├─────────┼────┼────────────────────────────────┼────────────┼──────────────────────────────┤
|
230
|
+
│ 0 │ 1 │ 'v1_connect_session_table.sql' │ 'postgres' │ '2024-11-24 05:40:41.211617' │
|
231
|
+
└─────────┴────┴────────────────────────────────┴────────────┴──────────────────────────────┘
|
232
|
+
```
|
233
|
+
|
234
|
+
</details>
|
235
|
+
|
236
|
+
### Get Script
|
237
|
+
|
238
|
+
Gets the script for the last applied migration.
|
239
|
+
Can get the script for a specific migration if the `--filename` option is provided.
|
240
|
+
|
241
|
+
Command:
|
242
|
+
|
243
|
+
```bash
|
244
|
+
npx stepwise-migrations get-script --filename v2_auth.sql \
|
245
|
+
--connection=postgresql://postgres:postgres@127.0.0.1:5432/colliedb \
|
246
|
+
--schema=collie \
|
247
|
+
--path=./db/migration/
|
248
|
+
```
|
249
|
+
|
250
|
+
<details>
|
251
|
+
|
252
|
+
<summary>Example output</summary>
|
253
|
+
|
254
|
+
```sql
|
255
|
+
CREATE TABLE "users" (
|
256
|
+
id bigserial primary key,
|
257
|
+
email text unique not null,
|
258
|
+
first_name text not null,
|
259
|
+
last_name text not null,
|
260
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
261
|
+
);
|
121
262
|
```
|
122
263
|
|
264
|
+
</details>
|
265
|
+
|
123
266
|
### Drop
|
124
267
|
|
268
|
+
Drops the tables, schema and migration history table.
|
269
|
+
|
125
270
|
Command:
|
126
271
|
|
127
272
|
```bash
|
@@ -130,8 +275,12 @@ npx stepwise-migrations drop \
|
|
130
275
|
--schema=myschema
|
131
276
|
```
|
132
277
|
|
133
|
-
|
278
|
+
<details>
|
134
279
|
|
135
|
-
|
280
|
+
<summary>Example output</summary>
|
281
|
+
|
282
|
+
```text
|
136
283
|
Dropping the tables, schema and migration history table... done!
|
137
284
|
```
|
285
|
+
|
286
|
+
</details>
|
package/dist/db.js
CHANGED
@@ -42,7 +42,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
42
42
|
});
|
43
43
|
};
|
44
44
|
Object.defineProperty(exports, "__esModule", { value: true });
|
45
|
-
exports.dbCreateHistoryTable = exports.dbCreateSchema = exports.dbMigrationHistory = exports.dbTableExists = exports.dbHistorySchemaExists = exports.dbConnect = void 0;
|
45
|
+
exports.dbGetScript = exports.dbCreateHistoryTable = exports.dbAuditHistory = exports.dbCreateSchema = exports.dbMigrationHistory = exports.dbTableExists = exports.dbHistorySchemaExists = exports.dbConnect = void 0;
|
46
46
|
const pg_1 = __importStar(require("pg"));
|
47
47
|
pg_1.default.types.setTypeParser(1114, function (stringValue) {
|
48
48
|
return stringValue; //1114 for time without timezone type
|
@@ -88,15 +88,35 @@ const dbCreateSchema = (client, schema) => __awaiter(void 0, void 0, void 0, fun
|
|
88
88
|
console.log(`done!`);
|
89
89
|
});
|
90
90
|
exports.dbCreateSchema = dbCreateSchema;
|
91
|
+
const dbAuditHistory = (client, schema) => __awaiter(void 0, void 0, void 0, function* () {
|
92
|
+
const auditQuery = yield client.query(`SELECT * FROM ${schema}.stepwise_audit`);
|
93
|
+
return auditQuery.rows;
|
94
|
+
});
|
95
|
+
exports.dbAuditHistory = dbAuditHistory;
|
91
96
|
const dbCreateHistoryTable = (client, schema) => __awaiter(void 0, void 0, void 0, function* () {
|
92
97
|
process.stdout.write(`Creating migration history table... `);
|
93
|
-
yield client.query(`
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
98
|
+
yield client.query(`
|
99
|
+
CREATE TABLE IF NOT EXISTS ${schema}.stepwise_migrations (
|
100
|
+
id SERIAL PRIMARY KEY,
|
101
|
+
name TEXT UNIQUE NOT NULL,
|
102
|
+
script TEXT NOT NULL,
|
103
|
+
applied_by TEXT NOT NULL DEFAULT current_user,
|
104
|
+
applied_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
105
|
+
);
|
106
|
+
CREATE TABLE IF NOT EXISTS ${schema}.stepwise_audit (
|
107
|
+
id SERIAL PRIMARY KEY,
|
108
|
+
type TEXT NOT NULL,
|
109
|
+
name TEXT UNIQUE NOT NULL,
|
110
|
+
script TEXT NOT NULL,
|
111
|
+
applied_by TEXT NOT NULL DEFAULT current_user,
|
112
|
+
applied_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
113
|
+
);
|
114
|
+
`);
|
100
115
|
console.log(`done!`);
|
101
116
|
});
|
102
117
|
exports.dbCreateHistoryTable = dbCreateHistoryTable;
|
118
|
+
const dbGetScript = (client, schema, filename) => __awaiter(void 0, void 0, void 0, function* () {
|
119
|
+
const script = yield client.query(`SELECT script FROM ${schema}.stepwise_audit WHERE name = $1`, [filename]);
|
120
|
+
return script.rows[0].script;
|
121
|
+
});
|
122
|
+
exports.dbGetScript = dbGetScript;
|
package/dist/index.js
CHANGED
@@ -36,13 +36,16 @@ const main = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
36
36
|
const migrationHistory = yield (0, db_1.dbMigrationHistory)(client, schema);
|
37
37
|
const migrationFiles = yield (0, utils_1.readMigrationFiles)(argv.path);
|
38
38
|
(0, migrate_1.validateMigrationFiles)(migrationFiles, migrationHistory);
|
39
|
+
if (migrationFiles.length === migrationHistory.length) {
|
40
|
+
console.log("All migrations are already applied");
|
41
|
+
process.exit(0);
|
42
|
+
}
|
39
43
|
const migrationsToApply = migrationFiles.slice(migrationHistory.length, migrationHistory.length + nUp);
|
40
|
-
for (const { filename,
|
41
|
-
yield (0, migrate_1.applyMigration)(client, schema, filename,
|
44
|
+
for (const { filename, script } of migrationsToApply) {
|
45
|
+
yield (0, migrate_1.applyMigration)(client, schema, filename, script);
|
42
46
|
}
|
43
47
|
console.log(`All done! Applied ${migrationsToApply.length} migrations`);
|
44
|
-
|
45
|
-
console.table(yield (0, db_1.dbMigrationHistory)(client, schema));
|
48
|
+
(0, utils_1.printMigrationHistoryAndUnappliedMigrations)(yield (0, utils_1.readMigrationFiles)(argv.path), yield (0, db_1.dbMigrationHistory)(client, schema));
|
46
49
|
}
|
47
50
|
else if (command === "info") {
|
48
51
|
if (!historySchemaExists) {
|
@@ -52,10 +55,22 @@ const main = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
52
55
|
console.log("Migration history table does not exist");
|
53
56
|
}
|
54
57
|
if (historySchemaExists && tableExists) {
|
55
|
-
|
56
|
-
console.table(yield (0, db_1.dbMigrationHistory)(client, schema));
|
58
|
+
(0, utils_1.printMigrationHistory)(yield (0, db_1.dbMigrationHistory)(client, schema));
|
57
59
|
}
|
58
60
|
}
|
61
|
+
else if (command === "validate") {
|
62
|
+
if (!historySchemaExists) {
|
63
|
+
console.log("Schema does not exist");
|
64
|
+
}
|
65
|
+
if (!tableExists) {
|
66
|
+
console.log("Migration history table does not exist");
|
67
|
+
}
|
68
|
+
if (historySchemaExists && tableExists) {
|
69
|
+
(0, migrate_1.validateMigrationFiles)(yield (0, utils_1.readMigrationFiles)(argv.path), yield (0, db_1.dbMigrationHistory)(client, schema));
|
70
|
+
}
|
71
|
+
console.log("Validation passed");
|
72
|
+
(0, utils_1.printMigrationHistoryAndUnappliedMigrations)(yield (0, utils_1.readMigrationFiles)(argv.path), yield (0, db_1.dbMigrationHistory)(client, schema));
|
73
|
+
}
|
59
74
|
else if (command === "drop") {
|
60
75
|
process.stdout.write(`Dropping the tables, schema and migration history table... `);
|
61
76
|
yield client.query(`DROP SCHEMA IF EXISTS ${schema} CASCADE`);
|
@@ -68,12 +83,31 @@ const main = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
68
83
|
const reverseMigrationHistory = migrationHistory.reverse().slice(0, nDown);
|
69
84
|
const downMigrationFilesToApply = yield (0, utils_1.readDownMigrationFiles)(argv.path, reverseMigrationHistory);
|
70
85
|
(0, migrate_1.validateDownMigrationFiles)(downMigrationFilesToApply, reverseMigrationHistory);
|
71
|
-
for (const { filename,
|
72
|
-
yield (0, migrate_1.applyDownMigration)(client, schema, filename,
|
86
|
+
for (const { filename, script, upFilename } of downMigrationFilesToApply) {
|
87
|
+
yield (0, migrate_1.applyDownMigration)(client, schema, filename, script, upFilename);
|
73
88
|
}
|
74
|
-
console.log(`All done! Applied ${downMigrationFilesToApply.length} down
|
75
|
-
|
76
|
-
|
89
|
+
console.log(`All done! Applied ${downMigrationFilesToApply.length} down migration${downMigrationFilesToApply.length === 1 ? "" : "s"}`);
|
90
|
+
(0, utils_1.printMigrationHistoryAndUnappliedMigrations)(yield (0, utils_1.readMigrationFiles)(argv.path), yield (0, db_1.dbMigrationHistory)(client, schema));
|
91
|
+
}
|
92
|
+
else if (command === "audit") {
|
93
|
+
const auditHistory = yield (0, db_1.dbAuditHistory)(client, schema);
|
94
|
+
console.log("Audit history:");
|
95
|
+
console.table(auditHistory.map((row) => ({
|
96
|
+
id: row.id,
|
97
|
+
type: row.type,
|
98
|
+
name: row.name,
|
99
|
+
applied_by: row.applied_by,
|
100
|
+
applied_at: row.applied_at,
|
101
|
+
})));
|
102
|
+
}
|
103
|
+
else if (command === "get-script") {
|
104
|
+
const script = yield (0, db_1.dbGetScript)(client, schema, argv.filename);
|
105
|
+
console.log(script);
|
106
|
+
}
|
107
|
+
else {
|
108
|
+
console.error(`Invalid command: ${argv._[0]}`);
|
109
|
+
console.log(utils_1.usage);
|
110
|
+
process.exit(1);
|
77
111
|
}
|
78
112
|
client.release();
|
79
113
|
process.exit(0);
|
package/dist/migrate.js
CHANGED
@@ -8,8 +8,12 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
8
8
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
9
9
|
});
|
10
10
|
};
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
13
|
+
};
|
11
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
12
15
|
exports.applyDownMigration = exports.validateDownMigrationFiles = exports.applyMigration = exports.validateMigrationFiles = void 0;
|
16
|
+
const git_diff_1 = __importDefault(require("git-diff"));
|
13
17
|
const validateMigrationFiles = (migrationFiles, migrationHistory, isUp = true) => {
|
14
18
|
if (migrationFiles.length === 0) {
|
15
19
|
console.log("No migrations found");
|
@@ -19,12 +23,8 @@ const validateMigrationFiles = (migrationFiles, migrationHistory, isUp = true) =
|
|
19
23
|
console.error("Error: migration history is longer than the number of migration files, aborting.");
|
20
24
|
process.exit(1);
|
21
25
|
}
|
22
|
-
if (migrationFiles.length === migrationHistory.length && isUp) {
|
23
|
-
console.log("All migrations are already applied");
|
24
|
-
process.exit(0);
|
25
|
-
}
|
26
26
|
for (let i = 0; i < migrationFiles.length; i++) {
|
27
|
-
const { filename,
|
27
|
+
const { filename, script: migrationScript } = migrationFiles[i];
|
28
28
|
if (i >= migrationHistory.length) {
|
29
29
|
continue;
|
30
30
|
}
|
@@ -32,20 +32,25 @@ const validateMigrationFiles = (migrationFiles, migrationHistory, isUp = true) =
|
|
32
32
|
console.error(`Error: migration ${filename} has been renamed, aborting.`);
|
33
33
|
process.exit(1);
|
34
34
|
}
|
35
|
-
if (migrationHistory[i].
|
35
|
+
if (migrationHistory[i].script !== migrationScript) {
|
36
36
|
console.error(`Error: migration ${filename} has been modified, aborting.`);
|
37
|
+
console.log((0, git_diff_1.default)(migrationHistory[i].script, migrationScript, {
|
38
|
+
color: true,
|
39
|
+
noHeaders: true,
|
40
|
+
}));
|
37
41
|
process.exit(1);
|
38
42
|
}
|
39
43
|
}
|
40
44
|
};
|
41
45
|
exports.validateMigrationFiles = validateMigrationFiles;
|
42
|
-
const applyMigration = (client, schema, filename,
|
46
|
+
const applyMigration = (client, schema, filename, script) => __awaiter(void 0, void 0, void 0, function* () {
|
43
47
|
try {
|
44
48
|
process.stdout.write(`Applying migration ${filename}... `);
|
45
49
|
yield client.query("BEGIN");
|
46
50
|
yield client.query(`SET search_path TO ${schema};
|
47
|
-
${
|
48
|
-
yield client.query(`INSERT INTO ${schema}.stepwise_migrations (name,
|
51
|
+
${script.toString()}`);
|
52
|
+
yield client.query(`INSERT INTO ${schema}.stepwise_migrations (name, script) VALUES ($1, $2)`, [filename, script]);
|
53
|
+
yield client.query(`INSERT INTO ${schema}.stepwise_audit (type, name, script) VALUES ($1, $2, $3)`, ["up", filename, script]);
|
49
54
|
yield client.query("COMMIT");
|
50
55
|
console.log(`done!`);
|
51
56
|
}
|
@@ -72,13 +77,14 @@ const validateDownMigrationFiles = (downMigrationFilesToApply, reverseMigrationH
|
|
72
77
|
}
|
73
78
|
};
|
74
79
|
exports.validateDownMigrationFiles = validateDownMigrationFiles;
|
75
|
-
const applyDownMigration = (client, schema, filename,
|
80
|
+
const applyDownMigration = (client, schema, filename, script, upFilename) => __awaiter(void 0, void 0, void 0, function* () {
|
76
81
|
try {
|
77
82
|
process.stdout.write(`Applying down migration ${filename}... `);
|
78
83
|
yield client.query("BEGIN");
|
79
84
|
yield client.query(`SET search_path TO ${schema};
|
80
|
-
${
|
85
|
+
${script.toString()}`);
|
81
86
|
yield client.query(`DELETE FROM ${schema}.stepwise_migrations WHERE name = $1`, [upFilename]);
|
87
|
+
yield client.query(`INSERT INTO ${schema}.stepwise_audit (type, name, script) VALUES ($1, $2, $3)`, ["down", filename, script]);
|
82
88
|
yield client.query("COMMIT");
|
83
89
|
console.log(`done!`);
|
84
90
|
}
|
package/dist/utils.js
CHANGED
@@ -12,14 +12,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
12
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
13
13
|
};
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
15
|
-
exports.readDownMigrationFiles = exports.fileExists = exports.
|
16
|
-
const crypto_1 = __importDefault(require("crypto"));
|
15
|
+
exports.readDownMigrationFiles = exports.fileExists = exports.printMigrationHistory = exports.printMigrationHistoryAndUnappliedMigrations = exports.readMigrationFiles = exports.validateArgs = exports.usage = void 0;
|
17
16
|
const promises_1 = __importDefault(require("fs/promises"));
|
18
17
|
const path_1 = __importDefault(require("path"));
|
19
|
-
const calculateHash = (contents) => {
|
20
|
-
return crypto_1.default.createHash("sha256").update(contents).digest("hex");
|
21
|
-
};
|
22
|
-
exports.calculateHash = calculateHash;
|
23
18
|
exports.usage = `
|
24
19
|
Usage: stepwise-migrations [command] [options]
|
25
20
|
|
@@ -59,14 +54,6 @@ const validateArgs = (argv) => {
|
|
59
54
|
console.log(exports.usage);
|
60
55
|
process.exit(1);
|
61
56
|
}
|
62
|
-
if (argv._[0] !== "migrate" &&
|
63
|
-
argv._[0] !== "info" &&
|
64
|
-
argv._[0] !== "drop" &&
|
65
|
-
argv._[0] !== "down") {
|
66
|
-
console.error(`Invalid command: ${argv._[0]}`);
|
67
|
-
console.log(exports.usage);
|
68
|
-
process.exit(1);
|
69
|
-
}
|
70
57
|
};
|
71
58
|
exports.validateArgs = validateArgs;
|
72
59
|
const readMigrationFiles = (directory) => __awaiter(void 0, void 0, void 0, function* () {
|
@@ -79,18 +66,41 @@ const readMigrationFiles = (directory) => __awaiter(void 0, void 0, void 0, func
|
|
79
66
|
migrationFiles.sort();
|
80
67
|
const results = [];
|
81
68
|
for (const fullFilePath of migrationFiles) {
|
82
|
-
const
|
69
|
+
const script = yield promises_1.default.readFile(fullFilePath, "utf8");
|
83
70
|
results.push({
|
84
71
|
type: "up",
|
85
72
|
fullFilePath,
|
86
73
|
filename: path_1.default.basename(fullFilePath),
|
87
|
-
|
88
|
-
contents,
|
74
|
+
script,
|
89
75
|
});
|
90
76
|
}
|
91
77
|
return results;
|
92
78
|
});
|
93
79
|
exports.readMigrationFiles = readMigrationFiles;
|
80
|
+
const printMigrationHistoryAndUnappliedMigrations = (migrationFiles, migrationHistory) => {
|
81
|
+
console.log("Migration history:");
|
82
|
+
console.table(migrationHistory.map((h) => ({
|
83
|
+
id: h.id,
|
84
|
+
name: h.name,
|
85
|
+
applied_by: h.applied_by,
|
86
|
+
applied_at: h.applied_at,
|
87
|
+
})));
|
88
|
+
console.log("Unapplied migrations:");
|
89
|
+
console.table(migrationFiles.slice(migrationHistory.length).map((m) => ({
|
90
|
+
filename: m.filename,
|
91
|
+
})));
|
92
|
+
};
|
93
|
+
exports.printMigrationHistoryAndUnappliedMigrations = printMigrationHistoryAndUnappliedMigrations;
|
94
|
+
const printMigrationHistory = (migrationHistory) => {
|
95
|
+
console.log("Migration history:");
|
96
|
+
console.table(migrationHistory.map((h) => ({
|
97
|
+
id: h.id,
|
98
|
+
name: h.name,
|
99
|
+
applied_by: h.applied_by,
|
100
|
+
applied_at: h.applied_at,
|
101
|
+
})));
|
102
|
+
};
|
103
|
+
exports.printMigrationHistory = printMigrationHistory;
|
94
104
|
const fileExists = (path) => __awaiter(void 0, void 0, void 0, function* () {
|
95
105
|
try {
|
96
106
|
return (yield promises_1.default.stat(path)).isFile();
|
@@ -108,13 +118,13 @@ const readDownMigrationFiles = (directory, migrationHistory) => __awaiter(void 0
|
|
108
118
|
console.error(`Down migration file not found: ${fullFilePath}`);
|
109
119
|
process.exit(1);
|
110
120
|
}
|
111
|
-
const
|
121
|
+
const script = yield promises_1.default.readFile(fullFilePath, "utf8");
|
112
122
|
results.push({
|
113
123
|
type: "down",
|
114
124
|
fullFilePath,
|
115
125
|
filename: path_1.default.basename(fullFilePath),
|
116
126
|
upFilename: migration.name,
|
117
|
-
|
127
|
+
script,
|
118
128
|
});
|
119
129
|
}
|
120
130
|
return results;
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "stepwise-migrations",
|
3
|
-
"version": "1.0.
|
3
|
+
"version": "1.0.10",
|
4
4
|
"description": "",
|
5
5
|
"main": "index.js",
|
6
6
|
"scripts": {
|
@@ -15,6 +15,7 @@
|
|
15
15
|
"author": "github.com/mj1618",
|
16
16
|
"license": "MIT",
|
17
17
|
"devDependencies": {
|
18
|
+
"@types/git-diff": "^2.0.7",
|
18
19
|
"@types/pg": "^8.11.10",
|
19
20
|
"@types/yargs": "^17.0.33"
|
20
21
|
},
|
@@ -22,6 +23,7 @@
|
|
22
23
|
"stepwise-migrations": "dist/index.js"
|
23
24
|
},
|
24
25
|
"dependencies": {
|
26
|
+
"git-diff": "^2.0.6",
|
25
27
|
"pg": "^8.13.1",
|
26
28
|
"yargs": "^17.7.2"
|
27
29
|
}
|
package/src/db.ts
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
import pg, { Pool, PoolClient } from "pg";
|
2
|
-
import { MigrationRow } from "./types";
|
2
|
+
import { AuditRow, MigrationRow } from "./types";
|
3
3
|
|
4
4
|
pg.types.setTypeParser(1114, function (stringValue) {
|
5
5
|
return stringValue; //1114 for time without timezone type
|
@@ -61,19 +61,48 @@ export const dbCreateSchema = async (client: PoolClient, schema: string) => {
|
|
61
61
|
console.log(`done!`);
|
62
62
|
};
|
63
63
|
|
64
|
+
export const dbAuditHistory = async (client: PoolClient, schema: string) => {
|
65
|
+
const auditQuery = await client.query(
|
66
|
+
`SELECT * FROM ${schema}.stepwise_audit`
|
67
|
+
);
|
68
|
+
return auditQuery.rows as AuditRow[];
|
69
|
+
};
|
70
|
+
|
64
71
|
export const dbCreateHistoryTable = async (
|
65
72
|
client: PoolClient,
|
66
73
|
schema: string
|
67
74
|
) => {
|
68
75
|
process.stdout.write(`Creating migration history table... `);
|
69
76
|
await client.query(
|
70
|
-
`
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
+
`
|
78
|
+
CREATE TABLE IF NOT EXISTS ${schema}.stepwise_migrations (
|
79
|
+
id SERIAL PRIMARY KEY,
|
80
|
+
name TEXT UNIQUE NOT NULL,
|
81
|
+
script TEXT NOT NULL,
|
82
|
+
applied_by TEXT NOT NULL DEFAULT current_user,
|
83
|
+
applied_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
84
|
+
);
|
85
|
+
CREATE TABLE IF NOT EXISTS ${schema}.stepwise_audit (
|
86
|
+
id SERIAL PRIMARY KEY,
|
87
|
+
type TEXT NOT NULL,
|
88
|
+
name TEXT UNIQUE NOT NULL,
|
89
|
+
script TEXT NOT NULL,
|
90
|
+
applied_by TEXT NOT NULL DEFAULT current_user,
|
91
|
+
applied_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
92
|
+
);
|
93
|
+
`
|
77
94
|
);
|
78
95
|
console.log(`done!`);
|
79
96
|
};
|
97
|
+
|
98
|
+
export const dbGetScript = async (
|
99
|
+
client: PoolClient,
|
100
|
+
schema: string,
|
101
|
+
filename: string
|
102
|
+
) => {
|
103
|
+
const script = await client.query(
|
104
|
+
`SELECT script FROM ${schema}.stepwise_audit WHERE name = $1`,
|
105
|
+
[filename]
|
106
|
+
);
|
107
|
+
return script.rows[0].script;
|
108
|
+
};
|
package/src/index.ts
CHANGED
@@ -2,9 +2,11 @@
|
|
2
2
|
|
3
3
|
import yargs from "yargs";
|
4
4
|
import {
|
5
|
+
dbAuditHistory,
|
5
6
|
dbConnect,
|
6
7
|
dbCreateHistoryTable,
|
7
8
|
dbCreateSchema,
|
9
|
+
dbGetScript,
|
8
10
|
dbHistorySchemaExists,
|
9
11
|
dbMigrationHistory,
|
10
12
|
dbTableExists,
|
@@ -16,8 +18,11 @@ import {
|
|
16
18
|
validateMigrationFiles,
|
17
19
|
} from "./migrate";
|
18
20
|
import {
|
21
|
+
printMigrationHistory,
|
22
|
+
printMigrationHistoryAndUnappliedMigrations,
|
19
23
|
readDownMigrationFiles,
|
20
24
|
readMigrationFiles,
|
25
|
+
usage,
|
21
26
|
validateArgs,
|
22
27
|
} from "./utils";
|
23
28
|
|
@@ -47,18 +52,26 @@ const main = async () => {
|
|
47
52
|
|
48
53
|
validateMigrationFiles(migrationFiles, migrationHistory);
|
49
54
|
|
55
|
+
if (migrationFiles.length === migrationHistory.length) {
|
56
|
+
console.log("All migrations are already applied");
|
57
|
+
process.exit(0);
|
58
|
+
}
|
59
|
+
|
50
60
|
const migrationsToApply = migrationFiles.slice(
|
51
61
|
migrationHistory.length,
|
52
62
|
migrationHistory.length + nUp
|
53
63
|
);
|
54
64
|
|
55
|
-
for (const { filename,
|
56
|
-
await applyMigration(client, schema, filename,
|
65
|
+
for (const { filename, script } of migrationsToApply) {
|
66
|
+
await applyMigration(client, schema, filename, script);
|
57
67
|
}
|
58
68
|
|
59
69
|
console.log(`All done! Applied ${migrationsToApply.length} migrations`);
|
60
|
-
|
61
|
-
|
70
|
+
|
71
|
+
printMigrationHistoryAndUnappliedMigrations(
|
72
|
+
await readMigrationFiles(argv.path),
|
73
|
+
await dbMigrationHistory(client, schema)
|
74
|
+
);
|
62
75
|
} else if (command === "info") {
|
63
76
|
if (!historySchemaExists) {
|
64
77
|
console.log("Schema does not exist");
|
@@ -69,9 +82,29 @@ const main = async () => {
|
|
69
82
|
}
|
70
83
|
|
71
84
|
if (historySchemaExists && tableExists) {
|
72
|
-
|
73
|
-
|
85
|
+
printMigrationHistory(await dbMigrationHistory(client, schema));
|
86
|
+
}
|
87
|
+
} else if (command === "validate") {
|
88
|
+
if (!historySchemaExists) {
|
89
|
+
console.log("Schema does not exist");
|
90
|
+
}
|
91
|
+
|
92
|
+
if (!tableExists) {
|
93
|
+
console.log("Migration history table does not exist");
|
94
|
+
}
|
95
|
+
|
96
|
+
if (historySchemaExists && tableExists) {
|
97
|
+
validateMigrationFiles(
|
98
|
+
await readMigrationFiles(argv.path),
|
99
|
+
await dbMigrationHistory(client, schema)
|
100
|
+
);
|
74
101
|
}
|
102
|
+
console.log("Validation passed");
|
103
|
+
|
104
|
+
printMigrationHistoryAndUnappliedMigrations(
|
105
|
+
await readMigrationFiles(argv.path),
|
106
|
+
await dbMigrationHistory(client, schema)
|
107
|
+
);
|
75
108
|
} else if (command === "drop") {
|
76
109
|
process.stdout.write(
|
77
110
|
`Dropping the tables, schema and migration history table... `
|
@@ -98,18 +131,38 @@ const main = async () => {
|
|
98
131
|
downMigrationFilesToApply,
|
99
132
|
reverseMigrationHistory
|
100
133
|
);
|
101
|
-
for (const {
|
102
|
-
filename,
|
103
|
-
contents,
|
104
|
-
upFilename,
|
105
|
-
} of downMigrationFilesToApply) {
|
106
|
-
await applyDownMigration(client, schema, filename, contents, upFilename);
|
134
|
+
for (const { filename, script, upFilename } of downMigrationFilesToApply) {
|
135
|
+
await applyDownMigration(client, schema, filename, script, upFilename);
|
107
136
|
}
|
108
137
|
console.log(
|
109
|
-
`All done! Applied ${downMigrationFilesToApply.length} down
|
138
|
+
`All done! Applied ${downMigrationFilesToApply.length} down migration${
|
139
|
+
downMigrationFilesToApply.length === 1 ? "" : "s"
|
140
|
+
}`
|
141
|
+
);
|
142
|
+
|
143
|
+
printMigrationHistoryAndUnappliedMigrations(
|
144
|
+
await readMigrationFiles(argv.path),
|
145
|
+
await dbMigrationHistory(client, schema)
|
146
|
+
);
|
147
|
+
} else if (command === "audit") {
|
148
|
+
const auditHistory = await dbAuditHistory(client, schema);
|
149
|
+
console.log("Audit history:");
|
150
|
+
console.table(
|
151
|
+
auditHistory.map((row) => ({
|
152
|
+
id: row.id,
|
153
|
+
type: row.type,
|
154
|
+
name: row.name,
|
155
|
+
applied_by: row.applied_by,
|
156
|
+
applied_at: row.applied_at,
|
157
|
+
}))
|
110
158
|
);
|
111
|
-
|
112
|
-
|
159
|
+
} else if (command === "get-script") {
|
160
|
+
const script = await dbGetScript(client, schema, argv.filename);
|
161
|
+
console.log(script);
|
162
|
+
} else {
|
163
|
+
console.error(`Invalid command: ${argv._[0]}`);
|
164
|
+
console.log(usage);
|
165
|
+
process.exit(1);
|
113
166
|
}
|
114
167
|
|
115
168
|
client.release();
|
package/src/migrate.ts
CHANGED
@@ -1,8 +1,9 @@
|
|
1
|
+
import gitDiff from "git-diff";
|
1
2
|
import { PoolClient } from "pg";
|
2
3
|
import { MigrationRow } from "./types";
|
3
4
|
|
4
5
|
export const validateMigrationFiles = (
|
5
|
-
migrationFiles: { fullFilePath: string; filename: string;
|
6
|
+
migrationFiles: { fullFilePath: string; filename: string; script: string }[],
|
6
7
|
migrationHistory: MigrationRow[],
|
7
8
|
isUp: boolean = true
|
8
9
|
) => {
|
@@ -18,13 +19,8 @@ export const validateMigrationFiles = (
|
|
18
19
|
process.exit(1);
|
19
20
|
}
|
20
21
|
|
21
|
-
if (migrationFiles.length === migrationHistory.length && isUp) {
|
22
|
-
console.log("All migrations are already applied");
|
23
|
-
process.exit(0);
|
24
|
-
}
|
25
|
-
|
26
22
|
for (let i = 0; i < migrationFiles.length; i++) {
|
27
|
-
const { filename,
|
23
|
+
const { filename, script: migrationScript } = migrationFiles[i];
|
28
24
|
if (i >= migrationHistory.length) {
|
29
25
|
continue;
|
30
26
|
}
|
@@ -32,10 +28,18 @@ export const validateMigrationFiles = (
|
|
32
28
|
console.error(`Error: migration ${filename} has been renamed, aborting.`);
|
33
29
|
process.exit(1);
|
34
30
|
}
|
35
|
-
if (migrationHistory[i].
|
31
|
+
if (migrationHistory[i].script !== migrationScript) {
|
36
32
|
console.error(
|
37
33
|
`Error: migration ${filename} has been modified, aborting.`
|
38
34
|
);
|
35
|
+
|
36
|
+
console.log(
|
37
|
+
gitDiff(migrationHistory[i].script, migrationScript, {
|
38
|
+
color: true,
|
39
|
+
noHeaders: true,
|
40
|
+
})
|
41
|
+
);
|
42
|
+
|
39
43
|
process.exit(1);
|
40
44
|
}
|
41
45
|
}
|
@@ -45,8 +49,7 @@ export const applyMigration = async (
|
|
45
49
|
client: PoolClient,
|
46
50
|
schema: string,
|
47
51
|
filename: string,
|
48
|
-
|
49
|
-
hash: string
|
52
|
+
script: string
|
50
53
|
) => {
|
51
54
|
try {
|
52
55
|
process.stdout.write(`Applying migration ${filename}... `);
|
@@ -54,12 +57,17 @@ export const applyMigration = async (
|
|
54
57
|
|
55
58
|
await client.query(
|
56
59
|
`SET search_path TO ${schema};
|
57
|
-
${
|
60
|
+
${script.toString()}`
|
58
61
|
);
|
59
62
|
|
60
63
|
await client.query(
|
61
|
-
`INSERT INTO ${schema}.stepwise_migrations (name,
|
62
|
-
[filename,
|
64
|
+
`INSERT INTO ${schema}.stepwise_migrations (name, script) VALUES ($1, $2)`,
|
65
|
+
[filename, script]
|
66
|
+
);
|
67
|
+
|
68
|
+
await client.query(
|
69
|
+
`INSERT INTO ${schema}.stepwise_audit (type, name, script) VALUES ($1, $2, $3)`,
|
70
|
+
["up", filename, script]
|
63
71
|
);
|
64
72
|
|
65
73
|
await client.query("COMMIT");
|
@@ -98,7 +106,7 @@ export const applyDownMigration = async (
|
|
98
106
|
client: PoolClient,
|
99
107
|
schema: string,
|
100
108
|
filename: string,
|
101
|
-
|
109
|
+
script: string,
|
102
110
|
upFilename: string
|
103
111
|
) => {
|
104
112
|
try {
|
@@ -107,7 +115,7 @@ export const applyDownMigration = async (
|
|
107
115
|
|
108
116
|
await client.query(
|
109
117
|
`SET search_path TO ${schema};
|
110
|
-
${
|
118
|
+
${script.toString()}`
|
111
119
|
);
|
112
120
|
|
113
121
|
await client.query(
|
@@ -115,6 +123,11 @@ export const applyDownMigration = async (
|
|
115
123
|
[upFilename]
|
116
124
|
);
|
117
125
|
|
126
|
+
await client.query(
|
127
|
+
`INSERT INTO ${schema}.stepwise_audit (type, name, script) VALUES ($1, $2, $3)`,
|
128
|
+
["down", filename, script]
|
129
|
+
);
|
130
|
+
|
118
131
|
await client.query("COMMIT");
|
119
132
|
|
120
133
|
console.log(`done!`);
|
package/src/types.ts
CHANGED
package/src/utils.ts
CHANGED
@@ -1,12 +1,7 @@
|
|
1
|
-
import crypto from "crypto";
|
2
1
|
import fs from "fs/promises";
|
3
2
|
import path from "path";
|
4
3
|
import { MigrationRow } from "./types";
|
5
4
|
|
6
|
-
export const calculateHash = (contents: string) => {
|
7
|
-
return crypto.createHash("sha256").update(contents).digest("hex");
|
8
|
-
};
|
9
|
-
|
10
5
|
export const usage = `
|
11
6
|
Usage: stepwise-migrations [command] [options]
|
12
7
|
|
@@ -50,16 +45,6 @@ export const validateArgs = (argv: any) => {
|
|
50
45
|
console.log(usage);
|
51
46
|
process.exit(1);
|
52
47
|
}
|
53
|
-
if (
|
54
|
-
argv._[0] !== "migrate" &&
|
55
|
-
argv._[0] !== "info" &&
|
56
|
-
argv._[0] !== "drop" &&
|
57
|
-
argv._[0] !== "down"
|
58
|
-
) {
|
59
|
-
console.error(`Invalid command: ${argv._[0]}`);
|
60
|
-
console.log(usage);
|
61
|
-
process.exit(1);
|
62
|
-
}
|
63
48
|
};
|
64
49
|
|
65
50
|
export const readMigrationFiles = async (directory: string) => {
|
@@ -77,23 +62,54 @@ export const readMigrationFiles = async (directory: string) => {
|
|
77
62
|
type: "up";
|
78
63
|
fullFilePath: string;
|
79
64
|
filename: string;
|
80
|
-
|
81
|
-
contents: string;
|
65
|
+
script: string;
|
82
66
|
}[] = [];
|
83
67
|
for (const fullFilePath of migrationFiles) {
|
84
|
-
const
|
68
|
+
const script = await fs.readFile(fullFilePath, "utf8");
|
85
69
|
|
86
70
|
results.push({
|
87
71
|
type: "up",
|
88
72
|
fullFilePath,
|
89
73
|
filename: path.basename(fullFilePath),
|
90
|
-
|
91
|
-
contents,
|
74
|
+
script,
|
92
75
|
});
|
93
76
|
}
|
94
77
|
return results;
|
95
78
|
};
|
96
79
|
|
80
|
+
export const printMigrationHistoryAndUnappliedMigrations = (
|
81
|
+
migrationFiles: { filename: string }[],
|
82
|
+
migrationHistory: MigrationRow[]
|
83
|
+
) => {
|
84
|
+
console.log("Migration history:");
|
85
|
+
console.table(
|
86
|
+
migrationHistory.map((h) => ({
|
87
|
+
id: h.id,
|
88
|
+
name: h.name,
|
89
|
+
applied_by: h.applied_by,
|
90
|
+
applied_at: h.applied_at,
|
91
|
+
}))
|
92
|
+
);
|
93
|
+
console.log("Unapplied migrations:");
|
94
|
+
console.table(
|
95
|
+
migrationFiles.slice(migrationHistory.length).map((m) => ({
|
96
|
+
filename: m.filename,
|
97
|
+
}))
|
98
|
+
);
|
99
|
+
};
|
100
|
+
|
101
|
+
export const printMigrationHistory = (migrationHistory: MigrationRow[]) => {
|
102
|
+
console.log("Migration history:");
|
103
|
+
console.table(
|
104
|
+
migrationHistory.map((h) => ({
|
105
|
+
id: h.id,
|
106
|
+
name: h.name,
|
107
|
+
applied_by: h.applied_by,
|
108
|
+
applied_at: h.applied_at,
|
109
|
+
}))
|
110
|
+
);
|
111
|
+
};
|
112
|
+
|
97
113
|
export const fileExists = async (path: string) => {
|
98
114
|
try {
|
99
115
|
return (await fs.stat(path)).isFile();
|
@@ -112,7 +128,7 @@ export const readDownMigrationFiles = async (
|
|
112
128
|
filename: string;
|
113
129
|
upFilename: string;
|
114
130
|
|
115
|
-
|
131
|
+
script: string;
|
116
132
|
}[] = [];
|
117
133
|
for (const migration of migrationHistory) {
|
118
134
|
const fullFilePath = path.join(
|
@@ -123,13 +139,13 @@ export const readDownMigrationFiles = async (
|
|
123
139
|
console.error(`Down migration file not found: ${fullFilePath}`);
|
124
140
|
process.exit(1);
|
125
141
|
}
|
126
|
-
const
|
142
|
+
const script = await fs.readFile(fullFilePath, "utf8");
|
127
143
|
results.push({
|
128
144
|
type: "down",
|
129
145
|
fullFilePath,
|
130
146
|
filename: path.basename(fullFilePath),
|
131
147
|
upFilename: migration.name,
|
132
|
-
|
148
|
+
script,
|
133
149
|
});
|
134
150
|
}
|
135
151
|
return results;
|