stepwise-migrations 1.0.7 → 1.0.9
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 +48 -48
- package/dist/db.js +32 -13
- package/dist/index.js +56 -20
- package/dist/migrate.js +21 -13
- package/dist/utils.js +31 -22
- package/package.json +3 -1
- package/src/db.ts +41 -13
- package/src/index.ts +84 -29
- package/src/migrate.ts +32 -17
- package/src/types.ts +3 -1
- package/src/utils.ts +41 -26
package/README.md
CHANGED
@@ -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,20 +42,20 @@ 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
|
-
npx stepwise-migrations \
|
48
|
+
npx stepwise-migrations migrate \
|
42
49
|
--connection=postgresql://postgres:postgres@127.0.0.1:5432/mydatabase \
|
43
50
|
--schema=myschema \
|
44
|
-
--path=./db/migration/
|
45
|
-
migrate
|
51
|
+
--path=./db/migration/
|
46
52
|
```
|
47
53
|
|
48
54
|
## Examples
|
49
55
|
|
50
56
|
### Migrate
|
51
57
|
|
52
|
-
|
58
|
+
If all files are in a valid state, runs all the "up" migrations that have not been applied yet.
|
53
59
|
|
54
60
|
```bash
|
55
61
|
npx stepwise-migrations migrate \
|
@@ -58,22 +64,11 @@ npx stepwise-migrations migrate \
|
|
58
64
|
--path=./db/migration/
|
59
65
|
```
|
60
66
|
|
61
|
-
Outputs:
|
62
|
-
|
63
|
-
```
|
64
|
-
Connected to the database
|
65
|
-
Creating schema collie
|
66
|
-
Schema collie created
|
67
|
-
Creating migration history table
|
68
|
-
Migration history table created
|
69
|
-
Found 2 migration files
|
70
|
-
Applied migration V0_01__connect_session_table.sql
|
71
|
-
Applied migration V0_02__auth.sql
|
72
|
-
All done!
|
73
|
-
```
|
74
|
-
|
75
67
|
### Down
|
76
68
|
|
69
|
+
Runs a single down migration for the last applied migration.
|
70
|
+
Can run multiple down migrations if the `--ndown` option is provided.
|
71
|
+
|
77
72
|
Command:
|
78
73
|
|
79
74
|
```bash
|
@@ -83,23 +78,34 @@ npx stepwise-migrations down \
|
|
83
78
|
--path=./db/migration/
|
84
79
|
```
|
85
80
|
|
86
|
-
|
81
|
+
### Validate
|
87
82
|
|
83
|
+
Validates the migration files and the migration history table.
|
84
|
+
|
85
|
+
```bash
|
86
|
+
npx stepwise-migrations validate \
|
87
|
+
--connection=postgresql://postgres:postgres@127.0.0.1:5432/mydb \
|
88
|
+
--schema=myschema \
|
89
|
+
--path=./db/migration/
|
88
90
|
```
|
89
91
|
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
92
|
+
### Audit
|
93
|
+
|
94
|
+
Shows the audit history for the migrations in the database.
|
95
|
+
|
96
|
+
```bash
|
97
|
+
npx stepwise-migrations audit \
|
98
|
+
--connection=postgresql://postgres:postgres@127.0.0.1:5432/mydb \
|
99
|
+
--schema=myschema \
|
100
|
+
--path=./db/migration/
|
99
101
|
```
|
100
102
|
|
101
103
|
### Info
|
102
104
|
|
105
|
+
Shows the current state of the migrations in the database.
|
106
|
+
|
107
|
+
Command:
|
108
|
+
|
103
109
|
```bash
|
104
110
|
npx stepwise-migrations info \
|
105
111
|
--connection=postgresql://postgres:postgres@127.0.0.1:5432/mydb \
|
@@ -107,23 +113,11 @@ npx stepwise-migrations info \
|
|
107
113
|
--path=./db/migration/
|
108
114
|
```
|
109
115
|
|
110
|
-
|
116
|
+
### Drop
|
111
117
|
|
112
|
-
|
113
|
-
Connected to the database
|
114
|
-
Showing information about the current state of the migrations in the database
|
115
|
-
Migration history schema exists
|
116
|
-
Migration history table exists
|
117
|
-
Migration history:
|
118
|
-
┌─────────┬────┬────────────────────────────────────┬────────────────────────────────────────────────────────────────────┬────────────┬──────────────────────────────┐
|
119
|
-
│ (index) │ id │ name │ hash │ applied_by │ applied_at │
|
120
|
-
├─────────┼────┼────────────────────────────────────┼────────────────────────────────────────────────────────────────────┼────────────┼──────────────────────────────┤
|
121
|
-
│ 0 │ 1 │ 'V0_01__connect_session_table.sql' │ 'f08638e58139ae0e2dda24b1bdba29f3f2128597066a23d2bb382d448bbe9d7e' │ 'postgres' │ '2024-11-23 16:24:50.437496' │
|
122
|
-
│ 1 │ 2 │ 'V0_02__auth.sql' │ '0a4c5df39f03df85cb68ef0b297b913d7c15477fa9dcba13b6e0577d88258a8e' │ 'postgres' │ '2024-11-23 16:24:50.440493' │
|
123
|
-
└─────────┴────┴────────────────────────────────────┴────────────────────────────────────────────────────────────────────┴────────────┴──────────────────────────────┘
|
124
|
-
```
|
118
|
+
Drops the tables, schema and migration history table.
|
125
119
|
|
126
|
-
|
120
|
+
Command:
|
127
121
|
|
128
122
|
```bash
|
129
123
|
npx stepwise-migrations drop \
|
@@ -131,10 +125,16 @@ npx stepwise-migrations drop \
|
|
131
125
|
--schema=myschema
|
132
126
|
```
|
133
127
|
|
134
|
-
|
128
|
+
### Get Script
|
135
129
|
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
130
|
+
Gets the script for the last applied migration.
|
131
|
+
Can get the script for a specific migration if the `--filename` option is provided.
|
132
|
+
|
133
|
+
Command:
|
134
|
+
|
135
|
+
```bash
|
136
|
+
npx stepwise-migrations get-script \
|
137
|
+
--filename v1_users.sql \
|
138
|
+
--connection=postgresql://postgres:postgres@127.0.0.1:5432/mydb \
|
139
|
+
--schema=myschema
|
140
140
|
```
|
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
|
@@ -59,7 +59,6 @@ const dbConnect = (argv) => __awaiter(void 0, void 0, void 0, function* () {
|
|
59
59
|
try {
|
60
60
|
client = yield pool.connect();
|
61
61
|
yield client.query("SELECT 1");
|
62
|
-
console.log("Connected to the database");
|
63
62
|
}
|
64
63
|
catch (error) {
|
65
64
|
console.error("Failed to connect to the database", error);
|
@@ -84,20 +83,40 @@ const dbMigrationHistory = (client, schema) => __awaiter(void 0, void 0, void 0,
|
|
84
83
|
});
|
85
84
|
exports.dbMigrationHistory = dbMigrationHistory;
|
86
85
|
const dbCreateSchema = (client, schema) => __awaiter(void 0, void 0, void 0, function* () {
|
87
|
-
|
86
|
+
process.stdout.write(`Creating schema ${schema}... `);
|
88
87
|
yield client.query(`CREATE SCHEMA IF NOT EXISTS ${schema}`);
|
89
|
-
console.log(`
|
88
|
+
console.log(`done!`);
|
90
89
|
});
|
91
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;
|
92
96
|
const dbCreateHistoryTable = (client, schema) => __awaiter(void 0, void 0, void 0, function* () {
|
93
|
-
|
94
|
-
yield client.query(`
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
97
|
+
process.stdout.write(`Creating migration history table... `);
|
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
|
+
`);
|
115
|
+
console.log(`done!`);
|
102
116
|
});
|
103
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
@@ -35,29 +35,46 @@ const main = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
35
35
|
}
|
36
36
|
const migrationHistory = yield (0, db_1.dbMigrationHistory)(client, schema);
|
37
37
|
const migrationFiles = yield (0, utils_1.readMigrationFiles)(argv.path);
|
38
|
-
console.log(`Found ${migrationFiles.length} migration files`);
|
39
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
|
+
}
|
40
43
|
const migrationsToApply = migrationFiles.slice(migrationHistory.length, migrationHistory.length + nUp);
|
41
|
-
for (const { filename,
|
42
|
-
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);
|
43
46
|
}
|
44
|
-
console.log(
|
45
|
-
|
46
|
-
console.table(yield (0, db_1.dbMigrationHistory)(client, schema));
|
47
|
+
console.log(`All done! Applied ${migrationsToApply.length} migrations`);
|
48
|
+
(0, utils_1.printMigrationHistoryAndUnappliedMigrations)(yield (0, utils_1.readMigrationFiles)(argv.path), yield (0, db_1.dbMigrationHistory)(client, schema));
|
47
49
|
}
|
48
50
|
else if (command === "info") {
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
51
|
+
if (!historySchemaExists) {
|
52
|
+
console.log("Schema does not exist");
|
53
|
+
}
|
54
|
+
if (!tableExists) {
|
55
|
+
console.log("Migration history table does not exist");
|
56
|
+
}
|
57
|
+
if (historySchemaExists && tableExists) {
|
58
|
+
(0, utils_1.printMigrationHistory)(yield (0, db_1.dbMigrationHistory)(client, schema));
|
59
|
+
}
|
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));
|
56
73
|
}
|
57
74
|
else if (command === "drop") {
|
58
|
-
|
75
|
+
process.stdout.write(`Dropping the tables, schema and migration history table... `);
|
59
76
|
yield client.query(`DROP SCHEMA IF EXISTS ${schema} CASCADE`);
|
60
|
-
console.log(
|
77
|
+
console.log(`done!`);
|
61
78
|
}
|
62
79
|
else if (command === "down") {
|
63
80
|
const nDown = argv.ndown || 1;
|
@@ -66,12 +83,31 @@ const main = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
66
83
|
const reverseMigrationHistory = migrationHistory.reverse().slice(0, nDown);
|
67
84
|
const downMigrationFilesToApply = yield (0, utils_1.readDownMigrationFiles)(argv.path, reverseMigrationHistory);
|
68
85
|
(0, migrate_1.validateDownMigrationFiles)(downMigrationFilesToApply, reverseMigrationHistory);
|
69
|
-
for (const { filename,
|
70
|
-
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);
|
71
88
|
}
|
72
|
-
console.log(
|
73
|
-
|
74
|
-
|
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);
|
75
111
|
}
|
76
112
|
client.release();
|
77
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,21 +32,27 @@ 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 {
|
48
|
+
process.stdout.write(`Applying migration ${filename}... `);
|
44
49
|
yield client.query("BEGIN");
|
45
50
|
yield client.query(`SET search_path TO ${schema};
|
46
|
-
${
|
47
|
-
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]);
|
48
54
|
yield client.query("COMMIT");
|
49
|
-
console.log(`
|
55
|
+
console.log(`done!`);
|
50
56
|
}
|
51
57
|
catch (error) {
|
52
58
|
try {
|
@@ -71,14 +77,16 @@ const validateDownMigrationFiles = (downMigrationFilesToApply, reverseMigrationH
|
|
71
77
|
}
|
72
78
|
};
|
73
79
|
exports.validateDownMigrationFiles = validateDownMigrationFiles;
|
74
|
-
const applyDownMigration = (client, schema, filename,
|
80
|
+
const applyDownMigration = (client, schema, filename, script, upFilename) => __awaiter(void 0, void 0, void 0, function* () {
|
75
81
|
try {
|
82
|
+
process.stdout.write(`Applying down migration ${filename}... `);
|
76
83
|
yield client.query("BEGIN");
|
77
84
|
yield client.query(`SET search_path TO ${schema};
|
78
|
-
${
|
85
|
+
${script.toString()}`);
|
79
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]);
|
80
88
|
yield client.query("COMMIT");
|
81
|
-
console.log(`
|
89
|
+
console.log(`done!`);
|
82
90
|
}
|
83
91
|
catch (error) {
|
84
92
|
try {
|
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
|
|
@@ -42,11 +37,10 @@ Options:
|
|
42
37
|
--ndown Number of down migrations to apply (default: 1)
|
43
38
|
|
44
39
|
Example:
|
45
|
-
npx stepwise-migrations \
|
40
|
+
npx stepwise-migrations migrate \
|
46
41
|
--connection=postgresql://postgres:postgres@127.0.0.1:5432/mydatabase \
|
47
42
|
--schema=myschema \
|
48
|
-
--path=./db/migration/
|
49
|
-
migrate
|
43
|
+
--path=./db/migration/
|
50
44
|
`;
|
51
45
|
const validateArgs = (argv) => {
|
52
46
|
const required = ["connection", "schema", "path", "_"];
|
@@ -60,14 +54,6 @@ const validateArgs = (argv) => {
|
|
60
54
|
console.log(exports.usage);
|
61
55
|
process.exit(1);
|
62
56
|
}
|
63
|
-
if (argv._[0] !== "migrate" &&
|
64
|
-
argv._[0] !== "info" &&
|
65
|
-
argv._[0] !== "drop" &&
|
66
|
-
argv._[0] !== "down") {
|
67
|
-
console.error(`Invalid command: ${argv._[0]}`);
|
68
|
-
console.log(exports.usage);
|
69
|
-
process.exit(1);
|
70
|
-
}
|
71
57
|
};
|
72
58
|
exports.validateArgs = validateArgs;
|
73
59
|
const readMigrationFiles = (directory) => __awaiter(void 0, void 0, void 0, function* () {
|
@@ -80,18 +66,41 @@ const readMigrationFiles = (directory) => __awaiter(void 0, void 0, void 0, func
|
|
80
66
|
migrationFiles.sort();
|
81
67
|
const results = [];
|
82
68
|
for (const fullFilePath of migrationFiles) {
|
83
|
-
const
|
69
|
+
const script = yield promises_1.default.readFile(fullFilePath, "utf8");
|
84
70
|
results.push({
|
85
71
|
type: "up",
|
86
72
|
fullFilePath,
|
87
73
|
filename: path_1.default.basename(fullFilePath),
|
88
|
-
|
89
|
-
contents,
|
74
|
+
script,
|
90
75
|
});
|
91
76
|
}
|
92
77
|
return results;
|
93
78
|
});
|
94
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;
|
95
104
|
const fileExists = (path) => __awaiter(void 0, void 0, void 0, function* () {
|
96
105
|
try {
|
97
106
|
return (yield promises_1.default.stat(path)).isFile();
|
@@ -109,13 +118,13 @@ const readDownMigrationFiles = (directory, migrationHistory) => __awaiter(void 0
|
|
109
118
|
console.error(`Down migration file not found: ${fullFilePath}`);
|
110
119
|
process.exit(1);
|
111
120
|
}
|
112
|
-
const
|
121
|
+
const script = yield promises_1.default.readFile(fullFilePath, "utf8");
|
113
122
|
results.push({
|
114
123
|
type: "down",
|
115
124
|
fullFilePath,
|
116
125
|
filename: path_1.default.basename(fullFilePath),
|
117
126
|
upFilename: migration.name,
|
118
|
-
|
127
|
+
script,
|
119
128
|
});
|
120
129
|
}
|
121
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.9",
|
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
|
@@ -19,7 +19,6 @@ export const dbConnect = async (argv: { connection: string; ssl?: string }) => {
|
|
19
19
|
try {
|
20
20
|
client = await pool.connect();
|
21
21
|
await client.query("SELECT 1");
|
22
|
-
console.log("Connected to the database");
|
23
22
|
} catch (error) {
|
24
23
|
console.error("Failed to connect to the database", error);
|
25
24
|
process.exit(1);
|
@@ -57,24 +56,53 @@ export const dbMigrationHistory = async (
|
|
57
56
|
};
|
58
57
|
|
59
58
|
export const dbCreateSchema = async (client: PoolClient, schema: string) => {
|
60
|
-
|
59
|
+
process.stdout.write(`Creating schema ${schema}... `);
|
61
60
|
await client.query(`CREATE SCHEMA IF NOT EXISTS ${schema}`);
|
62
|
-
console.log(`
|
61
|
+
console.log(`done!`);
|
62
|
+
};
|
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[];
|
63
69
|
};
|
64
70
|
|
65
71
|
export const dbCreateHistoryTable = async (
|
66
72
|
client: PoolClient,
|
67
73
|
schema: string
|
68
74
|
) => {
|
69
|
-
|
75
|
+
process.stdout.write(`Creating migration history table... `);
|
70
76
|
await client.query(
|
71
|
-
`
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
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
|
+
`
|
94
|
+
);
|
95
|
+
console.log(`done!`);
|
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]
|
78
106
|
);
|
79
|
-
|
107
|
+
return script.rows[0].script;
|
80
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
|
|
@@ -44,40 +49,68 @@ const main = async () => {
|
|
44
49
|
|
45
50
|
const migrationHistory = await dbMigrationHistory(client, schema);
|
46
51
|
const migrationFiles = await readMigrationFiles(argv.path);
|
47
|
-
console.log(`Found ${migrationFiles.length} migration files`);
|
48
52
|
|
49
53
|
validateMigrationFiles(migrationFiles, migrationHistory);
|
50
54
|
|
55
|
+
if (migrationFiles.length === migrationHistory.length) {
|
56
|
+
console.log("All migrations are already applied");
|
57
|
+
process.exit(0);
|
58
|
+
}
|
59
|
+
|
51
60
|
const migrationsToApply = migrationFiles.slice(
|
52
61
|
migrationHistory.length,
|
53
62
|
migrationHistory.length + nUp
|
54
63
|
);
|
55
64
|
|
56
|
-
for (const { filename,
|
57
|
-
await applyMigration(client, schema, filename,
|
65
|
+
for (const { filename, script } of migrationsToApply) {
|
66
|
+
await applyMigration(client, schema, filename, script);
|
58
67
|
}
|
59
68
|
|
60
|
-
console.log(
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
"Showing information about the current state of the migrations in the database"
|
66
|
-
);
|
67
|
-
console.log(
|
68
|
-
historySchemaExists ? "Schema exists" : "Schema does not exist"
|
69
|
+
console.log(`All done! Applied ${migrationsToApply.length} migrations`);
|
70
|
+
|
71
|
+
printMigrationHistoryAndUnappliedMigrations(
|
72
|
+
await readMigrationFiles(argv.path),
|
73
|
+
await dbMigrationHistory(client, schema)
|
69
74
|
);
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
75
|
+
} else if (command === "info") {
|
76
|
+
if (!historySchemaExists) {
|
77
|
+
console.log("Schema does not exist");
|
78
|
+
}
|
79
|
+
|
80
|
+
if (!tableExists) {
|
81
|
+
console.log("Migration history table does not exist");
|
82
|
+
}
|
83
|
+
|
84
|
+
if (historySchemaExists && tableExists) {
|
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
|
+
);
|
101
|
+
}
|
102
|
+
console.log("Validation passed");
|
103
|
+
|
104
|
+
printMigrationHistoryAndUnappliedMigrations(
|
105
|
+
await readMigrationFiles(argv.path),
|
106
|
+
await dbMigrationHistory(client, schema)
|
74
107
|
);
|
75
|
-
console.log("Migration history:");
|
76
|
-
console.table(await dbMigrationHistory(client, schema));
|
77
108
|
} else if (command === "drop") {
|
78
|
-
|
109
|
+
process.stdout.write(
|
110
|
+
`Dropping the tables, schema and migration history table... `
|
111
|
+
);
|
79
112
|
await client.query(`DROP SCHEMA IF EXISTS ${schema} CASCADE`);
|
80
|
-
console.log(
|
113
|
+
console.log(`done!`);
|
81
114
|
} else if (command === "down") {
|
82
115
|
const nDown = argv.ndown || 1;
|
83
116
|
|
@@ -98,16 +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
|
-
console.log(
|
109
|
-
|
110
|
-
|
137
|
+
console.log(
|
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
|
+
}))
|
158
|
+
);
|
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);
|
111
166
|
}
|
112
167
|
|
113
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,25 +49,30 @@ 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 {
|
55
|
+
process.stdout.write(`Applying migration ${filename}... `);
|
52
56
|
await client.query("BEGIN");
|
53
57
|
|
54
58
|
await client.query(
|
55
59
|
`SET search_path TO ${schema};
|
56
|
-
${
|
60
|
+
${script.toString()}`
|
57
61
|
);
|
58
62
|
|
59
63
|
await client.query(
|
60
|
-
`INSERT INTO ${schema}.stepwise_migrations (name,
|
61
|
-
[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]
|
62
71
|
);
|
63
72
|
|
64
73
|
await client.query("COMMIT");
|
65
74
|
|
66
|
-
console.log(`
|
75
|
+
console.log(`done!`);
|
67
76
|
} catch (error) {
|
68
77
|
try {
|
69
78
|
await client.query("ROLLBACK");
|
@@ -97,15 +106,16 @@ export const applyDownMigration = async (
|
|
97
106
|
client: PoolClient,
|
98
107
|
schema: string,
|
99
108
|
filename: string,
|
100
|
-
|
109
|
+
script: string,
|
101
110
|
upFilename: string
|
102
111
|
) => {
|
103
112
|
try {
|
113
|
+
process.stdout.write(`Applying down migration ${filename}... `);
|
104
114
|
await client.query("BEGIN");
|
105
115
|
|
106
116
|
await client.query(
|
107
117
|
`SET search_path TO ${schema};
|
108
|
-
${
|
118
|
+
${script.toString()}`
|
109
119
|
);
|
110
120
|
|
111
121
|
await client.query(
|
@@ -113,9 +123,14 @@ export const applyDownMigration = async (
|
|
113
123
|
[upFilename]
|
114
124
|
);
|
115
125
|
|
126
|
+
await client.query(
|
127
|
+
`INSERT INTO ${schema}.stepwise_audit (type, name, script) VALUES ($1, $2, $3)`,
|
128
|
+
["down", filename, script]
|
129
|
+
);
|
130
|
+
|
116
131
|
await client.query("COMMIT");
|
117
132
|
|
118
|
-
console.log(`
|
133
|
+
console.log(`done!`);
|
119
134
|
} catch (error) {
|
120
135
|
try {
|
121
136
|
await client.query("ROLLBACK");
|
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
|
|
@@ -29,11 +24,10 @@ Options:
|
|
29
24
|
--ndown Number of down migrations to apply (default: 1)
|
30
25
|
|
31
26
|
Example:
|
32
|
-
npx stepwise-migrations \
|
27
|
+
npx stepwise-migrations migrate \
|
33
28
|
--connection=postgresql://postgres:postgres@127.0.0.1:5432/mydatabase \
|
34
29
|
--schema=myschema \
|
35
|
-
--path=./db/migration/
|
36
|
-
migrate
|
30
|
+
--path=./db/migration/
|
37
31
|
`;
|
38
32
|
|
39
33
|
export const validateArgs = (argv: any) => {
|
@@ -51,16 +45,6 @@ export const validateArgs = (argv: any) => {
|
|
51
45
|
console.log(usage);
|
52
46
|
process.exit(1);
|
53
47
|
}
|
54
|
-
if (
|
55
|
-
argv._[0] !== "migrate" &&
|
56
|
-
argv._[0] !== "info" &&
|
57
|
-
argv._[0] !== "drop" &&
|
58
|
-
argv._[0] !== "down"
|
59
|
-
) {
|
60
|
-
console.error(`Invalid command: ${argv._[0]}`);
|
61
|
-
console.log(usage);
|
62
|
-
process.exit(1);
|
63
|
-
}
|
64
48
|
};
|
65
49
|
|
66
50
|
export const readMigrationFiles = async (directory: string) => {
|
@@ -78,23 +62,54 @@ export const readMigrationFiles = async (directory: string) => {
|
|
78
62
|
type: "up";
|
79
63
|
fullFilePath: string;
|
80
64
|
filename: string;
|
81
|
-
|
82
|
-
contents: string;
|
65
|
+
script: string;
|
83
66
|
}[] = [];
|
84
67
|
for (const fullFilePath of migrationFiles) {
|
85
|
-
const
|
68
|
+
const script = await fs.readFile(fullFilePath, "utf8");
|
86
69
|
|
87
70
|
results.push({
|
88
71
|
type: "up",
|
89
72
|
fullFilePath,
|
90
73
|
filename: path.basename(fullFilePath),
|
91
|
-
|
92
|
-
contents,
|
74
|
+
script,
|
93
75
|
});
|
94
76
|
}
|
95
77
|
return results;
|
96
78
|
};
|
97
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
|
+
|
98
113
|
export const fileExists = async (path: string) => {
|
99
114
|
try {
|
100
115
|
return (await fs.stat(path)).isFile();
|
@@ -113,7 +128,7 @@ export const readDownMigrationFiles = async (
|
|
113
128
|
filename: string;
|
114
129
|
upFilename: string;
|
115
130
|
|
116
|
-
|
131
|
+
script: string;
|
117
132
|
}[] = [];
|
118
133
|
for (const migration of migrationHistory) {
|
119
134
|
const fullFilePath = path.join(
|
@@ -124,13 +139,13 @@ export const readDownMigrationFiles = async (
|
|
124
139
|
console.error(`Down migration file not found: ${fullFilePath}`);
|
125
140
|
process.exit(1);
|
126
141
|
}
|
127
|
-
const
|
142
|
+
const script = await fs.readFile(fullFilePath, "utf8");
|
128
143
|
results.push({
|
129
144
|
type: "down",
|
130
145
|
fullFilePath,
|
131
146
|
filename: path.basename(fullFilePath),
|
132
147
|
upFilename: migration.name,
|
133
|
-
|
148
|
+
script,
|
134
149
|
});
|
135
150
|
}
|
136
151
|
return results;
|