stepwise-migrations 1.0.14 → 1.0.15

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.
Files changed (42) hide show
  1. package/README.md +20 -20
  2. package/dist/{db.js → src/db.js} +73 -28
  3. package/dist/src/index.js +146 -0
  4. package/dist/src/state.js +123 -0
  5. package/dist/src/types.js +13 -0
  6. package/dist/src/utils.js +184 -0
  7. package/dist/src/validate.js +1 -0
  8. package/dist/test/index.test.js +129 -0
  9. package/dist/test/utils.js +51 -0
  10. package/docker-compose.yml +21 -0
  11. package/package.json +12 -6
  12. package/src/db.ts +92 -37
  13. package/src/index.ts +115 -80
  14. package/src/state.ts +166 -0
  15. package/src/types.ts +49 -4
  16. package/src/utils.ts +122 -66
  17. package/test/index.test.ts +166 -0
  18. package/test/migrations-invalid/v0_get_number.repeatable.sql +6 -0
  19. package/test/migrations-invalid/v1_first.sql +1 -0
  20. package/test/migrations-invalid/v2_second.sql +4 -0
  21. package/test/migrations-invalid/v2_second.undo.sql +1 -0
  22. package/test/migrations-invalid/v3_third.sql +4 -0
  23. package/test/migrations-invalid/v3_third.undo.sql +1 -0
  24. package/test/migrations-template/v0_get_number.repeatable.sql +6 -0
  25. package/test/migrations-template/v1_first.sql +4 -0
  26. package/test/migrations-template/v2_second.sql +4 -0
  27. package/test/migrations-template/v2_second.undo.sql +1 -0
  28. package/test/migrations-template/v3_third.sql +4 -0
  29. package/test/migrations-template/v3_third.undo.sql +1 -0
  30. package/test/migrations-valid/v0_get_number.repeatable.sql +8 -0
  31. package/test/migrations-valid/v1_first.sql +4 -0
  32. package/test/migrations-valid/v2_second.sql +4 -0
  33. package/test/migrations-valid/v2_second.undo.sql +1 -0
  34. package/test/migrations-valid/v3_third.sql +4 -0
  35. package/test/migrations-valid/v3_third.undo.sql +1 -0
  36. package/test/utils.ts +69 -0
  37. package/tsconfig.json +1 -1
  38. package/dist/index.js +0 -115
  39. package/dist/migrate.js +0 -102
  40. package/dist/types.js +0 -2
  41. package/dist/utils.js +0 -132
  42. package/src/migrate.ts +0 -143
