@serverless-devs/engine 0.0.1-beta.8 → 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/actions/index.d.ts +79 -0
- package/lib/actions/index.js +191 -33
- package/lib/actions/index.js.map +1 -1
- package/lib/constants/index.js +4 -0
- package/lib/constants/index.js.map +1 -1
- package/lib/index.d.ts +74 -1
- package/lib/index.js +206 -87
- package/lib/index.js.map +1 -1
- package/lib/types.d.ts +9 -8
- package/lib/types.js +1 -1
- package/lib/types.js.map +1 -1
- package/lib/utils/index.js +6 -7
- package/lib/utils/index.js.map +1 -1
- package/package.json +13 -7
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.
|
|
68
|
+
status: types_1.STEP_STATUS.PENDING,
|
|
59
69
|
completed: false,
|
|
60
70
|
error: [],
|
|
61
71
|
};
|
|
62
|
-
this.record = { status: types_1.STEP_STATUS.
|
|
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 =
|
|
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'),
|
|
75
|
-
|
|
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
|
-
|
|
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,8 +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 {
|
|
139
|
+
this.globalActionInstance.setValue('magic', this.getFilterContext());
|
|
140
|
+
this.globalActionInstance.setValue('command', command);
|
|
112
141
|
yield this.globalActionInstance.start(parse_spec_1.IHookType.PRE, { access, credential });
|
|
113
142
|
}
|
|
114
143
|
catch (error) {
|
|
@@ -117,14 +146,17 @@ class Engine {
|
|
|
117
146
|
yield this.doCompleted();
|
|
118
147
|
return this.context;
|
|
119
148
|
}
|
|
120
|
-
|
|
121
|
-
|
|
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 });
|
|
122
152
|
});
|
|
123
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".
|
|
124
155
|
const states = {
|
|
125
156
|
init: {
|
|
126
157
|
on: {
|
|
127
|
-
|
|
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'),
|
|
128
160
|
},
|
|
129
161
|
},
|
|
130
162
|
final: {
|
|
@@ -132,12 +164,10 @@ class Engine {
|
|
|
132
164
|
invoke: {
|
|
133
165
|
src: () => __awaiter(this, void 0, void 0, function* () {
|
|
134
166
|
// 执行终态是 error-with-continue 的时候,改为 success
|
|
135
|
-
const status = this.record.status === types_1.STEP_STATUS.ERROR_WITH_CONTINUE
|
|
136
|
-
? types_1.STEP_STATUS.SUCCESS
|
|
137
|
-
: this.record.status;
|
|
167
|
+
const status = this.record.status === types_1.STEP_STATUS.ERROR_WITH_CONTINUE ? types_1.STEP_STATUS.SUCCESS : this.record.status;
|
|
138
168
|
this.context.status = status;
|
|
139
169
|
yield this.doCompleted();
|
|
140
|
-
this.context.steps = (0, lodash_1.map)(this.context.steps,
|
|
170
|
+
this.context.steps = (0, lodash_1.map)(this.context.steps, item => (0, lodash_1.omit)(item, ['instance']));
|
|
141
171
|
this.context.output = this.getOutput();
|
|
142
172
|
debug(`context: ${(0, utils_1.stringify)(this.context)}`);
|
|
143
173
|
debug('engine end');
|
|
@@ -146,13 +176,10 @@ class Engine {
|
|
|
146
176
|
},
|
|
147
177
|
},
|
|
148
178
|
};
|
|
179
|
+
// Every states object has dynamic state, that based on the step.StepCount.
|
|
149
180
|
(0, lodash_1.each)(this.context.steps, (item, index) => {
|
|
150
|
-
const target = this.context.steps[index + 1]
|
|
151
|
-
|
|
152
|
-
: 'final';
|
|
153
|
-
const flowProject = yaml.useFlow
|
|
154
|
-
? (0, lodash_1.filter)(this.context.steps, (o) => o.flowId === item.flowId)
|
|
155
|
-
: [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];
|
|
156
183
|
states[item.stepCount] = {
|
|
157
184
|
invoke: {
|
|
158
185
|
id: item.stepCount,
|
|
@@ -165,9 +192,9 @@ class Engine {
|
|
|
165
192
|
this.recordContext(item, { status: types_1.STEP_STATUS.RUNNING });
|
|
166
193
|
// 检查全局的执行状态,如果是failure,则不执行该步骤, 并记录状态为 skipped
|
|
167
194
|
if (this.record.status === types_1.STEP_STATUS.FAILURE) {
|
|
168
|
-
return yield Promise.all((0, lodash_1.map)(flowProject,
|
|
195
|
+
return yield Promise.all((0, lodash_1.map)(flowProject, o => this.doSkip(o)));
|
|
169
196
|
}
|
|
170
|
-
return yield Promise.all((0, lodash_1.map)(flowProject,
|
|
197
|
+
return yield Promise.all((0, lodash_1.map)(flowProject, o => this.handleSrc(o)));
|
|
171
198
|
}),
|
|
172
199
|
onDone: {
|
|
173
200
|
target,
|
|
@@ -183,27 +210,41 @@ class Engine {
|
|
|
183
210
|
states,
|
|
184
211
|
});
|
|
185
212
|
const stepService = (0, xstate_1.interpret)(fetchMachine)
|
|
186
|
-
.onTransition(
|
|
213
|
+
.onTransition(state => state.value)
|
|
187
214
|
.start();
|
|
188
215
|
stepService.send('INIT');
|
|
189
216
|
}));
|
|
217
|
+
this.glog.__clear();
|
|
190
218
|
return res;
|
|
191
219
|
});
|
|
192
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
|
+
*/
|
|
193
225
|
getOutput() {
|
|
194
226
|
const output = {};
|
|
195
|
-
(0, lodash_1.each)(this.context.steps,
|
|
196
|
-
if (!(0, lodash_1.
|
|
227
|
+
(0, lodash_1.each)(this.context.steps, item => {
|
|
228
|
+
if (!(0, lodash_1.isNil)(item.output)) {
|
|
197
229
|
(0, lodash_1.set)(output, item.projectName, item.output);
|
|
198
230
|
}
|
|
199
231
|
});
|
|
200
232
|
return output;
|
|
201
233
|
}
|
|
234
|
+
/**
|
|
235
|
+
* Validates the 'steps' and 'command' present in 'this.spec'.
|
|
236
|
+
* Throws an error if either 'steps' or 'command' are missing.
|
|
237
|
+
*/
|
|
202
238
|
validate() {
|
|
203
|
-
const { steps,
|
|
239
|
+
const { steps, command } = this.spec;
|
|
204
240
|
(0, assert_1.default)(!(0, lodash_1.isEmpty)(steps), 'steps is required');
|
|
205
|
-
(0, assert_1.default)(
|
|
241
|
+
(0, assert_1.default)(command, 'command is required');
|
|
206
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
|
+
*/
|
|
207
248
|
download(steps) {
|
|
208
249
|
return __awaiter(this, void 0, void 0, function* () {
|
|
209
250
|
const newSteps = [];
|
|
@@ -222,17 +263,23 @@ class Engine {
|
|
|
222
263
|
const customLogger = (0, lodash_1.get)(this.options, 'logConfig.customLogger');
|
|
223
264
|
if (customLogger) {
|
|
224
265
|
debug('use custom logger');
|
|
225
|
-
if (customLogger
|
|
266
|
+
if ((customLogger === null || customLogger === void 0 ? void 0 : customLogger.CODE) === logger_1.default.CODE) {
|
|
226
267
|
return customLogger;
|
|
227
268
|
}
|
|
228
269
|
throw new Error('customLogger must be instance of Logger');
|
|
229
270
|
}
|
|
230
|
-
return new logger_1.default(Object.assign(Object.assign({ traceId: (0,
|
|
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) }));
|
|
231
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
|
+
*/
|
|
232
279
|
recordContext(item, options = {}) {
|
|
233
280
|
const { status, error, output, process_time, props, done } = options;
|
|
234
281
|
this.context.stepCount = item.stepCount;
|
|
235
|
-
this.context.steps = (0, lodash_1.map)(this.context.steps,
|
|
282
|
+
this.context.steps = (0, lodash_1.map)(this.context.steps, obj => {
|
|
236
283
|
if (obj.stepCount === item.stepCount) {
|
|
237
284
|
if (status) {
|
|
238
285
|
obj.status = status;
|
|
@@ -257,24 +304,39 @@ class Engine {
|
|
|
257
304
|
return obj;
|
|
258
305
|
});
|
|
259
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
|
+
*/
|
|
260
312
|
getFilterContext(item) {
|
|
261
313
|
const data = {
|
|
262
314
|
cwd: path_1.default.dirname(this.spec.yaml.path),
|
|
263
315
|
vars: this.spec.yaml.vars,
|
|
264
|
-
|
|
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 || {} };
|
|
320
|
+
}
|
|
321
|
+
if (item) {
|
|
322
|
+
data.credential = item.credential;
|
|
323
|
+
data.that = {
|
|
324
|
+
name: item.projectName,
|
|
325
|
+
access: item.access,
|
|
326
|
+
component: item.component,
|
|
327
|
+
props: data.resources[item.projectName].props,
|
|
328
|
+
output: data.resources[item.projectName].output,
|
|
329
|
+
};
|
|
268
330
|
}
|
|
269
|
-
data.that = {
|
|
270
|
-
name: item.projectName,
|
|
271
|
-
access: item.access,
|
|
272
|
-
component: item.component,
|
|
273
|
-
props: data[item.projectName].props,
|
|
274
|
-
output: data[item.projectName].output,
|
|
275
|
-
};
|
|
276
331
|
return data;
|
|
277
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
|
+
*/
|
|
278
340
|
doCompleted() {
|
|
279
341
|
return __awaiter(this, void 0, void 0, function* () {
|
|
280
342
|
this.context.completed = true;
|
|
@@ -305,18 +367,23 @@ class Engine {
|
|
|
305
367
|
}
|
|
306
368
|
});
|
|
307
369
|
}
|
|
370
|
+
/**
|
|
371
|
+
* Handles the execution process for a project step.
|
|
372
|
+
* @param item - The project step to handle.
|
|
373
|
+
*/
|
|
308
374
|
handleSrc(item) {
|
|
375
|
+
var _a, _b, _c;
|
|
309
376
|
return __awaiter(this, void 0, void 0, function* () {
|
|
310
|
-
|
|
377
|
+
const { command } = this.spec;
|
|
378
|
+
// Attempt to execute pre-hook and component logic for the project step.
|
|
311
379
|
try {
|
|
312
380
|
// project pre hook and project component
|
|
313
381
|
yield this.handleAfterSrc(item);
|
|
314
382
|
}
|
|
315
383
|
catch (error) {
|
|
316
|
-
// project fail hook
|
|
384
|
+
// On error, attempt to trigger the project's fail hook and update the recorded context.
|
|
317
385
|
try {
|
|
318
|
-
const
|
|
319
|
-
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));
|
|
320
387
|
this.recordContext(item, (0, lodash_1.get)(res, 'pluginOutput', {}));
|
|
321
388
|
}
|
|
322
389
|
catch (error) {
|
|
@@ -325,13 +392,13 @@ class Engine {
|
|
|
325
392
|
}
|
|
326
393
|
}
|
|
327
394
|
// 若记录的全局状态为true,则进行输出成功的日志
|
|
395
|
+
// If the global record status is SUCCESS, attempt to trigger the project's success hook.
|
|
328
396
|
if (this.record.status === types_1.STEP_STATUS.SUCCESS) {
|
|
329
397
|
// project success hook
|
|
330
398
|
try {
|
|
331
399
|
// 项目的output, 再次获取魔法变量
|
|
332
400
|
this.actionInstance.setValue('magic', this.getFilterContext(item));
|
|
333
|
-
const
|
|
334
|
-
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', {}) })));
|
|
335
402
|
this.recordContext(item, (0, lodash_1.get)(res, 'pluginOutput', {}));
|
|
336
403
|
}
|
|
337
404
|
catch (error) {
|
|
@@ -339,29 +406,47 @@ class Engine {
|
|
|
339
406
|
this.recordContext(item, { error });
|
|
340
407
|
}
|
|
341
408
|
}
|
|
342
|
-
// project complete hook
|
|
409
|
+
// Attempt to trigger the project's complete hook regardless of status.
|
|
343
410
|
try {
|
|
344
|
-
const
|
|
345
|
-
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', {}) })));
|
|
346
412
|
this.recordContext(item, (0, lodash_1.get)(res, 'pluginOutput', {}));
|
|
347
413
|
}
|
|
348
414
|
catch (error) {
|
|
349
415
|
this.record.status = types_1.STEP_STATUS.FAILURE;
|
|
350
|
-
this.recordContext(item, { error
|
|
416
|
+
this.recordContext(item, { error });
|
|
351
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.
|
|
352
422
|
if (this.record.status === types_1.STEP_STATUS.SUCCESS) {
|
|
353
|
-
this.logger.
|
|
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)`)}`);
|
|
354
427
|
}
|
|
428
|
+
// step执行完成后,释放logger
|
|
429
|
+
this.glog.__unset(item.projectName);
|
|
355
430
|
});
|
|
356
431
|
}
|
|
432
|
+
/**
|
|
433
|
+
* Handles the logic after a project step's execution.
|
|
434
|
+
* @param item - The project step to handle.
|
|
435
|
+
*/
|
|
357
436
|
handleAfterSrc(item) {
|
|
437
|
+
var _a;
|
|
358
438
|
return __awaiter(this, void 0, void 0, function* () {
|
|
359
439
|
try {
|
|
440
|
+
// Print detailed information of the project step for debugging purposes.
|
|
360
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.
|
|
361
445
|
item.credential = yield (0, utils_1.getCredential)(item.access, this.logger);
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
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.
|
|
365
450
|
const newAction = this.parseSpecInstance.parseActions(item.actions, parse_spec_1.IActionLevel.PROJECT);
|
|
366
451
|
debug(`project actions: ${JSON.stringify(newAction)}`);
|
|
367
452
|
this.actionInstance = new actions_1.default(newAction, {
|
|
@@ -370,26 +455,33 @@ class Engine {
|
|
|
370
455
|
logger: item.logger,
|
|
371
456
|
skipActions: this.spec.skipActions,
|
|
372
457
|
});
|
|
458
|
+
// Set values for the action instance.
|
|
373
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.
|
|
374
463
|
const newInputs = yield this.getProps(item);
|
|
375
|
-
|
|
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));
|
|
376
467
|
const response = yield this.doSrc(item, pluginResult);
|
|
377
468
|
// 记录全局的执行状态
|
|
378
469
|
if (this.record.editStatusAble) {
|
|
379
470
|
this.record.status = types_1.STEP_STATUS.SUCCESS;
|
|
380
471
|
}
|
|
381
|
-
//
|
|
472
|
+
// If the project step has an ID, update the corresponding record status and output.
|
|
382
473
|
if (item.id) {
|
|
383
474
|
this.record.steps = Object.assign(Object.assign({}, this.record.steps), { [item.id]: {
|
|
384
475
|
status: types_1.STEP_STATUS.SUCCESS,
|
|
385
476
|
output: response,
|
|
386
477
|
} });
|
|
387
478
|
}
|
|
388
|
-
|
|
389
|
-
this.recordContext(item, { status: types_1.STEP_STATUS.SUCCESS, output: response
|
|
479
|
+
// Update the project step's context to SUCCESS.
|
|
480
|
+
this.recordContext(item, { status: types_1.STEP_STATUS.SUCCESS, output: response });
|
|
390
481
|
}
|
|
391
482
|
catch (e) {
|
|
392
483
|
const error = e;
|
|
484
|
+
// Determine the status based on the project step's "continue-on-error" attribute.
|
|
393
485
|
const status = item['continue-on-error'] === true ? types_1.STEP_STATUS.ERROR_WITH_CONTINUE : types_1.STEP_STATUS.FAILURE;
|
|
394
486
|
// 记录全局的执行状态
|
|
395
487
|
if (this.record.editStatusAble) {
|
|
@@ -399,35 +491,42 @@ class Engine {
|
|
|
399
491
|
// 全局的执行状态一旦失败,便不可修改
|
|
400
492
|
this.record.editStatusAble = false;
|
|
401
493
|
}
|
|
494
|
+
// If the project step has an ID, update the corresponding record status.
|
|
402
495
|
if (item.id) {
|
|
403
496
|
this.record.steps = Object.assign(Object.assign({}, this.record.steps), { [item.id]: {
|
|
404
497
|
status,
|
|
405
498
|
} });
|
|
406
499
|
}
|
|
407
|
-
|
|
500
|
+
// Handle the error based on the project step's "continue-on-error" attribute.
|
|
408
501
|
if (item['continue-on-error']) {
|
|
409
|
-
this.recordContext(item, { status
|
|
502
|
+
this.recordContext(item, { status });
|
|
410
503
|
}
|
|
411
504
|
else {
|
|
412
|
-
this.recordContext(item, { status, error
|
|
505
|
+
this.recordContext(item, { status, error });
|
|
413
506
|
throw error;
|
|
414
507
|
}
|
|
415
508
|
}
|
|
416
509
|
});
|
|
417
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
|
+
*/
|
|
418
516
|
getProps(item) {
|
|
419
517
|
return __awaiter(this, void 0, void 0, function* () {
|
|
420
518
|
const magic = this.getFilterContext(item);
|
|
421
519
|
debug(`magic context: ${JSON.stringify(magic)}`);
|
|
422
520
|
const newInputs = (0, parse_spec_1.getInputs)(item.props, magic);
|
|
423
|
-
const { projectName,
|
|
521
|
+
const { projectName, command } = this.spec;
|
|
424
522
|
const result = {
|
|
425
523
|
cwd: this.options.cwd,
|
|
524
|
+
name: (0, lodash_1.get)(this.spec, 'yaml.appName'),
|
|
426
525
|
props: newInputs,
|
|
427
|
-
command
|
|
428
|
-
args: (0, lodash_1.filter)(this.options.args,
|
|
526
|
+
command,
|
|
527
|
+
args: (0, lodash_1.filter)(this.options.args, o => !(0, lodash_1.includes)([projectName, command], o)),
|
|
429
528
|
yaml: {
|
|
430
|
-
path: this.spec
|
|
529
|
+
path: (0, lodash_1.get)(this.spec, 'yaml.path'),
|
|
431
530
|
},
|
|
432
531
|
resource: {
|
|
433
532
|
name: item.projectName,
|
|
@@ -445,78 +544,98 @@ class Engine {
|
|
|
445
544
|
return result;
|
|
446
545
|
});
|
|
447
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
|
+
*/
|
|
448
553
|
doSrc(item, data = {}) {
|
|
449
554
|
return __awaiter(this, void 0, void 0, function* () {
|
|
450
|
-
|
|
555
|
+
// Extract command and projectName from the specification.
|
|
556
|
+
const { command = '', projectName } = this.spec;
|
|
557
|
+
// Retrieve properties for the given project step.
|
|
451
558
|
const newInputs = yield this.getProps(item);
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
this.
|
|
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);
|
|
455
563
|
// 服务级操作
|
|
456
564
|
if (projectName) {
|
|
457
|
-
if ((0, lodash_1.isFunction)(item.instance[
|
|
565
|
+
if ((0, lodash_1.isFunction)(item.instance[command])) {
|
|
458
566
|
// 方法存在,执行报错,退出码101
|
|
459
567
|
try {
|
|
460
|
-
return yield item.instance[
|
|
568
|
+
return yield item.instance[command](this.record.componentProps);
|
|
461
569
|
}
|
|
462
570
|
catch (e) {
|
|
463
571
|
const useAllowFailure = (0, utils_1.getAllowFailure)(item.allow_failure, {
|
|
464
572
|
exitCode: constants_1.EXIT_CODE.COMPONENT,
|
|
465
|
-
command
|
|
573
|
+
command,
|
|
466
574
|
});
|
|
467
575
|
if (useAllowFailure)
|
|
468
576
|
return;
|
|
469
577
|
const error = e;
|
|
470
|
-
throw new utils_2.
|
|
578
|
+
throw new utils_2.DevsError(error.message, {
|
|
579
|
+
data: (0, lodash_1.get)(e, 'data'),
|
|
580
|
+
stack: error.stack,
|
|
471
581
|
exitCode: constants_1.EXIT_CODE.COMPONENT,
|
|
472
|
-
prefix: `
|
|
582
|
+
prefix: `[${item.projectName}] failed to [${command}]:`,
|
|
473
583
|
});
|
|
474
584
|
}
|
|
475
585
|
}
|
|
476
586
|
const useAllowFailure = (0, utils_1.getAllowFailure)(item.allow_failure, {
|
|
477
587
|
exitCode: constants_1.EXIT_CODE.DEVS,
|
|
478
|
-
command
|
|
588
|
+
command,
|
|
479
589
|
});
|
|
480
590
|
if (useAllowFailure)
|
|
481
591
|
return;
|
|
482
592
|
// 方法不存在,此时系统将会认为是未找到组件方法,系统的exit code为100;
|
|
483
|
-
throw new utils_2.
|
|
593
|
+
throw new utils_2.DevsError(`The [${command}] command was not found.`, {
|
|
484
594
|
exitCode: constants_1.EXIT_CODE.DEVS,
|
|
485
|
-
tips: `Please check the component ${item.component} has the ${
|
|
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}]:`,
|
|
486
597
|
});
|
|
487
598
|
}
|
|
488
599
|
// 应用级操作
|
|
489
|
-
if ((0, lodash_1.isFunction)(item.instance[
|
|
600
|
+
if ((0, lodash_1.isFunction)(item.instance[command])) {
|
|
490
601
|
// 方法存在,执行报错,退出码101
|
|
491
602
|
try {
|
|
492
|
-
return yield item.instance[
|
|
603
|
+
return yield item.instance[command](this.record.componentProps);
|
|
493
604
|
}
|
|
494
605
|
catch (e) {
|
|
495
606
|
const useAllowFailure = (0, utils_1.getAllowFailure)(item.allow_failure, {
|
|
496
607
|
exitCode: constants_1.EXIT_CODE.COMPONENT,
|
|
497
|
-
command
|
|
608
|
+
command,
|
|
498
609
|
});
|
|
499
610
|
if (useAllowFailure)
|
|
500
611
|
return;
|
|
501
612
|
const error = e;
|
|
502
|
-
throw new utils_2.
|
|
613
|
+
throw new utils_2.DevsError(error.message, {
|
|
614
|
+
data: (0, lodash_1.get)(e, 'data'),
|
|
615
|
+
stack: error.stack,
|
|
503
616
|
exitCode: constants_1.EXIT_CODE.COMPONENT,
|
|
504
|
-
prefix: `
|
|
617
|
+
prefix: `[${item.projectName}] failed to [${command}]:`,
|
|
505
618
|
});
|
|
506
619
|
}
|
|
507
620
|
}
|
|
508
621
|
// 方法不存在,进行警告,但是并不会报错,最终的exit code为0;
|
|
509
|
-
this.logger.tips(`The [${
|
|
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`);
|
|
510
623
|
});
|
|
511
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
|
+
*/
|
|
512
630
|
doSkip(item) {
|
|
513
631
|
return __awaiter(this, void 0, void 0, function* () {
|
|
514
|
-
// id
|
|
632
|
+
// If the step has an 'id', set its status to 'SKIP' in the record.
|
|
515
633
|
if (item.id) {
|
|
516
634
|
this.record.steps = Object.assign(Object.assign({}, this.record.steps), { [item.id]: {
|
|
517
635
|
status: types_1.STEP_STATUS.SKIP,
|
|
518
636
|
} });
|
|
519
637
|
}
|
|
638
|
+
// Mark the step's status as 'SKIP' and set its processing time to 0.
|
|
520
639
|
this.recordContext(item, { status: types_1.STEP_STATUS.SKIP, process_time: 0 });
|
|
521
640
|
return Promise.resolve();
|
|
522
641
|
});
|