@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
- const serviceConfig = await loadConfigFromFile({
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')}, searching for services`
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
- for (const { serviceConfig } of serviceConfigs) {
61
- try {
62
- await commandMap[command]({
63
- ...options,
64
- serviceName: serviceConfig.serviceName,
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
- } catch (error) {
67
- log.error('Failed to run command', command, options, serviceConfig);
68
- console.error(error);
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
  };
@@ -102,7 +102,6 @@ const handlePublish = async (
102
102
  clientPropertyId,
103
103
  environment: options.publish,
104
104
  });
105
- console.log('upload');
106
105
  } catch (err) {
107
106
  log.debug(err);
108
107
  process.exit(1);
@@ -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 === 'prod' ? 'job' : 'jb.dev',
40
- Environment_Name: options.target === 'prod' ? 'production' : 'dev',
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`
@@ -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 - default: true, Whether to throw validation errors or just log them.
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
- spinner = ora('Fetching local configs').start();
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
- spinner.text = 'Updating configs…';
173
- await setConfigs(api, toSet, spinner);
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
- spinner.succeed(`Project configs applied to ${chalk.bold(host)}`);
178
-
175
+ log.info(``);
176
+ log.info(`Project configs applied to ${chalk.bold(host)}`);
179
177
  return true;
180
178
  } catch (error) {
181
- spinner.fail('Failed to apply project configs');
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 deletePromises = Object.entries(toRemove).flatMap(
245
- ([configName, keys]) =>
246
- keys.map(async (key) => {
247
- try {
248
- await api.deleteConfig(configName, key);
249
- } catch (error) {
250
- log.error('Failed to remove config', { configName, key, error });
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
- await Promise.all(deletePromises);
259
+ return Promise.all(operations);
256
260
  };
257
261
 
258
262
  const setConfigs = async (api, toSet) => {
259
- const setPromises = Object.entries(toSet).flatMap(([configName, config]) =>
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('Failed to set config', { configName, key, value, 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
- await Promise.all(setPromises);
280
+ return Promise.all(operations);
270
281
  };
271
282
 
272
283
  module.exports = {
@@ -47,6 +47,8 @@ exports.resolveConfig = async (options = {}) => {
47
47
  },
48
48
  };
49
49
 
50
+ Hoek.assert(options.serviceName, 'serviceName is required');
51
+
50
52
  const rootDir = await this.findFertConfigDir();
51
53
  const git = simpleGit({ baseDir: rootDir });
52
54
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@madgex/fert",
3
- "version": "5.0.1",
3
+ "version": "5.0.3",
4
4
  "description": "Tool to help build the V6 branding",
5
5
  "bin": {
6
6
  "fert": "./bin/cli.js"