@oliasoft-open-source/node-json-migrator 2.1.0 → 2.3.0-beta-1
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/dist/create/create.js +1 -15
- package/dist/database/database.js +1 -18
- package/dist/executed/executed.js +4 -17
- package/dist/git/git.js +1 -14
- package/dist/glob/glob.js +3 -16
- package/dist/hash/hash.js +1 -5
- package/dist/history/history.js +17 -25
- package/dist/index.js +0 -7
- package/dist/migrate/migrate.js +1 -4
- package/dist/pending/pending.js +0 -10
- package/dist/pipe/pipe.js +3 -6
- package/dist/plan/cached-planned-version.js +0 -4
- package/dist/plan/plan.js +17 -56
- package/dist/plan/planned-version.js +2 -7
- package/dist/plan/validator.js +20 -58
- package/package.json +2 -2
- package/release-notes.md +10 -0
- package/scripts/send-mattermost-message.sh +0 -0
package/dist/create/create.js
CHANGED
|
@@ -4,19 +4,12 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.createMigration = void 0;
|
|
7
|
-
|
|
8
7
|
var _path = _interopRequireDefault(require("path"));
|
|
9
|
-
|
|
10
8
|
var _fs = require("fs");
|
|
11
|
-
|
|
12
9
|
var _chalk = _interopRequireDefault(require("chalk"));
|
|
13
|
-
|
|
14
10
|
var _validator = require("../plan/validator");
|
|
15
|
-
|
|
16
11
|
var _plan = require("../plan/plan");
|
|
17
|
-
|
|
18
12
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
19
|
-
|
|
20
13
|
const templateMigrationFile = `import produce from 'immer';
|
|
21
14
|
//other imports not allowed
|
|
22
15
|
|
|
@@ -24,7 +17,6 @@ export default (dataset) => produce(dataset, (draft) => {
|
|
|
24
17
|
// https://immerjs.github.io/immer/produce#example
|
|
25
18
|
});
|
|
26
19
|
`;
|
|
27
|
-
|
|
28
20
|
const templateTestFile = fileName => `import migrate from './${fileName}';
|
|
29
21
|
|
|
30
22
|
describe('describe dataset change', () => {
|
|
@@ -39,6 +31,7 @@ describe('describe dataset change', () => {
|
|
|
39
31
|
});
|
|
40
32
|
});
|
|
41
33
|
`;
|
|
34
|
+
|
|
42
35
|
/**
|
|
43
36
|
* Generate a new dataset migration
|
|
44
37
|
*
|
|
@@ -46,22 +39,16 @@ describe('describe dataset change', () => {
|
|
|
46
39
|
* @param {String} description description of change-set / migration
|
|
47
40
|
*/
|
|
48
41
|
|
|
49
|
-
|
|
50
42
|
const createMigration = async (directory, description) => {
|
|
51
43
|
if (!(0, _validator.validateFileDescription)(description)) {
|
|
52
44
|
throw new Error('Invalid migration description');
|
|
53
45
|
}
|
|
54
|
-
|
|
55
46
|
try {
|
|
56
47
|
const directoryFullPath = _path.default.resolve(directory, description);
|
|
57
|
-
|
|
58
48
|
const fileName = `${description}.js`;
|
|
59
49
|
const testFileName = `${description}.test.js`;
|
|
60
|
-
|
|
61
50
|
const filePath = _path.default.resolve(directoryFullPath, fileName);
|
|
62
|
-
|
|
63
51
|
const testFilePath = _path.default.resolve(directoryFullPath, testFileName);
|
|
64
|
-
|
|
65
52
|
await _fs.promises.mkdir(directoryFullPath, {
|
|
66
53
|
recursive: true
|
|
67
54
|
});
|
|
@@ -86,5 +73,4 @@ const createMigration = async (directory, description) => {
|
|
|
86
73
|
throw new Error('Unable to create migration file');
|
|
87
74
|
}
|
|
88
75
|
};
|
|
89
|
-
|
|
90
76
|
exports.createMigration = createMigration;
|
|
@@ -4,7 +4,6 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.replaceMigrationsRecords = exports.migrationTablesExist = exports.insertVersions = exports.insertMigrationsRecords = exports.getVersions = exports.getPlanFromVersion = exports.getMigrationRecords = exports.createMigrationsTables = void 0;
|
|
7
|
-
|
|
8
7
|
const migrationTablesExist = (db, entity) => {
|
|
9
8
|
const query = `
|
|
10
9
|
SELECT EXISTS (
|
|
@@ -15,9 +14,7 @@ const migrationTablesExist = (db, entity) => {
|
|
|
15
14
|
`;
|
|
16
15
|
return db.any(query);
|
|
17
16
|
};
|
|
18
|
-
|
|
19
17
|
exports.migrationTablesExist = migrationTablesExist;
|
|
20
|
-
|
|
21
18
|
const createMigrationsTables = (db, entity) => {
|
|
22
19
|
const query = `
|
|
23
20
|
CREATE TABLE IF NOT EXISTS ${entity}_migrations (
|
|
@@ -36,9 +33,7 @@ const createMigrationsTables = (db, entity) => {
|
|
|
36
33
|
`;
|
|
37
34
|
return db.any(query);
|
|
38
35
|
};
|
|
39
|
-
|
|
40
36
|
exports.createMigrationsTables = createMigrationsTables;
|
|
41
|
-
|
|
42
37
|
const getMigrationRecords = (db, entity) => {
|
|
43
38
|
const query = `
|
|
44
39
|
SELECT file_hash, file_name, sequence FROM ${entity}_migrations
|
|
@@ -50,9 +45,7 @@ const getMigrationRecords = (db, entity) => {
|
|
|
50
45
|
sequence: r.sequence
|
|
51
46
|
})));
|
|
52
47
|
};
|
|
53
|
-
|
|
54
48
|
exports.getMigrationRecords = getMigrationRecords;
|
|
55
|
-
|
|
56
49
|
const insertMigrationsRecords = (db, pgpHelpers, entity, migrations) => {
|
|
57
50
|
if (migrations.length) {
|
|
58
51
|
const toInsert = migrations.map(m => ({
|
|
@@ -60,7 +53,6 @@ const insertMigrationsRecords = (db, pgpHelpers, entity, migrations) => {
|
|
|
60
53
|
file_hash: m.fileHash,
|
|
61
54
|
sequence: m.sequence
|
|
62
55
|
})).filter(m => m.file_hash !== null); //can't insert null file hash
|
|
63
|
-
|
|
64
56
|
const cs = new pgpHelpers.ColumnSet(['file_name', 'file_hash', 'sequence'], {
|
|
65
57
|
table: `${entity}_migrations`
|
|
66
58
|
});
|
|
@@ -69,9 +61,7 @@ const insertMigrationsRecords = (db, pgpHelpers, entity, migrations) => {
|
|
|
69
61
|
return db.none(query);
|
|
70
62
|
}
|
|
71
63
|
};
|
|
72
|
-
|
|
73
64
|
exports.insertMigrationsRecords = insertMigrationsRecords;
|
|
74
|
-
|
|
75
65
|
const replaceMigrationsRecords = async (db, pgpHelpers, entity, migrations) => {
|
|
76
66
|
/*
|
|
77
67
|
OW-9043: Table is used for change detection/tracking. DELETE ALL not
|
|
@@ -82,9 +72,7 @@ const replaceMigrationsRecords = async (db, pgpHelpers, entity, migrations) => {
|
|
|
82
72
|
await db.none(truncate);
|
|
83
73
|
await insertMigrationsRecords(db, pgpHelpers, entity, migrations);
|
|
84
74
|
};
|
|
85
|
-
|
|
86
75
|
exports.replaceMigrationsRecords = replaceMigrationsRecords;
|
|
87
|
-
|
|
88
76
|
const insertVersions = (db, pgpHelpers, entity, records) => {
|
|
89
77
|
const cs = new pgpHelpers.ColumnSet(['version', 'plan'], {
|
|
90
78
|
table: `${entity}_versions`
|
|
@@ -93,19 +81,15 @@ const insertVersions = (db, pgpHelpers, entity, records) => {
|
|
|
93
81
|
const query = pgpHelpers.insert(records, cs) + onConflict;
|
|
94
82
|
return db.none(query);
|
|
95
83
|
};
|
|
96
|
-
|
|
97
84
|
exports.insertVersions = insertVersions;
|
|
98
|
-
|
|
99
85
|
const getPlanFromVersion = (db, entity, version) => {
|
|
100
86
|
const query = `
|
|
101
87
|
SELECT plan FROM ${entity}_versions
|
|
102
88
|
WHERE version=$1;
|
|
103
89
|
`;
|
|
104
|
-
return db.oneOrNone(query, [version], r =>
|
|
90
|
+
return db.oneOrNone(query, [version], r => r?.plan || null);
|
|
105
91
|
};
|
|
106
|
-
|
|
107
92
|
exports.getPlanFromVersion = getPlanFromVersion;
|
|
108
|
-
|
|
109
93
|
const getVersions = async (db, entity) => {
|
|
110
94
|
const query = `
|
|
111
95
|
SELECT
|
|
@@ -115,5 +99,4 @@ const getVersions = async (db, entity) => {
|
|
|
115
99
|
`;
|
|
116
100
|
return db.any(query);
|
|
117
101
|
};
|
|
118
|
-
|
|
119
102
|
exports.getVersions = getVersions;
|
|
@@ -4,17 +4,11 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.getExecutedMigrationsFromVersion = void 0;
|
|
7
|
-
|
|
8
7
|
var _chalk = _interopRequireDefault(require("chalk"));
|
|
9
|
-
|
|
10
8
|
var _database = require("../database/database");
|
|
11
|
-
|
|
12
9
|
var _validator = require("../plan/validator");
|
|
13
|
-
|
|
14
10
|
var _git = require("../git/git");
|
|
15
|
-
|
|
16
11
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
17
|
-
|
|
18
12
|
/**
|
|
19
13
|
* Get the executed migrations for a given payload version
|
|
20
14
|
*
|
|
@@ -27,43 +21,37 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
|
|
|
27
21
|
* @param {Array<Object>} plannedMigrations planned migration entries
|
|
28
22
|
* returns Promise<Array<Object>> executed migration entries
|
|
29
23
|
*/
|
|
24
|
+
|
|
30
25
|
const getExecutedMigrationsFromVersion = async (database, pgpHelpers, entity, directory, currentVersion, plannedVersion, plannedMigrations) => {
|
|
31
26
|
if (!currentVersion) {
|
|
32
27
|
//when there is no current version, payload has never been migrated
|
|
33
28
|
//so executed entries is empty
|
|
34
29
|
return [];
|
|
35
30
|
}
|
|
36
|
-
|
|
37
31
|
if (currentVersion === plannedVersion) {
|
|
38
32
|
//when planned version matches current version, there is nothing to migrate
|
|
39
33
|
return plannedMigrations;
|
|
40
34
|
} else {
|
|
41
35
|
//otherwise we look up the historical plan file for that version from the DB
|
|
42
36
|
let planFromVersion = await (0, _database.getPlanFromVersion)(database, entity, currentVersion);
|
|
43
|
-
|
|
44
37
|
if (planFromVersion === null) {
|
|
45
|
-
var _historicalPlans$find;
|
|
46
|
-
|
|
47
38
|
//if not in database, try to get it from the git history
|
|
48
39
|
console.warn(_chalk.default.yellow(`Version not found in ${entity}_version table, trying git history instead`));
|
|
49
40
|
const planFilePath = `${directory}/plan.json`;
|
|
50
41
|
const {
|
|
51
42
|
historicalPlans
|
|
52
|
-
} = await (0, _git.getHistoricalPlansFromGit)(planFilePath);
|
|
53
|
-
|
|
43
|
+
} = await (0, _git.getHistoricalPlansFromGit)(planFilePath);
|
|
44
|
+
//cache the response in the entity versions table
|
|
54
45
|
await (0, _database.insertVersions)(database, pgpHelpers, entity, historicalPlans);
|
|
55
|
-
planFromVersion =
|
|
46
|
+
planFromVersion = historicalPlans.find(p => p.version === currentVersion)?.plan || null;
|
|
56
47
|
}
|
|
57
|
-
|
|
58
48
|
if (planFromVersion !== null) {
|
|
59
49
|
let migrationsFromHistory = null;
|
|
60
|
-
|
|
61
50
|
try {
|
|
62
51
|
migrationsFromHistory = JSON.parse(planFromVersion);
|
|
63
52
|
} catch {
|
|
64
53
|
throw new Error(`Invalid record in ${entity}_version table (cannot parse JSON)`);
|
|
65
54
|
}
|
|
66
|
-
|
|
67
55
|
if (!(0, _validator.validatePlan)(migrationsFromHistory)) {
|
|
68
56
|
throw new Error(`Invalid plan in ${entity}_version table (does not match schema)`);
|
|
69
57
|
} else {
|
|
@@ -74,5 +62,4 @@ const getExecutedMigrationsFromVersion = async (database, pgpHelpers, entity, di
|
|
|
74
62
|
}
|
|
75
63
|
}
|
|
76
64
|
};
|
|
77
|
-
|
|
78
65
|
exports.getExecutedMigrationsFromVersion = getExecutedMigrationsFromVersion;
|
package/dist/git/git.js
CHANGED
|
@@ -4,15 +4,10 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.getPlanRevisionsFromGit = exports.getPlanFromGit = exports.getHistoricalPlansFromGit = exports.exec = void 0;
|
|
7
|
-
|
|
8
7
|
var _chalk = _interopRequireDefault(require("chalk"));
|
|
9
|
-
|
|
10
8
|
var _child_process = require("child_process");
|
|
11
|
-
|
|
12
9
|
var _hash = require("../hash/hash");
|
|
13
|
-
|
|
14
10
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
15
|
-
|
|
16
11
|
const exec = command => new Promise((resolve, reject) => {
|
|
17
12
|
const args = ['-c', command];
|
|
18
13
|
const thread = (0, _child_process.spawn)('bash', args, {
|
|
@@ -31,16 +26,13 @@ const exec = command => new Promise((resolve, reject) => {
|
|
|
31
26
|
reject(stdErr.join(''));
|
|
32
27
|
return;
|
|
33
28
|
}
|
|
34
|
-
|
|
35
29
|
resolve(stdOut.join(''));
|
|
36
30
|
});
|
|
37
31
|
});
|
|
38
|
-
|
|
39
32
|
exports.exec = exec;
|
|
40
|
-
|
|
41
33
|
const getPlanRevisionsFromGit = async planFilePath => {
|
|
42
34
|
const stdOut = await exec(`git rev-list --pretty='format:%H|%ad' HEAD ${planFilePath}`);
|
|
43
|
-
const lines = stdOut.split(/\r\n|\r|\n/).filter(r => r
|
|
35
|
+
const lines = stdOut.split(/\r\n|\r|\n/).filter(r => r?.includes('|'));
|
|
44
36
|
return lines.map(line => {
|
|
45
37
|
const [version, timeStamp] = line.split('|');
|
|
46
38
|
const utcTimeStamp = new Date(timeStamp).toISOString();
|
|
@@ -50,15 +42,11 @@ const getPlanRevisionsFromGit = async planFilePath => {
|
|
|
50
42
|
};
|
|
51
43
|
});
|
|
52
44
|
};
|
|
53
|
-
|
|
54
45
|
exports.getPlanRevisionsFromGit = getPlanRevisionsFromGit;
|
|
55
|
-
|
|
56
46
|
const getPlanFromGit = async (planFilePath, revision) => {
|
|
57
47
|
return exec(`git show ${revision}:${planFilePath}`);
|
|
58
48
|
};
|
|
59
|
-
|
|
60
49
|
exports.getPlanFromGit = getPlanFromGit;
|
|
61
|
-
|
|
62
50
|
const getHistoricalPlansFromGit = async planFilePath => {
|
|
63
51
|
try {
|
|
64
52
|
const revisions = await getPlanRevisionsFromGit(planFilePath);
|
|
@@ -79,5 +67,4 @@ const getHistoricalPlansFromGit = async planFilePath => {
|
|
|
79
67
|
throw new Error('Unable to fetch plan.json history from git');
|
|
80
68
|
}
|
|
81
69
|
};
|
|
82
|
-
|
|
83
70
|
exports.getHistoricalPlansFromGit = getHistoricalPlansFromGit;
|
package/dist/glob/glob.js
CHANGED
|
@@ -4,16 +4,12 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.sortFilePathsByFilename = exports.getUniqueSkippedFileNames = exports.getSkippedFilePaths = exports.getMigrationFilePaths = void 0;
|
|
7
|
-
|
|
8
7
|
var _path = _interopRequireDefault(require("path"));
|
|
9
|
-
|
|
10
8
|
var _globPromise = _interopRequireDefault(require("glob-promise"));
|
|
11
|
-
|
|
12
9
|
var _lodash = require("lodash");
|
|
13
|
-
|
|
14
10
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
15
|
-
|
|
16
11
|
const matchSkip = /(.skip|.skip\d+).js$/g;
|
|
12
|
+
|
|
17
13
|
/**
|
|
18
14
|
* Sorts migration file paths
|
|
19
15
|
*
|
|
@@ -24,48 +20,39 @@ const matchSkip = /(.skip|.skip\d+).js$/g;
|
|
|
24
20
|
const sortFilePathsByFilename = filePaths => (0, _lodash.sortBy)(filePaths, file => {
|
|
25
21
|
return _path.default.parse(file).base;
|
|
26
22
|
});
|
|
23
|
+
|
|
27
24
|
/**
|
|
28
25
|
* Gets paths to entity migrations in a directory
|
|
29
26
|
*
|
|
30
27
|
* @param {String} pathToMigrations
|
|
31
28
|
* @returns {Promise<Array<String>>} matching migration filePaths
|
|
32
29
|
*/
|
|
33
|
-
|
|
34
|
-
|
|
35
30
|
exports.sortFilePathsByFilename = sortFilePathsByFilename;
|
|
36
|
-
|
|
37
31
|
const getMigrationFilePaths = async pathToMigrations => {
|
|
38
32
|
const filePath = pattern => `${pathToMigrations}${pattern}`;
|
|
39
|
-
|
|
40
33
|
const filePaths = await (0, _globPromise.default)(filePath('/**/*.js'), {
|
|
41
34
|
ignore: [filePath('/**/*.skip?([0-9]*).js'), filePath('/**/index.js'), filePath('/**/plan.json'), filePath('/**/*.bundle.js'), filePath('/**/*.test.js'), filePath('/**/*.spec.js'), filePath('/**/__tests__/*'), filePath('/**/__test__/*')]
|
|
42
35
|
});
|
|
43
36
|
return sortFilePathsByFilename(filePaths);
|
|
44
37
|
};
|
|
38
|
+
|
|
45
39
|
/**
|
|
46
40
|
* Gets paths to skipped migrations in a directory
|
|
47
41
|
*
|
|
48
42
|
* @param {String} pathToMigrations
|
|
49
43
|
* @returns {Promise<Array<String>>} skipped migration filePaths
|
|
50
44
|
*/
|
|
51
|
-
|
|
52
|
-
|
|
53
45
|
exports.getMigrationFilePaths = getMigrationFilePaths;
|
|
54
|
-
|
|
55
46
|
const getSkippedFilePaths = async pathToMigrations => {
|
|
56
47
|
const filePath = pattern => `${pathToMigrations}${pattern}`;
|
|
57
|
-
|
|
58
48
|
const filePaths = await (0, _globPromise.default)(filePath('/**/*.skip?([0-9]*).js'), {
|
|
59
49
|
ignore: [filePath('/**/index.js'), filePath('/**/plan.json'), filePath('/**/*.bundle.js'), filePath('/**/*.test.js'), filePath('/**/*.spec.js'), filePath('/**/__tests__/*'), filePath('/**/__test__/*')]
|
|
60
50
|
});
|
|
61
51
|
return sortFilePathsByFilename(filePaths);
|
|
62
52
|
};
|
|
63
|
-
|
|
64
53
|
exports.getSkippedFilePaths = getSkippedFilePaths;
|
|
65
|
-
|
|
66
54
|
const getUniqueSkippedFileNames = skippedPaths => {
|
|
67
55
|
const fileNames = skippedPaths.map(p => _path.default.parse(p).base.replace(matchSkip, '.js'));
|
|
68
56
|
return Array.from(new Set(fileNames));
|
|
69
57
|
};
|
|
70
|
-
|
|
71
58
|
exports.getUniqueSkippedFileNames = getUniqueSkippedFileNames;
|
package/dist/hash/hash.js
CHANGED
|
@@ -4,19 +4,15 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.hash = void 0;
|
|
7
|
-
|
|
8
7
|
var crypto = _interopRequireWildcard(require("crypto"));
|
|
9
|
-
|
|
10
8
|
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
|
|
11
|
-
|
|
12
9
|
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
|
|
13
|
-
|
|
14
10
|
/**
|
|
15
11
|
* Generate SHA256 hash from a string
|
|
16
12
|
*
|
|
17
13
|
* @param {String} string
|
|
18
14
|
* @returns {String} SHA256 hash
|
|
19
15
|
*/
|
|
20
|
-
const hash = string => crypto.createHash('sha256').update(string).digest('hex');
|
|
21
16
|
|
|
17
|
+
const hash = string => crypto.createHash('sha256').update(string).digest('hex');
|
|
22
18
|
exports.hash = hash;
|
package/dist/history/history.js
CHANGED
|
@@ -4,15 +4,10 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.printVersionHistory = void 0;
|
|
7
|
-
|
|
8
7
|
var _chalk = _interopRequireDefault(require("chalk"));
|
|
9
|
-
|
|
10
8
|
var _git = require("../git/git");
|
|
11
|
-
|
|
12
9
|
var _database = require("../database/database");
|
|
13
|
-
|
|
14
10
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
15
|
-
|
|
16
11
|
/**
|
|
17
12
|
* Parse plan.json
|
|
18
13
|
*
|
|
@@ -29,6 +24,7 @@ const parsePlan = plan => {
|
|
|
29
24
|
}
|
|
30
25
|
})();
|
|
31
26
|
};
|
|
27
|
+
|
|
32
28
|
/**
|
|
33
29
|
* @typedef {Object} Configuration
|
|
34
30
|
* @property {string} directory (path to migrations directory)
|
|
@@ -42,8 +38,6 @@ const parsePlan = plan => {
|
|
|
42
38
|
* @param {Object} args
|
|
43
39
|
* @param {Configuration} args.config
|
|
44
40
|
*/
|
|
45
|
-
|
|
46
|
-
|
|
47
41
|
const printVersionHistory = async ({
|
|
48
42
|
config
|
|
49
43
|
}) => {
|
|
@@ -52,35 +46,34 @@ const printVersionHistory = async ({
|
|
|
52
46
|
entity,
|
|
53
47
|
directory
|
|
54
48
|
} = config;
|
|
55
|
-
|
|
56
49
|
try {
|
|
57
|
-
const planFilePath = `${directory}/plan.json`;
|
|
50
|
+
const planFilePath = `${directory}/plan.json`;
|
|
58
51
|
|
|
52
|
+
//read version history from database cache
|
|
59
53
|
const versionHistoryFromDatabase = await (0, _database.getVersions)(database, entity);
|
|
60
54
|
const fullDatabaseVersionHistory = versionHistoryFromDatabase.map(d => {
|
|
61
|
-
const plan = parsePlan(d
|
|
62
|
-
const finalMigration = plan
|
|
55
|
+
const plan = parsePlan(d?.plan);
|
|
56
|
+
const finalMigration = plan?.length ? plan[plan.length - 1] : null;
|
|
63
57
|
return {
|
|
64
|
-
planVersion: d
|
|
65
|
-
lastFileName:
|
|
66
|
-
lastSequence:
|
|
58
|
+
planVersion: d?.version,
|
|
59
|
+
lastFileName: finalMigration?.fileName || null,
|
|
60
|
+
lastSequence: finalMigration?.sequence || null
|
|
67
61
|
};
|
|
68
|
-
});
|
|
62
|
+
});
|
|
69
63
|
|
|
64
|
+
//read full version history from git logs (back to beginning of time)
|
|
70
65
|
const {
|
|
71
66
|
historicalPlans: fullHistoricalPlans
|
|
72
67
|
} = await (0, _git.getHistoricalPlansFromGit)(planFilePath);
|
|
73
68
|
const fullGitVersionHistory = fullHistoricalPlans.map(p => {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
const plan = parsePlan(p === null || p === void 0 ? void 0 : p.plan);
|
|
77
|
-
const finalMigration = plan !== null && plan !== void 0 && plan.length ? plan[plan.length - 1] : null;
|
|
69
|
+
const plan = parsePlan(p?.plan);
|
|
70
|
+
const finalMigration = plan?.length ? plan[plan.length - 1] : null;
|
|
78
71
|
return {
|
|
79
|
-
gitCommitHash: p
|
|
80
|
-
planVersion: p
|
|
81
|
-
timeStamp: p
|
|
82
|
-
lastFileName:
|
|
83
|
-
lastSequence:
|
|
72
|
+
gitCommitHash: p?.revision?.version,
|
|
73
|
+
planVersion: p?.version,
|
|
74
|
+
timeStamp: p?.revision?.timeStamp,
|
|
75
|
+
lastFileName: finalMigration?.fileName || null,
|
|
76
|
+
lastSequence: finalMigration?.sequence || null
|
|
84
77
|
};
|
|
85
78
|
});
|
|
86
79
|
const output = {
|
|
@@ -93,5 +86,4 @@ const printVersionHistory = async ({
|
|
|
93
86
|
throw new Error('Unable to print debug history');
|
|
94
87
|
}
|
|
95
88
|
};
|
|
96
|
-
|
|
97
89
|
exports.printVersionHistory = printVersionHistory;
|
package/dist/index.js
CHANGED
|
@@ -45,17 +45,10 @@ Object.defineProperty(exports, "printVersionHistory", {
|
|
|
45
45
|
return _history.printVersionHistory;
|
|
46
46
|
}
|
|
47
47
|
});
|
|
48
|
-
|
|
49
48
|
var _create = require("./create/create");
|
|
50
|
-
|
|
51
49
|
var _plan = require("./plan/plan");
|
|
52
|
-
|
|
53
50
|
var _plannedVersion = require("./plan/planned-version");
|
|
54
|
-
|
|
55
51
|
var _migrate = require("./migrate/migrate");
|
|
56
|
-
|
|
57
52
|
var _pipe = require("./pipe/pipe");
|
|
58
|
-
|
|
59
53
|
var _database = require("./database/database");
|
|
60
|
-
|
|
61
54
|
var _history = require("./history/history");
|
package/dist/migrate/migrate.js
CHANGED
|
@@ -4,11 +4,8 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.migrate = void 0;
|
|
7
|
-
|
|
8
7
|
var _pipe = require("../pipe/pipe");
|
|
9
|
-
|
|
10
8
|
var _pending = require("../pending/pending");
|
|
11
|
-
|
|
12
9
|
/**
|
|
13
10
|
* @typedef {Array|Object} Payload
|
|
14
11
|
*/
|
|
@@ -43,6 +40,7 @@ var _pending = require("../pending/pending");
|
|
|
43
40
|
* @param {Configuration} args.config
|
|
44
41
|
* @returns Promise<{nextPayload: Payload, nextVersion: string}>
|
|
45
42
|
*/
|
|
43
|
+
|
|
46
44
|
const migrate = async ({
|
|
47
45
|
payload,
|
|
48
46
|
config
|
|
@@ -59,5 +57,4 @@ const migrate = async ({
|
|
|
59
57
|
nextVersion
|
|
60
58
|
};
|
|
61
59
|
};
|
|
62
|
-
|
|
63
60
|
exports.migrate = migrate;
|
package/dist/pending/pending.js
CHANGED
|
@@ -4,15 +4,10 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.getPendingMigrators = exports.getPendingMigrations = void 0;
|
|
7
|
-
|
|
8
7
|
var _chalk = _interopRequireDefault(require("chalk"));
|
|
9
|
-
|
|
10
8
|
var _plan = require("../plan/plan");
|
|
11
|
-
|
|
12
9
|
var _executed = require("../executed/executed");
|
|
13
|
-
|
|
14
10
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
15
|
-
|
|
16
11
|
const getPendingMigrations = ({
|
|
17
12
|
plannedMigrations,
|
|
18
13
|
executedMigrations,
|
|
@@ -20,21 +15,17 @@ const getPendingMigrations = ({
|
|
|
20
15
|
}) => {
|
|
21
16
|
const executedMigrationFileNames = executedMigrations.map(e => e.fileName);
|
|
22
17
|
const pendingMigrations = plannedMigrations.filter(m => !executedMigrationFileNames.includes(m.fileName));
|
|
23
|
-
|
|
24
18
|
if (printPendingFileNames) {
|
|
25
19
|
pendingMigrations.forEach(m => {
|
|
26
20
|
console.log(_chalk.default.gray(` ${m.fileName}`));
|
|
27
21
|
});
|
|
28
22
|
}
|
|
29
|
-
|
|
30
23
|
return pendingMigrations.map(m => ({
|
|
31
24
|
fileName: m.fileName,
|
|
32
25
|
migrator: m.migrator
|
|
33
26
|
}));
|
|
34
27
|
};
|
|
35
|
-
|
|
36
28
|
exports.getPendingMigrations = getPendingMigrations;
|
|
37
|
-
|
|
38
29
|
const getPendingMigrators = async ({
|
|
39
30
|
config
|
|
40
31
|
}) => {
|
|
@@ -64,5 +55,4 @@ const getPendingMigrators = async ({
|
|
|
64
55
|
nextVersion
|
|
65
56
|
};
|
|
66
57
|
};
|
|
67
|
-
|
|
68
58
|
exports.getPendingMigrators = getPendingMigrators;
|
package/dist/pipe/pipe.js
CHANGED
|
@@ -4,11 +4,11 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.pipe = void 0;
|
|
7
|
-
|
|
8
7
|
const errorMessage = (fileName, message, stack) => `Unhandled exception in file ${fileName}: ${message}
|
|
9
8
|
|
|
10
9
|
${stack}
|
|
11
10
|
`;
|
|
11
|
+
|
|
12
12
|
/**
|
|
13
13
|
* Executes the pending migrators on a payload, returns modified payload
|
|
14
14
|
*
|
|
@@ -25,17 +25,14 @@ const errorMessage = (fileName, message, stack) => `Unhandled exception in file
|
|
|
25
25
|
* @returns {Object} migratedPayload
|
|
26
26
|
*/
|
|
27
27
|
|
|
28
|
-
|
|
29
28
|
const pipe = (migrations, payload) => migrations.reduce((v, m) => {
|
|
30
|
-
if (typeof
|
|
29
|
+
if (typeof m?.migrator !== 'function') {
|
|
31
30
|
throw new TypeError('Expected a function');
|
|
32
31
|
}
|
|
33
|
-
|
|
34
32
|
try {
|
|
35
33
|
return m.migrator(v);
|
|
36
34
|
} catch (error) {
|
|
37
|
-
throw new Error(errorMessage(m
|
|
35
|
+
throw new Error(errorMessage(m?.fileName, error.message, error.stack));
|
|
38
36
|
}
|
|
39
37
|
}, payload);
|
|
40
|
-
|
|
41
38
|
exports.pipe = pipe;
|
|
@@ -5,13 +5,9 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.setCachedPlannedVersion = exports.getCachedPlannedVersion = void 0;
|
|
7
7
|
let cachedPlannedVersion = null;
|
|
8
|
-
|
|
9
8
|
const getCachedPlannedVersion = () => cachedPlannedVersion;
|
|
10
|
-
|
|
11
9
|
exports.getCachedPlannedVersion = getCachedPlannedVersion;
|
|
12
|
-
|
|
13
10
|
const setCachedPlannedVersion = version => {
|
|
14
11
|
cachedPlannedVersion = version;
|
|
15
12
|
};
|
|
16
|
-
|
|
17
13
|
exports.setCachedPlannedVersion = setCachedPlannedVersion;
|
package/dist/plan/plan.js
CHANGED
|
@@ -4,30 +4,21 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.writePlan = exports.sortPlanEntries = exports.readPlan = exports.parsePlan = exports.getPlannedMigrations = exports.getNextSequenceString = exports.generatePlanEntries = void 0;
|
|
7
|
-
|
|
8
7
|
var _path = _interopRequireDefault(require("path"));
|
|
9
|
-
|
|
10
8
|
var _fs = require("fs");
|
|
11
|
-
|
|
12
9
|
var _moduleFromString = require("module-from-string");
|
|
13
|
-
|
|
14
10
|
var _hash = require("../hash/hash");
|
|
15
|
-
|
|
16
11
|
var _glob = require("../glob/glob");
|
|
17
|
-
|
|
18
12
|
var _validator = require("./validator");
|
|
19
|
-
|
|
20
13
|
var _database = require("../database/database");
|
|
21
|
-
|
|
22
14
|
var _cachedPlannedVersion = require("./cached-planned-version");
|
|
23
|
-
|
|
24
15
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
25
|
-
|
|
26
16
|
//https://stackoverflow.com/a/31102605/942635
|
|
27
17
|
const sortEntry = entry => Object.keys(entry).sort().reduce((obj, key) => {
|
|
28
18
|
obj[key] = entry[key];
|
|
29
19
|
return obj;
|
|
30
20
|
}, {});
|
|
21
|
+
|
|
31
22
|
/**
|
|
32
23
|
* Sorts the plan entries by sequence string
|
|
33
24
|
*
|
|
@@ -35,44 +26,36 @@ const sortEntry = entry => Object.keys(entry).sort().reduce((obj, key) => {
|
|
|
35
26
|
* returns Array<Object>
|
|
36
27
|
*/
|
|
37
28
|
|
|
38
|
-
|
|
39
29
|
const maxSequence = 1000000;
|
|
40
|
-
|
|
41
|
-
|
|
30
|
+
const sortPlanEntries = plannedMigrations => plannedMigrations
|
|
31
|
+
//dotted sequence sort https://stackoverflow.com/a/40201629/942635
|
|
42
32
|
.sort((a, b) => a.sequence.replace(/\d+/g, n => +n + maxSequence).localeCompare(b.sequence.replace(/\d+/g, n => +n + maxSequence))).map(p => sortEntry(p));
|
|
33
|
+
|
|
43
34
|
/**
|
|
44
35
|
* Get next automatic sequence string
|
|
45
36
|
*
|
|
46
37
|
* @param {String} sortedPlannedMigrations
|
|
47
38
|
* returns String next sequence
|
|
48
39
|
*/
|
|
49
|
-
|
|
50
|
-
|
|
51
40
|
exports.sortPlanEntries = sortPlanEntries;
|
|
52
|
-
|
|
53
41
|
const getNextSequenceString = sortedPlannedMigrations => {
|
|
54
42
|
const lastMigration = sortedPlannedMigrations[sortedPlannedMigrations.length - 1];
|
|
55
|
-
|
|
56
43
|
if (!lastMigration) {
|
|
57
44
|
return '1';
|
|
58
45
|
}
|
|
59
|
-
|
|
60
46
|
const currentLastSequenceNumber = lastMigration.sequence;
|
|
61
47
|
return `${parseInt(`${currentLastSequenceNumber}`, 10) + 1}`;
|
|
62
48
|
};
|
|
49
|
+
|
|
63
50
|
/**
|
|
64
51
|
* Read the plan.json file
|
|
65
52
|
*
|
|
66
53
|
* @param {String} directory path to migrations directory
|
|
67
54
|
* returns Promise<String> plan.json string
|
|
68
55
|
*/
|
|
69
|
-
|
|
70
|
-
|
|
71
56
|
exports.getNextSequenceString = getNextSequenceString;
|
|
72
|
-
|
|
73
57
|
const readPlan = async directory => {
|
|
74
58
|
let plan;
|
|
75
|
-
|
|
76
59
|
try {
|
|
77
60
|
plan = await _fs.promises.readFile(`${directory}/plan.json`, {
|
|
78
61
|
encoding: 'utf8'
|
|
@@ -82,31 +65,27 @@ const readPlan = async directory => {
|
|
|
82
65
|
return '[]';
|
|
83
66
|
}
|
|
84
67
|
};
|
|
68
|
+
|
|
85
69
|
/**
|
|
86
70
|
* Parse the plan.json string into a sorted object
|
|
87
71
|
*
|
|
88
72
|
* @param {String} plan plan.json string
|
|
89
73
|
* returns Array<Object> migration entries from plan.json
|
|
90
74
|
*/
|
|
91
|
-
|
|
92
|
-
|
|
93
75
|
exports.readPlan = readPlan;
|
|
94
|
-
|
|
95
76
|
const parsePlan = plan => {
|
|
96
77
|
let plannedMigrations;
|
|
97
|
-
|
|
98
78
|
try {
|
|
99
79
|
plannedMigrations = JSON.parse(plan);
|
|
100
80
|
} catch {
|
|
101
81
|
throw new Error('Invalid JSON for migrator plan.js (unable to parse file)');
|
|
102
82
|
}
|
|
103
|
-
|
|
104
83
|
if (!(0, _validator.validatePlan)(plannedMigrations)) {
|
|
105
84
|
throw new Error('Invalid JSON for migrator plan.js (does not match schema)');
|
|
106
85
|
}
|
|
107
|
-
|
|
108
86
|
return sortPlanEntries(plannedMigrations);
|
|
109
87
|
};
|
|
88
|
+
|
|
110
89
|
/**
|
|
111
90
|
* Write the plan.json file
|
|
112
91
|
*
|
|
@@ -114,34 +93,27 @@ const parsePlan = plan => {
|
|
|
114
93
|
* @param {String} content string to be written to file
|
|
115
94
|
* returns Promise<>
|
|
116
95
|
*/
|
|
117
|
-
|
|
118
|
-
|
|
119
96
|
exports.parsePlan = parsePlan;
|
|
120
|
-
|
|
121
97
|
const writePlan = async (directory, content) => {
|
|
122
98
|
//todo this is not being written in the correct order
|
|
123
99
|
await _fs.promises.writeFile(_path.default.resolve(`${directory}/plan.json`), content, {
|
|
124
100
|
encoding: 'utf8'
|
|
125
101
|
});
|
|
126
102
|
};
|
|
103
|
+
|
|
127
104
|
/**
|
|
128
105
|
* Generate plan entries
|
|
129
106
|
*
|
|
130
107
|
* @param {Array<Object>} sortedMigrationEntries
|
|
131
108
|
* returns Array<Object>
|
|
132
109
|
*/
|
|
133
|
-
|
|
134
|
-
|
|
135
110
|
exports.writePlan = writePlan;
|
|
136
|
-
|
|
137
111
|
const generatePlanEntries = sortedMigrationEntries => sortedMigrationEntries.map(m => ({
|
|
138
112
|
fileHash: m.fileHash,
|
|
139
113
|
fileName: m.fileName,
|
|
140
114
|
sequence: m.sequence
|
|
141
115
|
}));
|
|
142
|
-
|
|
143
116
|
exports.generatePlanEntries = generatePlanEntries;
|
|
144
|
-
|
|
145
117
|
const getPlannedMigrations = async ({
|
|
146
118
|
config
|
|
147
119
|
}) => {
|
|
@@ -154,7 +126,6 @@ const getPlannedMigrations = async ({
|
|
|
154
126
|
dry,
|
|
155
127
|
importModule = true
|
|
156
128
|
} = config;
|
|
157
|
-
|
|
158
129
|
if (database && !dry) {
|
|
159
130
|
/*
|
|
160
131
|
OW-9043 and OW-9238: The tool tries to self-create its own meta tables
|
|
@@ -164,12 +135,10 @@ const getPlannedMigrations = async ({
|
|
|
164
135
|
because it throws errors.
|
|
165
136
|
*/
|
|
166
137
|
const tablesExist = await (0, _database.migrationTablesExist)(database, entity);
|
|
167
|
-
|
|
168
138
|
if (!tablesExist) {
|
|
169
139
|
await (0, _database.createMigrationsTables)(database, entity);
|
|
170
140
|
}
|
|
171
141
|
}
|
|
172
|
-
|
|
173
142
|
const rawPlan = await readPlan(directory);
|
|
174
143
|
const parsedPlan = parsePlan(rawPlan);
|
|
175
144
|
const plannedMigrations = parsedPlan.map(m => ({
|
|
@@ -180,8 +149,6 @@ const getPlannedMigrations = async ({
|
|
|
180
149
|
const filePaths = await (0, _glob.getMigrationFilePaths)(directory);
|
|
181
150
|
const skippedFilePaths = await (0, _glob.getSkippedFilePaths)(directory);
|
|
182
151
|
const plannedMigrationsWithFileEntries = await Promise.all(plannedMigrations.map(async m => {
|
|
183
|
-
var _await$importFromStri;
|
|
184
|
-
|
|
185
152
|
const {
|
|
186
153
|
fileName
|
|
187
154
|
} = m;
|
|
@@ -190,17 +157,15 @@ const getPlannedMigrations = async ({
|
|
|
190
157
|
const script = filePath ? await _fs.promises.readFile(filePath, {
|
|
191
158
|
encoding: 'utf8'
|
|
192
159
|
}) : null;
|
|
193
|
-
const migrator = script && importModule ? (
|
|
194
|
-
const fileHash = script ? (0, _hash.hash)(script) : null;
|
|
160
|
+
const migrator = script && importModule ? (await (0, _moduleFromString.importFromString)(script))?.default : null;
|
|
161
|
+
const fileHash = script ? (0, _hash.hash)(script) : null;
|
|
195
162
|
|
|
163
|
+
//skipped files
|
|
196
164
|
const skipMatch = '(.skip|.skip\\d+).js$';
|
|
197
|
-
|
|
198
165
|
const nameMatch = _path.default.parse(m.fileName).name;
|
|
199
|
-
|
|
200
166
|
const regex = new RegExp(`${nameMatch}${skipMatch}`, 'g');
|
|
201
167
|
const matchingSkippedFilePaths = skippedFilePaths.filter(f => {
|
|
202
168
|
const name = _path.default.parse(f).base;
|
|
203
|
-
|
|
204
169
|
return name.match(regex);
|
|
205
170
|
});
|
|
206
171
|
const skippedFileHashes = await Promise.all(matchingSkippedFilePaths.map(async skippedFilePath => {
|
|
@@ -209,7 +174,8 @@ const getPlannedMigrations = async ({
|
|
|
209
174
|
});
|
|
210
175
|
return skippedScript ? (0, _hash.hash)(skippedScript) : null;
|
|
211
176
|
}));
|
|
212
|
-
return {
|
|
177
|
+
return {
|
|
178
|
+
...m,
|
|
213
179
|
filePath,
|
|
214
180
|
isValidFileName,
|
|
215
181
|
script,
|
|
@@ -221,32 +187,29 @@ const getPlannedMigrations = async ({
|
|
|
221
187
|
const historicalMigrations = database ? await (0, _database.getMigrationRecords)(database, entity) : [];
|
|
222
188
|
const plannedMigrationsWithHistory = plannedMigrationsWithFileEntries.map(m => {
|
|
223
189
|
const historicalMigration = historicalMigrations.find(h => h.fileName === m.fileName);
|
|
224
|
-
return {
|
|
225
|
-
|
|
226
|
-
|
|
190
|
+
return {
|
|
191
|
+
...m,
|
|
192
|
+
fileHashFromHistory: historicalMigration?.fileHash || null,
|
|
193
|
+
sequenceFromHistory: historicalMigration?.sequence || null
|
|
227
194
|
};
|
|
228
195
|
});
|
|
229
196
|
(0, _validator.throwIfFilesNotFound)(plannedMigrationsWithHistory, importModule);
|
|
230
197
|
(0, _validator.throwIfFileNamesInvalid)(plannedMigrationsWithHistory);
|
|
231
198
|
(0, _validator.throwIfFileNamesNotUnique)(plannedMigrationsWithHistory);
|
|
232
199
|
(0, _validator.throwIfSequenceNotUnique)(plannedMigrationsWithHistory);
|
|
233
|
-
|
|
234
200
|
if (!force) {
|
|
235
201
|
(0, _validator.warnIfFilesHaveBeenRemovedFromPlan)(plannedMigrationsWithHistory, historicalMigrations);
|
|
236
202
|
(0, _validator.throwIfFilesHaveChanged)(plannedMigrationsWithHistory);
|
|
237
203
|
(0, _validator.throwIfSequenceHasChanged)(plannedMigrationsWithHistory);
|
|
238
204
|
}
|
|
239
|
-
|
|
240
205
|
(0, _validator.throwIfSequenceHasIntegerGaps)(plannedMigrationsWithHistory);
|
|
241
206
|
const validatedPlannedMigrations = generatePlanEntries(plannedMigrationsWithHistory);
|
|
242
207
|
const validatedPlan = JSON.stringify(validatedPlannedMigrations, null, 2);
|
|
243
208
|
const nextVersion = (0, _hash.hash)(validatedPlan);
|
|
244
209
|
const planHasChanged = validatedPlan !== rawPlan;
|
|
245
|
-
|
|
246
210
|
if (!dry && planHasChanged) {
|
|
247
211
|
await writePlan(directory, validatedPlan);
|
|
248
212
|
}
|
|
249
|
-
|
|
250
213
|
if (database && !dry) {
|
|
251
214
|
await (0, _database.replaceMigrationsRecords)(database, pgpHelpers, entity, validatedPlannedMigrations);
|
|
252
215
|
const versionRecord = [{
|
|
@@ -255,12 +218,10 @@ const getPlannedMigrations = async ({
|
|
|
255
218
|
}];
|
|
256
219
|
await (0, _database.insertVersions)(database, pgpHelpers, entity, versionRecord);
|
|
257
220
|
}
|
|
258
|
-
|
|
259
221
|
(0, _cachedPlannedVersion.setCachedPlannedVersion)(nextVersion);
|
|
260
222
|
return {
|
|
261
223
|
plannedMigrations: plannedMigrationsWithHistory,
|
|
262
224
|
nextVersion
|
|
263
225
|
};
|
|
264
226
|
};
|
|
265
|
-
|
|
266
227
|
exports.getPlannedMigrations = getPlannedMigrations;
|
|
@@ -4,15 +4,10 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.getPlannedVersion = void 0;
|
|
7
|
-
|
|
8
7
|
var _fs = require("fs");
|
|
9
|
-
|
|
10
8
|
var _hash = require("../hash/hash");
|
|
11
|
-
|
|
12
9
|
var _plan = require("./plan");
|
|
13
|
-
|
|
14
10
|
var _cachedPlannedVersion = require("./cached-planned-version");
|
|
15
|
-
|
|
16
11
|
const getPlannedVersion = async ({
|
|
17
12
|
config
|
|
18
13
|
}) => {
|
|
@@ -25,10 +20,10 @@ const getPlannedVersion = async ({
|
|
|
25
20
|
const plannedVersion = (0, _hash.hash)(currentPlan);
|
|
26
21
|
const cachedPlannedVersion = (0, _cachedPlannedVersion.getCachedPlannedVersion)();
|
|
27
22
|
return cachedPlannedVersion === plannedVersion ? cachedPlannedVersion : (await (0, _plan.getPlannedMigrations)({
|
|
28
|
-
config: {
|
|
23
|
+
config: {
|
|
24
|
+
...config,
|
|
29
25
|
importModule: false
|
|
30
26
|
}
|
|
31
27
|
})).nextVersion;
|
|
32
28
|
};
|
|
33
|
-
|
|
34
29
|
exports.getPlannedVersion = getPlannedVersion;
|
package/dist/plan/validator.js
CHANGED
|
@@ -4,27 +4,21 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.warnIfFilesHaveBeenRemovedFromPlan = exports.validatePlan = exports.validateFileName = exports.validateFileDescription = exports.throwIfSequenceNotUnique = exports.throwIfSequenceHasIntegerGaps = exports.throwIfSequenceHasChanged = exports.throwIfFilesNotFound = exports.throwIfFilesHaveChanged = exports.throwIfFileNamesNotUnique = exports.throwIfFileNamesInvalid = void 0;
|
|
7
|
-
|
|
8
7
|
var _chalk = _interopRequireDefault(require("chalk"));
|
|
9
|
-
|
|
10
8
|
var _lodash = require("lodash");
|
|
11
|
-
|
|
12
9
|
var _ajv = _interopRequireDefault(require("ajv"));
|
|
13
|
-
|
|
14
10
|
var _ajvErrors = _interopRequireDefault(require("ajv-errors"));
|
|
15
|
-
|
|
16
11
|
var _planSchema = _interopRequireDefault(require("./plan.schema.json"));
|
|
17
|
-
|
|
18
12
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
19
|
-
|
|
20
13
|
const isNonEmptyString = input => (0, _lodash.isString)(input) && input.length > 0;
|
|
21
|
-
|
|
22
14
|
const ajv = new _ajv.default({
|
|
23
15
|
allErrors: true
|
|
24
16
|
});
|
|
25
|
-
(0, _ajvErrors.default)(ajv);
|
|
17
|
+
(0, _ajvErrors.default)(ajv);
|
|
26
18
|
|
|
19
|
+
//sequence pattern: https://regex101.com/r/4qHZLm/1
|
|
27
20
|
const planValidator = ajv.compile(_planSchema.default);
|
|
21
|
+
|
|
28
22
|
/**
|
|
29
23
|
* Validates if a plan matches the schema
|
|
30
24
|
*
|
|
@@ -37,92 +31,77 @@ const validatePlan = plan => {
|
|
|
37
31
|
const {
|
|
38
32
|
errors
|
|
39
33
|
} = planValidator;
|
|
40
|
-
|
|
41
|
-
if (errors !== null && errors !== void 0 && errors.length) {
|
|
34
|
+
if (errors?.length) {
|
|
42
35
|
errors.forEach(e => console.error(`${e.message}${e.instancePath ? `at ${e.instancePath}` : ''}`));
|
|
43
36
|
}
|
|
44
|
-
|
|
45
37
|
return valid;
|
|
46
38
|
};
|
|
39
|
+
|
|
47
40
|
/**
|
|
48
41
|
* Validates if file description format is valid
|
|
49
42
|
*
|
|
50
43
|
* @param {String} description
|
|
51
44
|
* @returns {Boolean} valid
|
|
52
45
|
*/
|
|
53
|
-
|
|
54
|
-
|
|
55
46
|
exports.validatePlan = validatePlan;
|
|
56
|
-
|
|
57
47
|
const validateFileDescription = description => {
|
|
58
48
|
if (!description) {
|
|
59
49
|
return false;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
|
|
50
|
+
}
|
|
51
|
+
//https://regex101.com/r/IO0dzF/1
|
|
63
52
|
const validFileName = /^([a-z]+|(([a-z]+-)+[a-z]+))$/g;
|
|
64
53
|
return validFileName.test(description);
|
|
65
54
|
};
|
|
55
|
+
|
|
66
56
|
/**
|
|
67
57
|
* Validates if fileName format is valid
|
|
68
58
|
*
|
|
69
59
|
* @param {String} fileName
|
|
70
60
|
* @returns {Boolean} valid
|
|
71
61
|
*/
|
|
72
|
-
|
|
73
|
-
|
|
74
62
|
exports.validateFileDescription = validateFileDescription;
|
|
75
|
-
|
|
76
63
|
const validateFileName = fileName => {
|
|
77
64
|
//https://regex101.com/r/EX2RlS/1
|
|
78
65
|
const validFileName = /^([a-z]+|(([a-z]+-)+[a-z]+))(.js|.skip.js)$/g;
|
|
79
66
|
return validFileName.test(fileName);
|
|
80
67
|
};
|
|
68
|
+
|
|
81
69
|
/**
|
|
82
70
|
* Throws if file names are invalid
|
|
83
71
|
*
|
|
84
72
|
* @param {Array<Object>} migrationEntries
|
|
85
73
|
*/
|
|
86
|
-
|
|
87
|
-
|
|
88
74
|
exports.validateFileName = validateFileName;
|
|
89
|
-
|
|
90
75
|
const throwIfFileNamesInvalid = fileEntries => {
|
|
91
76
|
const invalidFileNames = fileEntries.filter(f => !f.isValidFileName).map(f => f.fileName);
|
|
92
|
-
|
|
93
77
|
if (invalidFileNames.length) {
|
|
94
78
|
invalidFileNames.forEach(name => console.error(_chalk.default.red(name)));
|
|
95
79
|
throw new Error('Invalid migration filename format (use kebab-case)');
|
|
96
80
|
}
|
|
97
81
|
};
|
|
82
|
+
|
|
98
83
|
/**
|
|
99
84
|
* Warns if files have been removed
|
|
100
85
|
*
|
|
101
86
|
* @param {Array<Object>} migrationEntries
|
|
102
87
|
* @param {Array<Object>} historicalMigrations
|
|
103
88
|
*/
|
|
104
|
-
|
|
105
|
-
|
|
106
89
|
exports.throwIfFileNamesInvalid = throwIfFileNamesInvalid;
|
|
107
|
-
|
|
108
90
|
const warnIfFilesHaveBeenRemovedFromPlan = (migrationEntries, historicalMigrations) => {
|
|
109
91
|
const fileNames = migrationEntries.map(f => f.fileName);
|
|
110
92
|
const deletedFilesNames = historicalMigrations.filter(e => !fileNames.includes(e.fileName)).map(e => e.fileName);
|
|
111
|
-
|
|
112
93
|
if (deletedFilesNames.length) {
|
|
113
94
|
deletedFilesNames.forEach(name => console.warn(_chalk.default.yellow(name)));
|
|
114
95
|
console.warn(_chalk.default.yellow('Previously executed migration files have been deleted from plan.json (rename file to .skip.js and replace with a new file instead)'));
|
|
115
96
|
}
|
|
116
97
|
};
|
|
98
|
+
|
|
117
99
|
/**
|
|
118
100
|
* Throws error if any pre-existing file has changed
|
|
119
101
|
*
|
|
120
102
|
* @param {Array<Object>} migrationEntries
|
|
121
103
|
*/
|
|
122
|
-
|
|
123
|
-
|
|
124
104
|
exports.warnIfFilesHaveBeenRemovedFromPlan = warnIfFilesHaveBeenRemovedFromPlan;
|
|
125
|
-
|
|
126
105
|
const throwIfFilesHaveChanged = migrationEntries => {
|
|
127
106
|
const changedFilesNames = migrationEntries.filter(m => {
|
|
128
107
|
const {
|
|
@@ -135,21 +114,18 @@ const throwIfFilesHaveChanged = migrationEntries => {
|
|
|
135
114
|
const matchesPlan = !isNonEmptyString(fileHashFromPlan) || [fileHash].concat(skippedFileHashes).includes(fileHashFromPlan);
|
|
136
115
|
return !(matchesHistory && matchesPlan);
|
|
137
116
|
}).map(f => f.fileName);
|
|
138
|
-
|
|
139
117
|
if (changedFilesNames.length) {
|
|
140
118
|
changedFilesNames.forEach(name => console.error(_chalk.default.red(name)));
|
|
141
119
|
throw new Error('Not allowed to change migration files (for unreleased local work, you can use the `force` option)');
|
|
142
120
|
}
|
|
143
121
|
};
|
|
122
|
+
|
|
144
123
|
/**
|
|
145
124
|
* Throws error if previous sequence numbers have changed
|
|
146
125
|
*
|
|
147
126
|
* @param {Array<Object>} migrationEntries
|
|
148
127
|
*/
|
|
149
|
-
|
|
150
|
-
|
|
151
128
|
exports.throwIfFilesHaveChanged = throwIfFilesHaveChanged;
|
|
152
|
-
|
|
153
129
|
const throwIfSequenceHasChanged = migrationEntries => {
|
|
154
130
|
const changeSequences = migrationEntries.filter(m => {
|
|
155
131
|
const {
|
|
@@ -159,46 +135,39 @@ const throwIfSequenceHasChanged = migrationEntries => {
|
|
|
159
135
|
const changed = (0, _lodash.isString)(sequenceFromHistory) && sequenceFromHistory !== sequence;
|
|
160
136
|
return changed;
|
|
161
137
|
}).map(f => f.fileName);
|
|
162
|
-
|
|
163
138
|
if (changeSequences.length) {
|
|
164
139
|
changeSequences.forEach(name => console.error(_chalk.default.red(name)));
|
|
165
140
|
throw new Error('Not allowed to change migration sequences in plan.json');
|
|
166
141
|
}
|
|
167
142
|
};
|
|
143
|
+
|
|
168
144
|
/**
|
|
169
145
|
* Throws error if sequence numbers are not unique
|
|
170
146
|
*
|
|
171
147
|
* @param {Array<Object>} migrationEntries
|
|
172
148
|
*/
|
|
173
|
-
|
|
174
|
-
|
|
175
149
|
exports.throwIfSequenceHasChanged = throwIfSequenceHasChanged;
|
|
176
|
-
|
|
177
150
|
const throwIfSequenceNotUnique = migrationEntries => {
|
|
178
151
|
const sequenceNumbers = migrationEntries.map(f => f.sequence);
|
|
179
|
-
const repeatedSequenceNumbers = sequenceNumbers.filter(
|
|
152
|
+
const repeatedSequenceNumbers = sequenceNumbers.filter(
|
|
153
|
+
//https://stackoverflow.com/a/59517965/942635
|
|
180
154
|
(s => v => s.has(v) || !s.add(v))(new Set()));
|
|
181
155
|
const duplicates = migrationEntries.filter(f => repeatedSequenceNumbers.includes(f.sequence)).map(f => f.fileName);
|
|
182
|
-
|
|
183
156
|
if (duplicates.length) {
|
|
184
157
|
duplicates.forEach(fileName => console.error(_chalk.default.red(fileName)));
|
|
185
158
|
throw new Error('Migrations must have unique sequence numbers in plan.json');
|
|
186
159
|
}
|
|
187
160
|
};
|
|
161
|
+
|
|
188
162
|
/**
|
|
189
163
|
* Throws error if sequence numbers are not unique
|
|
190
164
|
*
|
|
191
165
|
* @param {Array<Object>} migrationEntries
|
|
192
166
|
*/
|
|
193
|
-
|
|
194
|
-
|
|
195
167
|
exports.throwIfSequenceNotUnique = throwIfSequenceNotUnique;
|
|
196
|
-
|
|
197
168
|
const throwIfSequenceHasIntegerGaps = migrationEntries => {
|
|
198
169
|
const toInteger = sequence => parseInt(sequence.split('.')[0], 10);
|
|
199
|
-
|
|
200
170
|
const unique = arr => Array.from(new Set(arr));
|
|
201
|
-
|
|
202
171
|
const sequences = migrationEntries.map(s => s.sequence);
|
|
203
172
|
const sequenceIntegers = sequences.map(s => toInteger(s));
|
|
204
173
|
const uniqueSequenceIntegers = unique(sequenceIntegers);
|
|
@@ -208,47 +177,40 @@ const throwIfSequenceHasIntegerGaps = migrationEntries => {
|
|
|
208
177
|
length: max
|
|
209
178
|
}, (v, k) => k + 1);
|
|
210
179
|
const missing = (0, _lodash.xor)(orderedUniqueSequenceIntegers, expected);
|
|
211
|
-
|
|
212
180
|
if (missing.length) {
|
|
213
181
|
throw new Error(`Migration sequence numbers in plan.json have unexpected gaps: ${missing.join(', ')}`);
|
|
214
182
|
}
|
|
215
183
|
};
|
|
184
|
+
|
|
216
185
|
/**
|
|
217
186
|
* Throws error if file names are not unique
|
|
218
187
|
*
|
|
219
188
|
* @param {Array<Object>} migrationEntries
|
|
220
189
|
*/
|
|
221
|
-
|
|
222
|
-
|
|
223
190
|
exports.throwIfSequenceHasIntegerGaps = throwIfSequenceHasIntegerGaps;
|
|
224
|
-
|
|
225
191
|
const throwIfFileNamesNotUnique = migrationEntries => {
|
|
226
192
|
const fileNames = migrationEntries.map(f => f.fileName);
|
|
227
|
-
const repeatedFileNames = fileNames.filter(
|
|
193
|
+
const repeatedFileNames = fileNames.filter(
|
|
194
|
+
//https://stackoverflow.com/a/59517965/942635
|
|
228
195
|
(s => v => s.has(v) || !s.add(v))(new Set()));
|
|
229
|
-
|
|
230
196
|
if (repeatedFileNames.length) {
|
|
231
197
|
repeatedFileNames.forEach(fileName => console.error(_chalk.default.red(fileName)));
|
|
232
198
|
throw new Error('Migration file names must be unique');
|
|
233
199
|
}
|
|
234
200
|
};
|
|
201
|
+
|
|
235
202
|
/**
|
|
236
203
|
* Throws error if files listed in plan.json are found
|
|
237
204
|
*
|
|
238
205
|
* @param {Array<Object>} migrationEntries
|
|
239
206
|
* @param {Boolean} importModule
|
|
240
207
|
*/
|
|
241
|
-
|
|
242
|
-
|
|
243
208
|
exports.throwIfFileNamesNotUnique = throwIfFileNamesNotUnique;
|
|
244
|
-
|
|
245
209
|
const throwIfFilesNotFound = (migrationEntries, importModule) => {
|
|
246
210
|
const migrationsWithoutFiles = migrationEntries.filter(m => !m.script || importModule && !m.script && !m.migrator);
|
|
247
|
-
|
|
248
211
|
if (migrationsWithoutFiles.length) {
|
|
249
212
|
migrationsWithoutFiles.forEach(migration => console.error(_chalk.default.red(migration.fileName)));
|
|
250
213
|
throw new Error('Migration files from plan.json are missing from filesystem');
|
|
251
214
|
}
|
|
252
215
|
};
|
|
253
|
-
|
|
254
216
|
exports.throwIfFilesNotFound = throwIfFilesNotFound;
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oliasoft-open-source/node-json-migrator",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.0-beta-1",
|
|
4
4
|
"description": "A library for JSON migrations",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"build": "npx babel src --ignore 'src/**/*.test.js' --out-dir dist --copy-files --no-copy-ignored",
|
|
7
|
-
"test": "
|
|
7
|
+
"test": "yarn run prettier:check && yarn run lint:check && yarn run test:unit:coverage",
|
|
8
8
|
"test:unit": "jest --silent=true",
|
|
9
9
|
"test:unit:coverage": "jest --silent=true --collectCoverage=true",
|
|
10
10
|
"test:unit:verbose": "jest --silent=false --verbose",
|
package/release-notes.md
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# Node JSON Migrator Release Notes
|
|
2
|
+
|
|
3
|
+
## 2.3.0
|
|
4
|
+
|
|
5
|
+
- switch from npm to yarn (to match other repos)
|
|
6
|
+
- Add optional CI pipeline that automates publishing of beta releases ([OW-10003](https://oliasoft.atlassian.net/browse/OW-10003))
|
|
7
|
+
|
|
8
|
+
## 2.2.0
|
|
9
|
+
|
|
10
|
+
- first publish of release notes
|
|
File without changes
|