@serverless-devs/engine 0.0.1-beta.9 → 0.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/lib/index.js CHANGED
@@ -22,6 +22,9 @@ var __importStar = (this && this.__importStar) || function (mod) {
22
22
  __setModuleDefault(result, mod);
23
23
  return result;
24
24
  };
25
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
26
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
27
+ };
25
28
  var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
26
29
  function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
27
30
  return new (P || (P = Promise))(function (resolve, reject) {
@@ -46,33 +49,49 @@ const actions_1 = __importDefault(require("./actions"));
46
49
  const credential_1 = __importDefault(require("@serverless-devs/credential"));
47
50
  const load_component_1 = __importDefault(require("@serverless-devs/load-component"));
48
51
  const logger_1 = __importDefault(require("@serverless-devs/logger"));
49
- const utils = __importStar(require("@serverless-devs/utils"));
50
52
  const utils_2 = require("@serverless-devs/utils");
51
53
  const constants_1 = require("./constants");
52
54
  const assert_1 = __importDefault(require("assert"));
55
+ __exportStar(require("./types"), exports);
53
56
  const debug = require('@serverless-cd/debug')('serverless-devs:engine');
57
+ /**
58
+ * Engine Class
59
+ *
60
+ * This class provides an engine to handle Serverless Devs operations and steps.
61
+ * It operates based on the xstate state machine library, ensuring that execution follows
62
+ * the predefined flow and states.
63
+ */
54
64
  class Engine {
55
65
  constructor(options) {
56
66
  this.options = options;
57
67
  this.context = {
58
- status: types_1.STEP_STATUS.PENING,
68
+ status: types_1.STEP_STATUS.PENDING,
59
69
  completed: false,
60
70
  error: [],
61
71
  };
62
- this.record = { status: types_1.STEP_STATUS.PENING, editStatusAble: true };
72
+ this.record = { status: types_1.STEP_STATUS.PENDING, editStatusAble: true };
63
73
  this.spec = {};
64
74
  debug('engine start');
65
75
  // 初始化参数
66
76
  this.options.args = (0, lodash_1.get)(this.options, 'args', process.argv.slice(2));
67
77
  this.options.cwd = (0, lodash_1.get)(this.options, 'cwd', process.cwd());
68
- this.options.template = utils.getAbsolutePath((0, lodash_1.get)(this.options, 'template'), this.options.cwd);
78
+ this.options.template = (0, utils_2.getAbsolutePath)((0, lodash_1.get)(this.options, 'template'), this.options.cwd);
69
79
  debug(`engine options: ${(0, utils_1.stringify)(options)}`);
70
80
  }
81
+ /**
82
+ * Initialization steps before starting the engine.
83
+ */
71
84
  beforeStart() {
72
85
  return __awaiter(this, void 0, void 0, function* () {
86
+ // 初始化 logger
87
+ this.glog = this.getLogger();
88
+ this.logger = this.glog.__generate('engine');
73
89
  // 初始化 spec
74
- this.parseSpecInstance = new parse_spec_1.default((0, lodash_1.get)(this.options, 'template'), this.options.args);
75
- this.spec = this.parseSpecInstance.start();
90
+ this.parseSpecInstance = new parse_spec_1.default((0, lodash_1.get)(this.options, 'template'), {
91
+ argv: this.options.args,
92
+ logger: this.logger,
93
+ });
94
+ this.spec = yield this.parseSpecInstance.start();
76
95
  // 初始化行参环境变量 > .env (parse-spec require .env)
77
96
  (0, lodash_1.each)(this.options.env, (value, key) => {
78
97
  process.env[key] = value;
@@ -80,16 +99,22 @@ class Engine {
80
99
  const { steps: _steps } = this.spec;
81
100
  // 参数校验
82
101
  this.validate();
83
- // 初始化 logger
84
- this.glog = this.getLogger();
85
- this.logger = this.glog.__generate('engine');
86
102
  this.context.steps = yield this.download(_steps);
87
103
  });
88
104
  }
89
- // engine应收敛所有的异常,不应该抛出异常
105
+ /**
106
+ * Start the engine.
107
+ *
108
+ * This is the primary execution function for the engine. It is responsible for starting
109
+ * the entire engine and handling each step.
110
+ *
111
+ * @note engine应收敛所有的异常,不应该抛出异常
112
+ */
90
113
  start() {
91
114
  return __awaiter(this, void 0, void 0, function* () {
92
115
  this.context.status = types_1.STEP_STATUS.RUNNING;
116
+ // Haoran: should set this.record.status to RUNNING?
117
+ this.record.status = types_1.STEP_STATUS.RUNNING;
93
118
  try {
94
119
  yield this.beforeStart();
95
120
  }
@@ -99,7 +124,8 @@ class Engine {
99
124
  this.context.error.push(error);
100
125
  return this.context;
101
126
  }
102
- const { steps: _steps, yaml, access = yaml.access } = this.spec;
127
+ const { steps: _steps, yaml, command, access = yaml.access } = this.spec;
128
+ this.logger.write(`${(0, utils_2.emoji)('⌛')} Steps for [${command}] of [${(0, lodash_1.get)(this.spec, 'yaml.appName')}]\n${chalk_1.default.gray('====================')}`);
103
129
  // 初始化全局的 action
104
130
  this.globalActionInstance = new actions_1.default(yaml.actions, {
105
131
  hookLevel: parse_spec_1.IActionLevel.GLOBAL,
@@ -107,9 +133,11 @@ class Engine {
107
133
  skipActions: this.spec.skipActions,
108
134
  });
109
135
  const credential = yield (0, utils_1.getCredential)(access, this.logger);
136
+ this.context.credential = credential;
110
137
  // 处理 global-pre
111
138
  try {
112
139
  this.globalActionInstance.setValue('magic', this.getFilterContext());
140
+ this.globalActionInstance.setValue('command', command);
113
141
  yield this.globalActionInstance.start(parse_spec_1.IHookType.PRE, { access, credential });
114
142
  }
115
143
  catch (error) {
@@ -118,14 +146,17 @@ class Engine {
118
146
  yield this.doCompleted();
119
147
  return this.context;
120
148
  }
121
- this.context.steps = (0, lodash_1.map)(this.context.steps, (item) => {
122
- return Object.assign(Object.assign({}, item), { stepCount: (0, lodash_1.uniqueId)(), status: types_1.STEP_STATUS.PENING, done: false });
149
+ // Assign the id, pending status and etc for all steps.
150
+ this.context.steps = (0, lodash_1.map)(this.context.steps, item => {
151
+ return Object.assign(Object.assign({}, item), { stepCount: (0, lodash_1.uniqueId)(), status: types_1.STEP_STATUS.PENDING, done: false });
123
152
  });
124
153
  const res = yield new Promise((resolve) => __awaiter(this, void 0, void 0, function* () {
154
+ // Every states object has two fixed states, "init" and "final".
125
155
  const states = {
126
156
  init: {
127
157
  on: {
128
- INIT: (0, lodash_1.get)(this.context.steps, '[0].stepCount'),
158
+ // Haoran: May this.context.steps be empty?
159
+ INIT: this.context.steps.length === 0 ? 'final' : (0, lodash_1.get)(this.context.steps, '[0].stepCount'),
129
160
  },
130
161
  },
131
162
  final: {
@@ -133,12 +164,10 @@ class Engine {
133
164
  invoke: {
134
165
  src: () => __awaiter(this, void 0, void 0, function* () {
135
166
  // 执行终态是 error-with-continue 的时候,改为 success
136
- const status = this.record.status === types_1.STEP_STATUS.ERROR_WITH_CONTINUE
137
- ? types_1.STEP_STATUS.SUCCESS
138
- : this.record.status;
167
+ const status = this.record.status === types_1.STEP_STATUS.ERROR_WITH_CONTINUE ? types_1.STEP_STATUS.SUCCESS : this.record.status;
139
168
  this.context.status = status;
140
169
  yield this.doCompleted();
141
- this.context.steps = (0, lodash_1.map)(this.context.steps, (item) => (0, lodash_1.omit)(item, ['instance']));
170
+ this.context.steps = (0, lodash_1.map)(this.context.steps, item => (0, lodash_1.omit)(item, ['instance']));
142
171
  this.context.output = this.getOutput();
143
172
  debug(`context: ${(0, utils_1.stringify)(this.context)}`);
144
173
  debug('engine end');
@@ -147,13 +176,10 @@ class Engine {
147
176
  },
148
177
  },
149
178
  };
179
+ // Every states object has dynamic state, that based on the step.StepCount.
150
180
  (0, lodash_1.each)(this.context.steps, (item, index) => {
151
- const target = this.context.steps[index + 1]
152
- ? (0, lodash_1.get)(this.context.steps, `[${index + 1}].stepCount`)
153
- : 'final';
154
- const flowProject = yaml.useFlow
155
- ? (0, lodash_1.filter)(this.context.steps, (o) => o.flowId === item.flowId)
156
- : [item];
181
+ const target = this.context.steps[index + 1] ? (0, lodash_1.get)(this.context.steps, `[${index + 1}].stepCount`) : 'final';
182
+ const flowProject = yaml.useFlow ? (0, lodash_1.filter)(this.context.steps, o => o.flowId === item.flowId) : [item];
157
183
  states[item.stepCount] = {
158
184
  invoke: {
159
185
  id: item.stepCount,
@@ -166,9 +192,9 @@ class Engine {
166
192
  this.recordContext(item, { status: types_1.STEP_STATUS.RUNNING });
167
193
  // 检查全局的执行状态,如果是failure,则不执行该步骤, 并记录状态为 skipped
168
194
  if (this.record.status === types_1.STEP_STATUS.FAILURE) {
169
- return yield Promise.all((0, lodash_1.map)(flowProject, (o) => this.doSkip(o)));
195
+ return yield Promise.all((0, lodash_1.map)(flowProject, o => this.doSkip(o)));
170
196
  }
171
- return yield Promise.all((0, lodash_1.map)(flowProject, (o) => this.handleSrc(o)));
197
+ return yield Promise.all((0, lodash_1.map)(flowProject, o => this.handleSrc(o)));
172
198
  }),
173
199
  onDone: {
174
200
  target,
@@ -184,27 +210,41 @@ class Engine {
184
210
  states,
185
211
  });
186
212
  const stepService = (0, xstate_1.interpret)(fetchMachine)
187
- .onTransition((state) => state.value)
213
+ .onTransition(state => state.value)
188
214
  .start();
189
215
  stepService.send('INIT');
190
216
  }));
217
+ this.glog.__clear();
191
218
  return res;
192
219
  });
193
220
  }
221
+ /**
222
+ * Extracts and returns an object containing the output of each step.
223
+ * The object's key is the project name and the value is the output of that project.
224
+ */
194
225
  getOutput() {
195
226
  const output = {};
196
- (0, lodash_1.each)(this.context.steps, (item) => {
197
- if (!(0, lodash_1.isEmpty)(item.output)) {
227
+ (0, lodash_1.each)(this.context.steps, item => {
228
+ if (!(0, lodash_1.isNil)(item.output)) {
198
229
  (0, lodash_1.set)(output, item.projectName, item.output);
199
230
  }
200
231
  });
201
232
  return output;
202
233
  }
234
+ /**
235
+ * Validates the 'steps' and 'command' present in 'this.spec'.
236
+ * Throws an error if either 'steps' or 'command' are missing.
237
+ */
203
238
  validate() {
204
- const { steps, method } = this.spec;
239
+ const { steps, command } = this.spec;
205
240
  (0, assert_1.default)(!(0, lodash_1.isEmpty)(steps), 'steps is required');
206
- (0, assert_1.default)(method, 'method is required');
241
+ (0, assert_1.default)(command, 'command is required');
207
242
  }
243
+ /**
244
+ * Asynchronously downloads and initializes the given steps.
245
+ * For each step, it loads the specified component and associates a logger with it.
246
+ * Returns an array containing the initialized steps.
247
+ */
208
248
  download(steps) {
209
249
  return __awaiter(this, void 0, void 0, function* () {
210
250
  const newSteps = [];
@@ -223,17 +263,23 @@ class Engine {
223
263
  const customLogger = (0, lodash_1.get)(this.options, 'logConfig.customLogger');
224
264
  if (customLogger) {
225
265
  debug('use custom logger');
226
- if (customLogger instanceof logger_1.default) {
266
+ if ((customLogger === null || customLogger === void 0 ? void 0 : customLogger.CODE) === logger_1.default.CODE) {
227
267
  return customLogger;
228
268
  }
229
269
  throw new Error('customLogger must be instance of Logger');
230
270
  }
231
- return new logger_1.default(Object.assign(Object.assign({ traceId: (0, lodash_1.get)(process.env, 'serverless_devs_trace_id', (0, utils_1.randomId)()), logDir: path_1.default.join(utils.getRootHome(), 'logs') }, this.options.logConfig), { level: (0, lodash_1.get)(this.options, 'logConfig.level', this.spec.debug ? 'DEBUG' : 'INFO') }));
271
+ return new logger_1.default(Object.assign(Object.assign({ traceId: (0, utils_2.traceid)(), logDir: path_1.default.join((0, utils_2.getRootHome)(), 'logs') }, this.options.logConfig), { level: (0, lodash_1.get)(this.options, 'logConfig.level', this.spec.debug ? 'DEBUG' : undefined) }));
232
272
  }
273
+ /**
274
+ * Updates the context for the given step based on the provided options.
275
+ *
276
+ * @param item - The current step being processed.
277
+ * @param options - An object containing details like status, error, output, etc. to update the step's context.
278
+ */
233
279
  recordContext(item, options = {}) {
234
280
  const { status, error, output, process_time, props, done } = options;
235
281
  this.context.stepCount = item.stepCount;
236
- this.context.steps = (0, lodash_1.map)(this.context.steps, (obj) => {
282
+ this.context.steps = (0, lodash_1.map)(this.context.steps, obj => {
237
283
  if (obj.stepCount === item.stepCount) {
238
284
  if (status) {
239
285
  obj.status = status;
@@ -258,26 +304,39 @@ class Engine {
258
304
  return obj;
259
305
  });
260
306
  }
307
+ /**
308
+ * Generates a context data for the given step containing details like cwd, vars, and other steps' outputs and props.
309
+ * @param item - The current step being processed.
310
+ * @returns - The generated context data.
311
+ */
261
312
  getFilterContext(item) {
262
313
  const data = {
263
314
  cwd: path_1.default.dirname(this.spec.yaml.path),
264
315
  vars: this.spec.yaml.vars,
316
+ resources: {},
265
317
  };
266
318
  for (const obj of this.context.steps) {
267
- data[obj.projectName] = { output: obj.output || {}, props: obj.props || {} };
319
+ data.resources[obj.projectName] = { output: obj.output || {}, props: obj.props || {} };
268
320
  }
269
321
  if (item) {
270
- data.credentials = item.credential;
322
+ data.credential = item.credential;
271
323
  data.that = {
272
324
  name: item.projectName,
273
325
  access: item.access,
274
326
  component: item.component,
275
- props: data[item.projectName].props,
276
- output: data[item.projectName].output,
327
+ props: data.resources[item.projectName].props,
328
+ output: data.resources[item.projectName].output,
277
329
  };
278
330
  }
279
331
  return data;
280
332
  }
333
+ /**
334
+ * Handles the subsequent operations after a step has been completed.
335
+ * 1. Marks the step as completed.
336
+ * 2. If the step status is FAILURE, attempts to trigger the global FAIL hook.
337
+ * 3. If the step status is SUCCESS, attempts to trigger the global SUCCESS hook.
338
+ * 4. Regardless of the step's status, tries to trigger the global COMPLETE hook to denote completion.
339
+ */
281
340
  doCompleted() {
282
341
  return __awaiter(this, void 0, void 0, function* () {
283
342
  this.context.completed = true;
@@ -308,18 +367,23 @@ class Engine {
308
367
  }
309
368
  });
310
369
  }
370
+ /**
371
+ * Handles the execution process for a project step.
372
+ * @param item - The project step to handle.
373
+ */
311
374
  handleSrc(item) {
375
+ var _a, _b, _c;
312
376
  return __awaiter(this, void 0, void 0, function* () {
313
- this.logger.debug(`Start executing project ${item.projectName}`);
377
+ const { command } = this.spec;
378
+ // Attempt to execute pre-hook and component logic for the project step.
314
379
  try {
315
380
  // project pre hook and project component
316
381
  yield this.handleAfterSrc(item);
317
382
  }
318
383
  catch (error) {
319
- // project fail hook
384
+ // On error, attempt to trigger the project's fail hook and update the recorded context.
320
385
  try {
321
- const newInputs = yield this.getProps(item);
322
- const res = yield this.actionInstance.start(parse_spec_1.IHookType.FAIL, newInputs);
386
+ const res = yield ((_a = this.actionInstance) === null || _a === void 0 ? void 0 : _a.start(parse_spec_1.IHookType.FAIL, this.record.componentProps));
323
387
  this.recordContext(item, (0, lodash_1.get)(res, 'pluginOutput', {}));
324
388
  }
325
389
  catch (error) {
@@ -328,13 +392,13 @@ class Engine {
328
392
  }
329
393
  }
330
394
  // 若记录的全局状态为true,则进行输出成功的日志
395
+ // If the global record status is SUCCESS, attempt to trigger the project's success hook.
331
396
  if (this.record.status === types_1.STEP_STATUS.SUCCESS) {
332
397
  // project success hook
333
398
  try {
334
399
  // 项目的output, 再次获取魔法变量
335
400
  this.actionInstance.setValue('magic', this.getFilterContext(item));
336
- const newInputs = yield this.getProps(item);
337
- const res = yield this.actionInstance.start(parse_spec_1.IHookType.SUCCESS, Object.assign(Object.assign({}, newInputs), { output: (0, lodash_1.get)(item, 'output', {}) }));
401
+ const res = yield ((_b = this.actionInstance) === null || _b === void 0 ? void 0 : _b.start(parse_spec_1.IHookType.SUCCESS, Object.assign(Object.assign({}, this.record.componentProps), { output: (0, lodash_1.get)(item, 'output', {}) })));
338
402
  this.recordContext(item, (0, lodash_1.get)(res, 'pluginOutput', {}));
339
403
  }
340
404
  catch (error) {
@@ -342,29 +406,47 @@ class Engine {
342
406
  this.recordContext(item, { error });
343
407
  }
344
408
  }
345
- // project complete hook
409
+ // Attempt to trigger the project's complete hook regardless of status.
346
410
  try {
347
- const newInputs = yield this.getProps(item);
348
- const res = yield this.actionInstance.start(parse_spec_1.IHookType.COMPLETE, Object.assign(Object.assign({}, newInputs), { output: (0, lodash_1.get)(item, 'output', {}) }));
411
+ const res = yield ((_c = this.actionInstance) === null || _c === void 0 ? void 0 : _c.start(parse_spec_1.IHookType.COMPLETE, Object.assign(Object.assign({}, this.record.componentProps), { output: (0, lodash_1.get)(item, 'output', {}) })));
349
412
  this.recordContext(item, (0, lodash_1.get)(res, 'pluginOutput', {}));
350
413
  }
351
414
  catch (error) {
352
415
  this.record.status = types_1.STEP_STATUS.FAILURE;
353
- this.recordContext(item, { error, done: true });
416
+ this.recordContext(item, { error });
354
417
  }
418
+ // 记录项目已经执行完成
419
+ const process_time = (0, utils_1.getProcessTime)(this.record.startTime);
420
+ this.recordContext(item, { done: true, process_time });
421
+ // Log output based on the record status.
355
422
  if (this.record.status === types_1.STEP_STATUS.SUCCESS) {
356
- this.logger.debug(`Project ${item.projectName} successfully to execute`);
423
+ this.logger.write(`${chalk_1.default.green('✔')} ${chalk_1.default.gray(`[${item.projectName}] completed (${process_time}s)`)}`);
424
+ }
425
+ if (this.record.status === types_1.STEP_STATUS.FAILURE) {
426
+ this.logger.write(`${chalk_1.default.red('✖')} ${chalk_1.default.gray(`[${item.projectName}] failed to [${command}] (${process_time}s)`)}`);
357
427
  }
428
+ // step执行完成后,释放logger
429
+ this.glog.__unset(item.projectName);
358
430
  });
359
431
  }
432
+ /**
433
+ * Handles the logic after a project step's execution.
434
+ * @param item - The project step to handle.
435
+ */
360
436
  handleAfterSrc(item) {
437
+ var _a;
361
438
  return __awaiter(this, void 0, void 0, function* () {
362
439
  try {
440
+ // Print detailed information of the project step for debugging purposes.
363
441
  debug(`project item: ${(0, utils_1.stringify)(item)}`);
442
+ // Extract the command from the specification.
443
+ const { command } = this.spec;
444
+ // Retrieve the credentials for the project step.
364
445
  item.credential = yield (0, utils_1.getCredential)(item.access, this.logger);
365
- (0, lodash_1.each)(item.credential, (v) => {
366
- this.glog.__setSecret([v]);
367
- });
446
+ // Set a secret for each credential.
447
+ const { AccessKeyID, AccessKeySecret } = item.credential;
448
+ this.glog.__setSecret([AccessKeyID, AccessKeySecret]);
449
+ // Parse actions for the project step and initialize a new action instance.
368
450
  const newAction = this.parseSpecInstance.parseActions(item.actions, parse_spec_1.IActionLevel.PROJECT);
369
451
  debug(`project actions: ${JSON.stringify(newAction)}`);
370
452
  this.actionInstance = new actions_1.default(newAction, {
@@ -373,26 +455,33 @@ class Engine {
373
455
  logger: item.logger,
374
456
  skipActions: this.spec.skipActions,
375
457
  });
458
+ // Set values for the action instance.
376
459
  this.actionInstance.setValue('magic', this.getFilterContext(item));
460
+ this.actionInstance.setValue('step', item);
461
+ this.actionInstance.setValue('command', command);
462
+ // Retrieve properties for the project step.
377
463
  const newInputs = yield this.getProps(item);
378
- const pluginResult = yield this.actionInstance.start(parse_spec_1.IHookType.PRE, newInputs);
464
+ this.actionInstance.setValue('componentProps', newInputs);
465
+ // Start the pre-hook and execute the logic for the project step.
466
+ const pluginResult = yield ((_a = this.actionInstance) === null || _a === void 0 ? void 0 : _a.start(parse_spec_1.IHookType.PRE, newInputs));
379
467
  const response = yield this.doSrc(item, pluginResult);
380
468
  // 记录全局的执行状态
381
469
  if (this.record.editStatusAble) {
382
470
  this.record.status = types_1.STEP_STATUS.SUCCESS;
383
471
  }
384
- // id 添加状态
472
+ // If the project step has an ID, update the corresponding record status and output.
385
473
  if (item.id) {
386
474
  this.record.steps = Object.assign(Object.assign({}, this.record.steps), { [item.id]: {
387
475
  status: types_1.STEP_STATUS.SUCCESS,
388
476
  output: response,
389
477
  } });
390
478
  }
391
- const process_time = (0, utils_1.getProcessTime)(this.record.startTime);
392
- this.recordContext(item, { status: types_1.STEP_STATUS.SUCCESS, output: response, process_time });
479
+ // Update the project step's context to SUCCESS.
480
+ this.recordContext(item, { status: types_1.STEP_STATUS.SUCCESS, output: response });
393
481
  }
394
482
  catch (e) {
395
483
  const error = e;
484
+ // Determine the status based on the project step's "continue-on-error" attribute.
396
485
  const status = item['continue-on-error'] === true ? types_1.STEP_STATUS.ERROR_WITH_CONTINUE : types_1.STEP_STATUS.FAILURE;
397
486
  // 记录全局的执行状态
398
487
  if (this.record.editStatusAble) {
@@ -402,35 +491,42 @@ class Engine {
402
491
  // 全局的执行状态一旦失败,便不可修改
403
492
  this.record.editStatusAble = false;
404
493
  }
494
+ // If the project step has an ID, update the corresponding record status.
405
495
  if (item.id) {
406
496
  this.record.steps = Object.assign(Object.assign({}, this.record.steps), { [item.id]: {
407
497
  status,
408
498
  } });
409
499
  }
410
- const process_time = (0, utils_1.getProcessTime)(this.record.startTime);
500
+ // Handle the error based on the project step's "continue-on-error" attribute.
411
501
  if (item['continue-on-error']) {
412
- this.recordContext(item, { status, process_time });
502
+ this.recordContext(item, { status });
413
503
  }
414
504
  else {
415
- this.recordContext(item, { status, error, process_time });
505
+ this.recordContext(item, { status, error });
416
506
  throw error;
417
507
  }
418
508
  }
419
509
  });
420
510
  }
511
+ /**
512
+ * Retrieve properties for a specific project step.
513
+ * @param item - The project step for which properties are to be retrieved.
514
+ * @returns An object containing properties related to the project step.
515
+ */
421
516
  getProps(item) {
422
517
  return __awaiter(this, void 0, void 0, function* () {
423
518
  const magic = this.getFilterContext(item);
424
519
  debug(`magic context: ${JSON.stringify(magic)}`);
425
520
  const newInputs = (0, parse_spec_1.getInputs)(item.props, magic);
426
- const { projectName, method } = this.spec;
521
+ const { projectName, command } = this.spec;
427
522
  const result = {
428
523
  cwd: this.options.cwd,
524
+ name: (0, lodash_1.get)(this.spec, 'yaml.appName'),
429
525
  props: newInputs,
430
- command: method,
431
- args: (0, lodash_1.filter)(this.options.args, (o) => !(0, lodash_1.includes)([projectName, method], o)),
526
+ command,
527
+ args: (0, lodash_1.filter)(this.options.args, o => !(0, lodash_1.includes)([projectName, command], o)),
432
528
  yaml: {
433
- path: this.spec.yaml.path,
529
+ path: (0, lodash_1.get)(this.spec, 'yaml.path'),
434
530
  },
435
531
  resource: {
436
532
  name: item.projectName,
@@ -448,78 +544,98 @@ class Engine {
448
544
  return result;
449
545
  });
450
546
  }
547
+ /**
548
+ * Executes the appropriate action based on the provided project step.
549
+ * @param item - The project step to be executed.
550
+ * @param data - Additional data which may contain plugin output.
551
+ * @returns Result of the executed action, if applicable.
552
+ */
451
553
  doSrc(item, data = {}) {
452
554
  return __awaiter(this, void 0, void 0, function* () {
453
- const { method = '', projectName } = this.spec;
555
+ // Extract command and projectName from the specification.
556
+ const { command = '', projectName } = this.spec;
557
+ // Retrieve properties for the given project step.
454
558
  const newInputs = yield this.getProps(item);
455
- const componentProps = (0, lodash_1.isEmpty)(data.pluginOutput) ? newInputs : data.pluginOutput;
456
- debug(`component props: ${(0, utils_1.stringify)(componentProps)}`);
457
- this.actionInstance.setValue('componentProps', componentProps);
559
+ // Set component properties based on the provided data or the newly retrieved properties.
560
+ this.record.componentProps = (0, lodash_1.isEmpty)(data.pluginOutput) ? newInputs : data.pluginOutput;
561
+ debug(`component props: ${(0, utils_1.stringify)(this.record.componentProps)}`);
562
+ this.actionInstance.setValue('componentProps', this.record.componentProps);
458
563
  // 服务级操作
459
564
  if (projectName) {
460
- if ((0, lodash_1.isFunction)(item.instance[method])) {
565
+ if ((0, lodash_1.isFunction)(item.instance[command])) {
461
566
  // 方法存在,执行报错,退出码101
462
567
  try {
463
- return yield item.instance[method](componentProps);
568
+ return yield item.instance[command](this.record.componentProps);
464
569
  }
465
570
  catch (e) {
466
571
  const useAllowFailure = (0, utils_1.getAllowFailure)(item.allow_failure, {
467
572
  exitCode: constants_1.EXIT_CODE.COMPONENT,
468
- command: method,
573
+ command,
469
574
  });
470
575
  if (useAllowFailure)
471
576
  return;
472
577
  const error = e;
473
- throw new utils_2.TipsError(error.message, {
578
+ throw new utils_2.DevsError(error.message, {
579
+ data: (0, lodash_1.get)(e, 'data'),
580
+ stack: error.stack,
474
581
  exitCode: constants_1.EXIT_CODE.COMPONENT,
475
- prefix: `Project ${item.projectName} failed to execute:`,
582
+ prefix: `[${item.projectName}] failed to [${command}]:`,
476
583
  });
477
584
  }
478
585
  }
479
586
  const useAllowFailure = (0, utils_1.getAllowFailure)(item.allow_failure, {
480
587
  exitCode: constants_1.EXIT_CODE.DEVS,
481
- command: method,
588
+ command,
482
589
  });
483
590
  if (useAllowFailure)
484
591
  return;
485
592
  // 方法不存在,此时系统将会认为是未找到组件方法,系统的exit code为100;
486
- throw new utils_2.TipsError(`The [${method}] command was not found.`, {
593
+ throw new utils_2.DevsError(`The [${command}] command was not found.`, {
487
594
  exitCode: constants_1.EXIT_CODE.DEVS,
488
- tips: `Please check the component ${item.component} has the ${method} method. Serverless Devs documents:${chalk_1.default.underline('https://github.com/Serverless-Devs/Serverless-Devs/blob/master/docs/zh/command')}`,
595
+ tips: `Please check the component ${item.component} has the ${command} command. Serverless Devs documents:${chalk_1.default.underline('https://github.com/Serverless-Devs/Serverless-Devs/blob/master/docs/zh/command')}`,
596
+ prefix: `[${item.projectName}] failed to [${command}]:`,
489
597
  });
490
598
  }
491
599
  // 应用级操作
492
- if ((0, lodash_1.isFunction)(item.instance[method])) {
600
+ if ((0, lodash_1.isFunction)(item.instance[command])) {
493
601
  // 方法存在,执行报错,退出码101
494
602
  try {
495
- return yield item.instance[method](componentProps);
603
+ return yield item.instance[command](this.record.componentProps);
496
604
  }
497
605
  catch (e) {
498
606
  const useAllowFailure = (0, utils_1.getAllowFailure)(item.allow_failure, {
499
607
  exitCode: constants_1.EXIT_CODE.COMPONENT,
500
- command: method,
608
+ command,
501
609
  });
502
610
  if (useAllowFailure)
503
611
  return;
504
612
  const error = e;
505
- throw new utils_2.TipsError(error.message, {
613
+ throw new utils_2.DevsError(error.message, {
614
+ data: (0, lodash_1.get)(e, 'data'),
615
+ stack: error.stack,
506
616
  exitCode: constants_1.EXIT_CODE.COMPONENT,
507
- prefix: `Project ${item.projectName} failed to execute:`,
617
+ prefix: `[${item.projectName}] failed to [${command}]:`,
508
618
  });
509
619
  }
510
620
  }
511
621
  // 方法不存在,进行警告,但是并不会报错,最终的exit code为0;
512
- this.logger.tips(`The [${method}] command was not found.`, `Please check the component ${item.component} has the ${method} method. Serverless Devs documents:https://github.com/Serverless-Devs/Serverless-Devs/blob/master/docs/zh/command`);
622
+ this.logger.tips(`The [${command}] command was not found.`, `Please check the component ${item.component} has the ${command} command. Serverless Devs documents:https://github.com/Serverless-Devs/Serverless-Devs/blob/master/docs/zh/command`);
513
623
  });
514
624
  }
625
+ /**
626
+ * Handles the project step that is marked to be skipped.
627
+ * @param item - The project step to be skipped.
628
+ * @returns A resolved Promise.
629
+ */
515
630
  doSkip(item) {
516
631
  return __awaiter(this, void 0, void 0, function* () {
517
- // id 添加状态
632
+ // If the step has an 'id', set its status to 'SKIP' in the record.
518
633
  if (item.id) {
519
634
  this.record.steps = Object.assign(Object.assign({}, this.record.steps), { [item.id]: {
520
635
  status: types_1.STEP_STATUS.SKIP,
521
636
  } });
522
637
  }
638
+ // Mark the step's status as 'SKIP' and set its processing time to 0.
523
639
  this.recordContext(item, { status: types_1.STEP_STATUS.SKIP, process_time: 0 });
524
640
  return Promise.resolve();
525
641
  });