@certd/pipeline 1.0.0

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.
Files changed (45) hide show
  1. package/.eslintrc +21 -0
  2. package/.mocharc.json +5 -0
  3. package/.prettierrc +3 -0
  4. package/README.md +16 -0
  5. package/index.ts +1 -0
  6. package/package.json +61 -0
  7. package/src/access/api.ts +18 -0
  8. package/src/access/decorator.ts +39 -0
  9. package/src/access/index.ts +3 -0
  10. package/src/access/registry.ts +4 -0
  11. package/src/context/index.ts +6 -0
  12. package/src/core/context.ts +88 -0
  13. package/src/core/executor.ts +207 -0
  14. package/src/core/index.ts +4 -0
  15. package/src/core/run-history.ts +158 -0
  16. package/src/core/storage.ts +140 -0
  17. package/src/d.ts/fast-crud.ts +115 -0
  18. package/src/d.ts/index.ts +2 -0
  19. package/src/d.ts/pipeline.ts +119 -0
  20. package/src/decorator/common.ts +17 -0
  21. package/src/decorator/index.ts +2 -0
  22. package/src/decorator/utils.ts +42 -0
  23. package/src/index.ts +9 -0
  24. package/src/midway/configuration.ts +52 -0
  25. package/src/midway/index.ts +5 -0
  26. package/src/plugin/api.ts +60 -0
  27. package/src/plugin/decorator.ts +63 -0
  28. package/src/plugin/index.ts +3 -0
  29. package/src/plugin/registry.ts +4 -0
  30. package/src/plugin/test/echo-plugin.ts +31 -0
  31. package/src/registry/index.ts +1 -0
  32. package/src/registry/registry.ts +57 -0
  33. package/src/utils/index.ts +7 -0
  34. package/src/utils/util.log.ts +35 -0
  35. package/src/utils/util.request.ts +58 -0
  36. package/src/utils/util.sleep.ts +7 -0
  37. package/test/cert.fake.test.ts +58 -0
  38. package/test/echo-plugin.ts +30 -0
  39. package/test/index.test.ts +16 -0
  40. package/test/pipeline/access-service-test.ts +10 -0
  41. package/test/pipeline/init.test.ts +15 -0
  42. package/test/pipeline/pipeline.define.ts +66 -0
  43. package/test/pipeline/pipeline.test.ts +18 -0
  44. package/tsconfig.json +20 -0
  45. package/vite.config.js +35 -0
