@madgex/fert 5.0.1 → 5.0.3
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.
|
@@ -3,13 +3,16 @@ const chalk = require('chalk');
|
|
|
3
3
|
const {
|
|
4
4
|
loadServiceConfigFiles,
|
|
5
5
|
loadConfigFromFile,
|
|
6
|
+
findFertConfigDir,
|
|
6
7
|
} = require('../utils/index.js');
|
|
8
|
+
const { getConfigAPI, validateLocalConfigs } = require('../utils/configs.js');
|
|
9
|
+
const { handlePublish } = require('../commands/configs.js');
|
|
7
10
|
|
|
8
11
|
const commandDevSever = require('./dev-server.js');
|
|
9
12
|
const commandBuild = require('./build.js');
|
|
10
13
|
const commandPublish = require('./publish.js');
|
|
11
|
-
const { log } = require('../utils/logging.js');
|
|
12
14
|
const { FERT_SERVICE_CONFIG_FILENAME } = require('../../constants.js');
|
|
15
|
+
const { log } = require('../utils/logging.js');
|
|
13
16
|
|
|
14
17
|
const commandMap = {
|
|
15
18
|
dev: commandDevSever,
|
|
@@ -33,39 +36,79 @@ module.exports.serivceCommandBootstrap = async function serivceCommandBootstrap(
|
|
|
33
36
|
}
|
|
34
37
|
|
|
35
38
|
// explicitly run a single service - if we are inside a service folder
|
|
39
|
+
let cwdServiceConfig;
|
|
36
40
|
try {
|
|
37
41
|
// try to load service config in process.cwd
|
|
38
|
-
|
|
42
|
+
cwdServiceConfig = await loadConfigFromFile({
|
|
39
43
|
dir: process.cwd(),
|
|
40
44
|
filename: FERT_SERVICE_CONFIG_FILENAME,
|
|
41
45
|
silent: true,
|
|
42
46
|
});
|
|
43
|
-
if (serviceConfig) {
|
|
44
|
-
console.log(`🔦 ${chalk.green('Running single service mode')}`);
|
|
45
|
-
await commandMap[command]({
|
|
46
|
-
...options,
|
|
47
|
-
serviceName: serviceConfig.serviceName,
|
|
48
|
-
});
|
|
49
|
-
return;
|
|
50
|
-
}
|
|
51
47
|
} catch {
|
|
52
48
|
// no service config file in cwd
|
|
53
49
|
}
|
|
54
|
-
|
|
50
|
+
// found service config in cwd, only load this single service
|
|
51
|
+
if (cwdServiceConfig) {
|
|
52
|
+
console.log(`🔦 ${chalk.green('Running single service mode')}`);
|
|
53
|
+
await commandMap[command]({
|
|
54
|
+
...options,
|
|
55
|
+
serviceName: cwdServiceConfig.serviceName,
|
|
56
|
+
});
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
55
59
|
// not single service - try to find and load all services
|
|
56
60
|
const serviceConfigs = await loadServiceConfigFiles();
|
|
61
|
+
|
|
62
|
+
if (!serviceConfigs.length) {
|
|
63
|
+
log.error('No services found\n');
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
|
|
57
67
|
console.log(
|
|
58
|
-
`🔦 ${chalk.green('Running multi service mode')},
|
|
68
|
+
`🔦 ${chalk.green('Running multi service mode')}, calling ${chalk.cyan(command)} on ${chalk.cyan(serviceConfigs.length)} ${serviceConfigs.length === 1 ? 'service' : 'services'} [${serviceConfigs.map((i) => chalk.cyanBright(i?.serviceConfig?.serviceName || 'Unknown')).join(', ')}]`
|
|
59
69
|
);
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
70
|
+
try {
|
|
71
|
+
for (const { serviceConfig } of serviceConfigs) {
|
|
72
|
+
try {
|
|
73
|
+
await commandMap[command]({
|
|
74
|
+
...options,
|
|
75
|
+
serviceName: serviceConfig.serviceName,
|
|
76
|
+
});
|
|
77
|
+
} catch (err) {
|
|
78
|
+
throw new Error(
|
|
79
|
+
`Failed to run command ${command} on service ${serviceConfig.serviceName}: ${err.message}`,
|
|
80
|
+
{ cause: err }
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// if we're publishing, update all configs on configuration API
|
|
86
|
+
if (command === 'publish') {
|
|
87
|
+
options.target = options.target === 'dev' ? 'dev' : 'production';
|
|
88
|
+
const rootDir = await findFertConfigDir();
|
|
89
|
+
const { clientPropertyId } = await loadConfigFromFile({ dir: rootDir });
|
|
90
|
+
const api = await getConfigAPI({
|
|
91
|
+
clientPropertyId,
|
|
92
|
+
environment: options.target,
|
|
65
93
|
});
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
94
|
+
|
|
95
|
+
// validate & upload local configs to configuration API
|
|
96
|
+
if (!options.dryRun) {
|
|
97
|
+
await handlePublish({ clientPropertyId, workingDir: rootDir }, api, {
|
|
98
|
+
publish: options.target,
|
|
99
|
+
});
|
|
100
|
+
} else {
|
|
101
|
+
await validateLocalConfigs({
|
|
102
|
+
clientPropertyId,
|
|
103
|
+
workingDir: rootDir,
|
|
104
|
+
throwable: true,
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
log.success(`${chalk.green('Publish completed successfully')}`);
|
|
69
109
|
}
|
|
110
|
+
} catch (error) {
|
|
111
|
+
log.error('Failed to run command', command, options);
|
|
112
|
+
console.error(error);
|
|
70
113
|
}
|
|
71
114
|
};
|
package/bin/commands/configs.js
CHANGED
package/bin/commands/publish.js
CHANGED
|
@@ -5,10 +5,6 @@ const { resolveConfig } = require('../utils');
|
|
|
5
5
|
const { log } = require('../utils/logging');
|
|
6
6
|
const getAwsParam = require('./publish-tasks/get-aws-parameter');
|
|
7
7
|
const AssetStoreUploader = require('./publish-tasks/asset-store-uploader');
|
|
8
|
-
const {
|
|
9
|
-
updateProjectConfigs,
|
|
10
|
-
validateLocalConfigs,
|
|
11
|
-
} = require('../utils/configs.js');
|
|
12
8
|
const {
|
|
13
9
|
getCloudFrontDistributionsForDomain,
|
|
14
10
|
} = require('../utils/lookup-cf-distribution-ids');
|
|
@@ -23,7 +19,12 @@ const {
|
|
|
23
19
|
|
|
24
20
|
module.exports = async (options) => {
|
|
25
21
|
const fertConfig = await resolveConfig(options);
|
|
26
|
-
const validTargets = ['dev', 'prod'];
|
|
22
|
+
const validTargets = ['dev', 'prod', 'production'];
|
|
23
|
+
|
|
24
|
+
// handle legacy options
|
|
25
|
+
if (options.target === 'prod') {
|
|
26
|
+
options.target = 'production';
|
|
27
|
+
}
|
|
27
28
|
|
|
28
29
|
if (!validTargets.includes(options.target)) {
|
|
29
30
|
throw Error(
|
|
@@ -36,8 +37,8 @@ module.exports = async (options) => {
|
|
|
36
37
|
}
|
|
37
38
|
|
|
38
39
|
const templateCtx = {
|
|
39
|
-
env: options.target === '
|
|
40
|
-
Environment_Name: options.target
|
|
40
|
+
env: options.target === 'dev' ? 'jb.dev' : 'job',
|
|
41
|
+
Environment_Name: options.target,
|
|
41
42
|
fertConfig,
|
|
42
43
|
};
|
|
43
44
|
|
|
@@ -82,19 +83,6 @@ module.exports = async (options) => {
|
|
|
82
83
|
if (!options.dryRun) {
|
|
83
84
|
uploadResult = await assetStore.uploadDir(localDir);
|
|
84
85
|
}
|
|
85
|
-
// validate & upload local configs to configuration API
|
|
86
|
-
if (!options.dryRun) {
|
|
87
|
-
await updateProjectConfigs({
|
|
88
|
-
workingDir: fertConfig.rootDir,
|
|
89
|
-
clientPropertyId: fertConfig.clientPropertyId,
|
|
90
|
-
environment: fertConfig.currBranch === 'master' ? 'production' : 'dev',
|
|
91
|
-
});
|
|
92
|
-
} else {
|
|
93
|
-
await validateLocalConfigs({
|
|
94
|
-
workingDir: fertConfig.rootDir,
|
|
95
|
-
clientPropertyId: fertConfig.clientPropertyId,
|
|
96
|
-
});
|
|
97
|
-
}
|
|
98
86
|
|
|
99
87
|
log.success(
|
|
100
88
|
`Publish complete in ${((uploadResult?.duration || 1) / 1000).toFixed(0)}s\n`
|
package/bin/utils/configs.js
CHANGED
|
@@ -3,7 +3,6 @@ const path = require('node:path');
|
|
|
3
3
|
const Hoek = require('@hapi/hoek');
|
|
4
4
|
const { log } = require('../utils/logging');
|
|
5
5
|
const chalk = require('chalk');
|
|
6
|
-
const ora = require('ora');
|
|
7
6
|
const { CONFIG_DIR } = require('../../constants');
|
|
8
7
|
|
|
9
8
|
/**
|
|
@@ -12,7 +11,7 @@ const { CONFIG_DIR } = require('../../constants');
|
|
|
12
11
|
* @param {Object} options - required, The options object.
|
|
13
12
|
* @param {string} options.workingDir - required, Working dir for configs - should usually be 'root' where fert.config.js is
|
|
14
13
|
* @param {string} options.clientPropertyId - required, clientPropertyId
|
|
15
|
-
* @param {boolean} options.throwable -
|
|
14
|
+
* @param {boolean} [options.throwable=false] - Whether to throw validation errors or just log them.
|
|
16
15
|
*
|
|
17
16
|
* @returns {Promise<void>} - Returns a promise that resolves if all configurations are valid.
|
|
18
17
|
* @throws {Error} - Throws an error if any configuration is invalid.
|
|
@@ -153,7 +152,6 @@ const updateProjectConfigs = async ({
|
|
|
153
152
|
Hoek.assert(workingDir, 'workingDir is required');
|
|
154
153
|
|
|
155
154
|
const dir = path.join(workingDir, CONFIG_DIR);
|
|
156
|
-
let spinner;
|
|
157
155
|
|
|
158
156
|
if (!fs.existsSync(dir)) {
|
|
159
157
|
log.debug(`Directory does not exist: ${dir}`);
|
|
@@ -164,22 +162,22 @@ const updateProjectConfigs = async ({
|
|
|
164
162
|
try {
|
|
165
163
|
await validateLocalConfigs({ clientPropertyId, workingDir });
|
|
166
164
|
const api = await getConfigAPI({ clientPropertyId, environment });
|
|
167
|
-
|
|
165
|
+
|
|
166
|
+
log.info('Updating configs…');
|
|
168
167
|
|
|
169
168
|
const localConfigs = loadLocalConfigs(workingDir);
|
|
170
169
|
const { toRemove, toSet } = collateConfigs(api, localConfigs);
|
|
171
170
|
|
|
172
|
-
|
|
173
|
-
await setConfigs(api, toSet,
|
|
174
|
-
await removeConfigs(api, toRemove, spinner);
|
|
171
|
+
// Will throw on first failure
|
|
172
|
+
await Promise.all([setConfigs(api, toSet), removeConfigs(api, toRemove)]);
|
|
175
173
|
|
|
176
174
|
const { host } = new URL(api.options.apiUrl);
|
|
177
|
-
|
|
178
|
-
|
|
175
|
+
log.info(``);
|
|
176
|
+
log.info(`Project configs applied to ${chalk.bold(host)}`);
|
|
179
177
|
return true;
|
|
180
178
|
} catch (error) {
|
|
181
|
-
|
|
182
|
-
log.error(error);
|
|
179
|
+
log.info(``);
|
|
180
|
+
log.error('Failed to apply project configs', error);
|
|
183
181
|
throw error;
|
|
184
182
|
}
|
|
185
183
|
};
|
|
@@ -241,32 +239,45 @@ const getUnsetKeysWithDefaults = (defaults = {}, config = {}) => {
|
|
|
241
239
|
};
|
|
242
240
|
|
|
243
241
|
const removeConfigs = async (api, toRemove) => {
|
|
244
|
-
const
|
|
245
|
-
(
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
242
|
+
const operations = Object.entries(toRemove).flatMap(([configName, keys]) =>
|
|
243
|
+
keys.map(async (key) => {
|
|
244
|
+
try {
|
|
245
|
+
await api.deleteConfig(configName, key);
|
|
246
|
+
log.success(
|
|
247
|
+
`Successfully removed ${chalk.bold(key)} from ${chalk.bold(configName)}`
|
|
248
|
+
);
|
|
249
|
+
} catch (error) {
|
|
250
|
+
log.error(
|
|
251
|
+
`Failed to remove ${chalk.bold(key)} from ${chalk.bold(configName)}:`,
|
|
252
|
+
error
|
|
253
|
+
);
|
|
254
|
+
throw error;
|
|
255
|
+
}
|
|
256
|
+
})
|
|
253
257
|
);
|
|
254
258
|
|
|
255
|
-
|
|
259
|
+
return Promise.all(operations);
|
|
256
260
|
};
|
|
257
261
|
|
|
258
262
|
const setConfigs = async (api, toSet) => {
|
|
259
|
-
const
|
|
263
|
+
const operations = Object.entries(toSet).flatMap(([configName, config]) =>
|
|
260
264
|
Object.entries(config).map(async ([key, value]) => {
|
|
261
265
|
try {
|
|
262
266
|
await api.setConfig(configName, key, value);
|
|
267
|
+
log.success(
|
|
268
|
+
`Successfully set ${chalk.bold(key)} in ${chalk.bold(configName)}`
|
|
269
|
+
);
|
|
263
270
|
} catch (error) {
|
|
264
|
-
log.error(
|
|
271
|
+
log.error(
|
|
272
|
+
`Failed to set ${chalk.bold(key)} in ${chalk.bold(configName)}:`,
|
|
273
|
+
error
|
|
274
|
+
);
|
|
275
|
+
throw error;
|
|
265
276
|
}
|
|
266
277
|
})
|
|
267
278
|
);
|
|
268
279
|
|
|
269
|
-
|
|
280
|
+
return Promise.all(operations);
|
|
270
281
|
};
|
|
271
282
|
|
|
272
283
|
module.exports = {
|
package/bin/utils/index.js
CHANGED