@jungvonmatt/contentful-migrations 4.0.1 → 5.0.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/README.md +3 -1
- package/index.js +25 -10
- package/lib/backend.js +11 -6
- 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 +19 -8
- package/package.json +19 -14
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
|
@@ -13,9 +13,9 @@ const { createMigration, runMigrations, fetchMigration, executeMigration } = req
|
|
|
13
13
|
const { versionDelete, versionAdd } = require('./lib/version');
|
|
14
14
|
const { transferContent } = require('./lib/content');
|
|
15
15
|
const { createOfflineDocs } = require('./lib/doc');
|
|
16
|
+
const { createEnvironment, removeEnvironment, resetEnvironment } = require('./lib/environment');
|
|
16
17
|
const { getConfig, askAll, askMissing, STORAGE_CONTENT, STORAGE_TAG } = require('./lib/config');
|
|
17
18
|
const pkg = require('./package.json');
|
|
18
|
-
const { createEnvironment, removeEnvironment } = require('./lib/environment');
|
|
19
19
|
|
|
20
20
|
require('dotenv').config();
|
|
21
21
|
|
|
@@ -30,6 +30,7 @@ const parseArgs = (cmd) => {
|
|
|
30
30
|
destEnvironmentId: cmd.destEnvironmentId || parent.destEnvironmentId,
|
|
31
31
|
verbose: cmd.verbose || parent.verbose,
|
|
32
32
|
template: cmd.template || parent.template,
|
|
33
|
+
yes: cmd.yes || parent.yes,
|
|
33
34
|
extension: cmd.extension || parent.extension,
|
|
34
35
|
bail: cmd.bail || parent.bail,
|
|
35
36
|
};
|
|
@@ -47,7 +48,10 @@ const errorHandler = (error, log) => {
|
|
|
47
48
|
};
|
|
48
49
|
|
|
49
50
|
const actionRunner = (fn, log = true) => {
|
|
50
|
-
return (...args) =>
|
|
51
|
+
return (...args) => {
|
|
52
|
+
const verbose = args.some((arg) => arg.verbose);
|
|
53
|
+
return fn(...args).catch((error) => errorHandler(error, verbose || log));
|
|
54
|
+
};
|
|
51
55
|
};
|
|
52
56
|
|
|
53
57
|
const program = new Command();
|
|
@@ -60,7 +64,7 @@ program
|
|
|
60
64
|
actionRunner(async (cmd) => {
|
|
61
65
|
const config = await getConfig(parseArgs(cmd || {}));
|
|
62
66
|
const verified = await askAll(config);
|
|
63
|
-
const { managementToken, accessToken, environmentId, ...rest } = verified;
|
|
67
|
+
const { managementToken, accessToken, environmentId, spaceId, ...rest } = verified;
|
|
64
68
|
|
|
65
69
|
if (verified.storage === STORAGE_CONTENT) {
|
|
66
70
|
await initializeContentModel({ ...config, ...verified });
|
|
@@ -70,6 +74,10 @@ program
|
|
|
70
74
|
await migrateToTagStorage({ ...config, ...verified });
|
|
71
75
|
}
|
|
72
76
|
|
|
77
|
+
if (!process.env.CONTENTFUL_SPACE_ID) {
|
|
78
|
+
rest.spaceId = spaceId;
|
|
79
|
+
}
|
|
80
|
+
|
|
73
81
|
// try to store in package.json
|
|
74
82
|
const localPkg = await pkgUp();
|
|
75
83
|
if (localPkg) {
|
|
@@ -121,6 +129,7 @@ program
|
|
|
121
129
|
.option('-e, --environment-id <environment-id>', 'Change the Contentful environment')
|
|
122
130
|
.option('-p, --path <path/to/migrations>', 'Change the path where the migrations are stored')
|
|
123
131
|
.option('-v, --verbose', 'Verbosity')
|
|
132
|
+
.option('-y, --yes', 'Assume "yes" as answer to all prompts and run non-interactively.')
|
|
124
133
|
.option('--bail', 'Abort execution after first failed migration (default: true)', true)
|
|
125
134
|
.option('--no-bail', 'Ignore failed migrations')
|
|
126
135
|
.description('Execute all unexecuted migrations available.')
|
|
@@ -139,13 +148,15 @@ program
|
|
|
139
148
|
}
|
|
140
149
|
|
|
141
150
|
await runMigrations(verified);
|
|
142
|
-
},
|
|
151
|
+
}, false)
|
|
143
152
|
);
|
|
144
153
|
|
|
145
154
|
program
|
|
146
155
|
.command('execute <file>')
|
|
147
156
|
.option('-s, --space-id <space-id>', 'Contentful space id')
|
|
148
157
|
.option('-e, --environment-id <environment-id>', 'Change the Contentful environment')
|
|
158
|
+
.option('-v, --verbose', 'Verbosity')
|
|
159
|
+
.option('-y, --yes', 'Assume "yes" as answer to all prompts and run non-interactively.')
|
|
149
160
|
.description('Execute a single migration.')
|
|
150
161
|
.action(
|
|
151
162
|
actionRunner(async (file, options) => {
|
|
@@ -169,6 +180,7 @@ program
|
|
|
169
180
|
.command('version <file>')
|
|
170
181
|
.option('-s, --space-id <space-id>', 'Contentful space id')
|
|
171
182
|
.option('-e, --environment-id <environment-id>', 'Change the Contentful environment')
|
|
183
|
+
.option('-v, --verbose', 'Verbosity')
|
|
172
184
|
.option('--add', 'Mark migration as migrated')
|
|
173
185
|
.option('--remove', 'Delete migration entry in Contentful')
|
|
174
186
|
.description('Manually mark a migration as migrated or not. (Only available with the Content-model storage)')
|
|
@@ -202,15 +214,17 @@ program
|
|
|
202
214
|
program
|
|
203
215
|
.command('environment <environment-id>')
|
|
204
216
|
.option('-s, --space-id <space-id>', 'Contentful space id')
|
|
217
|
+
.option('-v, --verbose', 'Verbosity')
|
|
205
218
|
.option('--create', 'Create new contentful environment')
|
|
206
219
|
.option('--remove', 'Delete contentful environment')
|
|
207
220
|
.option('--reset', 'Reset contentful environment')
|
|
221
|
+
.option('--source-environment-id <environment-id>', 'Set the source environment to clone new environment from')
|
|
208
222
|
.description('Add or remove a contentful environment for migrations')
|
|
209
223
|
.action(
|
|
210
224
|
actionRunner(async (environmentId, options) => {
|
|
211
|
-
const { remove, create } = options;
|
|
212
|
-
const config = await getConfig(parseArgs(options || {}));
|
|
213
|
-
const verified = await askMissing(config);
|
|
225
|
+
const { remove, create, reset } = options;
|
|
226
|
+
const config = await getConfig(parseArgs({ ...(options || {}), environmentId }));
|
|
227
|
+
const verified = await askMissing(config, ['accessToken', 'spaceId', 'environmentId']);
|
|
214
228
|
|
|
215
229
|
if (create) {
|
|
216
230
|
return createEnvironment(environmentId, verified);
|
|
@@ -238,7 +252,7 @@ program
|
|
|
238
252
|
.action(
|
|
239
253
|
actionRunner(async (cmd) => {
|
|
240
254
|
const config = await getConfig(parseArgs(cmd || {}));
|
|
241
|
-
const verified = await askMissing(config);
|
|
255
|
+
const verified = await askMissing(config, ['accessToken', 'spaceId', 'environmentId']);
|
|
242
256
|
await createOfflineDocs(verified);
|
|
243
257
|
}, true)
|
|
244
258
|
);
|
|
@@ -249,14 +263,15 @@ program
|
|
|
249
263
|
.requiredOption('--dest-environment-id <environment-id>', 'Set the Contentful destination environment (to)')
|
|
250
264
|
.option('-s, --space-id <space-id>', 'Contentful space id')
|
|
251
265
|
.option('-c, --content-type <content-type>', 'Specify content-type')
|
|
266
|
+
.option('-v, --verbose', 'Verbosity')
|
|
267
|
+
.option('-y, --yes', 'Assume "yes" as answer to all prompts and run non-interactively.')
|
|
252
268
|
.option('--diff', 'Manually choose skip/overwrite for every conflict')
|
|
253
269
|
.option('--force', 'No manual diffing. Overwrites all conflicting entries/assets')
|
|
254
|
-
.option('-v, --verbose', 'Verbosity')
|
|
255
270
|
.description('Transfer content from source environment to destination environment')
|
|
256
271
|
.action(
|
|
257
272
|
actionRunner(async (cmd) => {
|
|
258
273
|
const config = await getConfig(parseArgs(cmd || {}));
|
|
259
|
-
const verified = await askMissing(config);
|
|
274
|
+
const verified = await askMissing({ ...config, environmentId: 'not-used' });
|
|
260
275
|
|
|
261
276
|
// run migrations on destination environment
|
|
262
277
|
await transferContent({
|
package/lib/backend.js
CHANGED
|
@@ -360,14 +360,19 @@ const getNewMigrations = async (config) => {
|
|
|
360
360
|
const migrations = await globby(`${directory}/*.js`);
|
|
361
361
|
|
|
362
362
|
if (storage === STORAGE_CONTENT) {
|
|
363
|
-
|
|
363
|
+
try {
|
|
364
|
+
const versions = await getMigrationVersions(config);
|
|
364
365
|
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
366
|
+
return migrations.filter((file) => {
|
|
367
|
+
const name = path.basename(file);
|
|
368
|
+
const [, num] = /^(\d+)-/.exec(name);
|
|
368
369
|
|
|
369
|
-
|
|
370
|
-
|
|
370
|
+
return !(versions || []).includes(parseInt(num, 10));
|
|
371
|
+
});
|
|
372
|
+
} catch (error) {
|
|
373
|
+
console.error(chalk.red('\nError:'), `Missing migration content type. Run ${chalk.cyan('npx migrations init')}`);
|
|
374
|
+
process.exit(1);
|
|
375
|
+
}
|
|
371
376
|
}
|
|
372
377
|
|
|
373
378
|
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
|
@@ -9,7 +9,7 @@ const {
|
|
|
9
9
|
const chalk = require('chalk');
|
|
10
10
|
const { getEnvironment } = require('./contentful');
|
|
11
11
|
|
|
12
|
-
const { STATE_SUCCESS, STATE_FAILURE } = require('./config');
|
|
12
|
+
const { confirm, STATE_SUCCESS, STATE_FAILURE } = require('./config');
|
|
13
13
|
|
|
14
14
|
const { storeMigration, getNewMigrations } = require('./backend');
|
|
15
15
|
|
|
@@ -69,7 +69,6 @@ const executeMigration = async (file, config) => {
|
|
|
69
69
|
const { accessToken, spaceId } = config || {};
|
|
70
70
|
const client = await getEnvironment(config);
|
|
71
71
|
const environmentId = client.sys.id;
|
|
72
|
-
|
|
73
72
|
const name = path.basename(file);
|
|
74
73
|
const [, version] = /^(\d+)-/.exec(name);
|
|
75
74
|
|
|
@@ -81,7 +80,11 @@ const executeMigration = async (file, config) => {
|
|
|
81
80
|
yes: true,
|
|
82
81
|
};
|
|
83
82
|
|
|
84
|
-
console.log(`\nRun migration ${chalk.green(version)}
|
|
83
|
+
console.log(`\nRun migration ${chalk.green(version)} in environment ${chalk.green(environmentId)}`);
|
|
84
|
+
const proceed = await confirm(config);
|
|
85
|
+
if (!proceed) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
85
88
|
try {
|
|
86
89
|
await runMigration(options);
|
|
87
90
|
await storeMigration({ version, name, state: STATE_SUCCESS }, config);
|
|
@@ -104,17 +107,25 @@ const runMigrations = async (config) => {
|
|
|
104
107
|
|
|
105
108
|
const client = await getEnvironment(config);
|
|
106
109
|
const environmentId = client.sys.id;
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
+
let migrations = [];
|
|
111
|
+
try {
|
|
112
|
+
migrations = await getNewMigrations(config);
|
|
113
|
+
} catch (error) {
|
|
114
|
+
console.log(error.message);
|
|
115
|
+
throw error;
|
|
116
|
+
}
|
|
110
117
|
console.log(
|
|
111
118
|
`Found ${chalk.green(migrations.length)} unexecuted migrations in environment ${chalk.green(environmentId)}`
|
|
112
119
|
);
|
|
113
120
|
|
|
114
|
-
|
|
121
|
+
const proceed = migrations.length === 0 || (await confirm(config));
|
|
122
|
+
if (!proceed) {
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
|
|
115
126
|
for await (const file of migrations) {
|
|
116
127
|
try {
|
|
117
|
-
|
|
128
|
+
await executeMigration(file, { ...config, yes: true });
|
|
118
129
|
} catch (error) {
|
|
119
130
|
if (bail) {
|
|
120
131
|
throw error;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jungvonmatt/contentful-migrations",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.0.1",
|
|
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.5.1",
|
|
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": "^
|
|
38
|
+
"commander": "^8.3.0",
|
|
39
39
|
"common-tags": "^1.8.0",
|
|
40
40
|
"contentful-cli": "^1.8.23",
|
|
41
41
|
"contentful-import": "^8.2.22",
|
|
42
42
|
"contentful-management": "^7.32.0",
|
|
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
|
-
"git-branch": "^2.0.1",
|
|
50
49
|
"globby": "^11.0.3",
|
|
51
|
-
"inquirer": "^8.
|
|
52
|
-
"markdown-table": "^
|
|
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.0.0",
|
|
56
55
|
"node-object-hash": "^2.3.10",
|
|
57
56
|
"pkg-up": "^3.1.0"
|
|
58
57
|
},
|
|
59
58
|
"devDependencies": {
|
|
60
59
|
"babel-eslint": "^10.1.0",
|
|
61
|
-
"eslint": "^
|
|
60
|
+
"eslint": "^8.1.0",
|
|
62
61
|
"eslint-config-prettier": "^8.3.0",
|
|
63
|
-
"eslint-plugin-prettier": "^
|
|
64
|
-
"husky": "^
|
|
65
|
-
"lint-staged": "^11.
|
|
66
|
-
"prettier": "^2.
|
|
62
|
+
"eslint-plugin-prettier": "^4.0.0",
|
|
63
|
+
"husky": "^7.0.4",
|
|
64
|
+
"lint-staged": "^11.2.6",
|
|
65
|
+
"prettier": "^2.4.1"
|
|
67
66
|
},
|
|
68
67
|
"publishConfig": {
|
|
69
68
|
"access": "public"
|
|
@@ -107,5 +106,11 @@
|
|
|
107
106
|
"hooks": {
|
|
108
107
|
"pre-commit": "lint-staged"
|
|
109
108
|
}
|
|
109
|
+
},
|
|
110
|
+
"migrations": {
|
|
111
|
+
"spaceId": "89na9aswdzfj",
|
|
112
|
+
"storage": "content",
|
|
113
|
+
"migrationContentTypeId": "contentful-migrations",
|
|
114
|
+
"directory": "migrations"
|
|
110
115
|
}
|
|
111
116
|
}
|