@cto.ai/ops
Version:
š» CTO.ai Ops - The CLI built for Teams š
126 lines (125 loc) ⢠5.21 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
const sdk_1 = require("@cto.ai/sdk");
const child_process_1 = require("child_process");
const debug_1 = tslib_1.__importDefault(require("debug"));
const uuid_1 = require("uuid");
const path = tslib_1.__importStar(require("path"));
const fs = tslib_1.__importStar(require("fs-extra"));
const CustomErrors_1 = require("../errors/CustomErrors");
const utils_1 = require("../utils");
const stateAndConfigHelpers_1 = require("../utils/stateAndConfigHelpers");
const debug = debug_1.default('ops:WorkflowService');
const { callOutCyan, whiteBright, bold, redBright } = sdk_1.ux.colors;
class WorkflowService {
async run(workflow, opParams, config) {
try {
const { name, steps } = Object.assign(Object.assign({}, workflow), { steps: interpolateRunCmd(workflow, config.team.name) });
workflow = getRunEnv(workflow, config);
setRunEnv(workflow, config);
const options = {
stdio: 'inherit',
shell: true,
env: process.env,
};
const workflowCommands = steps.map(convertCommandToSpawnFunction(options));
const errors = [];
const workflowPipeline = utils_1.asyncPipe(...workflowCommands);
const finalOutput = await workflowPipeline({
errors,
args: opParams,
});
const { errors: finalErrors } = finalOutput;
if (finalErrors.length) {
console.log(`\nāļø Workflow ${callOutCyan(name)} failed.`);
finalErrors.forEach((error, i) => {
console.log(redBright(`š¤ There was a problem with the ${whiteBright(error.runCommand)}.\n`));
});
}
!finalErrors.length &&
_printMessage(`š Workflow ${callOutCyan(name)} completed successfully.`);
}
catch (err) {
debug('%O', err);
throw err;
}
}
}
exports.WorkflowService = WorkflowService;
const getRunEnv = (workflow, config) => {
const runId = uuid_1.v4();
workflow.runId = runId;
const opsHome = `${process.env.HOME ||
process.env.USERPROFILE}/.config/@cto.ai/ops`;
workflow.opsHome = opsHome === undefined ? '' : opsHome;
workflow.stateDir = `/${config.team.name}/${workflow.name}/${runId}`;
workflow.configDir = `/${config.team.name}/${workflow.name}`;
if (!fs.existsSync(workflow.stateDir)) {
try {
fs.ensureDirSync(path.resolve(workflow.opsHome + workflow.stateDir));
}
catch (err) {
this.debug('%O', err);
throw new CustomErrors_1.CouldNotMakeDir(err, path.resolve(workflow.opsHome + workflow.stateDir));
}
}
return workflow;
};
// TODO this should be refactored so with the opService setEnv to make it dry
const setRunEnv = (workflow, config) => {
const defaultEnv = {
SDK_STATE_DIR: workflow.stateDir,
SDK_CONFIG_DIR: workflow.configDir,
RUN_ID: workflow.runId,
OPS_OP_NAME: workflow.name,
OPS_TEAM_NAME: config.team.name,
OPS_ACCESS_TOKEN: config.tokens.accessToken,
};
const opsYamlEnv = workflow.env
? workflow.env.reduce(convertEnvStringsToObject, {})
: {};
workflow.env = Object.entries(Object.assign(Object.assign({}, defaultEnv), opsYamlEnv))
.map(overrideEnvWithProcessEnv(process.env))
.map(([key, val]) => `${key}=${val}`);
workflow.env.forEach(env => {
const envParts = env.split('=');
process.env[envParts[0]] = envParts[1];
});
};
const convertEnvStringsToObject = (acc, curr) => {
const [key, val] = curr.split('=');
if (!val) {
return Object.assign({}, acc);
}
return Object.assign(Object.assign({}, acc), { [key]: val });
};
const overrideEnvWithProcessEnv = (processEnv) => ([key, val]) => [key, processEnv[key] || val];
const interpolateRunCmd = ({ steps, runId, name }, teamName) => {
if (!steps.length) {
throw new CustomErrors_1.NoStepsFound();
}
return steps.map(step => {
step = stateAndConfigHelpers_1.replaceStateDirEnv(step, teamName, name, runId);
return stateAndConfigHelpers_1.replaceConfigDirEnv(step, teamName, name);
});
};
const convertCommandToSpawnFunction = (options) => (runCommand) => {
return _runWorkflow(options)(runCommand);
};
const _runWorkflow = (options) => _runWorkflowHof(options);
const _runWorkflowHof = (options) => (runCommand) => async ({ errors, args, }) => {
console.log(`\n ${bold(`š Running ${runCommand}`)} \n`, ``);
const childProcess = child_process_1.spawn(runCommand, [], options);
const exitResponse = await utils_1.onExit(childProcess);
if (exitResponse) {
_printMessage(`š Running ${runCommand}`);
}
const newErrors = exitResponse
? [...errors, { exitResponse, runCommand }]
: [...errors];
return { errors: newErrors, args };
};
const _printMessage = (boldText, normalText = '') => {
console.log(`\n ${bold(boldText)} ${normalText}\n`);
};