@jungvonmatt/contentful-migrations 4.0.2 → 5.0.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/README.md +3 -1
- package/index.js +18 -8
- package/lib/backend.js +14 -7
- package/lib/config.js +29 -13
- package/lib/content.js +3 -10
- package/lib/contentful.js +7 -3
- package/lib/doc.js +6 -5
- package/lib/environment.js +6 -9
- package/lib/migration.js +34 -8
- package/package.json +22 -18
package/README.md
CHANGED
|
@@ -29,7 +29,6 @@ This initializes migrations and stores the config values in the `package.json` o
|
|
|
29
29
|
| accessToken | `undefined` | Contentful Management Token. Just run `npx contentful login` and you're done. |
|
|
30
30
|
| spaceId | `undefined` | Contentful Space id. Will fallback to `process.env.CONTENTFUL_SPACE_ID` if not set. |
|
|
31
31
|
| environmentId | `undefined` | Contentful Environment id. Will fallback to `process.env.CONTENTFUL_ENVIRONMENT_ID` if not set.<br/>If neither `environmentId` nor `CONTENTFUL_ENVIRONMENT_ID` is available we search for environment whose id matches the current git branch |
|
|
32
|
-
| fallbackEnvironmentId | `'master'` | Contentful Space environment. Acts as default if there is no environment named after the current git branch or the passed env doesn't exist |
|
|
33
32
|
| storage | `undefined` | We need to keep a hint to the executed migrations inside Contentful. You can choose between **content* and **tag**. <br/><br/>**Content** will add a new content type to your Contentful environment and stores the state of every migration as content entry (recommended approach) <br/>**tag** Will only store the latest version inside a tag. You need to preserve the right order yourself. When you add a new migration with an older version number it will not be executed. |
|
|
34
33
|
| fieldId | `'migration'` | Id of the tag where the migration version is stored (only used with storage `tag`) |
|
|
35
34
|
| migrationContentTypeId | `'contentful-migrations'` | Id of the migration content-type (only used with storage `content`) |
|
|
@@ -48,6 +47,9 @@ npx migrations environment <environment-id> --create
|
|
|
48
47
|
|
|
49
48
|
# Remove an environment
|
|
50
49
|
npx migrations environment <environment-id> --remove
|
|
50
|
+
|
|
51
|
+
# Reset an environment
|
|
52
|
+
npx migrations environment <environment-id> --reset
|
|
51
53
|
```
|
|
52
54
|
|
|
53
55
|
## Generating blank migrations
|
package/index.js
CHANGED
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
/* eslint-env node */
|
|
5
5
|
const fs = require('fs-extra');
|
|
6
6
|
const path = require('path');
|
|
7
|
-
const pkgUp = require('pkg-up');
|
|
8
7
|
const chalk = require('chalk');
|
|
9
8
|
const { Command } = require('commander');
|
|
10
9
|
|
|
@@ -13,9 +12,9 @@ const { createMigration, runMigrations, fetchMigration, executeMigration } = req
|
|
|
13
12
|
const { versionDelete, versionAdd } = require('./lib/version');
|
|
14
13
|
const { transferContent } = require('./lib/content');
|
|
15
14
|
const { createOfflineDocs } = require('./lib/doc');
|
|
15
|
+
const { createEnvironment, removeEnvironment, resetEnvironment } = require('./lib/environment');
|
|
16
16
|
const { getConfig, askAll, askMissing, STORAGE_CONTENT, STORAGE_TAG } = require('./lib/config');
|
|
17
17
|
const pkg = require('./package.json');
|
|
18
|
-
const { createEnvironment, removeEnvironment } = require('./lib/environment');
|
|
19
18
|
|
|
20
19
|
require('dotenv').config();
|
|
21
20
|
|
|
@@ -30,6 +29,7 @@ const parseArgs = (cmd) => {
|
|
|
30
29
|
destEnvironmentId: cmd.destEnvironmentId || parent.destEnvironmentId,
|
|
31
30
|
verbose: cmd.verbose || parent.verbose,
|
|
32
31
|
template: cmd.template || parent.template,
|
|
32
|
+
yes: cmd.yes || parent.yes,
|
|
33
33
|
extension: cmd.extension || parent.extension,
|
|
34
34
|
bail: cmd.bail || parent.bail,
|
|
35
35
|
};
|
|
@@ -63,7 +63,7 @@ program
|
|
|
63
63
|
actionRunner(async (cmd) => {
|
|
64
64
|
const config = await getConfig(parseArgs(cmd || {}));
|
|
65
65
|
const verified = await askAll(config);
|
|
66
|
-
const { managementToken, accessToken, environmentId, ...rest } = verified;
|
|
66
|
+
const { managementToken, accessToken, environmentId, spaceId, ...rest } = verified;
|
|
67
67
|
|
|
68
68
|
if (verified.storage === STORAGE_CONTENT) {
|
|
69
69
|
await initializeContentModel({ ...config, ...verified });
|
|
@@ -73,7 +73,13 @@ program
|
|
|
73
73
|
await migrateToTagStorage({ ...config, ...verified });
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
+
if (!process.env.CONTENTFUL_SPACE_ID) {
|
|
77
|
+
rest.spaceId = spaceId;
|
|
78
|
+
}
|
|
79
|
+
|
|
76
80
|
// try to store in package.json
|
|
81
|
+
|
|
82
|
+
const {pkgUp} = await import('pkg-up');
|
|
77
83
|
const localPkg = await pkgUp();
|
|
78
84
|
if (localPkg) {
|
|
79
85
|
const packageJson = await fs.readJson(localPkg);
|
|
@@ -124,6 +130,7 @@ program
|
|
|
124
130
|
.option('-e, --environment-id <environment-id>', 'Change the Contentful environment')
|
|
125
131
|
.option('-p, --path <path/to/migrations>', 'Change the path where the migrations are stored')
|
|
126
132
|
.option('-v, --verbose', 'Verbosity')
|
|
133
|
+
.option('-y, --yes', 'Assume "yes" as answer to all prompts and run non-interactively.')
|
|
127
134
|
.option('--bail', 'Abort execution after first failed migration (default: true)', true)
|
|
128
135
|
.option('--no-bail', 'Ignore failed migrations')
|
|
129
136
|
.description('Execute all unexecuted migrations available.')
|
|
@@ -150,6 +157,7 @@ program
|
|
|
150
157
|
.option('-s, --space-id <space-id>', 'Contentful space id')
|
|
151
158
|
.option('-e, --environment-id <environment-id>', 'Change the Contentful environment')
|
|
152
159
|
.option('-v, --verbose', 'Verbosity')
|
|
160
|
+
.option('-y, --yes', 'Assume "yes" as answer to all prompts and run non-interactively.')
|
|
153
161
|
.description('Execute a single migration.')
|
|
154
162
|
.action(
|
|
155
163
|
actionRunner(async (file, options) => {
|
|
@@ -211,12 +219,13 @@ program
|
|
|
211
219
|
.option('--create', 'Create new contentful environment')
|
|
212
220
|
.option('--remove', 'Delete contentful environment')
|
|
213
221
|
.option('--reset', 'Reset contentful environment')
|
|
222
|
+
.option('--source-environment-id <environment-id>', 'Set the source environment to clone new environment from')
|
|
214
223
|
.description('Add or remove a contentful environment for migrations')
|
|
215
224
|
.action(
|
|
216
225
|
actionRunner(async (environmentId, options) => {
|
|
217
|
-
const { remove, create } = options;
|
|
218
|
-
const config = await getConfig(parseArgs(options || {}));
|
|
219
|
-
const verified = await askMissing(config);
|
|
226
|
+
const { remove, create, reset } = options;
|
|
227
|
+
const config = await getConfig(parseArgs({ ...(options || {}), environmentId }));
|
|
228
|
+
const verified = await askMissing(config, ['accessToken', 'spaceId', 'environmentId']);
|
|
220
229
|
|
|
221
230
|
if (create) {
|
|
222
231
|
return createEnvironment(environmentId, verified);
|
|
@@ -244,7 +253,7 @@ program
|
|
|
244
253
|
.action(
|
|
245
254
|
actionRunner(async (cmd) => {
|
|
246
255
|
const config = await getConfig(parseArgs(cmd || {}));
|
|
247
|
-
const verified = await askMissing(config);
|
|
256
|
+
const verified = await askMissing(config, ['accessToken', 'spaceId', 'environmentId']);
|
|
248
257
|
await createOfflineDocs(verified);
|
|
249
258
|
}, true)
|
|
250
259
|
);
|
|
@@ -256,13 +265,14 @@ program
|
|
|
256
265
|
.option('-s, --space-id <space-id>', 'Contentful space id')
|
|
257
266
|
.option('-c, --content-type <content-type>', 'Specify content-type')
|
|
258
267
|
.option('-v, --verbose', 'Verbosity')
|
|
268
|
+
.option('-y, --yes', 'Assume "yes" as answer to all prompts and run non-interactively.')
|
|
259
269
|
.option('--diff', 'Manually choose skip/overwrite for every conflict')
|
|
260
270
|
.option('--force', 'No manual diffing. Overwrites all conflicting entries/assets')
|
|
261
271
|
.description('Transfer content from source environment to destination environment')
|
|
262
272
|
.action(
|
|
263
273
|
actionRunner(async (cmd) => {
|
|
264
274
|
const config = await getConfig(parseArgs(cmd || {}));
|
|
265
|
-
const verified = await askMissing(config);
|
|
275
|
+
const verified = await askMissing({ ...config, environmentId: 'not-used' });
|
|
266
276
|
|
|
267
277
|
// run migrations on destination environment
|
|
268
278
|
await transferContent({
|
package/lib/backend.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
const path = require('path');
|
|
2
|
-
const globby = require('globby');
|
|
3
2
|
const chalk = require('chalk');
|
|
4
3
|
const cliProgress = require('cli-progress');
|
|
5
4
|
const { getEnvironment, getDefaultLocale } = require('./contentful');
|
|
@@ -192,6 +191,8 @@ const migrateToContentStorage = async (config) => {
|
|
|
192
191
|
|
|
193
192
|
const client = await getEnvironment(config);
|
|
194
193
|
const version = await getMigrationVersionFromTag(config);
|
|
194
|
+
|
|
195
|
+
const {globby} = await import('globby');
|
|
195
196
|
const migrations = await globby(`${directory}/*.js`);
|
|
196
197
|
const environmentId = client.sys.id;
|
|
197
198
|
const filtered = migrations.filter((file) => {
|
|
@@ -357,17 +358,23 @@ const getLatestVersion = async (config) => {
|
|
|
357
358
|
*/
|
|
358
359
|
const getNewMigrations = async (config) => {
|
|
359
360
|
const { directory, storage } = config || {};
|
|
361
|
+
const {globby} = await import('globby');
|
|
360
362
|
const migrations = await globby(`${directory}/*.js`);
|
|
361
363
|
|
|
362
364
|
if (storage === STORAGE_CONTENT) {
|
|
363
|
-
|
|
365
|
+
try {
|
|
366
|
+
const versions = await getMigrationVersions(config);
|
|
364
367
|
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
+
return migrations.filter((file) => {
|
|
369
|
+
const name = path.basename(file);
|
|
370
|
+
const [, num] = /^(\d+)-/.exec(name);
|
|
368
371
|
|
|
369
|
-
|
|
370
|
-
|
|
372
|
+
return !(versions || []).includes(parseInt(num, 10));
|
|
373
|
+
});
|
|
374
|
+
} catch (error) {
|
|
375
|
+
console.error(chalk.red('\nError:'), `Missing migration content type. Run ${chalk.cyan('npx migrations init')}`);
|
|
376
|
+
process.exit(1);
|
|
377
|
+
}
|
|
371
378
|
}
|
|
372
379
|
|
|
373
380
|
const version = await getMigrationVersionFromTag(config);
|
package/lib/config.js
CHANGED
|
@@ -2,7 +2,6 @@ const path = require('path');
|
|
|
2
2
|
const inquirer = require('inquirer');
|
|
3
3
|
const mergeOptions = require('merge-options').bind({ ignoreUndefined: true });
|
|
4
4
|
const { cosmiconfig } = require('cosmiconfig');
|
|
5
|
-
const branch = require('git-branch');
|
|
6
5
|
|
|
7
6
|
const { getSpaces, getEnvironments } = require('./contentful');
|
|
8
7
|
|
|
@@ -20,7 +19,6 @@ const getConfig = async (args) => {
|
|
|
20
19
|
const defaultOptions = {
|
|
21
20
|
fieldId: 'migration',
|
|
22
21
|
migrationContentTypeId: 'contentful-migrations',
|
|
23
|
-
fallbackEnvironmentId: 'master',
|
|
24
22
|
host: 'api.contentful.com',
|
|
25
23
|
directory: path.resolve(process.cwd(), 'migrations'),
|
|
26
24
|
};
|
|
@@ -31,10 +29,6 @@ const getConfig = async (args) => {
|
|
|
31
29
|
accessToken: process.env.CONTENTFUL_MANAGEMENT_TOKEN,
|
|
32
30
|
};
|
|
33
31
|
|
|
34
|
-
try {
|
|
35
|
-
defaultOptions.branch = await branch();
|
|
36
|
-
} catch (error) {}
|
|
37
|
-
|
|
38
32
|
let contentfulCliOptions = {};
|
|
39
33
|
try {
|
|
40
34
|
// get configuration from contentful rc file (created by the contentful cli command)
|
|
@@ -92,7 +86,7 @@ const getPromts = (data) => {
|
|
|
92
86
|
name: 'spaceId',
|
|
93
87
|
message: 'Space ID',
|
|
94
88
|
choices: async (answers) => {
|
|
95
|
-
const spaces = await getSpaces(answers);
|
|
89
|
+
const spaces = await getSpaces({ ...(data || {}), ...answers });
|
|
96
90
|
return spaces.map((space) => ({
|
|
97
91
|
name: `${space.name} (${space.sys.id})`,
|
|
98
92
|
value: space.sys.id,
|
|
@@ -104,14 +98,14 @@ const getPromts = (data) => {
|
|
|
104
98
|
},
|
|
105
99
|
{
|
|
106
100
|
type: 'list',
|
|
107
|
-
name: '
|
|
108
|
-
message: '
|
|
101
|
+
name: 'environmentId',
|
|
102
|
+
message: 'Environment ID',
|
|
109
103
|
choices: async (answers) => {
|
|
110
|
-
const environments = await getEnvironments(answers);
|
|
104
|
+
const environments = await getEnvironments({ ...(data || {}), ...answers });
|
|
111
105
|
return environments.map((environment) => environment.sys.id);
|
|
112
106
|
},
|
|
113
107
|
default: function () {
|
|
114
|
-
return data.
|
|
108
|
+
return data.environmentId;
|
|
115
109
|
},
|
|
116
110
|
},
|
|
117
111
|
{
|
|
@@ -174,8 +168,13 @@ const askAll = async (data = {}) => {
|
|
|
174
168
|
return answers;
|
|
175
169
|
};
|
|
176
170
|
|
|
177
|
-
const askMissing = async (data = {}) => {
|
|
178
|
-
const
|
|
171
|
+
const askMissing = async (data = {}, requiredFields = undefined) => {
|
|
172
|
+
const allQuestions = getPromts(data);
|
|
173
|
+
if (!requiredFields) {
|
|
174
|
+
requiredFields = allQuestions.map(({ name }) => name);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const missingPromts = getPromts(data).filter(({ name }) => !data[name] && requiredFields.includes(name));
|
|
179
178
|
|
|
180
179
|
// Check if storage changed to content and run initialization
|
|
181
180
|
const missingStorage = missingPromts.some((prompt) => prompt.name === 'storage');
|
|
@@ -185,9 +184,26 @@ const askMissing = async (data = {}) => {
|
|
|
185
184
|
return { ...data, ...answers, missingStorageModel: missingStorage && storage === STORAGE_CONTENT };
|
|
186
185
|
};
|
|
187
186
|
|
|
187
|
+
const confirm = async (config = {}) => {
|
|
188
|
+
if (config.yes) {
|
|
189
|
+
return true;
|
|
190
|
+
}
|
|
191
|
+
const { proceed } = await inquirer.prompt([
|
|
192
|
+
{
|
|
193
|
+
type: 'confirm',
|
|
194
|
+
name: 'check',
|
|
195
|
+
message: 'Do you wish to proceed?',
|
|
196
|
+
default: true,
|
|
197
|
+
},
|
|
198
|
+
]);
|
|
199
|
+
|
|
200
|
+
return proceed;
|
|
201
|
+
};
|
|
202
|
+
|
|
188
203
|
module.exports.getConfig = getConfig;
|
|
189
204
|
module.exports.askAll = askAll;
|
|
190
205
|
module.exports.askMissing = askMissing;
|
|
206
|
+
module.exports.confirm = confirm;
|
|
191
207
|
module.exports.STORAGE_TAG = STORAGE_TAG;
|
|
192
208
|
module.exports.STORAGE_CONTENT = STORAGE_CONTENT;
|
|
193
209
|
module.exports.STATE_SUCCESS = STATE_SUCCESS;
|
package/lib/content.js
CHANGED
|
@@ -5,6 +5,7 @@ const { getContent, getContentId, getLinkedAssets, getLinkedEntries } = require(
|
|
|
5
5
|
const { diff } = require('./diff');
|
|
6
6
|
const { buildTree } = require('./tree');
|
|
7
7
|
const { getLatestVersion } = require('./backend');
|
|
8
|
+
const { confirm } = require('./config');
|
|
8
9
|
|
|
9
10
|
const resolveConflicts = async (sourceEntries, destEntries, options) => {
|
|
10
11
|
const { contentTypes, diffConflicts, forceOverwrite } = options;
|
|
@@ -150,16 +151,8 @@ const transferContent = async (config) => {
|
|
|
150
151
|
)} from ${chalk.cyan(sourceEnvironmentId)} to ${chalk.cyan(destEnvironmentId)}`
|
|
151
152
|
);
|
|
152
153
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
type: 'confirm',
|
|
156
|
-
message: 'Do you wish to proceed?',
|
|
157
|
-
default: true,
|
|
158
|
-
name: 'proceed',
|
|
159
|
-
},
|
|
160
|
-
]);
|
|
161
|
-
|
|
162
|
-
if (proceed !== true) {
|
|
154
|
+
proceed = await confirm(config);
|
|
155
|
+
if (!proceed) {
|
|
163
156
|
return;
|
|
164
157
|
}
|
|
165
158
|
|
package/lib/contentful.js
CHANGED
|
@@ -156,18 +156,22 @@ const getEnvironment = async (options) => {
|
|
|
156
156
|
if (environmentCache.has(cacheKey)) {
|
|
157
157
|
return environmentCache.get(cacheKey);
|
|
158
158
|
}
|
|
159
|
-
const { environmentId,
|
|
159
|
+
const { environmentId, spaceId } = options || {};
|
|
160
160
|
const space = await getSpace(options);
|
|
161
161
|
|
|
162
162
|
const { items: environments } = await space.getEnvironments();
|
|
163
163
|
|
|
164
164
|
const environmentIds = (environments || []).map((env) => env.sys.id);
|
|
165
165
|
|
|
166
|
-
const targetEnvironmentId = environmentId
|
|
166
|
+
const targetEnvironmentId = environmentId;
|
|
167
167
|
if (targetEnvironmentId && environmentIds.includes(targetEnvironmentId)) {
|
|
168
168
|
environmentCache.set(cacheKey, space.getEnvironment(targetEnvironmentId));
|
|
169
|
+
} else if (targetEnvironmentId) {
|
|
170
|
+
throw new Error(`Environment "${targetEnvironmentId}" is not available in space "${spaceId}"`);
|
|
169
171
|
} else {
|
|
170
|
-
|
|
172
|
+
throw new Error(
|
|
173
|
+
'Missing environment id. Use -e <environment-id> or set environment variable CONTENTFUL_ENVIRONMENT_ID'
|
|
174
|
+
);
|
|
171
175
|
}
|
|
172
176
|
|
|
173
177
|
return environmentCache.get(cacheKey);
|
package/lib/doc.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
const path = require('path');
|
|
2
|
-
const table = require('markdown-table');
|
|
3
2
|
const chalk = require('chalk');
|
|
4
3
|
const fs = require('fs-extra');
|
|
5
4
|
const Mustache = require('mustache');
|
|
6
5
|
const { getEditorInterfaces, getContentTypes, getContentTypeId, getContentId } = require('./contentful');
|
|
7
6
|
|
|
8
|
-
const getFieldTable = (fields, editorInterfaces) => {
|
|
7
|
+
const getFieldTable = async (fields, editorInterfaces) => {
|
|
8
|
+
const { markdownTable: table } = await import('markdown-table');
|
|
9
9
|
const { controls = [] } = editorInterfaces;
|
|
10
10
|
return table([
|
|
11
11
|
['Id', 'Name', 'Type', 'Required', 'Localized', 'HelpText', 'Remarks'],
|
|
@@ -27,7 +27,8 @@ const getFieldTable = (fields, editorInterfaces) => {
|
|
|
27
27
|
]);
|
|
28
28
|
};
|
|
29
29
|
|
|
30
|
-
const createDoc = (contentType, editorInterfaces) => {
|
|
30
|
+
const createDoc = async (contentType, editorInterfaces) => {
|
|
31
|
+
const { markdownTable: table } = await import('markdown-table');
|
|
31
32
|
const { name, displayField, description, sys, fields } = contentType || {};
|
|
32
33
|
const { id } = sys || {};
|
|
33
34
|
|
|
@@ -50,7 +51,7 @@ ${description}
|
|
|
50
51
|
|
|
51
52
|
## Fields
|
|
52
53
|
|
|
53
|
-
${getFieldTable(fields, editorInterfaces)}
|
|
54
|
+
${await getFieldTable(fields, editorInterfaces)}
|
|
54
55
|
`;
|
|
55
56
|
};
|
|
56
57
|
|
|
@@ -98,7 +99,7 @@ const createOfflineDocs = async (config) => {
|
|
|
98
99
|
const data = prepareData(contentType, interfaces);
|
|
99
100
|
content = await customRender(data);
|
|
100
101
|
} else {
|
|
101
|
-
content = createDoc(contentType, interfaces);
|
|
102
|
+
content = await createDoc(contentType, interfaces);
|
|
102
103
|
}
|
|
103
104
|
|
|
104
105
|
const filename = `${getContentId(contentType)}.${extension}`;
|
package/lib/environment.js
CHANGED
|
@@ -20,11 +20,11 @@ const getActiveApiKey = async (config) => {
|
|
|
20
20
|
* @param {Object} config The config object including all required data
|
|
21
21
|
*/
|
|
22
22
|
const createEnvironment = async (environmentId, config) => {
|
|
23
|
-
const {
|
|
24
|
-
console.log(`\nCreating new environment ${chalk.green(environmentId)} from ${chalk.green(
|
|
23
|
+
const { sourceEnvironmentId = 'master' } = config;
|
|
24
|
+
console.log(`\nCreating new environment ${chalk.green(environmentId)} from ${chalk.green(sourceEnvironmentId)}`);
|
|
25
25
|
|
|
26
26
|
const space = await getSpace(config);
|
|
27
|
-
const environment = await space.createEnvironmentWithId(environmentId, { name: environmentId },
|
|
27
|
+
const environment = await space.createEnvironmentWithId(environmentId, { name: environmentId }, sourceEnvironmentId);
|
|
28
28
|
|
|
29
29
|
const apiKey = await getActiveApiKey(config);
|
|
30
30
|
|
|
@@ -52,9 +52,8 @@ const createEnvironment = async (environmentId, config) => {
|
|
|
52
52
|
* @param {Object} config The config object including all required data
|
|
53
53
|
*/
|
|
54
54
|
const removeEnvironment = async (environmentId, config) => {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
throw new Error('Removing the fallback environment or the master environment is not supported');
|
|
55
|
+
if (['master'].includes(environmentId)) {
|
|
56
|
+
throw new Error('Removing the master environment is not supported');
|
|
58
57
|
}
|
|
59
58
|
console.log(`\nRemoving environment ${chalk.green(environmentId)}`);
|
|
60
59
|
const space = await getSpace(config);
|
|
@@ -79,7 +78,7 @@ const removeEnvironment = async (environmentId, config) => {
|
|
|
79
78
|
*/
|
|
80
79
|
const resetEnvironment = async (environmentId, config) => {
|
|
81
80
|
if (['master'].includes(environmentId)) {
|
|
82
|
-
throw new Error('Removing the
|
|
81
|
+
throw new Error('Removing the master environment is not supported');
|
|
83
82
|
}
|
|
84
83
|
console.log(`\nRemoving environment ${chalk.green(environmentId)}`);
|
|
85
84
|
const space = await getSpace(config);
|
|
@@ -88,8 +87,6 @@ const resetEnvironment = async (environmentId, config) => {
|
|
|
88
87
|
await environment.delete();
|
|
89
88
|
} catch {}
|
|
90
89
|
await createEnvironment(environmentId, config);
|
|
91
|
-
|
|
92
|
-
console.log(chalk.green('\nDone'), '🚀');
|
|
93
90
|
};
|
|
94
91
|
|
|
95
92
|
module.exports.createEnvironment = createEnvironment;
|
package/lib/migration.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const fs = require('fs-extra');
|
|
2
|
+
const prettier = require('prettier');
|
|
2
3
|
const path = require('path');
|
|
3
4
|
const { stripIndent } = require('common-tags');
|
|
4
5
|
const runMigration = require('contentful-migration/built/bin/cli').runMigration;
|
|
@@ -9,7 +10,7 @@ const {
|
|
|
9
10
|
const chalk = require('chalk');
|
|
10
11
|
const { getEnvironment } = require('./contentful');
|
|
11
12
|
|
|
12
|
-
const { STATE_SUCCESS, STATE_FAILURE } = require('./config');
|
|
13
|
+
const { confirm, STATE_SUCCESS, STATE_FAILURE } = require('./config');
|
|
13
14
|
|
|
14
15
|
const { storeMigration, getNewMigrations } = require('./backend');
|
|
15
16
|
|
|
@@ -33,10 +34,18 @@ const createMigration = async (config) => {
|
|
|
33
34
|
// See: https://github.com/contentful/contentful-migration
|
|
34
35
|
}`;
|
|
35
36
|
|
|
36
|
-
await fs.outputFile(filename, content);
|
|
37
|
+
await fs.outputFile(filename, await format(filename, content));
|
|
37
38
|
console.log(`Generated new migration file to ${chalk.green(filename)}`);
|
|
38
39
|
};
|
|
39
40
|
|
|
41
|
+
const format = async (file, content) => {
|
|
42
|
+
const prettierOptions = await prettier.resolveConfig(file, {editorconfig: true});
|
|
43
|
+
return prettier.format(content, {
|
|
44
|
+
parser: 'babel',
|
|
45
|
+
...prettierOptions,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
40
49
|
/**
|
|
41
50
|
* Fetch migration from contentful
|
|
42
51
|
* @param {Object} config The config object including all required data
|
|
@@ -51,8 +60,12 @@ const fetchMigration = async (config) => {
|
|
|
51
60
|
|
|
52
61
|
const promises = contentTypes.map(async (entry) => {
|
|
53
62
|
const filename = path.join(directory, `${timestamp++}-create-${entry.sys.id}-migration.js`);
|
|
63
|
+
|
|
64
|
+
|
|
54
65
|
const content = await generateMigrationScript(client, [entry]);
|
|
55
|
-
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
await fs.outputFile(filename, await format(filename, content));
|
|
56
69
|
console.log(`Generated new migration file to ${chalk.green(filename)}`);
|
|
57
70
|
});
|
|
58
71
|
|
|
@@ -69,7 +82,6 @@ const executeMigration = async (file, config) => {
|
|
|
69
82
|
const { accessToken, spaceId } = config || {};
|
|
70
83
|
const client = await getEnvironment(config);
|
|
71
84
|
const environmentId = client.sys.id;
|
|
72
|
-
|
|
73
85
|
const name = path.basename(file);
|
|
74
86
|
const [, version] = /^(\d+)-/.exec(name);
|
|
75
87
|
|
|
@@ -81,7 +93,11 @@ const executeMigration = async (file, config) => {
|
|
|
81
93
|
yes: true,
|
|
82
94
|
};
|
|
83
95
|
|
|
84
|
-
console.log(`\nRun migration ${chalk.green(version)}
|
|
96
|
+
console.log(`\nRun migration ${chalk.green(version)} in environment ${chalk.green(environmentId)}`);
|
|
97
|
+
const proceed = await confirm(config);
|
|
98
|
+
if (!proceed) {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
85
101
|
try {
|
|
86
102
|
await runMigration(options);
|
|
87
103
|
await storeMigration({ version, name, state: STATE_SUCCESS }, config);
|
|
@@ -104,15 +120,25 @@ const runMigrations = async (config) => {
|
|
|
104
120
|
|
|
105
121
|
const client = await getEnvironment(config);
|
|
106
122
|
const environmentId = client.sys.id;
|
|
107
|
-
|
|
123
|
+
let migrations = [];
|
|
124
|
+
try {
|
|
125
|
+
migrations = await getNewMigrations(config);
|
|
126
|
+
} catch (error) {
|
|
127
|
+
console.log(error.message);
|
|
128
|
+
throw error;
|
|
129
|
+
}
|
|
108
130
|
console.log(
|
|
109
131
|
`Found ${chalk.green(migrations.length)} unexecuted migrations in environment ${chalk.green(environmentId)}`
|
|
110
132
|
);
|
|
111
133
|
|
|
112
|
-
|
|
134
|
+
const proceed = migrations.length === 0 || (await confirm(config));
|
|
135
|
+
if (!proceed) {
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
|
|
113
139
|
for await (const file of migrations) {
|
|
114
140
|
try {
|
|
115
|
-
|
|
141
|
+
await executeMigration(file, { ...config, yes: true });
|
|
116
142
|
} catch (error) {
|
|
117
143
|
if (bail) {
|
|
118
144
|
throw error;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jungvonmatt/contentful-migrations",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.0.2",
|
|
4
4
|
"description": "Helper to handle migrations in contentful",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"files": [
|
|
@@ -30,40 +30,39 @@
|
|
|
30
30
|
"content-type"
|
|
31
31
|
],
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@contentful/rich-text-plain-text-renderer": "^
|
|
34
|
-
"array.prototype.flatmap": "^1.2.
|
|
33
|
+
"@contentful/rich-text-plain-text-renderer": "^15.6.2",
|
|
34
|
+
"array.prototype.flatmap": "^1.2.5",
|
|
35
35
|
"ascii-tree": "^0.3.0",
|
|
36
36
|
"chalk": "^4.1.2",
|
|
37
37
|
"cli-progress": "^3.9.1",
|
|
38
|
-
"commander": "^
|
|
39
|
-
"common-tags": "^1.8.
|
|
38
|
+
"commander": "^8.3.0",
|
|
39
|
+
"common-tags": "^1.8.2",
|
|
40
40
|
"contentful-cli": "^1.8.23",
|
|
41
41
|
"contentful-import": "^8.2.22",
|
|
42
|
-
"contentful-management": "^7.
|
|
42
|
+
"contentful-management": "^7.45.2",
|
|
43
43
|
"contentful-migration": "^4.3.0",
|
|
44
|
-
"cosmiconfig": "^7.0.
|
|
44
|
+
"cosmiconfig": "^7.0.1",
|
|
45
45
|
"deep-diff": "^1.0.2",
|
|
46
46
|
"diff": "^5.0.0",
|
|
47
47
|
"dotenv": "^10.0.0",
|
|
48
48
|
"fs-extra": "^10.0.0",
|
|
49
|
-
"
|
|
50
|
-
"
|
|
51
|
-
"
|
|
52
|
-
"markdown-table": "^2.0.0",
|
|
49
|
+
"globby": "^12.0.2",
|
|
50
|
+
"inquirer": "^8.2.0",
|
|
51
|
+
"markdown-table": "^3.0.1",
|
|
53
52
|
"merge-options": "^3.0.4",
|
|
54
53
|
"mustache": "^4.2.0",
|
|
55
|
-
"node-fetch": "^
|
|
54
|
+
"node-fetch": "^3.1.0",
|
|
56
55
|
"node-object-hash": "^2.3.10",
|
|
57
|
-
"
|
|
56
|
+
"prettier": "^2.4.1",
|
|
57
|
+
"pkg-up": "^4.0.0"
|
|
58
58
|
},
|
|
59
59
|
"devDependencies": {
|
|
60
60
|
"babel-eslint": "^10.1.0",
|
|
61
|
-
"eslint": "^
|
|
61
|
+
"eslint": "^8.3.0",
|
|
62
62
|
"eslint-config-prettier": "^8.3.0",
|
|
63
|
-
"eslint-plugin-prettier": "^
|
|
64
|
-
"husky": "^
|
|
65
|
-
"lint-staged": "^
|
|
66
|
-
"prettier": "^2.3.2"
|
|
63
|
+
"eslint-plugin-prettier": "^4.0.0",
|
|
64
|
+
"husky": "^7.0.4",
|
|
65
|
+
"lint-staged": "^12.1.2"
|
|
67
66
|
},
|
|
68
67
|
"publishConfig": {
|
|
69
68
|
"access": "public"
|
|
@@ -107,5 +106,10 @@
|
|
|
107
106
|
"hooks": {
|
|
108
107
|
"pre-commit": "lint-staged"
|
|
109
108
|
}
|
|
109
|
+
},
|
|
110
|
+
"migrations": {
|
|
111
|
+
"storage": "content",
|
|
112
|
+
"migrationContentTypeId": "contentful-migrations",
|
|
113
|
+
"directory": "migrations"
|
|
110
114
|
}
|
|
111
115
|
}
|