@suitegeezus/suitecloud-cli 3.1.4 → 3.1.5
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/package.json +1 -1
- package/src/CLI.js +12 -2
- package/src/commands/Command.js +15 -7
- package/src/core/CommandActionExecutor.js +35 -16
package/package.json
CHANGED
package/src/CLI.js
CHANGED
|
@@ -148,7 +148,7 @@ module.exports = class CLI {
|
|
|
148
148
|
"name": "customflag",
|
|
149
149
|
"option": "customflag",
|
|
150
150
|
"description": "A custom boolean that will be passed into the hooks. Has no effect unless you implement the logic in a hook",
|
|
151
|
-
"mandatory":
|
|
151
|
+
"mandatory": true,
|
|
152
152
|
"type": "FLAG",
|
|
153
153
|
"usage": "",
|
|
154
154
|
"defaultOption": false,
|
|
@@ -168,11 +168,21 @@ module.exports = class CLI {
|
|
|
168
168
|
"name": "debug",
|
|
169
169
|
"option": "debug",
|
|
170
170
|
"description": "Directory to dump debug data for debugging. Creates a unique {command}.{datetime}.json file per run",
|
|
171
|
-
"mandatory":
|
|
171
|
+
"mandatory": true,
|
|
172
172
|
"type": "SINGLE",
|
|
173
173
|
"usage": "\"./debug-output\"",
|
|
174
174
|
"defaultOption": false,
|
|
175
175
|
"disableInIntegrationMode": false
|
|
176
|
+
},
|
|
177
|
+
"skiphooks": {
|
|
178
|
+
"name": "skiphooks",
|
|
179
|
+
"option": "skiphooks",
|
|
180
|
+
"description": "Skip hook execution. Values: 'pre' (skip beforeExecuting), 'post' (skip onCompleted/onError), 'all' (skip all hooks)",
|
|
181
|
+
"mandatory": true,
|
|
182
|
+
"type": "SINGLE",
|
|
183
|
+
"usage": "\"pre|post|all|none\"",
|
|
184
|
+
"defaultOption": "none",
|
|
185
|
+
"disableInIntegrationMode": false
|
|
176
186
|
}
|
|
177
187
|
}
|
|
178
188
|
);
|
package/src/commands/Command.js
CHANGED
|
@@ -24,7 +24,8 @@ const NEVER_PARAMS = {
|
|
|
24
24
|
'NOCONFIG': 'noconfig',
|
|
25
25
|
'CUSTOMFLAG': 'customflag',
|
|
26
26
|
'CUSTOMOPTIONS': 'customoptions',
|
|
27
|
-
'DEBUG': 'debug'
|
|
27
|
+
'DEBUG': 'debug',
|
|
28
|
+
'SKIPHOOKS': 'skiphooks'
|
|
28
29
|
};
|
|
29
30
|
|
|
30
31
|
/** @type {import('./Command')} */
|
|
@@ -71,7 +72,7 @@ class Command {
|
|
|
71
72
|
|
|
72
73
|
const preExec = await this._action.preExecute(execParams);
|
|
73
74
|
|
|
74
|
-
this._validateActionParameters(preExec);
|
|
75
|
+
this._validateActionParameters(preExec, Object.values(NEVER_PARAMS));
|
|
75
76
|
|
|
76
77
|
const exec = await this._action.execute(preExec);
|
|
77
78
|
const actionResult = await this._action.postExecute(exec);
|
|
@@ -91,12 +92,19 @@ class Command {
|
|
|
91
92
|
})
|
|
92
93
|
}
|
|
93
94
|
|
|
94
|
-
_validateActionParameters(params) {
|
|
95
|
+
_validateActionParameters(params, ignores=[]) {
|
|
95
96
|
const commandOptionsValidator = new CommandOptionsValidator();
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
97
|
+
|
|
98
|
+
// remove NEVER_PARAMS from this validation as they don't get passed down to the exec layer
|
|
99
|
+
const options = ignores.reduce((acc,ignore)=> {
|
|
100
|
+
delete acc.commandOptions[ignore];
|
|
101
|
+
return acc;
|
|
102
|
+
},{
|
|
103
|
+
commandOptions: this._commandMetadata.options,
|
|
104
|
+
arguments: params,
|
|
105
|
+
}
|
|
106
|
+
);
|
|
107
|
+
const validationErrors = commandOptionsValidator.validate(options);
|
|
100
108
|
if (validationErrors.length > 0) {
|
|
101
109
|
throwValidationException(validationErrors, this._runInInteractiveMode, this._commandMetadata);
|
|
102
110
|
}
|
|
@@ -52,6 +52,7 @@ module.exports = class CommandActionExecutor {
|
|
|
52
52
|
let commandUserExtension;
|
|
53
53
|
const commandName = context.commandName;
|
|
54
54
|
const debugFilePath = this._getDebugFilePath(context.arguments.debug, commandName);
|
|
55
|
+
const skipHooks = context.arguments.skiphooks;
|
|
55
56
|
try {
|
|
56
57
|
const commandMetadata = this._commandsMetadataService.getCommandMetadataByName(commandName);
|
|
57
58
|
if (context.arguments.config) {
|
|
@@ -68,6 +69,7 @@ module.exports = class CommandActionExecutor {
|
|
|
68
69
|
const runInInteractiveMode = context.runInInteractiveMode;
|
|
69
70
|
const commandArguments = this._extractOptionValuesFromArguments(commandMetadata.options, context.arguments);
|
|
70
71
|
|
|
72
|
+
|
|
71
73
|
// need the
|
|
72
74
|
const projectFolder = this._cliConfigurationService.getProjectFolder(commandName, commandArguments?.project);
|
|
73
75
|
const projectPath = this._cliConfigurationService.getProjectPath(projectFolder);
|
|
@@ -144,9 +146,12 @@ module.exports = class CommandActionExecutor {
|
|
|
144
146
|
process.env[ENV_VARS.SUITECLOUD_PROJECT_ROOT] = this._executionPath;
|
|
145
147
|
// this might modified but we need the user's hooks to take advantage of their current values
|
|
146
148
|
process.env[ENV_VARS.SUITECLOUD_AUTHID] = authId;
|
|
147
|
-
|
|
148
|
-
this._dumpDebugFile(debugFilePath, '
|
|
149
|
-
|
|
149
|
+
const skipPre = skipHooks === 'pre' || skipHooks === 'all';
|
|
150
|
+
this._dumpDebugFile(debugFilePath, undefined, 'FIRST', runInInteractiveMode, skipPre);
|
|
151
|
+
this._dumpDebugFile(debugFilePath, 'beforeExecuting', beforeExecutingOptions, runInInteractiveMode, skipPre);
|
|
152
|
+
const beforeExecutingOutput = skipPre
|
|
153
|
+
? { arguments: beforeExecutingOptions.arguments }
|
|
154
|
+
: await commandUserExtension.beforeExecuting(beforeExecutingOptions);
|
|
150
155
|
const overriddenArguments = beforeExecutingOutput.arguments;
|
|
151
156
|
|
|
152
157
|
// do not allow this argument to be overwritten (i.e. change it back)
|
|
@@ -156,7 +161,7 @@ module.exports = class CommandActionExecutor {
|
|
|
156
161
|
// update the authid as well (not recommended but possible) -- should this be in the isSetupRequired check?
|
|
157
162
|
// EXPERIMENTAL: ALLOW UPDATE OF THE AUTHID AT THIS TIME
|
|
158
163
|
if (commandMetadata.isSetupRequired)
|
|
159
|
-
authId = beforeExecutingOutput?.arguments?.authid || authId;
|
|
164
|
+
authId = beforeExecutingOutput?.['arguments']?.authid || authId;
|
|
160
165
|
|
|
161
166
|
if (commandMetadata.isSetupRequired && !context.arguments[AUTHORIZATION_PROPERTIES_KEYS.SKIP_AUHTORIZATION_CHECK]) {
|
|
162
167
|
// check if reauthz is needed to show proper message before continuing with the execution
|
|
@@ -173,6 +178,7 @@ module.exports = class CommandActionExecutor {
|
|
|
173
178
|
// command execution
|
|
174
179
|
// src/commands/Command.js, run(inputParams) => execution flow for all commands
|
|
175
180
|
const actionResult = await command.run(overriddenArguments);
|
|
181
|
+
const skipPost = skipHooks === 'post' || skipHooks === 'all';
|
|
176
182
|
|
|
177
183
|
if (context.runInInteractiveMode) {
|
|
178
184
|
// generate non-interactive equivalent
|
|
@@ -182,22 +188,23 @@ module.exports = class CommandActionExecutor {
|
|
|
182
188
|
|
|
183
189
|
if (actionResult.isSuccess() && commandUserExtension.onCompleted) {
|
|
184
190
|
// run onCompleted(output) from suitecloud.config.js
|
|
185
|
-
this._dumpDebugFile(debugFilePath, 'onCompleted', actionResult, runInInteractiveMode);
|
|
186
|
-
commandUserExtension.onCompleted(actionResult);
|
|
191
|
+
this._dumpDebugFile(debugFilePath, 'onCompleted', actionResult, runInInteractiveMode, skipPost);
|
|
192
|
+
if (!skipPost) commandUserExtension.onCompleted(actionResult);
|
|
187
193
|
} else if (!actionResult.isSuccess() && commandUserExtension.onError) {
|
|
188
194
|
// run onError(error) from suitecloud.config.js
|
|
189
195
|
const errorData = ActionResultUtils.getErrorMessagesString(actionResult);
|
|
190
|
-
this._dumpDebugFile(debugFilePath, 'onError', errorData, runInInteractiveMode);
|
|
191
|
-
commandUserExtension.onError(errorData);
|
|
196
|
+
this._dumpDebugFile(debugFilePath, 'onError', errorData, runInInteractiveMode, skipPost);
|
|
197
|
+
if (!skipPost) commandUserExtension.onError(errorData);
|
|
192
198
|
}
|
|
193
199
|
return actionResult;
|
|
194
200
|
|
|
195
201
|
} catch (error) {
|
|
196
202
|
let errorMessage = this._logGenericError(error);
|
|
203
|
+
const skipPostHooksCatch = skipHooks === 'post' || skipHooks === 'all';
|
|
197
204
|
if (commandUserExtension && commandUserExtension.onError) {
|
|
198
205
|
// run onError(error) from suitecloud.config.js
|
|
199
|
-
this._dumpDebugFile(debugFilePath, 'onError', error, context.runInInteractiveMode);
|
|
200
|
-
commandUserExtension.onError(error);
|
|
206
|
+
this._dumpDebugFile(debugFilePath, 'onError', error, context.runInInteractiveMode, skipPostHooksCatch);
|
|
207
|
+
if (!skipPostHooksCatch) commandUserExtension.onError(error);
|
|
201
208
|
}
|
|
202
209
|
return ActionResult.Builder.withErrors(Array.isArray(errorMessage) ? errorMessage : [errorMessage]).build();
|
|
203
210
|
}
|
|
@@ -245,8 +252,19 @@ module.exports = class CommandActionExecutor {
|
|
|
245
252
|
_extractOptionValuesFromArguments(options, args) {
|
|
246
253
|
const optionValues = {};
|
|
247
254
|
for (const optionId in options) {
|
|
248
|
-
|
|
249
|
-
|
|
255
|
+
const optionMeta = options[optionId];
|
|
256
|
+
// apply defaults first
|
|
257
|
+
if (options.hasOwnProperty(optionId)){
|
|
258
|
+
if(optionMeta?.mandatory) {
|
|
259
|
+
if (optionMeta?.type === "FLAG") {
|
|
260
|
+
optionValues[optionId] = Boolean(optionMeta.defaultOption)
|
|
261
|
+
} else if(!["undefined"].includes( typeof optionMeta?.defaultOption)){
|
|
262
|
+
optionValues[optionId] = optionMeta.defaultOption;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
if(args.hasOwnProperty(optionId)) {
|
|
266
|
+
optionValues[optionId] = args[optionId];
|
|
267
|
+
}
|
|
250
268
|
}
|
|
251
269
|
}
|
|
252
270
|
|
|
@@ -297,8 +315,7 @@ module.exports = class CommandActionExecutor {
|
|
|
297
315
|
_getDebugFilePath(debugDir, commandName) {
|
|
298
316
|
if (!debugDir) return null;
|
|
299
317
|
const sanitizedCommandName = commandName.replace(/:/g, '-');
|
|
300
|
-
const
|
|
301
|
-
const filename = `${sanitizedCommandName}.${datetime}.json`;
|
|
318
|
+
const filename = `${sanitizedCommandName}.json`;
|
|
302
319
|
return path.join(debugDir, filename);
|
|
303
320
|
}
|
|
304
321
|
|
|
@@ -308,9 +325,10 @@ module.exports = class CommandActionExecutor {
|
|
|
308
325
|
* @param hookName
|
|
309
326
|
* @param {'FIRST'|'LAST'|*} data
|
|
310
327
|
* @param {boolean} runInInteractiveMode
|
|
328
|
+
* @param {boolean} skipped
|
|
311
329
|
* @private
|
|
312
330
|
*/
|
|
313
|
-
_dumpDebugFile(debugFilePath, hookName, data, runInInteractiveMode = false) {
|
|
331
|
+
_dumpDebugFile(debugFilePath, hookName, data, runInInteractiveMode = false, skipped=false) {
|
|
314
332
|
if (!debugFilePath || !data ) return;
|
|
315
333
|
if (data === 'FIRST') {
|
|
316
334
|
fs.writeFileSync(debugFilePath, '[]');
|
|
@@ -323,9 +341,10 @@ module.exports = class CommandActionExecutor {
|
|
|
323
341
|
.forEach(key => { envVars[key] = process.env[key]; });
|
|
324
342
|
const entry = {
|
|
325
343
|
hook: hookName,
|
|
344
|
+
skipped,
|
|
326
345
|
timestamp: new Date().toISOString(),
|
|
327
346
|
env: envVars,
|
|
328
|
-
data: data
|
|
347
|
+
data: data,
|
|
329
348
|
};
|
|
330
349
|
|
|
331
350
|
// Print to console in interactive mode
|