UNPKG

forest-cli

Version:

The Lumberjacks' toolbelt is the Forest CLI which makes easy to manage your back office application directly from the terminal.

311 lines 28 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const core_1 = require("@oclif/core"); const crypto_1 = require("crypto"); const promises_1 = require("fs/promises"); const abstract_authenticated_command_1 = __importDefault(require("../../abstract-authenticated-command")); const errors_1 = require("../../services/layout/errors"); const layout_manager_1 = __importDefault(require("../../services/layout/layout-manager")); const plan_format_1 = require("../../services/layout/plan-format"); const resolve_command_scope_1 = require("../../services/layout/resolve-command-scope"); const sync_1 = require("../../services/layout/sync"); const workflow_apply_1 = require("../../services/layout/workflow-apply"); const workflow_bpmn_1 = require("../../services/layout/workflow-bpmn"); function readStdin() { return new Promise((resolve, reject) => { let data = ''; process.stdin.setEncoding('utf8'); process.stdin.on('data', chunk => { data += chunk; }); process.stdin.on('end', () => resolve(data)); process.stdin.on('error', reject); }); } /** Read the spec: from the file when given, else from the (non-TTY) stdin pipe. */ async function readInput(filePath) { if (!filePath) return readStdin(); try { return await (0, promises_1.readFile)(filePath, 'utf8'); } catch (error) { const reason = error.code === 'ENOENT' ? 'no such file' : error.message; throw new workflow_bpmn_1.WorkflowSpecError(`Cannot read spec file "${filePath}": ${reason}.`); } } /** Add a new (BPMN-less) workflow shell to the layout. */ function addWorkflowShell(manager, scope, shell) { return manager.patchDomain('workflows', [ { op: 'add', path: '/workflows/-', value: { collectionId: shell.collection, id: shell.id, isVisible: true, name: shell.name, position: shell.position, segmentIds: shell.segments, }, }, ], scope); } /** * `forest workflow apply` — create-or-update ONE workflow from a JSON `steps` * spec, without touching the rest of the layout. The spec is the source of * truth: editing a workflow (e.g. adding a step) means editing the JSON and * re-applying — the whole BPMN is recompiled and re-uploaded when it differs * (it is atomic; there is no per-step patch), and the shell metadata (name, * segments, position when pinned) is converged with `replace` patches. Upsert * is matched by explicit `id` (which must exist — a stale id is an error, not * a create), else by name + collection. * * Sequence: compile steps -> BPMN; show the plan and confirm; PATCH the shell * (add if new, metadata replaces otherwise); upload the BPMN to the env's S3 * (presigned) when it changed; PATCH the returned `bpmnAwsS3Identifier`. */ class WorkflowApplyCommand extends abstract_authenticated_command_1.default { constructor(argv, config, plan) { super(argv, config, plan); const { assertPresent, env, inquirer } = this.context; assertPresent({ env, inquirer }); this.env = env; this.inquirer = inquirer; } async runAuthenticated() { const { args, flags } = await this.parse(WorkflowApplyCommand); const fromStdin = !args.file || args.file === '-'; if (!this.checkStdinUsage(fromStdin, flags)) { this.exit(2); return; } let spec; let bpmn; try { spec = (0, workflow_apply_1.parseWorkflowSpec)(await readInput(fromStdin ? undefined : args.file)); bpmn = (0, workflow_bpmn_1.compileWorkflowToBpmn)(spec); } catch (error) { if (this.reportSpecError(error)) return; throw error; } const scope = await (0, resolve_command_scope_1.resolveCommandScope)({ baseEnv: this.env, flags: { env: flags.env, projectId: flags.projectId, team: flags.team }, }); const manager = new layout_manager_1.default(); try { await this.planAndExecute(manager, scope, spec, bpmn, flags, fromStdin); } catch (error) { // Planning-time spec/environment mismatches (e.g. an `id` matching no // remote workflow) get the same clean-error treatment as parse errors. if (this.reportSpecError(error)) return; if (error instanceof errors_1.LayoutApiError && error.status !== 401) { this.logger.error((0, plan_format_1.explainApiError)(error, [])); this.exit(2); } throw error; } } async planAndExecute(manager, scope, spec, bpmn, flags, fromStdin) { const plan = await this.planApply(manager, scope, spec, bpmn); if (plan.match && !plan.bpmnChanged && plan.shellChanges.length === 0) { this.logger.success('Nothing to apply: the workflow already matches the spec.'); return; } this.printPlan(plan, spec, scope, flags['dry-run']); if (flags['dry-run']) { this.log('\n(dry-run: nothing sent)'); return; } if (!flags.force && !(await this.confirmOrBail(fromStdin, scope))) return; await this.executePlan(manager, scope, spec, plan); this.printOutcome(plan, spec, scope); } reportSpecError(error) { if (error instanceof workflow_bpmn_1.WorkflowSpecError) { this.logger.error(error.message); this.exit(2); return true; } return false; } /** M2 guards: no silent hang on a TTY, no interactive prompt when stdin carries the spec. */ checkStdinUsage(fromStdin, flags) { if (!fromStdin) return true; if (process.stdin.isTTY) { this.logger.error('Missing spec: pass a file path, or pipe JSON via stdin (e.g. `forest workflow apply spec.json`).'); return false; } if (flags.projectId === undefined || !flags.env || !flags.team) { this.logger.error('Reading the spec from stdin disables interactive prompts: pass -p, -e and -t explicitly.'); return false; } return true; } /** Resolve everything the apply needs (match, metadata diff, BPMN diff) — read-only. */ async planApply(manager, scope, spec, bpmn) { const existing = (await manager.getLayoutDomain('workflows', scope)); const matches = (0, workflow_apply_1.findWorkflowMatches)(existing, spec); // An explicit `id` means "update that workflow" — silently creating a new // one (reusing a stale/mistyped id) would bypass the name+collection upsert. if (spec.id !== undefined && matches.length === 0) { throw new workflow_bpmn_1.WorkflowSpecError(`No workflow with id "${spec.id}" found in this environment. ` + 'Remove `id` from the spec to create a new workflow, or fix it to update an existing one.'); } if (matches.length > 1) { this.logger.warn(`${matches.length} workflows named "${spec.name}" exist on collection ${spec.collection} — updating the first (id ${matches[0].id}).`); } const match = matches[0]; if (match && match.collectionId !== spec.collection) { throw new Error(`Workflow "${match.id}" is on collection "${match.collectionId}", not "${spec.collection}". ` + 'A workflow cannot change collection — remove the `id`/rename to create a new one.'); } const id = match?.id ?? (0, crypto_1.randomUUID)(); const renderingId = await manager.getRenderingId(scope); // Reuses the layout-apply idempotency brick: byte-compare the compiled BPMN // against the stored version, so an unchanged spec uploads nothing (and a // re-apply is a true no-op). const [bpmnPlan] = match ? await (0, sync_1.planWorkflowBpmn)(manager, scope, [ { collectionId: spec.collection, id, name: spec.name, segmentIds: spec.segments, start: spec.start, steps: spec.steps, }, ], existing, renderingId) : [{ bpmn, changed: true }]; return { bpmn: bpmnPlan.bpmn, bpmnChanged: bpmnPlan.changed, existingCount: existing.length, id, match, renderingId, shellChanges: match ? (0, workflow_apply_1.planShellUpdate)(match, spec) : [], }; } /** Say what is about to happen (or would, on --dry-run) before doing anything. */ printPlan(plan, spec, scope, dryRun) { const verb = dryRun ? 'Would' : 'Will'; const target = `${scope.environmentName} / ${scope.teamName}`; const stepCount = (spec.steps ?? []).length; if (!plan.match) { this.log(`${verb} CREATE workflow "${spec.name}" on collection ${spec.collection} (${stepCount} steps) on ${target}.`); return; } this.log(`${verb} UPDATE workflow "${plan.match.name}" (id ${plan.match.id}) on ${target}:`); plan.shellChanges.forEach(change => this.log(` ~ ${change.label}`)); this.log(plan.bpmnChanged ? ` ~ BPMN: recompiled and replaced (${stepCount} steps) — the current BPMN and its layout edits will be overwritten` : ' = BPMN unchanged, upload will be skipped'); } /** Interactive confirmation — impossible when stdin carries the spec (require --force). */ async confirmOrBail(fromStdin, scope) { if (fromStdin) { this.logger.error('Cannot ask for confirmation when the spec is read from stdin: pass --force to apply.'); this.exit(2); return false; } const { confirm } = await this.inquirer.prompt([ { message: `Apply this workflow to ${scope.environmentName} / ${scope.teamName}?`, name: 'confirm', type: 'confirm', }, ]); if (!confirm) this.logger.info('Aborted: nothing was applied.'); return confirm; } async executePlan(manager, scope, spec, plan) { if (!plan.match) { await addWorkflowShell(manager, scope, { collection: spec.collection, id: plan.id, name: spec.name, position: spec.position ?? plan.existingCount, segments: spec.segments ?? [], }); } else if (plan.shellChanges.length > 0) { await manager.patchDomain('workflows', plan.shellChanges.map(change => change.op), scope); } if (plan.bpmnChanged) await this.uploadAndLinkBpmn(manager, scope, spec, plan); } /** * Upload the compiled BPMN and link it to the workflow. If this fails right * after a new shell was created, roll the shell back so no unusable, * BPMN-less workflow is left behind (an existing workflow is left as is — * its previous BPMN stays linked and re-applying converges). A failed * rollback is warned about, never swallowed. */ async uploadAndLinkBpmn(manager, scope, spec, plan) { try { const version = await manager.uploadWorkflowBpmn(scope, plan.id, spec.collection, plan.renderingId, plan.bpmn); await manager.patchDomain('workflows', [{ op: 'replace', path: `/workflows/${plan.id}/bpmnAwsS3Identifier`, value: version }], scope); } catch (uploadError) { if (!plan.match) { await manager .patchDomain('workflows', [{ op: 'remove', path: `/workflows/${plan.id}` }], scope) .catch(() => { this.logger.warn(`Failed to roll back workflow shell ${plan.id}: a workflow without BPMN may remain — delete it from the UI.`); }); } throw uploadError; } } /** Success message reflecting what actually changed. */ printOutcome(plan, spec, scope) { const name = this.chalk.bold(spec.name); const target = `${this.chalk.bold(scope.environmentName)} / ${this.chalk.bold(scope.teamName)}`; const stepCount = (spec.steps ?? []).length; if (!plan.match) { this.logger.success(`Created workflow ${name} (${stepCount} steps) on ${target}.`); return; } const details = [ ...plan.shellChanges.map(change => change.field), plan.bpmnChanged ? `BPMN uploaded (${stepCount} steps)` : 'BPMN unchanged, skipped', ]; this.logger.success(`Updated workflow ${name} on ${target} (${details.join(', ')}).`); } } exports.default = WorkflowApplyCommand; WorkflowApplyCommand.args = { file: core_1.Args.string({ description: 'JSON workflow spec ({ name, collection, steps, id?, segments?, position?, start?, version? }). "-" or omit to read stdin.', required: false, }), }; WorkflowApplyCommand.description = 'Create or update a workflow from a JSON steps spec (compiles to BPMN and uploads it). ' + 'Reading the spec from stdin disables prompts: pass -p/-e/-t and --force explicitly.'; WorkflowApplyCommand.flags = { 'dry-run': core_1.Flags.boolean({ description: 'Resolve the scope, match the workflow and print the plan; send nothing.', }), env: core_1.Flags.string({ char: 'e', description: 'Environment name or id.' }), force: core_1.Flags.boolean({ char: 'f', description: 'Skip the confirmation prompt (required when the spec is read from stdin).', }), projectId: core_1.Flags.integer({ char: 'p', description: 'Project id.' }), team: core_1.Flags.string({ char: 't', description: 'Team name or id.' }), }; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXBwbHkuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvY29tbWFuZHMvd29ya2Zsb3cvYXBwbHkudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7QUFRQSxzQ0FBMEM7QUFDMUMsbUNBQW9DO0FBQ3BDLDBDQUF1QztBQUV2QywwR0FBZ0Y7QUFDaEYseURBQThEO0FBQzlELDBGQUFpRTtBQUNqRSxtRUFBb0U7QUFDcEUsdUZBQWtGO0FBQ2xGLHFEQUE4RDtBQUM5RCx5RUFJOEM7QUFDOUMsdUVBQStGO0FBRS9GLFNBQVMsU0FBUztJQUNoQixPQUFPLElBQUksT0FBTyxDQUFDLENBQUMsT0FBTyxFQUFFLE1BQU0sRUFBRSxFQUFFO1FBQ3JDLElBQUksSUFBSSxHQUFHLEVBQUUsQ0FBQztRQUNkLE9BQU8sQ0FBQyxLQUFLLENBQUMsV0FBVyxDQUFDLE1BQU0sQ0FBQyxDQUFDO1FBQ2xDLE9BQU8sQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDLE1BQU0sRUFBRSxLQUFLLENBQUMsRUFBRTtZQUMvQixJQUFJLElBQUksS0FBSyxDQUFDO1FBQ2hCLENBQUMsQ0FBQyxDQUFDO1FBQ0gsT0FBTyxDQUFDLEtBQUssQ0FBQyxFQUFFLENBQUMsS0FBSyxFQUFFLEdBQUcsRUFBRSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDO1FBQzdDLE9BQU8sQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDLE9BQU8sRUFBRSxNQUFNLENBQUMsQ0FBQztJQUNwQyxDQUFDLENBQUMsQ0FBQztBQUNMLENBQUM7QUFFRCxtRkFBbUY7QUFDbkYsS0FBSyxVQUFVLFNBQVMsQ0FBQyxRQUE0QjtJQUNuRCxJQUFJLENBQUMsUUFBUTtRQUFFLE9BQU8sU0FBUyxFQUFFLENBQUM7SUFFbEMsSUFBSTtRQUNGLE9BQU8sTUFBTSxJQUFBLG1CQUFRLEVBQUMsUUFBUSxFQUFFLE1BQU0sQ0FBQyxDQUFDO0tBQ3pDO0lBQUMsT0FBTyxLQUFLLEVBQUU7UUFD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