adapt-migrations 1.2.0 → 1.3.0
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/.eslintignore +1 -1
- package/.eslintrc.json +14 -14
- package/.github/CONTRIBUTING.md +8 -0
- package/.github/ISSUE_TEMPLATE/config.yml +1 -0
- package/.github/ISSUE_TEMPLATE/issue_template.md +23 -0
- package/.github/ISSUE_TEMPLATE.md +17 -0
- package/.github/pull_request_template.md +25 -0
- package/.github/workflows/addtomainproject.yml +19 -0
- package/.github/workflows/releases.yml +25 -0
- package/README.md +106 -106
- package/api/commands.js +33 -33
- package/api/data.js +26 -26
- package/api/describe.js +12 -12
- package/api/errors.js +28 -28
- package/api/helpers.js +17 -17
- package/api/plugins.js +47 -47
- package/api/tests.js +56 -57
- package/api/where.js +46 -46
- package/examples/migrations.js +159 -159
- package/examples/script.js +78 -78
- package/index.js +70 -70
- package/lib/CacheManager.js +95 -95
- package/lib/Journal.js +303 -303
- package/lib/Logger.js +91 -91
- package/lib/Task.js +386 -381
- package/lib/TaskContext.js +27 -27
- package/lib/TaskTest.js +21 -19
- package/lib/lifecycle.js +50 -50
- package/package.json +54 -18
package/api/plugins.js
CHANGED
|
@@ -1,47 +1,47 @@
|
|
|
1
|
-
import { deferOrRunWrap, successStopOrErrorWrap } from '../lib/lifecycle.js'
|
|
2
|
-
import { valid } from 'semver'
|
|
3
|
-
|
|
4
|
-
export function removePlugin (description, config) {
|
|
5
|
-
return deferOrRunWrap(function (context) {
|
|
6
|
-
return successStopOrErrorWrap('removePlugin', description, async () => {
|
|
7
|
-
if (!description || !config) throw new Error('removePlugin - incorrectly configured')
|
|
8
|
-
|
|
9
|
-
context.fromPlugins = context.fromPlugins.filter(plugin => plugin.name !== config.name)
|
|
10
|
-
|
|
11
|
-
return true
|
|
12
|
-
})
|
|
13
|
-
}, { description, type: 'action' })
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
export function addPlugin (description, config) {
|
|
17
|
-
return deferOrRunWrap(function (context) {
|
|
18
|
-
return successStopOrErrorWrap('addPlugin', description, async () => {
|
|
19
|
-
if (!description || !config) throw new Error('addPlugin - incorrectly configured')
|
|
20
|
-
if (!valid(config.version)) throw new Error(`addPlugin - invalid version number ${config.version}`)
|
|
21
|
-
|
|
22
|
-
const newPlugin = context.toPlugins.find(plugin => (plugin.name === config.name))
|
|
23
|
-
if (!newPlugin) throw new Error(`addPlugin - ${config.name} not found`)
|
|
24
|
-
context.fromPlugins.push(newPlugin)
|
|
25
|
-
|
|
26
|
-
return true
|
|
27
|
-
})
|
|
28
|
-
}, { description, type: 'action' })
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
export function updatePlugin (description, config) {
|
|
32
|
-
return deferOrRunWrap(function (context) {
|
|
33
|
-
return successStopOrErrorWrap('updatePlugin', description, async () => {
|
|
34
|
-
if (!description || !config) throw new Error('updatePlugin - incorrectly configured')
|
|
35
|
-
if (!valid(config.version)) throw new Error(`updatePlugin - invalid version number ${config.version}`)
|
|
36
|
-
|
|
37
|
-
context.fromPlugins.forEach(plugin => {
|
|
38
|
-
if (plugin.name !== config.name) return
|
|
39
|
-
plugin.version = config.version
|
|
40
|
-
if (!config.framework) return
|
|
41
|
-
plugin.framework = config.framework
|
|
42
|
-
})
|
|
43
|
-
|
|
44
|
-
return true
|
|
45
|
-
})
|
|
46
|
-
}, { description, type: 'action' })
|
|
47
|
-
};
|
|
1
|
+
import { deferOrRunWrap, successStopOrErrorWrap } from '../lib/lifecycle.js'
|
|
2
|
+
import { valid } from 'semver'
|
|
3
|
+
|
|
4
|
+
export function removePlugin (description, config) {
|
|
5
|
+
return deferOrRunWrap(function (context) {
|
|
6
|
+
return successStopOrErrorWrap('removePlugin', description, async () => {
|
|
7
|
+
if (!description || !config) throw new Error('removePlugin - incorrectly configured')
|
|
8
|
+
|
|
9
|
+
context.fromPlugins = context.fromPlugins.filter(plugin => plugin.name !== config.name)
|
|
10
|
+
|
|
11
|
+
return true
|
|
12
|
+
})
|
|
13
|
+
}, { description, type: 'action' })
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export function addPlugin (description, config) {
|
|
17
|
+
return deferOrRunWrap(function (context) {
|
|
18
|
+
return successStopOrErrorWrap('addPlugin', description, async () => {
|
|
19
|
+
if (!description || !config) throw new Error('addPlugin - incorrectly configured')
|
|
20
|
+
if (!valid(config.version)) throw new Error(`addPlugin - invalid version number ${config.version}`)
|
|
21
|
+
|
|
22
|
+
const newPlugin = context.toPlugins.find(plugin => (plugin.name === config.name))
|
|
23
|
+
if (!newPlugin) throw new Error(`addPlugin - ${config.name} not found`)
|
|
24
|
+
context.fromPlugins.push(newPlugin)
|
|
25
|
+
|
|
26
|
+
return true
|
|
27
|
+
})
|
|
28
|
+
}, { description, type: 'action' })
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export function updatePlugin (description, config) {
|
|
32
|
+
return deferOrRunWrap(function (context) {
|
|
33
|
+
return successStopOrErrorWrap('updatePlugin', description, async () => {
|
|
34
|
+
if (!description || !config) throw new Error('updatePlugin - incorrectly configured')
|
|
35
|
+
if (!valid(config.version)) throw new Error(`updatePlugin - invalid version number ${config.version}`)
|
|
36
|
+
|
|
37
|
+
context.fromPlugins.forEach(plugin => {
|
|
38
|
+
if (plugin.name !== config.name) return
|
|
39
|
+
plugin.version = config.version
|
|
40
|
+
if (!config.framework) return
|
|
41
|
+
plugin.framework = config.framework
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
return true
|
|
45
|
+
})
|
|
46
|
+
}, { description, type: 'action' })
|
|
47
|
+
};
|
package/api/tests.js
CHANGED
|
@@ -1,57 +1,56 @@
|
|
|
1
|
-
import TaskTest from '../lib/TaskTest.js'
|
|
2
|
-
import { deferOrRunWrap } from '../lib/lifecycle.js'
|
|
3
|
-
import Logger from '../lib/Logger.js'
|
|
4
|
-
|
|
5
|
-
const logger = Logger.getInstance();
|
|
6
|
-
|
|
7
|
-
export function testSuccessWhere (description, {
|
|
8
|
-
fromPlugins,
|
|
9
|
-
toPlugins,
|
|
10
|
-
content
|
|
11
|
-
}) {
|
|
12
|
-
return deferOrRunWrap(() => {
|
|
13
|
-
logger.
|
|
14
|
-
return new TaskTest({
|
|
15
|
-
description,
|
|
16
|
-
shouldRun: true,
|
|
17
|
-
fromPlugins,
|
|
18
|
-
toPlugins,
|
|
19
|
-
content
|
|
20
|
-
})
|
|
21
|
-
}, { type: 'test' })
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
export function testStopWhere (description, {
|
|
25
|
-
fromPlugins,
|
|
26
|
-
toPlugins,
|
|
27
|
-
content
|
|
28
|
-
}) {
|
|
29
|
-
return deferOrRunWrap(() => {
|
|
30
|
-
logger.
|
|
31
|
-
return new TaskTest({
|
|
32
|
-
description,
|
|
33
|
-
shouldStop: true,
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
};
|
|
1
|
+
import TaskTest from '../lib/TaskTest.js'
|
|
2
|
+
import { deferOrRunWrap } from '../lib/lifecycle.js'
|
|
3
|
+
import Logger from '../lib/Logger.js'
|
|
4
|
+
|
|
5
|
+
const logger = Logger.getInstance();
|
|
6
|
+
|
|
7
|
+
export function testSuccessWhere (description, {
|
|
8
|
+
fromPlugins,
|
|
9
|
+
toPlugins,
|
|
10
|
+
content
|
|
11
|
+
}) {
|
|
12
|
+
return deferOrRunWrap(() => {
|
|
13
|
+
logger.info(`Test success where -- ${description}`)
|
|
14
|
+
return new TaskTest({
|
|
15
|
+
description,
|
|
16
|
+
shouldRun: true,
|
|
17
|
+
fromPlugins,
|
|
18
|
+
toPlugins,
|
|
19
|
+
content
|
|
20
|
+
})
|
|
21
|
+
}, { type: 'test' })
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export function testStopWhere (description, {
|
|
25
|
+
fromPlugins,
|
|
26
|
+
toPlugins,
|
|
27
|
+
content
|
|
28
|
+
}) {
|
|
29
|
+
return deferOrRunWrap(() => {
|
|
30
|
+
logger.info(`Test stop where -- ${description}`)
|
|
31
|
+
return new TaskTest({
|
|
32
|
+
description,
|
|
33
|
+
shouldStop: true,
|
|
34
|
+
fromPlugins,
|
|
35
|
+
toPlugins,
|
|
36
|
+
content
|
|
37
|
+
})
|
|
38
|
+
}, { type: 'test' })
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export function testErrorWhere (description, {
|
|
42
|
+
fromPlugins,
|
|
43
|
+
toPlugins,
|
|
44
|
+
content
|
|
45
|
+
}) {
|
|
46
|
+
return deferOrRunWrap(() => {
|
|
47
|
+
logger.info(`Test error where -- ${description}`)
|
|
48
|
+
return new TaskTest({
|
|
49
|
+
description,
|
|
50
|
+
shouldError: true,
|
|
51
|
+
fromPlugins,
|
|
52
|
+
toPlugins,
|
|
53
|
+
content
|
|
54
|
+
})
|
|
55
|
+
}, { type: 'test' })
|
|
56
|
+
};
|
package/api/where.js
CHANGED
|
@@ -1,46 +1,46 @@
|
|
|
1
|
-
import semver from 'semver'
|
|
2
|
-
import { deferOrRunWrap, successStopOrErrorWrap } from '../lib/lifecycle.js'
|
|
3
|
-
|
|
4
|
-
export function whereContent (description, callback) {
|
|
5
|
-
return deferOrRunWrap(({ content }) => {
|
|
6
|
-
return successStopOrErrorWrap('whereContent', description, async () => {
|
|
7
|
-
return callback(content)
|
|
8
|
-
})
|
|
9
|
-
}, { description, type: 'where' })
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
export function whereFromPlugin (description, callbackOrConfig) {
|
|
13
|
-
return deferOrRunWrap(({ fromPlugins }) => {
|
|
14
|
-
return successStopOrErrorWrap('whereFromPlugin', description, async () => {
|
|
15
|
-
const isCallback = (typeof callbackOrConfig === 'function')
|
|
16
|
-
if (isCallback) {
|
|
17
|
-
const callback = callbackOrConfig
|
|
18
|
-
return callback(fromPlugins)
|
|
19
|
-
}
|
|
20
|
-
const config = callbackOrConfig
|
|
21
|
-
return fromPlugins.some(plugin => {
|
|
22
|
-
if (config.name && plugin.name !== config.name) return false
|
|
23
|
-
if (config.version && !semver.satisfies(plugin.version, config.version)) return false
|
|
24
|
-
return true
|
|
25
|
-
})
|
|
26
|
-
})
|
|
27
|
-
}, { description, type: 'where' })
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
export function whereToPlugin (description, callbackOrConfig) {
|
|
31
|
-
return deferOrRunWrap(({ toPlugins }) => {
|
|
32
|
-
return successStopOrErrorWrap('whereToPlugin', description, async () => {
|
|
33
|
-
const isCallback = (typeof callbackOrConfig === 'function')
|
|
34
|
-
if (isCallback) {
|
|
35
|
-
const callback = callbackOrConfig
|
|
36
|
-
return callback(toPlugins)
|
|
37
|
-
}
|
|
38
|
-
const config = callbackOrConfig
|
|
39
|
-
return toPlugins.some(plugin => {
|
|
40
|
-
if (config.name && plugin.name !== config.name) return false
|
|
41
|
-
if (config.version && !semver.satisfies(plugin.version, config.version)) return false
|
|
42
|
-
return true
|
|
43
|
-
})
|
|
44
|
-
})
|
|
45
|
-
}, { description, type: 'where' })
|
|
46
|
-
};
|
|
1
|
+
import semver from 'semver'
|
|
2
|
+
import { deferOrRunWrap, successStopOrErrorWrap } from '../lib/lifecycle.js'
|
|
3
|
+
|
|
4
|
+
export function whereContent (description, callback) {
|
|
5
|
+
return deferOrRunWrap(({ content }) => {
|
|
6
|
+
return successStopOrErrorWrap('whereContent', description, async () => {
|
|
7
|
+
return callback(content)
|
|
8
|
+
})
|
|
9
|
+
}, { description, type: 'where' })
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export function whereFromPlugin (description, callbackOrConfig) {
|
|
13
|
+
return deferOrRunWrap(({ fromPlugins }) => {
|
|
14
|
+
return successStopOrErrorWrap('whereFromPlugin', description, async () => {
|
|
15
|
+
const isCallback = (typeof callbackOrConfig === 'function')
|
|
16
|
+
if (isCallback) {
|
|
17
|
+
const callback = callbackOrConfig
|
|
18
|
+
return callback(fromPlugins)
|
|
19
|
+
}
|
|
20
|
+
const config = callbackOrConfig
|
|
21
|
+
return fromPlugins.some(plugin => {
|
|
22
|
+
if (config.name && plugin.name !== config.name) return false
|
|
23
|
+
if (config.version && !semver.satisfies(plugin.version, config.version)) return false
|
|
24
|
+
return true
|
|
25
|
+
})
|
|
26
|
+
})
|
|
27
|
+
}, { description, type: 'where' })
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export function whereToPlugin (description, callbackOrConfig) {
|
|
31
|
+
return deferOrRunWrap(({ toPlugins }) => {
|
|
32
|
+
return successStopOrErrorWrap('whereToPlugin', description, async () => {
|
|
33
|
+
const isCallback = (typeof callbackOrConfig === 'function')
|
|
34
|
+
if (isCallback) {
|
|
35
|
+
const callback = callbackOrConfig
|
|
36
|
+
return callback(toPlugins)
|
|
37
|
+
}
|
|
38
|
+
const config = callbackOrConfig
|
|
39
|
+
return toPlugins.some(plugin => {
|
|
40
|
+
if (config.name && plugin.name !== config.name) return false
|
|
41
|
+
if (config.version && !semver.satisfies(plugin.version, config.version)) return false
|
|
42
|
+
return true
|
|
43
|
+
})
|
|
44
|
+
})
|
|
45
|
+
}, { description, type: 'where' })
|
|
46
|
+
};
|
package/examples/migrations.js
CHANGED
|
@@ -1,159 +1,159 @@
|
|
|
1
|
-
module.exports = function(grunt) {
|
|
2
|
-
|
|
3
|
-
const Helpers = require('../helpers')(grunt);
|
|
4
|
-
const globs = require('globs');
|
|
5
|
-
const path = require('path');
|
|
6
|
-
const fs = require('fs-extra');
|
|
7
|
-
const _ = require('underscore');
|
|
8
|
-
|
|
9
|
-
function unix(path) {
|
|
10
|
-
return path.replace(/\\/g, '/');
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
function dressPathIndex(fileItem) {
|
|
14
|
-
return {
|
|
15
|
-
...fileItem.item,
|
|
16
|
-
__index__: fileItem.index,
|
|
17
|
-
__path__: unix(fileItem.file.path)
|
|
18
|
-
};
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function undressPathIndex(object) {
|
|
22
|
-
const clone = { ...object };
|
|
23
|
-
delete clone.__index__;
|
|
24
|
-
delete clone.__path__;
|
|
25
|
-
return clone;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
grunt.registerTask('migration', 'Migrate from on verion to another', function(mode) {
|
|
29
|
-
const next = this.async();
|
|
30
|
-
const buildConfig = Helpers.generateConfigData();
|
|
31
|
-
const fileNameIncludes = grunt.option('file');
|
|
32
|
-
|
|
33
|
-
(async function() {
|
|
34
|
-
const migrations = await import('adapt-migrations');
|
|
35
|
-
const logger = migrations.Logger.getInstance();
|
|
36
|
-
const cwd = process.cwd();
|
|
37
|
-
const outputPath = path.join(cwd, './migrations/');
|
|
38
|
-
const cache = new migrations.CacheManager();
|
|
39
|
-
const cachePath = await cache.getCachePath({
|
|
40
|
-
outputPath: buildConfig.outputdir,
|
|
41
|
-
tempPath: outputPath
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
const framework = Helpers.getFramework();
|
|
45
|
-
logger.debug(`Using ${framework.useOutputData ? framework.outputPath : framework.sourcePath} folder for course data...`);
|
|
46
|
-
|
|
47
|
-
const plugins = framework.getPlugins().getAllPackageJSONFileItems().map(fileItem => fileItem.item);
|
|
48
|
-
const migrationScripts = Array.from(await new Promise(resolve => {
|
|
49
|
-
globs([
|
|
50
|
-
'*/*/migrations/**/*.js',
|
|
51
|
-
'core/migrations/**/*.js'
|
|
52
|
-
], { cwd: path.join(cwd, './src/'), absolute: true }, (err, files) => resolve(err ? null : files));
|
|
53
|
-
})).filter(filePath => {
|
|
54
|
-
if (!fileNameIncludes) return true;
|
|
55
|
-
return filePath.includes(fileNameIncludes);
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
await migrations.load({
|
|
59
|
-
cachePath,
|
|
60
|
-
scripts: migrationScripts,
|
|
61
|
-
logger
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
if (mode === 'capture') {
|
|
65
|
-
|
|
66
|
-
if (!fs.existsSync(outputPath)) fs.mkdirSync(outputPath);
|
|
67
|
-
const languages = framework.getData().languages.map((language) => language.name);
|
|
68
|
-
const languageFile = path.join(outputPath, 'captureLanguages.json');
|
|
69
|
-
fs.writeJSONSync(languageFile, languages);
|
|
70
|
-
languages.forEach(async (language, index) => {
|
|
71
|
-
logger.debug(`Migration -- Capture ${language}`)
|
|
72
|
-
const data = framework.getData();
|
|
73
|
-
// get all items from config.json file and all language files, append __index__ and __path__ to each item
|
|
74
|
-
const content = [
|
|
75
|
-
...data.configFile.fileItems,
|
|
76
|
-
...data.languages[index].getAllFileItems()
|
|
77
|
-
].map(dressPathIndex);
|
|
78
|
-
const captured = await migrations.capture({ content, fromPlugins: plugins, logger });
|
|
79
|
-
const outputFile = path.join(outputPath, `capture_${language}.json`);
|
|
80
|
-
fs.writeJSONSync(outputFile, captured);
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
logger.output(outputPath, 'capture');
|
|
84
|
-
return next();
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
if (mode === 'migrate') {
|
|
88
|
-
try {
|
|
89
|
-
const languagesFile = path.join(outputPath, 'captureLanguages.json');
|
|
90
|
-
const languages = fs.readJSONSync(languagesFile);
|
|
91
|
-
|
|
92
|
-
for (const language of languages) {
|
|
93
|
-
logger.debug(`Migration -- Migrate ${language}`)
|
|
94
|
-
const Journal = migrations.Journal;
|
|
95
|
-
if (!fs.existsSync(outputPath)) fs.mkdirSync(outputPath);
|
|
96
|
-
const outputFile = path.join(outputPath, `capture_${language}.json`);
|
|
97
|
-
const { content, fromPlugins } = fs.readJSONSync(outputFile);
|
|
98
|
-
const originalFromPlugins = JSON.parse(JSON.stringify(fromPlugins));
|
|
99
|
-
const journal = new Journal({
|
|
100
|
-
logger,
|
|
101
|
-
data: {
|
|
102
|
-
content,
|
|
103
|
-
fromPlugins,
|
|
104
|
-
originalFromPlugins,
|
|
105
|
-
toPlugins: plugins,
|
|
106
|
-
},
|
|
107
|
-
supplementEntry: (entry, data) => {
|
|
108
|
-
entry._id = data[entry.keys[0]][entry.keys[1]]?._id ?? '';
|
|
109
|
-
entry._type = data[entry.keys[0]][entry.keys[1]]?._type ?? '';
|
|
110
|
-
if (entry._type && data[entry.keys[0]][entry.keys[1]]?.[`_${entry._type}`]) {
|
|
111
|
-
entry[`_${entry._type}`] = data[entry.keys[0]][entry.keys[1]]?.[`_${entry._type}`] ?? '';
|
|
112
|
-
}
|
|
113
|
-
return entry;
|
|
114
|
-
}
|
|
115
|
-
});
|
|
116
|
-
await migrations.migrate({ journal, logger });
|
|
117
|
-
|
|
118
|
-
// Todo - {
|
|
119
|
-
// display changes success/failure and request user confirmation before completing
|
|
120
|
-
// move saving of content outside of the language loop
|
|
121
|
-
// only 1 confirmation per course rather than 1 per language
|
|
122
|
-
// output journal entries for revert
|
|
123
|
-
// }
|
|
124
|
-
|
|
125
|
-
// group all content items by path
|
|
126
|
-
const outputFilePathItems = _.groupBy(content, '__path__');
|
|
127
|
-
// sort items inside each path
|
|
128
|
-
Object.values(outputFilePathItems).forEach(outputFile => outputFile.sort((a, b) => a.__index__ - b.__index__));
|
|
129
|
-
// get paths
|
|
130
|
-
const outputFilePaths = Object.keys(outputFilePathItems);
|
|
131
|
-
|
|
132
|
-
outputFilePaths.forEach(outputPath => {
|
|
133
|
-
const outputItems = outputFilePathItems[outputPath];
|
|
134
|
-
if (!outputItems?.length) return;
|
|
135
|
-
const isSingleObject = (outputItems.length === 1 && outputItems[0].__index__ === null);
|
|
136
|
-
const stripped = isSingleObject
|
|
137
|
-
? undressPathIndex(outputItems[0]) // config.json, course.json
|
|
138
|
-
: outputItems.map(undressPathIndex); // contentObjects.json, articles.json, blocks.json, components.json
|
|
139
|
-
// console.log(journal.entries)
|
|
140
|
-
fs.writeJSONSync(outputPath, stripped, { replacer: null, spaces: 2 });
|
|
141
|
-
});
|
|
142
|
-
}
|
|
143
|
-
} catch (error) {
|
|
144
|
-
logger.error(error.stack);
|
|
145
|
-
}
|
|
146
|
-
logger.output(outputPath, 'migrate');
|
|
147
|
-
return next();
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
if (mode === 'test') {
|
|
151
|
-
await migrations.test();
|
|
152
|
-
return next();
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
return next();
|
|
156
|
-
})();
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
};
|
|
1
|
+
module.exports = function(grunt) {
|
|
2
|
+
|
|
3
|
+
const Helpers = require('../helpers')(grunt);
|
|
4
|
+
const globs = require('globs');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const fs = require('fs-extra');
|
|
7
|
+
const _ = require('underscore');
|
|
8
|
+
|
|
9
|
+
function unix(path) {
|
|
10
|
+
return path.replace(/\\/g, '/');
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function dressPathIndex(fileItem) {
|
|
14
|
+
return {
|
|
15
|
+
...fileItem.item,
|
|
16
|
+
__index__: fileItem.index,
|
|
17
|
+
__path__: unix(fileItem.file.path)
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function undressPathIndex(object) {
|
|
22
|
+
const clone = { ...object };
|
|
23
|
+
delete clone.__index__;
|
|
24
|
+
delete clone.__path__;
|
|
25
|
+
return clone;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
grunt.registerTask('migration', 'Migrate from on verion to another', function(mode) {
|
|
29
|
+
const next = this.async();
|
|
30
|
+
const buildConfig = Helpers.generateConfigData();
|
|
31
|
+
const fileNameIncludes = grunt.option('file');
|
|
32
|
+
|
|
33
|
+
(async function() {
|
|
34
|
+
const migrations = await import('adapt-migrations');
|
|
35
|
+
const logger = migrations.Logger.getInstance();
|
|
36
|
+
const cwd = process.cwd();
|
|
37
|
+
const outputPath = path.join(cwd, './migrations/');
|
|
38
|
+
const cache = new migrations.CacheManager();
|
|
39
|
+
const cachePath = await cache.getCachePath({
|
|
40
|
+
outputPath: buildConfig.outputdir,
|
|
41
|
+
tempPath: outputPath
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
const framework = Helpers.getFramework();
|
|
45
|
+
logger.debug(`Using ${framework.useOutputData ? framework.outputPath : framework.sourcePath} folder for course data...`);
|
|
46
|
+
|
|
47
|
+
const plugins = framework.getPlugins().getAllPackageJSONFileItems().map(fileItem => fileItem.item);
|
|
48
|
+
const migrationScripts = Array.from(await new Promise(resolve => {
|
|
49
|
+
globs([
|
|
50
|
+
'*/*/migrations/**/*.js',
|
|
51
|
+
'core/migrations/**/*.js'
|
|
52
|
+
], { cwd: path.join(cwd, './src/'), absolute: true }, (err, files) => resolve(err ? null : files));
|
|
53
|
+
})).filter(filePath => {
|
|
54
|
+
if (!fileNameIncludes) return true;
|
|
55
|
+
return filePath.includes(fileNameIncludes);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
await migrations.load({
|
|
59
|
+
cachePath,
|
|
60
|
+
scripts: migrationScripts,
|
|
61
|
+
logger
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
if (mode === 'capture') {
|
|
65
|
+
|
|
66
|
+
if (!fs.existsSync(outputPath)) fs.mkdirSync(outputPath);
|
|
67
|
+
const languages = framework.getData().languages.map((language) => language.name);
|
|
68
|
+
const languageFile = path.join(outputPath, 'captureLanguages.json');
|
|
69
|
+
fs.writeJSONSync(languageFile, languages);
|
|
70
|
+
languages.forEach(async (language, index) => {
|
|
71
|
+
logger.debug(`Migration -- Capture ${language}`)
|
|
72
|
+
const data = framework.getData();
|
|
73
|
+
// get all items from config.json file and all language files, append __index__ and __path__ to each item
|
|
74
|
+
const content = [
|
|
75
|
+
...data.configFile.fileItems,
|
|
76
|
+
...data.languages[index].getAllFileItems()
|
|
77
|
+
].map(dressPathIndex);
|
|
78
|
+
const captured = await migrations.capture({ content, fromPlugins: plugins, logger });
|
|
79
|
+
const outputFile = path.join(outputPath, `capture_${language}.json`);
|
|
80
|
+
fs.writeJSONSync(outputFile, captured);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
logger.output(outputPath, 'capture');
|
|
84
|
+
return next();
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (mode === 'migrate') {
|
|
88
|
+
try {
|
|
89
|
+
const languagesFile = path.join(outputPath, 'captureLanguages.json');
|
|
90
|
+
const languages = fs.readJSONSync(languagesFile);
|
|
91
|
+
|
|
92
|
+
for (const language of languages) {
|
|
93
|
+
logger.debug(`Migration -- Migrate ${language}`)
|
|
94
|
+
const Journal = migrations.Journal;
|
|
95
|
+
if (!fs.existsSync(outputPath)) fs.mkdirSync(outputPath);
|
|
96
|
+
const outputFile = path.join(outputPath, `capture_${language}.json`);
|
|
97
|
+
const { content, fromPlugins } = fs.readJSONSync(outputFile);
|
|
98
|
+
const originalFromPlugins = JSON.parse(JSON.stringify(fromPlugins));
|
|
99
|
+
const journal = new Journal({
|
|
100
|
+
logger,
|
|
101
|
+
data: {
|
|
102
|
+
content,
|
|
103
|
+
fromPlugins,
|
|
104
|
+
originalFromPlugins,
|
|
105
|
+
toPlugins: plugins,
|
|
106
|
+
},
|
|
107
|
+
supplementEntry: (entry, data) => {
|
|
108
|
+
entry._id = data[entry.keys[0]][entry.keys[1]]?._id ?? '';
|
|
109
|
+
entry._type = data[entry.keys[0]][entry.keys[1]]?._type ?? '';
|
|
110
|
+
if (entry._type && data[entry.keys[0]][entry.keys[1]]?.[`_${entry._type}`]) {
|
|
111
|
+
entry[`_${entry._type}`] = data[entry.keys[0]][entry.keys[1]]?.[`_${entry._type}`] ?? '';
|
|
112
|
+
}
|
|
113
|
+
return entry;
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
await migrations.migrate({ journal, logger });
|
|
117
|
+
|
|
118
|
+
// Todo - {
|
|
119
|
+
// display changes success/failure and request user confirmation before completing
|
|
120
|
+
// move saving of content outside of the language loop
|
|
121
|
+
// only 1 confirmation per course rather than 1 per language
|
|
122
|
+
// output journal entries for revert
|
|
123
|
+
// }
|
|
124
|
+
|
|
125
|
+
// group all content items by path
|
|
126
|
+
const outputFilePathItems = _.groupBy(content, '__path__');
|
|
127
|
+
// sort items inside each path
|
|
128
|
+
Object.values(outputFilePathItems).forEach(outputFile => outputFile.sort((a, b) => a.__index__ - b.__index__));
|
|
129
|
+
// get paths
|
|
130
|
+
const outputFilePaths = Object.keys(outputFilePathItems);
|
|
131
|
+
|
|
132
|
+
outputFilePaths.forEach(outputPath => {
|
|
133
|
+
const outputItems = outputFilePathItems[outputPath];
|
|
134
|
+
if (!outputItems?.length) return;
|
|
135
|
+
const isSingleObject = (outputItems.length === 1 && outputItems[0].__index__ === null);
|
|
136
|
+
const stripped = isSingleObject
|
|
137
|
+
? undressPathIndex(outputItems[0]) // config.json, course.json
|
|
138
|
+
: outputItems.map(undressPathIndex); // contentObjects.json, articles.json, blocks.json, components.json
|
|
139
|
+
// console.log(journal.entries)
|
|
140
|
+
fs.writeJSONSync(outputPath, stripped, { replacer: null, spaces: 2 });
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
} catch (error) {
|
|
144
|
+
logger.error(error.stack);
|
|
145
|
+
}
|
|
146
|
+
logger.output(outputPath, 'migrate');
|
|
147
|
+
return next();
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (mode === 'test') {
|
|
151
|
+
await migrations.test();
|
|
152
|
+
return next();
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return next();
|
|
156
|
+
})();
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
};
|