3 const fs
= require('fs');
4 const path
= require('path');
7 * Utility functions for wrangling schema migrations.
8 * This mostly just deals with sorting and comparing 'x.y.z' version
9 * strings, with some presumptions about directory layouts and whatnot.
13 * @typedef {object} SchemaVersionObject
14 * @property {number} major major
15 * @property {number} minor minor
16 * @property {number} patch patch
21 * Split a dotted version string into parts.
22 * @param {string} v version string
23 * @returns {SchemaVersionObject} version object
25 function schemaVersionStringToObject(v
) {
26 const [ major
, minor
, patch
] = v
.split('.', 3).map((x
) => parseInt(x
, 10));
27 return { major
, minor
, patch
};
32 * Render a version object numerically.
33 * Assumes no part will be greater than 1000.
34 * @param {SchemaVersionObject} v version object
35 * @returns {number} number
37 function schemaVersionObjectToNumber(v
) {
39 return parseInt(v
.major
) * vScale
* vScale
+ parseInt(v
.minor
) * vScale
+ parseInt(v
.patch
);
44 * Convert dotted version string into number.
45 * @param {string} v version string
46 * @returns {number} number
48 function schemaVersionStringToNumber(v
) {
49 return schemaVersionObjectToNumber(schemaVersionStringToObject(v
));
54 * Version string comparison, for sorting.
55 * @param {string} a version string
56 * @param {string} b version string
57 * @returns {number} cmp
59 function schemaVersionStringCmp(a
, b
) {
60 return schemaVersionStringToNumber(a
) - schemaVersionStringToNumber(b
);
65 * Check if an entry in a directory is a directory containing a migration file.
66 * @param {string} schemaDir schema dir
67 * @param {string} name name
68 * @param {string} migrationFile migration file
69 * @returns {boolean} is
71 function isSchemaMigrationDirectory(schemaDir
, name
, migrationFile
= 'apply.sql') {
72 // eslint-disable-next-line security/detect-non-literal-fs-filename
73 const nameStat
= fs
.statSync(path
.join(schemaDir
, name
));
74 if (nameStat
.isDirectory()) {
77 // eslint-disable-next-line security/detect-non-literal-fs-filename
78 applyStat
= fs
.statSync(path
.join(schemaDir
, name
, migrationFile
));
79 return applyStat
.isFile();
80 } catch (e
) { // eslint-disable-line no-unused-vars
89 * Return an array of schema migration directory names within engineDir,
90 * sorted in increasing order.
91 * @param {string} engineDir path to implementation
92 * @returns {string[]} versions
94 function allSchemaVersions(engineDir
) {
95 const schemaDir
= path
.join(engineDir
, 'sql', 'schema');
96 // eslint-disable-next-line security/detect-non-literal-fs-filename
97 const availableVersions
= fs
.readdirSync(schemaDir
).filter((d
) => isSchemaMigrationDirectory(schemaDir
, d
));
98 availableVersions
.sort(schemaVersionStringCmp
);
99 return availableVersions
;
104 * Return an array of schema migration directory names within engineDir,
105 * which are within supported range, and are greater than the current
106 * @param {string} engineDir path to implementation
107 * @param {SchemaVersionObject} current version
108 * @param {object} supported range of supported versions
109 * @param {SchemaVersionObject} supported.min minimum version
110 * @param {SchemaVersionObject} supported.max maximum version
111 * @returns {string[]} applicable versions
113 function unappliedSchemaVersions(engineDir
, current
, supported
) {
114 const min
= schemaVersionObjectToNumber(supported
.min
);
115 const max
= schemaVersionObjectToNumber(supported
.max
);
116 const cur
= schemaVersionObjectToNumber(current
);
117 const available
= allSchemaVersions(engineDir
);
118 return available
.filter((a
) => {
119 a
= schemaVersionStringToNumber(a
);
120 return a
>= min
&& a
<= max
&& a
> cur
;
126 schemaVersionStringToObject
,
127 schemaVersionObjectToNumber
,
128 schemaVersionStringToNumber
,
129 schemaVersionStringCmp
,
130 isSchemaMigrationDirectory
,
132 unappliedSchemaVersions
,