package/dist/index.js DELETED
@@ -1,115 +0,0 @@
1
- #!/usr/bin/env node
2
- "use strict";
3
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
4
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
5
- return new (P || (P = Promise))(function (resolve, reject) {
6
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
7
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
8
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
9
- step((generator = generator.apply(thisArg, _arguments || [])).next());
10
- });
11
- };
12
- var __importDefault = (this && this.__importDefault) || function (mod) {
13
- return (mod && mod.__esModule) ? mod : { "default": mod };
14
- };
15
- Object.defineProperty(exports, "__esModule", { value: true });
16
- const yargs_1 = __importDefault(require("yargs"));
17
- const db_1 = require("./db");
18
- const migrate_1 = require("./migrate");
19
- const utils_1 = require("./utils");
20
- const main = () => __awaiter(void 0, void 0, void 0, function* () {
21
- const argv = (0, yargs_1.default)(process.argv.slice(2)).argv;
22
- (0, utils_1.validateArgs)(argv);
23
- const schema = argv.schema;
24
- const command = argv._[0];
25
- const client = yield (0, db_1.dbConnect)(argv);
26
- const historySchemaExists = yield (0, db_1.dbHistorySchemaExists)(client, schema);
27
- const tableExists = yield (0, db_1.dbTableExists)(client, schema);
28
- if (command === "migrate") {
29
- const nUp = argv.nup || Infinity;
30
- if (!historySchemaExists) {
31
- yield (0, db_1.dbCreateSchema)(client, schema);
32
- }
33
- if (!tableExists) {
34
- yield (0, db_1.dbCreateHistoryTable)(client, schema);
35
- }
36
- const migrationHistory = yield (0, db_1.dbMigrationHistory)(client, schema);
37
- const migrationFiles = yield (0, utils_1.readMigrationFiles)(argv.path);
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
- }
43
- const migrationsToApply = migrationFiles.slice(migrationHistory.length, migrationHistory.length + nUp);
44
- for (const { filename, script } of migrationsToApply) {
45
- yield (0, migrate_1.applyMigration)(client, schema, filename, script);
46
- }
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));
49
- }
50
- else if (command === "info") {
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));
73
- }
74
- else if (command === "drop") {
75
- process.stdout.write(`Dropping the tables, schema and migration history table... `);
76
- yield client.query(`DROP SCHEMA IF EXISTS ${schema} CASCADE`);
77
- console.log(`done!`);
78
- }
79
- else if (command === "down") {
80
- const nDown = argv.ndown || 1;
81
- const migrationHistory = yield (0, db_1.dbMigrationHistory)(client, schema);
82
- (0, migrate_1.validateMigrationFiles)(yield (0, utils_1.readMigrationFiles)(argv.path), migrationHistory, false);
83
- const reverseMigrationHistory = migrationHistory.reverse().slice(0, nDown);
84
- const downMigrationFilesToApply = yield (0, utils_1.readDownMigrationFiles)(argv.path, reverseMigrationHistory);
85
- (0, migrate_1.validateDownMigrationFiles)(downMigrationFilesToApply, reverseMigrationHistory);
86
- for (const { filename, script, upFilename } of downMigrationFilesToApply) {
87
- yield (0, migrate_1.applyDownMigration)(client, schema, filename, script, upFilename);
88
- }
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);
111
- }
112
- client.release();
113
- process.exit(0);
114
- });
115
- main();
package/dist/migrate.js DELETED
@@ -1,102 +0,0 @@
1
- "use strict";
2
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
- return new (P || (P = Promise))(function (resolve, reject) {
5
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
- step((generator = generator.apply(thisArg, _arguments || [])).next());
9
- });
10
- };
11
- var __importDefault = (this && this.__importDefault) || function (mod) {
12
- return (mod && mod.__esModule) ? mod : { "default": mod };
13
- };
14
- Object.defineProperty(exports, "__esModule", { value: true });
15
- exports.applyDownMigration = exports.validateDownMigrationFiles = exports.applyMigration = exports.validateMigrationFiles = void 0;
16
- const git_diff_1 = __importDefault(require("git-diff"));
17
- const validateMigrationFiles = (migrationFiles, migrationHistory, isUp = true) => {
18
- if (migrationFiles.length === 0) {
19
- console.log("No migrations found");
20
- process.exit(0);
21
- }
22
- if (migrationFiles.length < migrationHistory.length) {
23
- console.error("Error: migration history is longer than the number of migration files, aborting.");
24
- process.exit(1);
25
- }
26
- for (let i = 0; i < migrationFiles.length; i++) {
27
- const { filename, script: migrationScript } = migrationFiles[i];
28
- if (i >= migrationHistory.length) {
29
- continue;
30
- }
31
- if (migrationHistory[i].name !== filename) {
32
- console.error(`Error: migration ${filename} has been renamed, aborting.`);
33
- process.exit(1);
34
- }
35
- if (migrationHistory[i].script !== migrationScript) {
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
- }));
41
- process.exit(1);
42
- }
43
- }
44
- };
45
- exports.validateMigrationFiles = validateMigrationFiles;
46
- const applyMigration = (client, schema, filename, script) => __awaiter(void 0, void 0, void 0, function* () {
47
- try {
48
- process.stdout.write(`Applying migration ${filename}... `);
49
- yield client.query("BEGIN");
50
- yield client.query(`SET search_path TO ${schema};
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]);
54
- yield client.query("COMMIT");
55
- console.log(`done!`);
56
- }
57
- catch (error) {
58
- try {
59
- yield client.query("ROLLBACK");
60
- }
61
- catch (error) {
62
- console.error("Error rolling back transaction", error);
63
- }
64
- console.error("Error applying migration", error);
65
- process.exit(1);
66
- }
67
- });
68
- exports.applyMigration = applyMigration;
69
- const validateDownMigrationFiles = (downMigrationFilesToApply, reverseMigrationHistory) => {
70
- for (let i = 0; i < downMigrationFilesToApply.length; i++) {
71
- const { filename } = downMigrationFilesToApply[i];
72
- if (filename.split(".down.sql")[0] !==
73
- reverseMigrationHistory[i].name.split(".sql")[0]) {
74
- console.error(`Migration ${filename} does not match the expected migration ${reverseMigrationHistory[i].name}`);
75
- process.exit(1);
76
- }
77
- }
78
- };
79
- exports.validateDownMigrationFiles = validateDownMigrationFiles;
80
- const applyDownMigration = (client, schema, filename, script, upFilename) => __awaiter(void 0, void 0, void 0, function* () {
81
- try {
82
- process.stdout.write(`Applying down migration ${filename}... `);
83
- yield client.query("BEGIN");
84
- yield client.query(`SET search_path TO ${schema};
85
- ${script.toString()}`);
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]);
88
- yield client.query("COMMIT");
89
- console.log(`done!`);
90
- }
91
- catch (error) {
92
- try {
93
- yield client.query("ROLLBACK");
94
- }
95
- catch (error) {
96
- console.error("Error rolling back transaction", error);
97
- }
98
- console.error("Error applying down migration", error);
99
- process.exit(1);
100
- }
101
- });
102
- exports.applyDownMigration = applyDownMigration;
package/dist/types.js DELETED
@@ -1,2 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
package/dist/utils.js DELETED
@@ -1,132 +0,0 @@
1
- "use strict";
2
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
- return new (P || (P = Promise))(function (resolve, reject) {
5
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
- step((generator = generator.apply(thisArg, _arguments || [])).next());
9
- });
10
- };
11
- var __importDefault = (this && this.__importDefault) || function (mod) {
12
- return (mod && mod.__esModule) ? mod : { "default": mod };
13
- };
14
- Object.defineProperty(exports, "__esModule", { value: true });
15
- exports.readDownMigrationFiles = exports.fileExists = exports.printMigrationHistory = exports.printMigrationHistoryAndUnappliedMigrations = exports.readMigrationFiles = exports.validateArgs = exports.usage = void 0;
16
- const promises_1 = __importDefault(require("fs/promises"));
17
- const path_1 = __importDefault(require("path"));
18
- exports.usage = `
19
- Usage: stepwise-migrations [command] [options]
20
-
21
- Commands:
22
- migrate
23
- Migrate the database to the latest version
24
- down
25
- Rollback the database to the previous version
26
- info
27
- Show information about the current state of the migrations in the database
28
- drop
29
- Drop the tables, schema and migration history table
30
-
31
- Options:
32
- --connection <connection> The connection string to use to connect to the database
33
- --schema <schema> The schema to use for the migrations
34
- --path <path> The path to the migrations directory
35
- --ssl true/false Whether to use SSL for the connection (default: false)
36
- --nup Number of up migrations to apply (default: all)
37
- --ndown Number of down migrations to apply (default: 1)
38
-
39
- Example:
40
- npx stepwise-migrations migrate \
41
- --connection=postgresql://postgres:postgres@127.0.0.1:5432/mydatabase \
42
- --schema=myschema \
43
- --path=./db/migration/
44
- `;
45
- const validateArgs = (argv) => {
46
- const required = ["connection", "schema", "path", "_"];
47
- if (required.some((key) => !(key in argv))) {
48
- console.error("Missing required arguments", required.filter((key) => !(key in argv)));
49
- console.log(exports.usage);
50
- process.exit(1);
51
- }
52
- if (argv._.length !== 1) {
53
- console.error(`Invalid number of arguments: ${argv._.length}`);
54
- console.log(exports.usage);
55
- process.exit(1);
56
- }
57
- };
58
- exports.validateArgs = validateArgs;
59
- const readMigrationFiles = (directory) => __awaiter(void 0, void 0, void 0, function* () {
60
- const files = yield promises_1.default.readdir(directory, { withFileTypes: true });
61
- const migrationFiles = files
62
- .filter((file) => file.isFile() &&
63
- file.name.endsWith(".sql") &&
64
- !file.name.endsWith(".down.sql"))
65
- .map((file) => path_1.default.join(directory, file.name));
66
- migrationFiles.sort();
67
- const results = [];
68
- for (const fullFilePath of migrationFiles) {
69
- const script = yield promises_1.default.readFile(fullFilePath, "utf8");
70
- results.push({
71
- type: "up",
72
- fullFilePath,
73
- filename: path_1.default.basename(fullFilePath),
74
- script,
75
- });
76
- }
77
- return results;
78
- });
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;
104
- const fileExists = (path) => __awaiter(void 0, void 0, void 0, function* () {
105
- try {
106
- return (yield promises_1.default.stat(path)).isFile();
107
- }
108
- catch (error) {
109
- return false;
110
- }
111
- });
112
- exports.fileExists = fileExists;
113
- const readDownMigrationFiles = (directory, migrationHistory) => __awaiter(void 0, void 0, void 0, function* () {
114
- const results = [];
115
- for (const migration of migrationHistory) {
116
- const fullFilePath = path_1.default.join(directory, `${migration.name.split(".sql")[0]}.down.sql`);
117
- if (!(yield (0, exports.fileExists)(fullFilePath))) {
118
- console.error(`Down migration file not found: ${fullFilePath}`);
119
- process.exit(1);
120
- }
121
- const script = yield promises_1.default.readFile(fullFilePath, "utf8");
122
- results.push({
123
- type: "down",
124
- fullFilePath,
125
- filename: path_1.default.basename(fullFilePath),
126
- upFilename: migration.name,
127
- script,
128
- });
129
- }
130
- return results;
131
- });
132
- exports.readDownMigrationFiles = readDownMigrationFiles;
package/src/migrate.ts DELETED
@@ -1,143 +0,0 @@
1
- import gitDiff from "git-diff";
2
- import { PoolClient } from "pg";
3
- import { MigrationRow } from "./types";
4
-
5
- export const validateMigrationFiles = (
6
- migrationFiles: { fullFilePath: string; filename: string; script: string }[],
7
- migrationHistory: MigrationRow[],
8
- isUp: boolean = true
9
- ) => {
10
- if (migrationFiles.length === 0) {
11
- console.log("No migrations found");
12
- process.exit(0);
13
- }
14
-
15
- if (migrationFiles.length < migrationHistory.length) {
16
- console.error(
17
- "Error: migration history is longer than the number of migration files, aborting."
18
- );
19
- process.exit(1);
20
- }
21
-
22
- for (let i = 0; i < migrationFiles.length; i++) {
23
- const { filename, script: migrationScript } = migrationFiles[i];
24
- if (i >= migrationHistory.length) {
25
- continue;
26
- }
27
- if (migrationHistory[i].name !== filename) {
28
- console.error(`Error: migration ${filename} has been renamed, aborting.`);
29
- process.exit(1);
30
- }
31
- if (migrationHistory[i].script !== migrationScript) {
32
- console.error(
33
- `Error: migration ${filename} has been modified, aborting.`
34
- );
35
-
36
- console.log(
37
- gitDiff(migrationHistory[i].script, migrationScript, {
38
- color: true,
39
- noHeaders: true,
40
- })
41
- );
42
-
43
- process.exit(1);
44
- }
45
- }
46
- };
47
-
48
- export const applyMigration = async (
49
- client: PoolClient,
50
- schema: string,
51
- filename: string,
52
- script: string
53
- ) => {
54
- try {
55
- process.stdout.write(`Applying migration ${filename}... `);
56
- await client.query("BEGIN");
57
-
58
- await client.query(
59
- `SET search_path TO ${schema};
60
- ${script.toString()}`
61
- );
62
-
63
- await client.query(
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]
71
- );
72
-
73
- await client.query("COMMIT");
74
-
75
- console.log(`done!`);
76
- } catch (error) {
77
- try {
78
- await client.query("ROLLBACK");
79
- } catch (error) {
80
- console.error("Error rolling back transaction", error);
81
- }
82
- console.error("Error applying migration", error);
83
- process.exit(1);
84
- }
85
- };
86
-
87
- export const validateDownMigrationFiles = (
88
- downMigrationFilesToApply: { filename: string }[],
89
- reverseMigrationHistory: MigrationRow[]
90
- ) => {
91
- for (let i = 0; i < downMigrationFilesToApply.length; i++) {
92
- const { filename } = downMigrationFilesToApply[i];
93
- if (
94
- filename.split(".down.sql")[0] !==
95
- reverseMigrationHistory[i].name.split(".sql")[0]
96
- ) {
97
- console.error(
98
- `Migration ${filename} does not match the expected migration ${reverseMigrationHistory[i].name}`
99
- );
100
- process.exit(1);
101
- }
102
- }
103
- };
104
-
105
- export const applyDownMigration = async (
106
- client: PoolClient,
107
- schema: string,
108
- filename: string,
109
- script: string,
110
- upFilename: string
111
- ) => {
112
- try {
113
- process.stdout.write(`Applying down migration ${filename}... `);
114
- await client.query("BEGIN");
115
-
116
- await client.query(
117
- `SET search_path TO ${schema};
118
- ${script.toString()}`
119
- );
120
-
121
- await client.query(
122
- `DELETE FROM ${schema}.stepwise_migrations WHERE name = $1`,
123
- [upFilename]
124
- );
125
-
126
- await client.query(
127
- `INSERT INTO ${schema}.stepwise_audit (type, name, script) VALUES ($1, $2, $3)`,
128
- ["down", filename, script]
129
- );
130
-
131
- await client.query("COMMIT");
132
-
133
- console.log(`done!`);
134
- } catch (error) {
135
- try {
136
- await client.query("ROLLBACK");
137
- } catch (error) {
138
- console.error("Error rolling back transaction", error);
139
- }
140
- console.error("Error applying down migration", error);
141
- process.exit(1);
142
- }
143
- };