@oliasoft-open-source/node-json-migrator 2.3.10 → 3.0.0-beta-2
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/immer-BsT8CIGL.cjs +678 -0
- package/dist/immer-C8oEWD0M.mjs +669 -0
- package/dist/index.cjs +16553 -0
- package/dist/index.d.cts +81 -0
- package/dist/index.d.mts +81 -0
- package/dist/index.mjs +16526 -0
- package/package.json +49 -57
- package/dist/create/create.js +0 -76
- package/dist/database/database.js +0 -102
- package/dist/executed/executed.js +0 -68
- package/dist/git/git.js +0 -130
- package/dist/glob/glob.js +0 -58
- package/dist/hash/hash.js +0 -16
- package/dist/history/history.js +0 -90
- package/dist/index.js +0 -54
- package/dist/migrate/migrate.js +0 -59
- package/dist/pending/pending.js +0 -73
- package/dist/pipe/pipe.js +0 -38
- package/dist/plan/cached-planned-version.js +0 -25
- package/dist/plan/plan.js +0 -274
- package/dist/plan/plan.schema.json +0 -20
- package/dist/plan/planned-version.js +0 -26
- package/dist/plan/validator.js +0 -216
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oliasoft-open-source/node-json-migrator",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0-beta-2",
|
|
4
4
|
"description": "A library for JSON migrations",
|
|
5
5
|
"homepage": "https://oliasoft-open-source.gitlab.io/node-postgresql-migrator",
|
|
6
6
|
"bugs": {
|
|
@@ -12,77 +12,69 @@
|
|
|
12
12
|
},
|
|
13
13
|
"license": "MIT",
|
|
14
14
|
"author": "Oliasoft AS and contributors",
|
|
15
|
-
"
|
|
15
|
+
"type": "module",
|
|
16
|
+
"exports": {
|
|
17
|
+
"require": {
|
|
18
|
+
"types": "./dist/index.d.cts",
|
|
19
|
+
"default": "./dist/index.cjs"
|
|
20
|
+
},
|
|
21
|
+
"import": {
|
|
22
|
+
"types": "./dist/index.d.mts",
|
|
23
|
+
"default": "./dist/index.mjs"
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"main": "./dist/index.cjs",
|
|
27
|
+
"module": "./dist/index.mjs",
|
|
28
|
+
"types": "./dist/index.d.cts",
|
|
16
29
|
"files": [
|
|
17
30
|
"dist"
|
|
18
31
|
],
|
|
19
32
|
"scripts": {
|
|
20
|
-
"build": "
|
|
21
|
-
"lint:check": "eslint
|
|
22
|
-
"lint:fix": "eslint --fix
|
|
23
|
-
"prepare": "husky
|
|
24
|
-
"prettier:check": "prettier --check \"**/*.{js,
|
|
25
|
-
"prettier:fix": "prettier --write \"**/*.{js,
|
|
26
|
-
"test": "yarn run prettier:check && yarn run lint:check && yarn run test:unit
|
|
27
|
-
"test:
|
|
28
|
-
"test:unit
|
|
29
|
-
"
|
|
33
|
+
"build": "pkgroll --clean-dist && cp ./LICENSE ./dist && cp ./README.md ./dist",
|
|
34
|
+
"lint:check": "npx eslint '**/*.{js,ts,json}'",
|
|
35
|
+
"lint:fix": "npx eslint --fix '**/*.{js,ts,json}'",
|
|
36
|
+
"prepare": "husky",
|
|
37
|
+
"prettier:check": "prettier --check \"**/*.{js,ts,json}\"",
|
|
38
|
+
"prettier:fix": "prettier --write \"**/*.{js,ts,json}\"",
|
|
39
|
+
"test": "yarn run prettier:check && yarn run lint:check && yarn run test:unit",
|
|
40
|
+
"test:integration": "tsx --test \"test/integration.test.ts\"",
|
|
41
|
+
"test:unit": "tsx --test --experimental-test-coverage \"src/**/*.test.ts\"",
|
|
42
|
+
"types:check": "tsc"
|
|
30
43
|
},
|
|
31
44
|
"lint-staged": {
|
|
32
45
|
"*.js": "eslint --cache --fix",
|
|
33
|
-
"*.{js,
|
|
46
|
+
"*.{js,ts,json}": [
|
|
34
47
|
"prettier --write"
|
|
35
48
|
]
|
|
36
49
|
},
|
|
37
50
|
"dependencies": {
|
|
38
|
-
"
|
|
39
|
-
"
|
|
40
|
-
"chalk": "^4.1.2",
|
|
41
|
-
"glob-promise": "^4.2.0",
|
|
42
|
-
"module-from-string": "^3.3.1"
|
|
51
|
+
"module-from-string": "^3.3.1",
|
|
52
|
+
"pg-promise": "^10||^11"
|
|
43
53
|
},
|
|
44
54
|
"devDependencies": {
|
|
45
|
-
"@
|
|
46
|
-
"@
|
|
47
|
-
"@
|
|
48
|
-
"
|
|
49
|
-
"
|
|
50
|
-
"
|
|
51
|
-
"
|
|
52
|
-
"
|
|
53
|
-
"
|
|
54
|
-
"
|
|
55
|
-
"
|
|
56
|
-
"
|
|
57
|
-
"
|
|
58
|
-
"
|
|
59
|
-
"
|
|
60
|
-
"
|
|
61
|
-
"
|
|
62
|
-
"
|
|
63
|
-
"
|
|
64
|
-
"
|
|
65
|
-
"eslint": "^7.32.0",
|
|
66
|
-
"eslint-config-airbnb": "^18.2.1",
|
|
67
|
-
"eslint-config-prettier": "^8.3.0",
|
|
68
|
-
"eslint-import-resolver-alias": "^1.1.2",
|
|
69
|
-
"eslint-plugin-import": "^2.24.2",
|
|
70
|
-
"husky": "^7.0.2",
|
|
71
|
-
"immer": "^9",
|
|
72
|
-
"jest": "^27.1.0",
|
|
73
|
-
"jest-diff": "^27.3.1",
|
|
74
|
-
"jest-matcher-utils": "^27.3.1",
|
|
75
|
-
"lint-staged": "^11.1.2",
|
|
76
|
-
"lodash": "^4",
|
|
77
|
-
"mock-fs": "^5.1.1",
|
|
78
|
-
"pg-mem": "^1.9.17",
|
|
79
|
-
"pg-promise": "^10",
|
|
80
|
-
"prettier": "2.3.2"
|
|
55
|
+
"@types/lodash-es": "^4.17.12",
|
|
56
|
+
"@types/mock-fs": "^4.13.4",
|
|
57
|
+
"@types/node": "^22.13.10",
|
|
58
|
+
"ajv": "^8.17.1",
|
|
59
|
+
"ajv-errors": "^3.0.0",
|
|
60
|
+
"chalk": "^5.4.1",
|
|
61
|
+
"eslint": "^9.22.0",
|
|
62
|
+
"eslint-config-prettier": "^10.1.1",
|
|
63
|
+
"glob": "^11.0.1",
|
|
64
|
+
"husky": "^9.1.7",
|
|
65
|
+
"immer": "^10.1.1",
|
|
66
|
+
"lint-staged": "^15.5.0",
|
|
67
|
+
"lodash-es": "^4.17.21",
|
|
68
|
+
"mock-fs": "^5.5.0",
|
|
69
|
+
"pg-mem": "^3.0.5",
|
|
70
|
+
"pkgroll": "^2.11.2",
|
|
71
|
+
"prettier": "3.5.3",
|
|
72
|
+
"tsx": "^4.19.3",
|
|
73
|
+
"typescript": "^5.8.2",
|
|
74
|
+
"typescript-eslint": "^8.26.1"
|
|
81
75
|
},
|
|
82
76
|
"peerDependencies": {
|
|
83
|
-
"
|
|
84
|
-
"lodash": "^4",
|
|
85
|
-
"pg-promise": "^10"
|
|
77
|
+
"pg-promise": "^10||^11"
|
|
86
78
|
},
|
|
87
79
|
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
|
|
88
80
|
}
|
package/dist/create/create.js
DELETED
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
exports.createMigration = void 0;
|
|
7
|
-
var _path = _interopRequireDefault(require("path"));
|
|
8
|
-
var _fs = require("fs");
|
|
9
|
-
var _chalk = _interopRequireDefault(require("chalk"));
|
|
10
|
-
var _validator = require("../plan/validator");
|
|
11
|
-
var _plan = require("../plan/plan");
|
|
12
|
-
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
13
|
-
const templateMigrationFile = `import produce from 'immer';
|
|
14
|
-
//other imports not allowed
|
|
15
|
-
|
|
16
|
-
export default (dataset) => produce(dataset, (draft) => {
|
|
17
|
-
// https://immerjs.github.io/immer/produce#example
|
|
18
|
-
});
|
|
19
|
-
`;
|
|
20
|
-
const templateTestFile = fileName => `import migrate from './${fileName}';
|
|
21
|
-
|
|
22
|
-
describe('describe dataset change', () => {
|
|
23
|
-
test('describe test', () => {
|
|
24
|
-
//arrange
|
|
25
|
-
const dataset = {};
|
|
26
|
-
|
|
27
|
-
//act
|
|
28
|
-
const nextDataset = migrate(dataset);
|
|
29
|
-
|
|
30
|
-
//assert
|
|
31
|
-
});
|
|
32
|
-
});
|
|
33
|
-
`;
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Generate a new dataset migration
|
|
37
|
-
*
|
|
38
|
-
* @param {String} directory path to migrations directory
|
|
39
|
-
* @param {String} description description of change-set / migration
|
|
40
|
-
*/
|
|
41
|
-
|
|
42
|
-
const createMigration = async (directory, description) => {
|
|
43
|
-
if (!(0, _validator.validateFileDescription)(description)) {
|
|
44
|
-
throw new Error('Invalid migration description');
|
|
45
|
-
}
|
|
46
|
-
try {
|
|
47
|
-
const directoryFullPath = _path.default.resolve(directory, description);
|
|
48
|
-
const fileName = `${description}.js`;
|
|
49
|
-
const testFileName = `${description}.test.js`;
|
|
50
|
-
const filePath = _path.default.resolve(directoryFullPath, fileName);
|
|
51
|
-
const testFilePath = _path.default.resolve(directoryFullPath, testFileName);
|
|
52
|
-
await _fs.promises.mkdir(directoryFullPath, {
|
|
53
|
-
recursive: true
|
|
54
|
-
});
|
|
55
|
-
await _fs.promises.writeFile(filePath, templateMigrationFile, {
|
|
56
|
-
encoding: 'utf8'
|
|
57
|
-
});
|
|
58
|
-
await _fs.promises.writeFile(testFilePath, templateTestFile(fileName), {
|
|
59
|
-
encoding: 'utf8'
|
|
60
|
-
});
|
|
61
|
-
const rawPlan = await (0, _plan.readPlan)(directory);
|
|
62
|
-
const plannedMigrations = (0, _plan.parsePlan)(rawPlan);
|
|
63
|
-
const nextPlannedMigrations = plannedMigrations.concat({
|
|
64
|
-
fileHash: '',
|
|
65
|
-
fileName,
|
|
66
|
-
sequence: (0, _plan.getNextSequenceString)(plannedMigrations)
|
|
67
|
-
});
|
|
68
|
-
const nextPlan = JSON.stringify(nextPlannedMigrations, null, 2);
|
|
69
|
-
await (0, _plan.writePlan)(directory, nextPlan);
|
|
70
|
-
console.log(_chalk.default.green(`Created new dataset migration ${fileName} ✓`));
|
|
71
|
-
} catch (error) {
|
|
72
|
-
console.log(_chalk.default.red(error));
|
|
73
|
-
throw new Error('Unable to create migration file');
|
|
74
|
-
}
|
|
75
|
-
};
|
|
76
|
-
exports.createMigration = createMigration;
|
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
exports.replaceMigrationsRecords = exports.migrationTablesExist = exports.insertVersions = exports.insertMigrationsRecords = exports.getVersions = exports.getPlanFromVersion = exports.getMigrationRecords = exports.createMigrationsTables = void 0;
|
|
7
|
-
const migrationTablesExist = (db, entity) => {
|
|
8
|
-
const query = `
|
|
9
|
-
SELECT EXISTS (
|
|
10
|
-
SELECT FROM information_schema.tables
|
|
11
|
-
WHERE table_schema = 'public'
|
|
12
|
-
AND table_name = '${entity}_migrations'
|
|
13
|
-
)
|
|
14
|
-
`;
|
|
15
|
-
return db.any(query);
|
|
16
|
-
};
|
|
17
|
-
exports.migrationTablesExist = migrationTablesExist;
|
|
18
|
-
const createMigrationsTables = (db, entity) => {
|
|
19
|
-
const query = `
|
|
20
|
-
CREATE TABLE IF NOT EXISTS ${entity}_migrations (
|
|
21
|
-
file_hash TEXT NOT NULL PRIMARY KEY,
|
|
22
|
-
file_name TEXT NOT NULL,
|
|
23
|
-
sequence TEXT NOT NULL,
|
|
24
|
-
created_on TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
25
|
-
UNIQUE (file_name),
|
|
26
|
-
UNIQUE (sequence)
|
|
27
|
-
);
|
|
28
|
-
CREATE TABLE IF NOT EXISTS ${entity}_versions (
|
|
29
|
-
version TEXT NOT NULL PRIMARY KEY,
|
|
30
|
-
plan TEXT NOT NULL,
|
|
31
|
-
created_on TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
32
|
-
);
|
|
33
|
-
`;
|
|
34
|
-
return db.any(query);
|
|
35
|
-
};
|
|
36
|
-
exports.createMigrationsTables = createMigrationsTables;
|
|
37
|
-
const getMigrationRecords = (db, entity) => {
|
|
38
|
-
const query = `
|
|
39
|
-
SELECT file_hash, file_name, sequence FROM ${entity}_migrations
|
|
40
|
-
ORDER BY sequence
|
|
41
|
-
`;
|
|
42
|
-
return db.manyOrNone(query).then(data => data.map(r => ({
|
|
43
|
-
fileHash: r.file_hash,
|
|
44
|
-
fileName: r.file_name,
|
|
45
|
-
sequence: r.sequence
|
|
46
|
-
})));
|
|
47
|
-
};
|
|
48
|
-
exports.getMigrationRecords = getMigrationRecords;
|
|
49
|
-
const insertMigrationsRecords = (db, pgpHelpers, entity, migrations) => {
|
|
50
|
-
if (migrations.length) {
|
|
51
|
-
const toInsert = migrations.map(m => ({
|
|
52
|
-
file_name: m.fileName,
|
|
53
|
-
file_hash: m.fileHash,
|
|
54
|
-
sequence: m.sequence
|
|
55
|
-
})).filter(m => m.file_hash !== null); //can't insert null file hash
|
|
56
|
-
const cs = new pgpHelpers.ColumnSet(['file_name', 'file_hash', 'sequence'], {
|
|
57
|
-
table: `${entity}_migrations`
|
|
58
|
-
});
|
|
59
|
-
const onConflict = ' ON CONFLICT(file_name) DO NOTHING';
|
|
60
|
-
const query = pgpHelpers.insert(toInsert, cs) + onConflict;
|
|
61
|
-
return db.none(query);
|
|
62
|
-
}
|
|
63
|
-
};
|
|
64
|
-
exports.insertMigrationsRecords = insertMigrationsRecords;
|
|
65
|
-
const replaceMigrationsRecords = async (db, pgpHelpers, entity, migrations) => {
|
|
66
|
-
/*
|
|
67
|
-
OW-9043: Table is used for change detection/tracking. DELETE ALL not
|
|
68
|
-
TRUNCATE here so that fewer permissions are needed in downstream
|
|
69
|
-
environments.
|
|
70
|
-
*/
|
|
71
|
-
const truncate = `DELETE FROM ${entity}_migrations`;
|
|
72
|
-
await db.none(truncate);
|
|
73
|
-
await insertMigrationsRecords(db, pgpHelpers, entity, migrations);
|
|
74
|
-
};
|
|
75
|
-
exports.replaceMigrationsRecords = replaceMigrationsRecords;
|
|
76
|
-
const insertVersions = (db, pgpHelpers, entity, records) => {
|
|
77
|
-
const cs = new pgpHelpers.ColumnSet(['version', 'plan'], {
|
|
78
|
-
table: `${entity}_versions`
|
|
79
|
-
});
|
|
80
|
-
const onConflict = ' ON CONFLICT(version) DO NOTHING';
|
|
81
|
-
const query = pgpHelpers.insert(records, cs) + onConflict;
|
|
82
|
-
return db.none(query);
|
|
83
|
-
};
|
|
84
|
-
exports.insertVersions = insertVersions;
|
|
85
|
-
const getPlanFromVersion = (db, entity, version) => {
|
|
86
|
-
const query = `
|
|
87
|
-
SELECT plan FROM ${entity}_versions
|
|
88
|
-
WHERE version=$1;
|
|
89
|
-
`;
|
|
90
|
-
return db.oneOrNone(query, [version], r => r?.plan || null);
|
|
91
|
-
};
|
|
92
|
-
exports.getPlanFromVersion = getPlanFromVersion;
|
|
93
|
-
const getVersions = async (db, entity) => {
|
|
94
|
-
const query = `
|
|
95
|
-
SELECT
|
|
96
|
-
version,
|
|
97
|
-
plan
|
|
98
|
-
FROM ${entity}_versions ORDER BY created_on DESC
|
|
99
|
-
`;
|
|
100
|
-
return db.any(query);
|
|
101
|
-
};
|
|
102
|
-
exports.getVersions = getVersions;
|
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
exports.getExecutedMigrationsFromVersion = void 0;
|
|
7
|
-
var _chalk = _interopRequireDefault(require("chalk"));
|
|
8
|
-
var _database = require("../database/database");
|
|
9
|
-
var _validator = require("../plan/validator");
|
|
10
|
-
var _git = require("../git/git");
|
|
11
|
-
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
12
|
-
/**
|
|
13
|
-
* Get the executed migrations for a given payload version
|
|
14
|
-
*
|
|
15
|
-
* @param {Object} database database connection object
|
|
16
|
-
* @param {Object} pgpHelpers pg-promise helpers
|
|
17
|
-
* @param {String} entity entity name for payload
|
|
18
|
-
* @param {String} directory path to migrations directory
|
|
19
|
-
* @param {String} currentVersion
|
|
20
|
-
* @param {String} plannedVersion
|
|
21
|
-
* @param {Array<Object>} plannedMigrations planned migration entries
|
|
22
|
-
* returns Promise<Array<Object>> executed migration entries
|
|
23
|
-
*/
|
|
24
|
-
|
|
25
|
-
const getExecutedMigrationsFromVersion = async (database, pgpHelpers, entity, directory, currentVersion, plannedVersion, plannedMigrations) => {
|
|
26
|
-
if (!currentVersion) {
|
|
27
|
-
//when there is no current version, payload has never been migrated
|
|
28
|
-
//so executed entries is empty
|
|
29
|
-
return [];
|
|
30
|
-
}
|
|
31
|
-
if (currentVersion === plannedVersion) {
|
|
32
|
-
//when planned version matches current version, there is nothing to migrate
|
|
33
|
-
return plannedMigrations;
|
|
34
|
-
} else {
|
|
35
|
-
//otherwise we look up the historical plan file for that version from the DB
|
|
36
|
-
let planFromVersion = await (0, _database.getPlanFromVersion)(database, entity, currentVersion);
|
|
37
|
-
if (planFromVersion === null) {
|
|
38
|
-
//if not in database, try to get it from the git history
|
|
39
|
-
console.warn(_chalk.default.yellow(`Version not found in ${entity}_version table, trying git history instead`));
|
|
40
|
-
const planFilePath = `${directory}/plan.json`;
|
|
41
|
-
const {
|
|
42
|
-
historicalPlans
|
|
43
|
-
} = await (0, _git.getHistoricalPlansFromGit)(planFilePath);
|
|
44
|
-
//cache the response in the entity versions table
|
|
45
|
-
if (historicalPlans.length) {
|
|
46
|
-
await (0, _database.insertVersions)(database, pgpHelpers, entity, historicalPlans);
|
|
47
|
-
planFromVersion = historicalPlans.find(p => p.version === currentVersion)?.plan || null;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
if (planFromVersion !== null) {
|
|
51
|
-
let migrationsFromHistory = null;
|
|
52
|
-
try {
|
|
53
|
-
migrationsFromHistory = JSON.parse(planFromVersion);
|
|
54
|
-
} catch {
|
|
55
|
-
throw new Error(`Invalid record in ${entity}_version table (cannot parse JSON)`);
|
|
56
|
-
}
|
|
57
|
-
if (!(0, _validator.validatePlan)(migrationsFromHistory)) {
|
|
58
|
-
throw new Error(`Invalid plan in ${entity}_version table (does not match schema)`);
|
|
59
|
-
} else {
|
|
60
|
-
return migrationsFromHistory;
|
|
61
|
-
}
|
|
62
|
-
} else {
|
|
63
|
-
console.error(_chalk.default.red(_git.historyError));
|
|
64
|
-
throw new Error(`Unable to migrate, could not find version history`);
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
};
|
|
68
|
-
exports.getExecutedMigrationsFromVersion = getExecutedMigrationsFromVersion;
|
package/dist/git/git.js
DELETED
|
@@ -1,130 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
exports.terminals = exports.isWindows = exports.historyError = exports.gitAvailable = exports.getTerminal = exports.getPlanRevisionsFromGit = exports.getPlanFromGit = exports.getHistoricalPlansFromGit = exports.exec = exports.commandAvailable = exports.cmdAvailable = exports.bashAvailable = void 0;
|
|
7
|
-
var _chalk = _interopRequireDefault(require("chalk"));
|
|
8
|
-
var _process = _interopRequireDefault(require("process"));
|
|
9
|
-
var _child_process = require("child_process");
|
|
10
|
-
var _hash = require("../hash/hash");
|
|
11
|
-
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
12
|
-
const {
|
|
13
|
-
platform
|
|
14
|
-
} = _process.default;
|
|
15
|
-
const isWindows = platform === 'win32';
|
|
16
|
-
exports.isWindows = isWindows;
|
|
17
|
-
const terminals = Object.freeze({
|
|
18
|
-
bash: 'bash',
|
|
19
|
-
cmd: 'cmd'
|
|
20
|
-
});
|
|
21
|
-
exports.terminals = terminals;
|
|
22
|
-
const historyError = `Unable to fetch git history. Possible reasons:
|
|
23
|
-
- Have you merged latest master branch into your environment (pull latest migrations)?
|
|
24
|
-
- Are you trying to downgrade data (not allowed), e.g. newer data into an older environment?
|
|
25
|
-
- Not allowed to export from TEST and import to PROD
|
|
26
|
-
- Not allowed to export datasets generated from migration feature branches into TEST, PROD or master ENVs
|
|
27
|
-
- On Windows? Do you have git and a terminal (bash or cmd) in your PATH? Try cmder.app, gitforwindows.org, or WSL
|
|
28
|
-
`;
|
|
29
|
-
exports.historyError = historyError;
|
|
30
|
-
const commandAvailable = command => {
|
|
31
|
-
try {
|
|
32
|
-
const exists = isWindows ? 'where' : 'which';
|
|
33
|
-
(0, _child_process.execSync)(`${exists} ${command}`);
|
|
34
|
-
return true;
|
|
35
|
-
} catch (error) {
|
|
36
|
-
return false;
|
|
37
|
-
}
|
|
38
|
-
};
|
|
39
|
-
exports.commandAvailable = commandAvailable;
|
|
40
|
-
const gitAvailable = () => commandAvailable('git');
|
|
41
|
-
exports.gitAvailable = gitAvailable;
|
|
42
|
-
const cmdAvailable = () => isWindows && commandAvailable('cmd');
|
|
43
|
-
exports.cmdAvailable = cmdAvailable;
|
|
44
|
-
const bashAvailable = () => {
|
|
45
|
-
if (commandAvailable('bash')) {
|
|
46
|
-
try {
|
|
47
|
-
const response = (0, _child_process.execSync)('bash --version').toString();
|
|
48
|
-
return response.includes('version');
|
|
49
|
-
} catch (error) {
|
|
50
|
-
return false;
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
return false;
|
|
54
|
-
};
|
|
55
|
-
exports.bashAvailable = bashAvailable;
|
|
56
|
-
const getTerminal = () => {
|
|
57
|
-
const bashActive = bashAvailable();
|
|
58
|
-
const cmdActive = cmdAvailable();
|
|
59
|
-
if (!(bashActive || cmdActive)) {
|
|
60
|
-
throw new Error('bash terminal (or cmd.exe on Windows) must be installed and in PATH');
|
|
61
|
-
}
|
|
62
|
-
return isWindows && !bashActive ? terminals.cmd : terminals.bash;
|
|
63
|
-
};
|
|
64
|
-
exports.getTerminal = getTerminal;
|
|
65
|
-
const exec = (command, terminal) => new Promise((resolve, reject) => {
|
|
66
|
-
const useWinCmd = terminal === terminals.cmd;
|
|
67
|
-
const args = useWinCmd ? ['/c', command] : ['-c', command];
|
|
68
|
-
const thread = (0, _child_process.spawn)(terminal, args, {
|
|
69
|
-
stdio: ['inherit', 'pipe', 'pipe']
|
|
70
|
-
});
|
|
71
|
-
const stdOut = [];
|
|
72
|
-
const stdErr = [];
|
|
73
|
-
thread.stdout.on('data', data => {
|
|
74
|
-
stdOut.push(data.toString());
|
|
75
|
-
});
|
|
76
|
-
thread.stderr.on('data', data => {
|
|
77
|
-
stdErr.push(data.toString());
|
|
78
|
-
});
|
|
79
|
-
thread.on('close', () => {
|
|
80
|
-
if (stdErr.length) {
|
|
81
|
-
reject(stdErr.join(''));
|
|
82
|
-
}
|
|
83
|
-
resolve(stdOut.join(''));
|
|
84
|
-
});
|
|
85
|
-
});
|
|
86
|
-
exports.exec = exec;
|
|
87
|
-
const getPlanRevisionsFromGit = async (planFilePath, terminal) => {
|
|
88
|
-
const command = terminal === terminals.bash ? `git rev-list --pretty="format:%H|%ad" HEAD ${planFilePath}` : `git rev-list --pretty="%H|%ad" HEAD ${planFilePath}`;
|
|
89
|
-
const stdOut = await exec(command, terminal);
|
|
90
|
-
const lines = stdOut.split(/\r\n|\r|\n/).filter(r => r?.includes('|')).map(r => r?.replace(/"/g, '')); //trim extra double quotes in Windows output
|
|
91
|
-
return lines.map(line => {
|
|
92
|
-
const [version, timeStamp] = line.split('|');
|
|
93
|
-
const utcTimeStamp = new Date(timeStamp).toISOString();
|
|
94
|
-
return {
|
|
95
|
-
version,
|
|
96
|
-
timeStamp: utcTimeStamp
|
|
97
|
-
};
|
|
98
|
-
});
|
|
99
|
-
};
|
|
100
|
-
exports.getPlanRevisionsFromGit = getPlanRevisionsFromGit;
|
|
101
|
-
const getPlanFromGit = async (planFilePath, revision, terminal) => {
|
|
102
|
-
return exec(`git show ${revision}:${planFilePath}`, terminal);
|
|
103
|
-
};
|
|
104
|
-
exports.getPlanFromGit = getPlanFromGit;
|
|
105
|
-
const getHistoricalPlansFromGit = async planFilePath => {
|
|
106
|
-
try {
|
|
107
|
-
const gitActive = gitAvailable();
|
|
108
|
-
if (!gitActive) {
|
|
109
|
-
throw new Error('git must be installed and in PATH');
|
|
110
|
-
}
|
|
111
|
-
const terminal = getTerminal();
|
|
112
|
-
const revisions = await getPlanRevisionsFromGit(planFilePath, terminal);
|
|
113
|
-
const historicalPlans = await Promise.all(revisions.map(async revision => {
|
|
114
|
-
const plan = await getPlanFromGit(planFilePath, revision.version, terminal);
|
|
115
|
-
const version = (0, _hash.hash)(plan);
|
|
116
|
-
return {
|
|
117
|
-
revision,
|
|
118
|
-
version,
|
|
119
|
-
plan
|
|
120
|
-
};
|
|
121
|
-
}));
|
|
122
|
-
return {
|
|
123
|
-
historicalPlans
|
|
124
|
-
};
|
|
125
|
-
} catch (error) {
|
|
126
|
-
console.error(_chalk.default.red(error));
|
|
127
|
-
throw new Error('Unable to fetch plan.json history from git');
|
|
128
|
-
}
|
|
129
|
-
};
|
|
130
|
-
exports.getHistoricalPlansFromGit = getHistoricalPlansFromGit;
|
package/dist/glob/glob.js
DELETED
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
exports.sortFilePathsByFilename = exports.getUniqueSkippedFileNames = exports.getSkippedFilePaths = exports.getMigrationFilePaths = void 0;
|
|
7
|
-
var _path = _interopRequireDefault(require("path"));
|
|
8
|
-
var _globPromise = _interopRequireDefault(require("glob-promise"));
|
|
9
|
-
var _lodash = require("lodash");
|
|
10
|
-
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
11
|
-
const matchSkip = /(.skip|.skip\d+).js$/g;
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Sorts migration file paths
|
|
15
|
-
*
|
|
16
|
-
* @param {Array<String>} filePaths
|
|
17
|
-
* @returns {Array<String>} sorted files
|
|
18
|
-
*/
|
|
19
|
-
|
|
20
|
-
const sortFilePathsByFilename = filePaths => (0, _lodash.sortBy)(filePaths, file => {
|
|
21
|
-
return _path.default.parse(file).base;
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Gets paths to entity migrations in a directory
|
|
26
|
-
*
|
|
27
|
-
* @param {String} pathToMigrations
|
|
28
|
-
* @returns {Promise<Array<String>>} matching migration filePaths
|
|
29
|
-
*/
|
|
30
|
-
exports.sortFilePathsByFilename = sortFilePathsByFilename;
|
|
31
|
-
const getMigrationFilePaths = async pathToMigrations => {
|
|
32
|
-
const filePath = pattern => `${pathToMigrations}${pattern}`;
|
|
33
|
-
const filePaths = await (0, _globPromise.default)(filePath('/**/*.js'), {
|
|
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__/*')]
|
|
35
|
-
});
|
|
36
|
-
return sortFilePathsByFilename(filePaths);
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Gets paths to skipped migrations in a directory
|
|
41
|
-
*
|
|
42
|
-
* @param {String} pathToMigrations
|
|
43
|
-
* @returns {Promise<Array<String>>} skipped migration filePaths
|
|
44
|
-
*/
|
|
45
|
-
exports.getMigrationFilePaths = getMigrationFilePaths;
|
|
46
|
-
const getSkippedFilePaths = async pathToMigrations => {
|
|
47
|
-
const filePath = pattern => `${pathToMigrations}${pattern}`;
|
|
48
|
-
const filePaths = await (0, _globPromise.default)(filePath('/**/*.skip?([0-9]*).js'), {
|
|
49
|
-
ignore: [filePath('/**/index.js'), filePath('/**/plan.json'), filePath('/**/*.bundle.js'), filePath('/**/*.test.js'), filePath('/**/*.spec.js'), filePath('/**/__tests__/*'), filePath('/**/__test__/*')]
|
|
50
|
-
});
|
|
51
|
-
return sortFilePathsByFilename(filePaths);
|
|
52
|
-
};
|
|
53
|
-
exports.getSkippedFilePaths = getSkippedFilePaths;
|
|
54
|
-
const getUniqueSkippedFileNames = skippedPaths => {
|
|
55
|
-
const fileNames = skippedPaths.map(p => _path.default.parse(p).base.replace(matchSkip, '.js'));
|
|
56
|
-
return Array.from(new Set(fileNames));
|
|
57
|
-
};
|
|
58
|
-
exports.getUniqueSkippedFileNames = getUniqueSkippedFileNames;
|
package/dist/hash/hash.js
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
exports.hash = void 0;
|
|
7
|
-
var _crypto = require("crypto");
|
|
8
|
-
/**
|
|
9
|
-
* Generate SHA256 hash from a string
|
|
10
|
-
*
|
|
11
|
-
* @param {String} string
|
|
12
|
-
* @returns {String} SHA256 hash
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
const hash = string => (0, _crypto.createHash)('sha256').update(string).digest('hex');
|
|
16
|
-
exports.hash = hash;
|
package/dist/history/history.js
DELETED
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
exports.printVersionHistory = void 0;
|
|
7
|
-
var _chalk = _interopRequireDefault(require("chalk"));
|
|
8
|
-
var _git = require("../git/git");
|
|
9
|
-
var _database = require("../database/database");
|
|
10
|
-
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
11
|
-
/**
|
|
12
|
-
* Parse plan.json
|
|
13
|
-
*
|
|
14
|
-
* @param {string} plan
|
|
15
|
-
* @returns object|null parsed plan
|
|
16
|
-
*/
|
|
17
|
-
const parsePlan = plan => {
|
|
18
|
-
return (() => {
|
|
19
|
-
try {
|
|
20
|
-
return JSON.parse(plan);
|
|
21
|
-
} catch (e) {
|
|
22
|
-
console.error('Unable to parse plan.json');
|
|
23
|
-
return null;
|
|
24
|
-
}
|
|
25
|
-
})();
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* @typedef {Object} Configuration
|
|
30
|
-
* @property {string} directory (path to migrations directory)
|
|
31
|
-
* @property {string} entity (entity name for payload, e.g. dataset)
|
|
32
|
-
* @property {Object} database (connected pg-promise database)
|
|
33
|
-
*/
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Prints the version history of plan.json (for debugging)
|
|
37
|
-
*
|
|
38
|
-
* @param {Object} args
|
|
39
|
-
* @param {Configuration} args.config
|
|
40
|
-
*/
|
|
41
|
-
const printVersionHistory = async ({
|
|
42
|
-
config
|
|
43
|
-
}) => {
|
|
44
|
-
const {
|
|
45
|
-
database,
|
|
46
|
-
entity,
|
|
47
|
-
directory
|
|
48
|
-
} = config;
|
|
49
|
-
try {
|
|
50
|
-
const planFilePath = `${directory}/plan.json`;
|
|
51
|
-
|
|
52
|
-
//read version history from database cache
|
|
53
|
-
const versionHistoryFromDatabase = await (0, _database.getVersions)(database, entity);
|
|
54
|
-
const fullDatabaseVersionHistory = versionHistoryFromDatabase.map(d => {
|
|
55
|
-
const plan = parsePlan(d?.plan);
|
|
56
|
-
const finalMigration = plan?.length ? plan[plan.length - 1] : null;
|
|
57
|
-
return {
|
|
58
|
-
planVersion: d?.version,
|
|
59
|
-
lastFileName: finalMigration?.fileName || null,
|
|
60
|
-
lastSequence: finalMigration?.sequence || null
|
|
61
|
-
};
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
//read full version history from git logs (back to beginning of time)
|
|
65
|
-
const {
|
|
66
|
-
historicalPlans: fullHistoricalPlans
|
|
67
|
-
} = await (0, _git.getHistoricalPlansFromGit)(planFilePath);
|
|
68
|
-
const fullGitVersionHistory = fullHistoricalPlans.map(p => {
|
|
69
|
-
const plan = parsePlan(p?.plan);
|
|
70
|
-
const finalMigration = plan?.length ? plan[plan.length - 1] : null;
|
|
71
|
-
return {
|
|
72
|
-
gitCommitHash: p?.revision?.version,
|
|
73
|
-
planVersion: p?.version,
|
|
74
|
-
timeStamp: p?.revision?.timeStamp,
|
|
75
|
-
lastFileName: finalMigration?.fileName || null,
|
|
76
|
-
lastSequence: finalMigration?.sequence || null
|
|
77
|
-
};
|
|
78
|
-
});
|
|
79
|
-
const output = {
|
|
80
|
-
fullDatabaseVersionHistory,
|
|
81
|
-
fullGitVersionHistory
|
|
82
|
-
};
|
|
83
|
-
console.log(JSON.stringify(output, null, 2));
|
|
84
|
-
} catch (error) {
|
|
85
|
-
console.error(_chalk.default.red(error));
|
|
86
|
-
console.error(_chalk.default.red(_git.historyError));
|
|
87
|
-
throw new Error('Unable to print debug history');
|
|
88
|
-
}
|
|
89
|
-
};
|
|
90
|
-
exports.printVersionHistory = printVersionHistory;
|