package/.eslintrc ADDED
@@ -0,0 +1,21 @@
1
+ {
2
+ "parser": "@typescript-eslint/parser",
3
+ "plugins": [
4
+ "@typescript-eslint"
5
+ ],
6
+ "extends": [
7
+ "plugin:@typescript-eslint/recommended",
8
+ "plugin:prettier/recommended",
9
+ "prettier"
10
+ ],
11
+ "env": {
12
+ "mocha": true
13
+ },
14
+ "rules": {
15
+ "@typescript-eslint/ban-ts-comment": "off",
16
+ "@typescript-eslint/ban-ts-ignore": "off",
17
+ "@typescript-eslint/no-explicit-any": "off",
18
+ // "no-unused-expressions": "off",
19
+ "max-len": [0, 160, 2, { "ignoreUrls": true }]
20
+ }
21
+ }
package/.mocharc.json ADDED
@@ -0,0 +1,5 @@
1
+ {
2
+ "extension": ["ts"],
3
+ "spec": "test/**/*.test.ts",
4
+ "require": "ts-node/register"
5
+ }
package/.prettierrc ADDED
@@ -0,0 +1,3 @@
1
+ {
2
+ "printWidth": 160
3
+ }
package/README.md ADDED
@@ -0,0 +1,16 @@
1
+ # Vue 3 + TypeScript + Vite
2
+
3
+ This template should help get you started developing with Vue 3 and TypeScript in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.
4
+
5
+ ## Recommended IDE Setup
6
+
7
+ - [VS Code](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar)
8
+
9
+ ## Type Support For `.vue` Imports in TS
10
+
11
+ Since TypeScript cannot handle type information for `.vue` imports, they are shimmed to be a generic Vue component type by default. In most cases this is fine if you don't really care about component prop types outside of templates. However, if you wish to get actual prop types in `.vue` imports (for example to get props validation when using manual `h(...)` calls), you can enable Volar's Take Over mode by following these steps:
12
+
13
+ 1. Run `Extensions: Show Built-in Extensions` from VS Code's command palette, look for `TypeScript and JavaScript Language Features`, then right click and select `Disable (Workspace)`. By default, Take Over mode will enable itself if the default TypeScript extension is disabled.
14
+ 2. Reload the VS Code window by running `Developer: Reload Window` from the command palette.
15
+
16
+ You can learn more about Take Over mode [here](https://github.com/johnsoncodehk/volar/discussions/471).
package/index.ts ADDED
@@ -0,0 +1 @@
1
+ export * from "./src";
package/package.json ADDED
@@ -0,0 +1,61 @@
1
+ {
2
+ "name": "@certd/pipeline",
3
+ "private": false,
4
+ "version": "1.0.0",
5
+ "main": "./dist/pipeline.umd.js",
6
+ "module": "./dist/pipeline.mjs",
7
+ "types": "./dist/d/index.d.ts",
8
+ "publishConfig": {
9
+ "main": "./dist/pipeline.umd.js",
10
+ "module": "./dist/pipeline.mjs",
11
+ "types": "./dist/d/index.d.ts"
12
+ },
13
+ "scripts": {
14
+ "dev": "vite",
15
+ "build": "vue-tsc --noEmit && vite build",
16
+ "preview": "vite preview"
17
+ },
18
+ "dependencies": {
19
+ "@certd/acme-client": "^1.0.0",
20
+ "axios": "^0.21.1",
21
+ "dayjs": "^1.11.6",
22
+ "lodash": "^4.17.21",
23
+ "log4js": "^6.3.0",
24
+ "node-forge": "^0.10.0",
25
+ "qs": "^6.9.4",
26
+ "reflect-metadata": "^0.1.13"
27
+ },
28
+ "devDependencies": {
29
+ "@alicloud/cs20151215": "^3.0.3",
30
+ "@alicloud/openapi-client": "^0.4.0",
31
+ "@alicloud/pop-core": "^1.7.10",
32
+ "@midwayjs/bootstrap": "^3.9.1",
33
+ "@midwayjs/cache": "^3.9.0",
34
+ "@midwayjs/cli": "^1.3.21",
35
+ "@midwayjs/core": "^3.0.0",
36
+ "@midwayjs/decorator": "^3.0.0",
37
+ "@midwayjs/koa": "^3.9.0",
38
+ "@midwayjs/logger": "^2.17.0",
39
+ "@midwayjs/typeorm": "^3.9.0",
40
+ "@midwayjs/validate": "^3.9.0",
41
+ "@rollup/plugin-typescript": "^11.0.0",
42
+ "@types/chai": "^4.3.3",
43
+ "@types/lodash": "^4.14.194",
44
+ "@types/mocha": "^10.0.0",
45
+ "@types/node-forge": "^1.3.0",
46
+ "@typescript-eslint/eslint-plugin": "^5.38.1",
47
+ "@typescript-eslint/parser": "^5.38.1",
48
+ "chai": "^4.3.6",
49
+ "eslint": "^8.24.0",
50
+ "eslint-config-prettier": "^8.5.0",
51
+ "eslint-plugin-import": "^2.26.0",
52
+ "eslint-plugin-node": "^11.1.0",
53
+ "eslint-plugin-prettier": "^4.2.1",
54
+ "mocha": "^10.1.0",
55
+ "ts-node": "^10.9.1",
56
+ "typescript": "^4.8.4",
57
+ "vite": "^3.1.0",
58
+ "vue-tsc": "^0.38.9"
59
+ },
60
+ "gitHead": "5950e1cae7cf30ebfc5128c15c7d1b0d101cbbb8"
61
+ }
@@ -0,0 +1,18 @@
1
+ import { Registrable } from "../registry";
2
+ import { FormItemProps } from "../d.ts";
3
+
4
+ export type AccessInputDefine = FormItemProps & {
5
+ title: string;
6
+ required?: boolean;
7
+ };
8
+ export type AccessDefine = Registrable & {
9
+ input?: {
10
+ [key: string]: AccessInputDefine;
11
+ };
12
+ };
13
+ export interface IAccessService {
14
+ getById(id: any): Promise<any>;
15
+ }
16
+
17
+ // eslint-disable-next-line @typescript-eslint/no-empty-interface
18
+ export interface IAccess {}
@@ -0,0 +1,39 @@
1
+ // src/decorator/memoryCache.decorator.ts
2
+ import { AccessDefine, AccessInputDefine } from "./api";
3
+ import { Decorator } from "../decorator";
4
+ import _ from "lodash";
5
+ import { accessRegistry } from "./registry";
6
+
7
+ // 提供一个唯一 key
8
+ export const ACCESS_CLASS_KEY = "pipeline:access";
9
+ export const ACCESS_INPUT_KEY = "pipeline:access:input";
10
+
11
+ export function IsAccess(define: AccessDefine): ClassDecorator {
12
+ return (target: any) => {
13
+ target = Decorator.target(target);
14
+
15
+ const inputs: any = {};
16
+ const properties = Decorator.getClassProperties(target);
17
+ for (const property in properties) {
18
+ const input = Reflect.getMetadata(ACCESS_INPUT_KEY, target, property);
19
+ if (input) {
20
+ inputs[property] = input;
21
+ }
22
+ }
23
+ _.merge(define, { input: inputs });
24
+ Reflect.defineMetadata(ACCESS_CLASS_KEY, define, target);
25
+ target.define = define;
26
+ accessRegistry.register(define.name, {
27
+ define,
28
+ target,
29
+ });
30
+ };
31
+ }
32
+
33
+ export function AccessInput(input?: AccessInputDefine): PropertyDecorator {
34
+ return (target, propertyKey) => {
35
+ target = Decorator.target(target, propertyKey);
36
+ // const _type = Reflect.getMetadata("design:type", target, propertyKey);
37
+ Reflect.defineMetadata(ACCESS_INPUT_KEY, input, target, propertyKey);
38
+ };
39
+ }
@@ -0,0 +1,3 @@
1
+ export * from "./api";
2
+ export * from "./registry";
3
+ export * from "./decorator";
@@ -0,0 +1,4 @@
1
+ import { Registry } from "../registry";
2
+
3
+ // @ts-ignore
4
+ export const accessRegistry = new Registry();
@@ -0,0 +1,6 @@
1
+ import { AxiosInstance } from "axios";
2
+ import { IContext } from "../core";
3
+
4
+ export type HttpClient = AxiosInstance;
5
+ export type UserContext = IContext;
6
+ export type PipelineContext = IContext;
@@ -0,0 +1,88 @@
1
+ import { IStorage, MemoryStorage } from "./storage";
2
+ const CONTEXT_VERSION_KEY = "contextVersion";
3
+ export interface IContext {
4
+ getInt(key: string): Promise<number>;
5
+ get(key: string): Promise<string | null>;
6
+ set(key: string, value: string): Promise<void>;
7
+ getObj<T = any>(key: string): Promise<T | null>;
8
+ setObj<T = any>(key: string, value: T): Promise<void>;
9
+ updateVersion(): Promise<void>;
10
+ initVersion(): Promise<void>;
11
+ }
12
+
13
+ export class ContextFactory {
14
+ storage: IStorage;
15
+ memoryStorage: IStorage;
16
+
17
+ constructor(storage: IStorage) {
18
+ this.storage = storage;
19
+ this.memoryStorage = new MemoryStorage();
20
+ }
21
+
22
+ getContext(scope: string, namespace: string): IContext {
23
+ const context = new StorageContext(scope, namespace, this.storage);
24
+ return context;
25
+ }
26
+
27
+ getMemoryContext(scope: string, namespace: string): IContext {
28
+ const context = new StorageContext(scope, namespace, this.memoryStorage);
29
+ return context;
30
+ }
31
+ }
32
+
33
+ export class StorageContext implements IContext {
34
+ storage: IStorage;
35
+ namespace: string;
36
+ scope: string;
37
+
38
+ _version = 0;
39
+ _initialVersion = 0;
40
+ constructor(scope: string, namespace: string, storage: IStorage) {
41
+ this.storage = storage;
42
+ this.scope = scope;
43
+ this.namespace = namespace;
44
+ }
45
+
46
+ async initVersion() {
47
+ const version = await this.getInt(CONTEXT_VERSION_KEY);
48
+ this._initialVersion = version;
49
+ this._version = version;
50
+ }
51
+
52
+ async updateVersion() {
53
+ if (this._version === this._initialVersion) {
54
+ this._version++;
55
+ }
56
+
57
+ await this.set(CONTEXT_VERSION_KEY, this._version.toString());
58
+ }
59
+
60
+ async get(key: string) {
61
+ const version = key === CONTEXT_VERSION_KEY ? 0 : this._version;
62
+ return await this.storage.get(this.scope, this.namespace, version.toString(), key);
63
+ }
64
+ async set(key: string, value: string) {
65
+ const version = key === CONTEXT_VERSION_KEY ? 0 : this._version;
66
+ return await this.storage.set(this.scope, this.namespace, version.toString(), key, value);
67
+ }
68
+
69
+ async getInt(key: string): Promise<number> {
70
+ const str = await this.get(key);
71
+ if (str) {
72
+ return parseInt(str);
73
+ }
74
+ return 0;
75
+ }
76
+ async getObj<T = any>(key: string): Promise<T | null> {
77
+ const str = await this.get(key);
78
+ if (str) {
79
+ const store = JSON.parse(str);
80
+ return store.value;
81
+ }
82
+ return null;
83
+ }
84
+
85
+ async setObj<T = any>(key: string, value: T) {
86
+ await this.set(key, JSON.stringify({ value }));
87
+ }
88
+ }
@@ -0,0 +1,207 @@
1
+ import { ConcurrencyStrategy, Pipeline, ResultType, Runnable, RunStrategy, Stage, Step, Task } from "../d.ts";
2
+ import _ from "lodash";
3
+ import { RunHistory, RunnableCollection } from "./run-history";
4
+ import { AbstractTaskPlugin, PluginDefine, pluginRegistry } from "../plugin";
5
+ import { ContextFactory, IContext } from "./context";
6
+ import { IStorage } from "./storage";
7
+ import { logger } from "../utils/util.log";
8
+ import { Logger } from "log4js";
9
+ import { request } from "../utils/util.request";
10
+ import { IAccessService } from "../access";
11
+ import { RegistryItem } from "../registry";
12
+ import { Decorator } from "../decorator";
13
+
14
+ export class Executor {
15
+ userId: any;
16
+ pipeline: Pipeline;
17
+ runtime!: RunHistory;
18
+ accessService: IAccessService;
19
+ contextFactory: ContextFactory;
20
+ logger: Logger;
21
+ pipelineContext!: IContext;
22
+ lastStatusMap!: RunnableCollection;
23
+ onChanged: (history: RunHistory) => void;
24
+ constructor(options: {
25
+ userId: any;
26
+ pipeline: Pipeline;
27
+ storage: IStorage;
28
+ onChanged: (history: RunHistory) => Promise<void>;
29
+ accessService: IAccessService;
30
+ }) {
31
+ this.pipeline = _.cloneDeep(options.pipeline);
32
+ this.onChanged = async (history: RunHistory) => {
33
+ await options.onChanged(history);
34
+ };
35
+ this.accessService = options.accessService;
36
+ this.userId = options.userId;
37
+ this.pipeline.userId = this.userId;
38
+ this.contextFactory = new ContextFactory(options.storage);
39
+ this.logger = logger;
40
+ this.pipelineContext = this.contextFactory.getContext("pipeline", this.pipeline.id);
41
+ }
42
+
43
+ async init() {
44
+ const lastRuntime = await this.pipelineContext.getObj(`lastRuntime`);
45
+ this.lastStatusMap = new RunnableCollection(lastRuntime?.pipeline);
46
+ }
47
+
48
+ async run(runtimeId: any = 0, triggerType: string) {
49
+ try {
50
+ await this.init();
51
+ const trigger = { type: triggerType };
52
+ // 读取last
53
+ this.runtime = new RunHistory(runtimeId, trigger, this.pipeline);
54
+ this.logger.info(`pipeline.${this.pipeline.id} start`);
55
+ await this.runWithHistory(this.pipeline, "pipeline", async () => {
56
+ await this.runStages(this.pipeline);
57
+ });
58
+ } catch (e) {
59
+ this.logger.error("pipeline 执行失败", e);
60
+ } finally {
61
+ await this.pipelineContext.setObj("lastRuntime", this.runtime);
62
+ this.logger.info(`pipeline.${this.pipeline.id} end`);
63
+ }
64
+ }
65
+
66
+ async runWithHistory(runnable: Runnable, runnableType: string, run: () => Promise<void>) {
67
+ runnable.runnableType = runnableType;
68
+ this.runtime.start(runnable);
69
+ await this.onChanged(this.runtime);
70
+
71
+ if (runnable.strategy?.runStrategy === RunStrategy.SkipWhenSucceed) {
72
+ //如果是成功后跳过策略
73
+ const lastNode = this.lastStatusMap.get(runnable.id);
74
+ const lastResult = lastNode?.status?.status;
75
+ const lastInput = JSON.stringify(lastNode?.status?.input);
76
+ let inputChanged = false;
77
+ if (runnableType === "step") {
78
+ const step = runnable as Step;
79
+ const input = JSON.stringify(step.input);
80
+ if (input != null && lastInput !== input) {
81
+ //参数有变化
82
+ inputChanged = true;
83
+ }
84
+ }
85
+ if (lastResult != null && lastResult === ResultType.success && !inputChanged) {
86
+ this.runtime.skip(runnable);
87
+ await this.onChanged(this.runtime);
88
+ return ResultType.skip;
89
+ }
90
+ }
91
+ try {
92
+ await run();
93
+ this.runtime.success(runnable);
94
+ await this.onChanged(this.runtime);
95
+ return ResultType.success;
96
+ } catch (e: any) {
97
+ this.runtime.error(runnable, e);
98
+ await this.onChanged(this.runtime);
99
+ throw e;
100
+ } finally {
101
+ this.runtime.finally(runnable);
102
+ }
103
+ }
104
+
105
+ private async runStages(pipeline: Pipeline) {
106
+ const resList: ResultType[] = [];
107
+ for (const stage of pipeline.stages) {
108
+ const res: ResultType = await this.runWithHistory(stage, "stage", async () => {
109
+ await this.runStage(stage);
110
+ });
111
+ resList.push(res);
112
+ }
113
+ return this.compositionResultType(resList);
114
+ }
115
+
116
+ async runStage(stage: Stage) {
117
+ const runnerList = [];
118
+ for (const task of stage.tasks) {
119
+ const runner = this.runWithHistory(task, "task", async () => {
120
+ await this.runTask(task);
121
+ });
122
+ runnerList.push(runner);
123
+ }
124
+
125
+ let resList: ResultType[] = [];
126
+ if (stage.concurrency === ConcurrencyStrategy.Parallel) {
127
+ resList = await Promise.all(runnerList);
128
+ } else {
129
+ for (let i = 0; i < runnerList.length; i++) {
130
+ const runner = runnerList[i];
131
+ resList[i] = await runner;
132
+ }
133
+ }
134
+ return this.compositionResultType(resList);
135
+ }
136
+
137
+ compositionResultType(resList: ResultType[]) {
138
+ let hasSuccess = false;
139
+ for (const type of resList) {
140
+ if (type === ResultType.error) {
141
+ return ResultType.error;
142
+ }
143
+ if (type === ResultType.success) {
144
+ hasSuccess = true;
145
+ }
146
+ }
147
+ if (hasSuccess) {
148
+ return ResultType.success;
149
+ }
150
+ return ResultType.error;
151
+ }
152
+
153
+ private async runTask(task: Task) {
154
+ const resList: ResultType[] = [];
155
+ for (const step of task.steps) {
156
+ step.runnableType = "step";
157
+ const res: ResultType = await this.runWithHistory(step, "step", async () => {
158
+ await this.runStep(step);
159
+ });
160
+ resList.push(res);
161
+ }
162
+ return this.compositionResultType(resList);
163
+ }
164
+
165
+ private async runStep(step: Step) {
166
+ const lastStatus = this.lastStatusMap.get(step.id);
167
+ //执行任务
168
+ const plugin: RegistryItem<AbstractTaskPlugin> = pluginRegistry.get(step.type);
169
+
170
+ // @ts-ignore
171
+ const instance: ITaskPlugin = new plugin.target();
172
+ // @ts-ignore
173
+ const define: PluginDefine = plugin.define;
174
+ //从outputContext读取输入参数
175
+ Decorator.inject(define.input, instance, step.input, (item, key) => {
176
+ if (item.component?.name === "pi-output-selector") {
177
+ const contextKey = step.input[key];
178
+ if (contextKey != null) {
179
+ step.input[key] = this.runtime.context[contextKey];
180
+ }
181
+ }
182
+ });
183
+
184
+ const context: any = {
185
+ logger: this.runtime._loggers[step.id],
186
+ accessService: this.accessService,
187
+ pipelineContext: this.pipelineContext,
188
+ lastStatus,
189
+ userContext: this.contextFactory.getContext("user", this.userId),
190
+ http: request,
191
+ };
192
+ Decorator.inject(define.autowire, instance, context);
193
+
194
+ await instance.onInstance();
195
+ await instance.execute();
196
+
197
+ if (instance.result.clearLastStatus) {
198
+ this.lastStatusMap.clear();
199
+ }
200
+ //输出到output context
201
+ _.forEach(define.output, (item, key) => {
202
+ step!.status!.output[key] = instance[key];
203
+ const stepOutputKey = `step.${step.id}.${key}`;
204
+ this.runtime.context[stepOutputKey] = instance[key];
205
+ });
206
+ }
207
+ }
@@ -0,0 +1,4 @@
1
+ export * from "./executor";
2
+ export * from "./run-history";
3
+ export * from "./context";
4
+ export * from "./storage";
@@ -0,0 +1,158 @@
1
+ import { Context, HistoryResult, Pipeline, ResultType, Runnable, RunnableMap, Stage, Step, Task } from "../d.ts";
2
+ import _ from "lodash";
3
+ import { buildLogger } from "../utils/util.log";
4
+ import { Logger } from "log4js";
5
+
6
+ export type HistoryStatus = {
7
+ result: HistoryResult;
8
+ logs: string[];
9
+ };
10
+
11
+ export type RunTrigger = {
12
+ type: string; // user , timer
13
+ };
14
+
15
+ export function NewRunHistory(obj: any) {
16
+ const history = new RunHistory(obj.id, obj.trigger, obj.pipeline);
17
+ history.context = obj.context;
18
+ history.logs = obj.logs;
19
+ history._loggers = obj.loggers;
20
+ return history;
21
+ }
22
+ export class RunHistory {
23
+ id!: string;
24
+ //运行时上下文变量
25
+ context: Context = {};
26
+ pipeline!: Pipeline;
27
+ logs: {
28
+ [runnableId: string]: string[];
29
+ } = {};
30
+ _loggers: {
31
+ [runnableId: string]: Logger;
32
+ } = {};
33
+ trigger!: RunTrigger;
34
+
35
+ constructor(runtimeId: any, trigger: RunTrigger, pipeline: Pipeline) {
36
+ this.id = runtimeId;
37
+ this.pipeline = pipeline;
38
+ this.trigger = trigger;
39
+ }
40
+
41
+ start(runnable: Runnable): HistoryResult {
42
+ const now = new Date().getTime();
43
+ this.logs[runnable.id] = [];
44
+ this._loggers[runnable.id] = buildLogger((text) => {
45
+ this.logs[runnable.id].push(text);
46
+ });
47
+ const input = (runnable as Step).input;
48
+ const status: HistoryResult = {
49
+ output: {},
50
+ input: _.cloneDeep(input),
51
+ status: ResultType.start,
52
+ startTime: now,
53
+ result: ResultType.start,
54
+ };
55
+ runnable.status = status;
56
+ this.log(runnable, `开始执行`);
57
+ return status;
58
+ }
59
+
60
+ success(runnable: Runnable) {
61
+ const now = new Date().getTime();
62
+ const status = runnable.status;
63
+ _.merge(status, {
64
+ status: "success",
65
+ endTime: now,
66
+ result: "success",
67
+ });
68
+ this.log(runnable, `执行成功`);
69
+ }
70
+
71
+ skip(runnable: Runnable) {
72
+ const now = new Date().getTime();
73
+ const status = runnable.status;
74
+ _.merge(status, {
75
+ status: "success",
76
+ endTime: now,
77
+ result: "skip",
78
+ });
79
+ this.log(runnable, `跳过`);
80
+ }
81
+
82
+ error(runnable: Runnable, e: Error) {
83
+ const now = new Date().getTime();
84
+ const status = runnable.status;
85
+ _.merge(status, {
86
+ status: ResultType.error,
87
+ endTime: now,
88
+ result: ResultType.error,
89
+ message: e.message,
90
+ });
91
+
92
+ this.logError(runnable, e);
93
+ }
94
+
95
+ log(runnable: Runnable, text: string) {
96
+ // @ts-ignore
97
+ this._loggers[runnable.id].info(`[${runnable.title}]<id:${runnable.id}> [${runnable.runnableType}]`, text);
98
+ }
99
+
100
+ logError(runnable: Runnable, e: Error) {
101
+ // @ts-ignore
102
+ this._loggers[runnable.id].error(`[${runnable.title}]<id:${runnable.id}> [${runnable.runnableType}]`, e);
103
+ }
104
+
105
+ finally(runnable: Runnable) {
106
+ //
107
+ }
108
+ }
109
+
110
+ export class RunnableCollection {
111
+ private collection: RunnableMap = {};
112
+ private pipeline!: Pipeline;
113
+ constructor(pipeline?: Pipeline) {
114
+ if (!pipeline) {
115
+ return;
116
+ }
117
+ this.pipeline = pipeline;
118
+ const map = this.toMap(pipeline);
119
+ this.collection = map;
120
+ }
121
+
122
+ private each<T extends Runnable>(list: T[], exec: (item: Runnable) => void) {
123
+ list.forEach((item) => {
124
+ exec(item);
125
+ if (item.runnableType === "pipeline") {
126
+ // @ts-ignore
127
+ this.each<Stage>(item.stages, exec);
128
+ } else if (item.runnableType === "stage") {
129
+ // @ts-ignore
130
+ this.each<Task>(item.tasks, exec);
131
+ } else if (item.runnableType === "task") {
132
+ // @ts-ignore
133
+ this.each<Step>(item.steps, exec);
134
+ }
135
+ });
136
+ }
137
+ private toMap(pipeline: Pipeline) {
138
+ const map: RunnableMap = {};
139
+
140
+ this.each(pipeline.stages, (item) => {
141
+ map[item.id] = item;
142
+ });
143
+ return map;
144
+ }
145
+
146
+ get(id: string): Runnable | undefined {
147
+ return this.collection[id];
148
+ }
149
+
150
+ clear() {
151
+ if (!this.pipeline) {
152
+ return;
153
+ }
154
+ this.each(this.pipeline.stages, (item) => {
155
+ item.status = undefined;
156
+ });
157
+ }
158
+ }