1 | "use strict";
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 | const tslib_1 = require("tslib");
|
4 | const sdk_1 = require("@cto.ai/sdk");
|
5 | const child_process_1 = require("child_process");
|
6 | const debug_1 = tslib_1.__importDefault(require("debug"));
|
7 | const uuid_1 = require("uuid");
|
8 | const path = tslib_1.__importStar(require("path"));
|
9 | const fs = tslib_1.__importStar(require("fs-extra"));
|
10 | const CustomErrors_1 = require("../errors/CustomErrors");
|
11 | const utils_1 = require("../utils");
|
12 | const stateAndConfigHelpers_1 = require("../utils/stateAndConfigHelpers");
|
13 | const debug = debug_1.default('ops:WorkflowService');
|
14 | const { callOutCyan, whiteBright, bold, redBright } = sdk_1.ux.colors;
|
15 | class WorkflowService {
|
16 | async run(workflow, opParams, config) {
|
17 | try {
|
18 | const { name, steps } = Object.assign({}, workflow, { steps: interpolateRunCmd(workflow, config.team.name) });
|
19 | workflow = getRunEnv(workflow, config);
|
20 | setRunEnv(workflow, config);
|
21 | const options = {
|
22 | stdio: 'inherit',
|
23 | shell: true,
|
24 | env: process.env,
|
25 | };
|
26 | const workflowCommands = steps.map(convertCommandToSpawnFunction(options));
|
27 | const errors = [];
|
28 | const workflowPipeline = utils_1.asyncPipe(...workflowCommands);
|
29 | const finalOutput = await workflowPipeline({
|
30 | errors,
|
31 | args: opParams,
|
32 | });
|
33 | const { errors: finalErrors } = finalOutput;
|
34 | if (finalErrors.length) {
|
35 | console.log(`\nāļø Workflow ${callOutCyan(name)} failed.`);
|
36 | finalErrors.forEach((error, i) => {
|
37 | console.log(redBright(`š¤ There was a problem with the ${whiteBright(error.runCommand)}.\n`));
|
38 | });
|
39 | }
|
40 | !finalErrors.length &&
|
41 | _printMessage(`š Workflow ${callOutCyan(name)} completed successfully.`);
|
42 | }
|
43 | catch (err) {
|
44 | debug('%O', err);
|
45 | throw err;
|
46 | }
|
47 | }
|
48 | }
|
49 | exports.WorkflowService = WorkflowService;
|
50 | const getRunEnv = (workflow, config) => {
|
51 | const runId = uuid_1.v4();
|
52 | workflow.runId = runId;
|
53 | const opsHome = `${process.env.HOME ||
|
54 | process.env.USERPROFILE}/.config/@cto.ai/ops`;
|
55 | workflow.opsHome = opsHome === undefined ? '' : opsHome;
|
56 | workflow.stateDir = `/${config.team.name}/${workflow.name}/${runId}`;
|
57 | workflow.configDir = `/${config.team.name}/${workflow.name}`;
|
58 | if (!fs.existsSync(workflow.stateDir)) {
|
59 | try {
|
60 | fs.ensureDirSync(path.resolve(workflow.opsHome + workflow.stateDir));
|
61 | }
|
62 | catch (err) {
|
63 | this.debug('%O', err);
|
64 | throw new CustomErrors_1.CouldNotMakeDir(err, path.resolve(workflow.opsHome + workflow.stateDir));
|
65 | }
|
66 | }
|
67 | return workflow;
|
68 | };
|
69 |
|
70 | const setRunEnv = (workflow, config) => {
|
71 | const defaultEnv = {
|
72 | STATE_DIR: workflow.stateDir,
|
73 | CONFIG_DIR: workflow.configDir,
|
74 | RUN_ID: workflow.runId,
|
75 | OPS_OP_NAME: workflow.name,
|
76 | OPS_TEAM_NAME: config.team.name,
|
77 | OPS_ACCESS_TOKEN: config.tokens.accessToken,
|
78 | };
|
79 | const opsYamlEnv = workflow.env
|
80 | ? workflow.env.reduce(convertEnvStringsToObject, {})
|
81 | : [];
|
82 | workflow.env = Object.entries(Object.assign({}, defaultEnv, opsYamlEnv))
|
83 | .map(overrideEnvWithProcessEnv(process.env))
|
84 | .map(([key, val]) => `${key}=${val}`);
|
85 | workflow.env.forEach(env => {
|
86 | const envParts = env.split('=');
|
87 | process.env[envParts[0]] = envParts[1];
|
88 | });
|
89 | };
|
90 | const convertEnvStringsToObject = (acc, curr) => {
|
91 | const [key, val] = curr.split('=');
|
92 | if (!val) {
|
93 | return Object.assign({}, acc);
|
94 | }
|
95 | return Object.assign({}, acc, { [key]: val });
|
96 | };
|
97 | const overrideEnvWithProcessEnv = (processEnv) => ([key, val]) => [key, processEnv[key] || val];
|
98 | const interpolateRunCmd = ({ steps, runId, name }, teamName) => {
|
99 | if (!steps.length) {
|
100 | throw new CustomErrors_1.NoStepsFound();
|
101 | }
|
102 | return steps.map(step => {
|
103 | step = stateAndConfigHelpers_1.replaceStateDirEnv(step, teamName, name, runId);
|
104 | return stateAndConfigHelpers_1.replaceConfigDirEnv(step, teamName, name);
|
105 | });
|
106 | };
|
107 | const convertCommandToSpawnFunction = (options) => (runCommand) => {
|
108 | return _runWorkflow(options)(runCommand);
|
109 | };
|
110 | const _runWorkflow = (options) => _runWorkflowHof(options);
|
111 | const _runWorkflowHof = (options) => (runCommand) => async ({ errors, args, }) => {
|
112 | console.log(`\n ${bold(`š Running ${runCommand}`)} \n`, ``);
|
113 | const childProcess = child_process_1.spawn(runCommand, [], options);
|
114 | const exitResponse = await utils_1.onExit(childProcess);
|
115 | if (exitResponse) {
|
116 | _printMessage(`š Running ${runCommand}`);
|
117 | }
|
118 | const newErrors = exitResponse
|
119 | ? [...errors, { exitResponse, runCommand }]
|
120 | : [...errors];
|
121 | return { errors: newErrors, args };
|
122 | };
|
123 | const _printMessage = (boldText, normalText = '') => {
|
124 | console.log(`\n ${bold(boldText)} ${normalText}\n`);
|
125 | };
|