Orchestrated Workflow

A parent command that executes child commands in sequence, passing data between steps. Useful for release pipelines, build systems, or deployments.

All Example Files

FILE EXPLORER
orchestrated-workflow.ts
1import { cli, makeComposableBuilder, chain } from 'cli-forge';
2
3// Shared options
4const withProjectOption = makeComposableBuilder((args) =>
5  args.option('project', {
6    type: 'string',
7    description: 'Project name to operate on',
8    required: true,
9  })
10);
11
12const withDryRunOption = makeComposableBuilder((args) =>
13  args.option('dryRun', {
14    type: 'boolean',
15    description: 'Print what would happen without making changes',
16    default: false,
17  })
18);
19
20// Result types for each step
21interface ValidationResult {
22  project: string;
23  isClean: boolean;
24  version: string;
25}
26
27interface BuildResult {
28  project: string;
29  outputPath: string;
30  artifacts: string[];
31}
32
33interface PublishResult {
34  project: string;
35  publishedVersion: string;
36  registry: string;
37}
38
39const withValidateCommand = makeComposableBuilder((args) =>
40  args.command('validate', {
41    description: 'Check that the project is ready for release',
42    builder: (cmd) => chain(cmd, withProjectOption),
43    handler: (args): ValidationResult => {
44      console.log(`Validating ${args.project}...`);
45
46      const result: ValidationResult = {
47        project: args.project,
48        isClean: true,
49        version: '1.2.3',
50      };
51
52      console.log(`Validation passed: ${args.project} v${result.version}`);
53      return result;
54    },
55  })
56);
57
58const withBuildCommand = makeComposableBuilder((args) =>
59  args.command('build', {
60    description: 'Build the project for distribution',
61    builder: (cmd) => chain(cmd, withProjectOption),
62    handler: (args): BuildResult => {
63      console.log(`Building ${args.project}...`);
64
65      const result: BuildResult = {
66        project: args.project,
67        outputPath: `dist/${args.project}`,
68        artifacts: [`${args.project}.js`, `${args.project}.d.ts`],
69      };
70
71      console.log(`Build output: ${result.outputPath}`);
72      console.log(`Artifacts: ${result.artifacts.join(', ')}`);
73      return result;
74    },
75  })
76);
77
78const withPublishCommand = makeComposableBuilder((args) =>
79  args.command('publish', {
80    description: 'Publish the built package to npm',
81    builder: (cmd) =>
82      chain(cmd, withProjectOption, withDryRunOption).option('registry', {
83        type: 'string',
84        description: 'Registry URL',
85        default: 'https://registry.npmjs.org',
86      }),
87    handler: (args): PublishResult => {
88      const action = args.dryRun ? '[dry-run] Would publish' : 'Publishing';
89      console.log(`${action} ${args.project} to ${args.registry}...`);
90
91      const result: PublishResult = {
92        project: args.project,
93        publishedVersion: '1.2.3',
94        registry: args.registry,
95      };
96
97      if (!args.dryRun) {
98        console.log(`Published ${result.project}@${result.publishedVersion}`);
99      }
100      return result;
101    },
102  })
103);
104
105cli('release', {
106  description: 'Validate, build, and publish a project',
107  builder: (args) =>
108    chain(
109      args,
110      withProjectOption,
111      withDryRunOption,
112      withValidateCommand,
113      withBuildCommand,
114      withPublishCommand
115    ),
116
117  handler: async (args, ctx) => {
118    const { project, dryRun } = args;
119    const children = ctx.command.getChildren();
120
121    const validateHandler = children.validate.getHandler();
122    const buildHandler = children.build.getHandler();
123    const publishHandler = children.publish.getHandler();
124
125    if (!validateHandler || !buildHandler || !publishHandler) {
126      throw new Error('Missing required command handlers');
127    }
128
129    console.log(`Starting release workflow for ${project}`);
130    console.log(dryRun ? '(dry run mode)\n' : '\n');
131
132    const validation = await validateHandler({ project });
133    if (!validation.isClean) {
134      console.error('Validation failed - aborting release');
135      process.exitCode = 1;
136      return;
137    }
138    console.log('');
139
140    const build = await buildHandler({ project: validation.project });
141    console.log('');
142
143    const publish = await publishHandler({
144      project: build.project,
145      dryRun,
146      registry: 'https://registry.npmjs.org',
147    });
148    console.log('');
149
150    console.log('='.repeat(40));
151    console.log('Release complete!');
152    console.log(`  Project: ${publish.project}`);
153    console.log(`  Version: ${publish.publishedVersion}`);
154    console.log(`  Artifacts: ${build.artifacts.length} files`);
155    console.log(`  Registry: ${publish.registry}`);
156  },
157}).forge();
158