@async/pipeline 0.1.3 → 0.1.4

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 (38) hide show
  1. package/README.md +3 -3
  2. package/dist/internal/core/cache.d.ts +5 -1
  3. package/dist/internal/core/cache.d.ts.map +1 -1
  4. package/dist/internal/core/cache.js +19 -9
  5. package/dist/internal/core/cache.js.map +1 -1
  6. package/dist/internal/core/index.d.ts +121 -3
  7. package/dist/internal/core/index.d.ts.map +1 -1
  8. package/dist/internal/core/index.js +105 -8
  9. package/dist/internal/core/index.js.map +1 -1
  10. package/dist/internal/core/runtime.js +1 -1
  11. package/dist/internal/core/runtime.js.map +1 -1
  12. package/dist/internal/lima/index.d.ts +1 -14
  13. package/dist/internal/lima/index.d.ts.map +1 -1
  14. package/dist/internal/lima/index.js +1 -64
  15. package/dist/internal/lima/index.js.map +1 -1
  16. package/dist/internal/node/cli.d.ts +9 -1
  17. package/dist/internal/node/cli.d.ts.map +1 -1
  18. package/dist/internal/node/cli.js +279 -172
  19. package/dist/internal/node/cli.js.map +1 -1
  20. package/dist/internal/node/doctor.js +2 -2
  21. package/dist/internal/node/doctor.js.map +1 -1
  22. package/dist/internal/node/github.d.ts +3 -1
  23. package/dist/internal/node/github.d.ts.map +1 -1
  24. package/dist/internal/node/github.js +31 -9
  25. package/dist/internal/node/github.js.map +1 -1
  26. package/dist/internal/node/index.d.ts +1 -0
  27. package/dist/internal/node/index.d.ts.map +1 -1
  28. package/dist/internal/node/index.js +1 -0
  29. package/dist/internal/node/index.js.map +1 -1
  30. package/dist/internal/node/runner.d.ts +98 -7
  31. package/dist/internal/node/runner.d.ts.map +1 -1
  32. package/dist/internal/node/runner.js +400 -73
  33. package/dist/internal/node/runner.js.map +1 -1
  34. package/dist/internal/node/store.d.ts +29 -5
  35. package/dist/internal/node/store.d.ts.map +1 -1
  36. package/dist/internal/node/store.js +225 -20
  37. package/dist/internal/node/store.js.map +1 -1
  38. package/package.json +1 -1
@@ -1,169 +1,207 @@
1
1
  #!/usr/bin/env node
2
2
  import { existsSync } from "node:fs";
3
3
  import { basename, resolve } from "node:path";
4
+ import { pathToFileURL } from "node:url";
4
5
  import { buildGraph, composePipelines, tasksForJob } from "../core/index.js";
5
6
  import { runDoctor } from "./doctor.js";
6
7
  import { checkGitHubWorkflow, jobsForGitHubEvent, readGitHubEventContext, renderGitHubWorkflow, writeGitHubWorkflow } from "./github.js";
7
8
  import { loadPipeline } from "./loader.js";
8
- import { runJob, runSingleTask } from "./runner.js";
9
+ import { commandProxy, dockerWorkspace, hostWorkspace, limaWorkspace, runJob, runSingleTask } from "./runner.js";
9
10
  import { createStore } from "./store.js";
10
11
  import { matrixForJob, readPipelineMetadata, resolveSources, sourceContext } from "./sources.js";
11
12
  import { checkTaskSync, describeTaskSync, renderTaskSync, writeTaskSync } from "./sync.js";
12
- async function main() {
13
- const [command, ...args] = process.argv.slice(2);
14
- const program = programName();
15
- const cwd = process.cwd();
16
- const configPath = findPipelineConfig(cwd);
17
- if (command === "doctor") {
18
- const checks = await runDoctor();
19
- for (const check of checks) {
20
- console.log(`${check.status.toUpperCase()} ${check.name}: ${check.message}`);
13
+ export async function runPipelineCli(options) {
14
+ let stdout = "";
15
+ let stderr = "";
16
+ const writeStdout = (text) => {
17
+ stdout += text;
18
+ };
19
+ const writeStderr = (text) => {
20
+ stderr += text;
21
+ };
22
+ const result = await runPipelineCliBuffered({
23
+ args: options.args,
24
+ workspace: options.workspace ?? hostWorkspace(),
25
+ program: options.program,
26
+ stdout: writeStdout,
27
+ stderr: writeStderr,
28
+ applyCommandPolicy: true
29
+ });
30
+ options.stdout?.(result.stdout);
31
+ options.stderr?.(result.stderr);
32
+ if (!options.stdout)
33
+ process.stdout.write(result.stdout);
34
+ if (!options.stderr)
35
+ process.stderr.write(result.stderr);
36
+ return result;
37
+ }
38
+ async function runPipelineCliBuffered(options) {
39
+ try {
40
+ const parsed = parseGlobalOptions(options.args);
41
+ const [commandName, ...args] = parsed.args;
42
+ const program = options.program ?? programName();
43
+ const cwd = options.workspace.cwd;
44
+ const configPath = findPipelineConfig(cwd);
45
+ let stdout = "";
46
+ let stderr = "";
47
+ const out = (text) => {
48
+ stdout += text;
49
+ };
50
+ const err = (text) => {
51
+ stderr += text;
52
+ };
53
+ if (commandName === "doctor") {
54
+ const checks = await runDoctor();
55
+ for (const check of checks)
56
+ out(`${check.status.toUpperCase()} ${check.name}: ${check.message}\n`);
57
+ return { code: checks.some((check) => check.status === "fail") ? 1 : 0, stdout, stderr };
21
58
  }
22
- process.exitCode = checks.some((check) => check.status === "fail") ? 1 : 0;
23
- return;
24
- }
25
- if (!command || command === "help" || command === "--help") {
26
- printHelp(program);
27
- return;
59
+ if (!commandName || commandName === "help" || commandName === "--help") {
60
+ out(printHelp(program));
61
+ return { code: 0, stdout, stderr };
62
+ }
63
+ if (!configPath) {
64
+ throw new Error(`No pipeline.ts, pipeline.mjs, or pipeline.js found in ${cwd}.`);
65
+ }
66
+ const pipeline = await loadPipeline(configPath);
67
+ const workspace = selectWorkspace(parsed.workspaceId, pipeline, options.workspace);
68
+ if (options.applyCommandPolicy && workspace.commands) {
69
+ return workspace.commands.run({
70
+ argv: ["async-pipeline", ...options.args],
71
+ cwd: workspace.cwd,
72
+ env: workspace.env
73
+ }, () => runPipelineCliBuffered({
74
+ ...options,
75
+ args: options.args,
76
+ workspace,
77
+ applyCommandPolicy: false
78
+ }));
79
+ }
80
+ const context = {
81
+ concurrency: parsed.concurrency,
82
+ cwd,
83
+ configPath,
84
+ pipeline,
85
+ workspace,
86
+ stdout: out,
87
+ stderr: err
88
+ };
89
+ const code = await dispatchCommand(commandName, args, context, program);
90
+ return { code, stdout, stderr };
28
91
  }
29
- if (!configPath) {
30
- throw new Error(`No pipeline.ts, pipeline.mjs, or pipeline.js found in ${cwd}.`);
92
+ catch (error) {
93
+ const message = `${error instanceof Error ? error.message : String(error)}\n`;
94
+ return { code: 1, stdout: "", stderr: message };
31
95
  }
32
- const pipeline = await loadPipeline(configPath);
33
- if (command === "sync") {
34
- await handleSyncCommand(args, { cwd, configPath, pipeline });
35
- return;
96
+ }
97
+ async function dispatchCommand(commandName, args, context, program) {
98
+ if (commandName === "sync") {
99
+ return handleSyncCommand(args, context);
36
100
  }
37
- if (command === "github") {
101
+ if (commandName === "github") {
38
102
  const subcommand = args[0] ?? "help";
39
103
  const paths = githubGenerationPaths(args.slice(1));
40
- const rendered = await renderGitHubWorkflow(pipeline, { cwd, configPath, ...paths });
104
+ const rendered = await renderGitHubWorkflow(context.pipeline, { cwd: context.cwd, configPath: context.configPath, ...paths });
41
105
  if (subcommand === "generate") {
42
- await writeGitHubWorkflow(rendered, cwd);
43
- console.log(`Generated ${rendered.workflowPath}`);
44
- console.log(`Generated ${rendered.lockPath}`);
45
- return;
106
+ await writeGitHubWorkflow(rendered, context.cwd);
107
+ context.stdout(`Generated ${rendered.workflowPath}\n`);
108
+ context.stdout(`Generated ${rendered.lockPath}\n`);
109
+ return 0;
46
110
  }
47
111
  if (subcommand === "check") {
48
- const issues = await checkGitHubWorkflow(rendered, cwd);
112
+ const issues = await checkGitHubWorkflow(rendered, context.cwd);
49
113
  if (issues.length > 0) {
50
114
  for (const issue of issues)
51
- console.error(issue);
52
- process.exitCode = 1;
53
- return;
115
+ context.stderr(`${issue}\n`);
116
+ return 1;
54
117
  }
55
- console.log("GitHub workflow is current.");
56
- return;
118
+ context.stdout("GitHub workflow is current.\n");
119
+ return 0;
57
120
  }
58
121
  if (subcommand === "run") {
59
- const context = await readGitHubEventContext(process.env);
60
- const jobs = jobsForGitHubEvent(pipeline, context);
122
+ const eventContext = await readGitHubEventContext(context.workspace.env);
123
+ const jobs = jobsForGitHubEvent(context.pipeline, eventContext);
61
124
  if (jobs.length === 0) {
62
- console.log(`No pipeline jobs matched GitHub event "${context.eventName}".`);
63
- return;
125
+ context.stdout(`No pipeline jobs matched GitHub event "${eventContext.eventName}".\n`);
126
+ return 0;
64
127
  }
65
128
  let failed = false;
66
129
  for (const selectedJob of jobs) {
67
- const graph = tasksForJob(pipeline, selectedJob.id);
68
- console.log(`Running ${pipeline.name}:${selectedJob.id} (${graph.executionOrder.join(" -> ")})`);
69
- const result = await runJob(pipeline, { cwd, jobId: selectedJob.id, mode: "ci" });
70
- console.log(`Pipeline ${result.status}: ${result.id}`);
130
+ const graph = tasksForJob(context.pipeline, selectedJob.id);
131
+ context.stdout(`Running ${context.pipeline.name}:${selectedJob.id} (${graph.executionOrder.join(" -> ")})\n`);
132
+ const result = await runJob(context.pipeline, { id: selectedJob.id, mode: "ci", workspace: context.workspace, concurrency: context.concurrency });
133
+ context.stdout(`Pipeline ${result.status}: ${result.id}\n`);
71
134
  if (result.status !== "passed")
72
135
  failed = true;
73
136
  }
74
- process.exitCode = failed ? 1 : 0;
75
- return;
137
+ return failed ? 1 : 0;
76
138
  }
77
139
  throw new Error(`Unknown github command "${subcommand}".`);
78
140
  }
79
- if (command === "list") {
80
- console.log("Jobs:");
81
- for (const jobId of Object.keys(pipeline.jobs).sort()) {
82
- console.log(` ${jobId}`);
83
- }
84
- console.log("Tasks:");
85
- for (const taskId of Object.keys(pipeline.tasks).sort()) {
86
- console.log(` ${taskId}`);
141
+ if (commandName === "list") {
142
+ context.stdout("Jobs:\n");
143
+ for (const jobId of Object.keys(context.pipeline.jobs).sort())
144
+ context.stdout(` ${jobId}\n`);
145
+ context.stdout("Tasks:\n");
146
+ for (const taskId of Object.keys(context.pipeline.tasks).sort())
147
+ context.stdout(` ${taskId}\n`);
148
+ if (Object.keys(context.pipeline.sources).length > 0) {
149
+ context.stdout("Sources:\n");
150
+ for (const sourceId of Object.keys(context.pipeline.sources).sort())
151
+ context.stdout(` ${sourceId}\n`);
87
152
  }
88
- if (Object.keys(pipeline.sources).length > 0) {
89
- console.log("Sources:");
90
- for (const sourceId of Object.keys(pipeline.sources).sort()) {
91
- console.log(` ${sourceId}`);
92
- }
93
- }
94
- return;
153
+ return 0;
95
154
  }
96
- if (command === "graph") {
155
+ if (commandName === "graph") {
97
156
  const formatIndex = args.indexOf("--format");
98
157
  const format = formatIndex >= 0 ? args[formatIndex + 1] : "json";
99
- const store = virtualStore(cwd);
100
- const graphPipeline = await loadAvailableSourceGraph(pipeline, cwd, store);
158
+ const store = virtualStore(context.cwd);
159
+ const graphPipeline = await loadAvailableSourceGraph(context.pipeline, context.cwd, store);
101
160
  const graph = buildGraph(graphPipeline);
102
161
  if (format === "json") {
103
- console.log(JSON.stringify(graph, null, 2));
104
- return;
162
+ context.stdout(`${JSON.stringify(graph, null, 2)}\n`);
163
+ return 0;
105
164
  }
106
165
  if (format === "dot") {
107
- console.log("digraph pipeline {");
166
+ context.stdout("digraph pipeline {\n");
108
167
  for (const task of graph.tasks) {
109
168
  if (task.dependsOn.length === 0)
110
- console.log(` "${task.id}";`);
111
- for (const dependency of task.dependsOn) {
112
- console.log(` "${dependency}" -> "${task.id}";`);
113
- }
169
+ context.stdout(` "${task.id}";\n`);
170
+ for (const dependency of task.dependsOn)
171
+ context.stdout(` "${dependency}" -> "${task.id}";\n`);
114
172
  }
115
- console.log("}");
116
- return;
173
+ context.stdout("}\n");
174
+ return 0;
117
175
  }
118
176
  throw new Error(`Unsupported graph format "${format}".`);
119
177
  }
120
- if (command === "explain") {
178
+ if (commandName === "explain") {
121
179
  const taskId = args[0];
122
180
  if (!taskId)
123
181
  throw new Error(`Usage: ${program} explain <task>`);
124
- const store = virtualStore(cwd);
125
- const explainPipeline = await loadAvailableSourceGraph(pipeline, cwd, store);
182
+ const store = virtualStore(context.cwd);
183
+ const explainPipeline = await loadAvailableSourceGraph(context.pipeline, context.cwd, store);
126
184
  const task = explainPipeline.tasks[taskId];
127
185
  if (!task)
128
186
  throw new Error(`Unknown task "${taskId}".`);
129
- console.log(JSON.stringify(task, jsonReplacer, 2));
130
- return;
187
+ context.stdout(`${JSON.stringify(task, jsonReplacer, 2)}\n`);
188
+ return 0;
131
189
  }
132
- if (command === "sources") {
133
- const subcommand = args[0] ?? "list";
134
- if (subcommand === "list") {
135
- const store = virtualStore(cwd);
136
- const sources = await resolveSources(pipeline, cwd, store, { sync: false, loadPipelines: false });
137
- for (const source of Object.values(sources)) {
138
- const detail = source.definition.type === "git"
139
- ? `${source.definition.url}#${source.definition.ref}`
140
- : source.definition.path;
141
- console.log(`${source.id}\t${source.definition.type}\t${detail}\t${source.dir}`);
142
- }
143
- return;
144
- }
145
- if (subcommand === "sync") {
146
- const store = await createStore(cwd);
147
- const sources = await resolveSources(pipeline, cwd, store, { sync: true, loadPipelines: true });
148
- for (const source of Object.values(sources)) {
149
- console.log(`${source.id}\t${source.record.commit ?? "unknown"}\t${source.dir}`);
150
- }
151
- return;
152
- }
153
- throw new Error(`Unknown sources command "${subcommand}".`);
190
+ if (commandName === "sources") {
191
+ return handleSourcesCommand(args, context);
154
192
  }
155
- if (command === "metadata") {
193
+ if (commandName === "metadata") {
156
194
  const formatIndex = args.indexOf("--format");
157
195
  const format = formatIndex >= 0 ? args[formatIndex + 1] : "json";
158
196
  if (format !== "json")
159
197
  throw new Error(`Unsupported metadata format "${format}".`);
160
198
  const includeSources = args.includes("--include-sources");
161
- const store = virtualStore(cwd);
162
- const metadata = await readPipelineMetadata(configPath, { cwd, includeSources, store });
163
- console.log(JSON.stringify(metadata, jsonReplacer, 2));
164
- return;
199
+ const store = virtualStore(context.cwd);
200
+ const metadata = await readPipelineMetadata(context.configPath, { cwd: context.cwd, includeSources, store });
201
+ context.stdout(`${JSON.stringify(metadata, jsonReplacer, 2)}\n`);
202
+ return 0;
165
203
  }
166
- if (command === "matrix") {
204
+ if (commandName === "matrix") {
167
205
  const jobId = args[0];
168
206
  if (!jobId)
169
207
  throw new Error(`Usage: ${program} matrix <job> --format github`);
@@ -171,35 +209,55 @@ async function main() {
171
209
  const format = formatIndex >= 0 ? args[formatIndex + 1] : "github";
172
210
  if (format !== "github")
173
211
  throw new Error(`Unsupported matrix format "${format}".`);
174
- console.log(JSON.stringify(matrixForJob(pipeline, jobId)));
175
- return;
212
+ context.stdout(`${JSON.stringify(matrixForJob(context.pipeline, jobId))}\n`);
213
+ return 0;
176
214
  }
177
- if (command === "run") {
215
+ if (commandName === "run") {
178
216
  const jobId = args[0];
179
217
  if (!jobId)
180
218
  throw new Error(`Usage: ${program} run <job>`);
181
- const graph = tasksForJob(pipeline, jobId);
182
- console.log(`Running ${pipeline.name}:${jobId} (${graph.executionOrder.join(" -> ")})`);
183
- const result = await runJob(pipeline, { cwd, jobId, mode: process.env.CI ? "ci" : "manual" });
184
- console.log(`Pipeline ${result.status}: ${result.id}`);
185
- process.exitCode = result.status === "passed" ? 0 : 1;
186
- return;
219
+ const graph = tasksForJob(context.pipeline, jobId);
220
+ context.stdout(`Running ${context.pipeline.name}:${jobId} (${graph.executionOrder.join(" -> ")})\n`);
221
+ const result = await runJob(context.pipeline, { id: jobId, mode: context.workspace.env.CI ? "ci" : "manual", workspace: context.workspace, concurrency: context.concurrency });
222
+ context.stdout(`Pipeline ${result.status}: ${result.id}\n`);
223
+ return result.status === "passed" ? 0 : 1;
187
224
  }
188
- if (command === "run-task") {
225
+ if (commandName === "run-task") {
189
226
  const taskId = args[0];
190
227
  if (!taskId)
191
228
  throw new Error(`Usage: ${program} run-task <task>`);
192
- const result = await runSingleTask(pipeline, taskId, { cwd, mode: process.env.CI ? "ci" : "manual" });
193
- console.log(`Task run ${result.status}: ${result.id}`);
194
- process.exitCode = result.status === "passed" ? 0 : 1;
195
- return;
229
+ const result = await runSingleTask(context.pipeline, taskId, { mode: context.workspace.env.CI ? "ci" : "manual", workspace: context.workspace, concurrency: context.concurrency });
230
+ context.stdout(`Task run ${result.status}: ${result.id}\n`);
231
+ return result.status === "passed" ? 0 : 1;
232
+ }
233
+ throw new Error(`Unknown command "${commandName}".`);
234
+ }
235
+ async function handleSourcesCommand(args, context) {
236
+ const subcommand = args[0] ?? "list";
237
+ if (subcommand === "list") {
238
+ const store = virtualStore(context.cwd);
239
+ const sources = await resolveSources(context.pipeline, context.cwd, store, { sync: false, loadPipelines: false });
240
+ for (const source of Object.values(sources)) {
241
+ const detail = source.definition.type === "git"
242
+ ? `${source.definition.url}#${source.definition.ref}`
243
+ : source.definition.path;
244
+ context.stdout(`${source.id}\t${source.definition.type}\t${detail}\t${source.dir}\n`);
245
+ }
246
+ return 0;
196
247
  }
197
- throw new Error(`Unknown command "${command}".`);
248
+ if (subcommand === "sync") {
249
+ const store = await createStore(context.cwd);
250
+ const sources = await resolveSources(context.pipeline, context.cwd, store, { sync: true, loadPipelines: true });
251
+ for (const source of Object.values(sources))
252
+ context.stdout(`${source.id}\t${source.record.commit ?? "unknown"}\t${source.dir}\n`);
253
+ return 0;
254
+ }
255
+ throw new Error(`Unknown sources command "${subcommand}".`);
198
256
  }
199
257
  function printHelp(program) {
200
- console.log(`Usage:
201
- ${program} run <job>
202
- ${program} run-task <task>
258
+ return `Usage:
259
+ ${program} run <job> [--workspace <id>] [--concurrency <n>]
260
+ ${program} run-task <task> [--workspace <id>] [--concurrency <n>]
203
261
  ${program} list
204
262
  ${program} graph --format json|dot
205
263
  ${program} explain <task>
@@ -218,8 +276,8 @@ function printHelp(program) {
218
276
  ${program} sync tasks check
219
277
  ${program} github generate [--workflow <path>] [--lock <path>]
220
278
  ${program} github check [--workflow <path>] [--lock <path>]
221
- ${program} github run
222
- ${program} doctor`);
279
+ ${program} github run [--workspace <id>] [--concurrency <n>]
280
+ ${program} doctor\n`;
223
281
  }
224
282
  async function handleSyncCommand(args, context) {
225
283
  const targetNames = new Set(["github", "tasks"]);
@@ -227,29 +285,25 @@ async function handleSyncCommand(args, context) {
227
285
  const target = targetNames.has(maybeTarget ?? "") ? maybeTarget : undefined;
228
286
  const subcommand = target ? args[1] ?? "list" : args[0] ?? "list";
229
287
  const rest = target ? args.slice(2) : args.slice(1);
230
- if (target === "github") {
231
- await handleSyncGitHubCommand(subcommand, rest, context, { requireConfigured: true });
232
- return;
233
- }
234
- if (target === "tasks") {
235
- await handleSyncTasksCommand(subcommand, context, { requireConfigured: true });
236
- return;
237
- }
288
+ if (target === "github")
289
+ return handleSyncGitHubCommand(subcommand, rest, context, { requireConfigured: true });
290
+ if (target === "tasks")
291
+ return handleSyncTasksCommand(subcommand, context, { requireConfigured: true });
238
292
  if (subcommand === "list") {
239
293
  let listed = false;
240
294
  if (context.pipeline.sync.github.enabled) {
241
- console.log(`GitHub workflow: ${context.pipeline.sync.github.workflow}`);
242
- console.log(`GitHub lock: ${context.pipeline.sync.github.lock}`);
295
+ context.stdout(`GitHub workflow: ${context.pipeline.sync.github.workflow}\n`);
296
+ context.stdout(`GitHub lock: ${context.pipeline.sync.github.lock}\n`);
243
297
  listed = true;
244
298
  }
245
299
  if (context.pipeline.sync.tasks.enabled) {
246
300
  for (const line of describeTaskSync(await renderTaskSync(context.pipeline, context)))
247
- console.log(line);
301
+ context.stdout(`${line}\n`);
248
302
  listed = true;
249
303
  }
250
304
  if (!listed)
251
- console.log("No sync targets configured.");
252
- return;
305
+ context.stdout("No sync targets configured.\n");
306
+ return 0;
253
307
  }
254
308
  if (subcommand === "generate") {
255
309
  let generated = false;
@@ -263,14 +317,14 @@ async function handleSyncCommand(args, context) {
263
317
  }
264
318
  if (!generated)
265
319
  throw new Error("No sync targets configured.");
266
- return;
320
+ return 0;
267
321
  }
268
322
  if (subcommand === "check") {
269
323
  const issues = [];
270
324
  let checked = false;
271
325
  if (context.pipeline.sync.github.enabled) {
272
326
  const paths = githubGenerationPaths(rest);
273
- const rendered = await renderGitHubWorkflow(context.pipeline, { ...context, ...paths });
327
+ const rendered = await renderGitHubWorkflow(context.pipeline, { cwd: context.cwd, configPath: context.configPath, ...paths });
274
328
  issues.push(...await checkGitHubWorkflow(rendered, context.cwd));
275
329
  checked = true;
276
330
  }
@@ -283,12 +337,11 @@ async function handleSyncCommand(args, context) {
283
337
  throw new Error("No sync targets configured.");
284
338
  if (issues.length > 0) {
285
339
  for (const issue of issues)
286
- console.error(issue);
287
- process.exitCode = 1;
288
- return;
340
+ context.stderr(`${issue}\n`);
341
+ return 1;
289
342
  }
290
- console.log("Sync targets are current.");
291
- return;
343
+ context.stdout("Sync targets are current.\n");
344
+ return 0;
292
345
  }
293
346
  throw new Error(`Unknown sync command "${subcommand}".`);
294
347
  }
@@ -297,28 +350,27 @@ async function handleSyncGitHubCommand(subcommand, args, context, options) {
297
350
  throw new Error("GitHub sync is not configured. Add sync.github to pipeline.ts.");
298
351
  }
299
352
  const paths = githubGenerationPaths(args);
300
- const rendered = await renderGitHubWorkflow(context.pipeline, { ...context, ...paths });
353
+ const rendered = await renderGitHubWorkflow(context.pipeline, { cwd: context.cwd, configPath: context.configPath, ...paths });
301
354
  if (subcommand === "list") {
302
- console.log(`GitHub workflow: ${rendered.workflowPath}`);
303
- console.log(`GitHub lock: ${rendered.lockPath}`);
304
- return;
355
+ context.stdout(`GitHub workflow: ${rendered.workflowPath}\n`);
356
+ context.stdout(`GitHub lock: ${rendered.lockPath}\n`);
357
+ return 0;
305
358
  }
306
359
  if (subcommand === "generate") {
307
360
  await writeGitHubWorkflow(rendered, context.cwd);
308
- console.log(`Generated ${rendered.workflowPath}`);
309
- console.log(`Generated ${rendered.lockPath}`);
310
- return;
361
+ context.stdout(`Generated ${rendered.workflowPath}\n`);
362
+ context.stdout(`Generated ${rendered.lockPath}\n`);
363
+ return 0;
311
364
  }
312
365
  if (subcommand === "check") {
313
366
  const issues = await checkGitHubWorkflow(rendered, context.cwd);
314
367
  if (issues.length > 0) {
315
368
  for (const issue of issues)
316
- console.error(issue);
317
- process.exitCode = 1;
318
- return;
369
+ context.stderr(`${issue}\n`);
370
+ return 1;
319
371
  }
320
- console.log("GitHub workflow is current.");
321
- return;
372
+ context.stdout("GitHub workflow is current.\n");
373
+ return 0;
322
374
  }
323
375
  throw new Error(`Unknown sync github command "${subcommand}".`);
324
376
  }
@@ -326,33 +378,84 @@ async function handleSyncTasksCommand(subcommand, context, options) {
326
378
  const rendered = await renderTaskSync(context.pipeline, context);
327
379
  if (subcommand === "list") {
328
380
  for (const line of describeTaskSync(rendered))
329
- console.log(line);
330
- if (options.requireConfigured && !rendered.enabled)
331
- process.exitCode = 1;
332
- return;
381
+ context.stdout(`${line}\n`);
382
+ return options.requireConfigured && !rendered.enabled ? 1 : 0;
333
383
  }
334
384
  if (subcommand === "generate") {
335
385
  if (options.requireConfigured && !rendered.enabled)
336
386
  throw new Error("Task sync is not configured. Add sync.tasks to pipeline.ts.");
337
387
  await writeTaskSync(rendered, context.cwd);
338
388
  for (const manifest of rendered.manifests)
339
- console.log(`Generated ${manifest.path}`);
340
- console.log(`Generated ${rendered.lockPath}`);
341
- return;
389
+ context.stdout(`Generated ${manifest.path}\n`);
390
+ context.stdout(`Generated ${rendered.lockPath}\n`);
391
+ return 0;
342
392
  }
343
393
  if (subcommand === "check") {
344
394
  const issues = await checkTaskSync(rendered, context.cwd, { requireConfigured: options.requireConfigured });
345
395
  if (issues.length > 0) {
346
396
  for (const issue of issues)
347
- console.error(issue);
348
- process.exitCode = 1;
349
- return;
397
+ context.stderr(`${issue}\n`);
398
+ return 1;
350
399
  }
351
- console.log("Task sync is current.");
352
- return;
400
+ context.stdout("Task sync is current.\n");
401
+ return 0;
353
402
  }
354
403
  throw new Error(`Unknown sync tasks command "${subcommand}".`);
355
404
  }
405
+ function parseGlobalOptions(args) {
406
+ const rest = [];
407
+ let concurrency;
408
+ let workspaceId;
409
+ for (let index = 0; index < args.length; index += 1) {
410
+ const arg = args[index];
411
+ if (arg === undefined)
412
+ continue;
413
+ if (arg === "--workspace") {
414
+ workspaceId = args[index + 1];
415
+ if (!workspaceId)
416
+ throw new Error("Usage: async-pipeline <command> --workspace <id>");
417
+ index += 1;
418
+ continue;
419
+ }
420
+ if (arg === "--concurrency") {
421
+ const raw = args[index + 1];
422
+ if (!raw)
423
+ throw new Error("Usage: async-pipeline <command> --concurrency <n>");
424
+ concurrency = Number(raw);
425
+ index += 1;
426
+ continue;
427
+ }
428
+ rest.push(arg);
429
+ }
430
+ return { args: rest, concurrency, workspaceId };
431
+ }
432
+ function selectWorkspace(workspaceId, pipeline, base) {
433
+ const commands = base.commands ?? (pipeline.commands ? commandProxy(pipeline.commands) : undefined);
434
+ if (!workspaceId || workspaceId === "host") {
435
+ return { ...base, commands };
436
+ }
437
+ const definition = pipeline.workspaces[workspaceId];
438
+ if (!definition)
439
+ throw new Error(`Unknown workspace "${workspaceId}".`);
440
+ return createWorkspaceFromDefinition(definition, base, commands);
441
+ }
442
+ function createWorkspaceFromDefinition(definition, base, commands) {
443
+ if (definition.kind === "host")
444
+ return hostWorkspace({ cwd: base.cwd, env: base.env, commands });
445
+ if (definition.kind === "lima")
446
+ return limaWorkspace({ cwd: base.cwd, env: base.env, vm: definition.vm, commands });
447
+ if (definition.kind === "docker") {
448
+ return dockerWorkspace({
449
+ cwd: base.cwd,
450
+ env: base.env,
451
+ image: definition.image,
452
+ workdir: definition.workdir,
453
+ volumes: definition.volumes,
454
+ commands
455
+ });
456
+ }
457
+ throw new Error(`Workspace kind "${definition.kind}" is declared but not executable in this tranche.`);
458
+ }
356
459
  function findPipelineConfig(cwd) {
357
460
  for (const fileName of ["pipeline.ts", "pipeline.mjs", "pipeline.js"]) {
358
461
  const configPath = resolve(cwd, fileName);
@@ -403,8 +506,12 @@ function virtualStore(root) {
403
506
  sourcesDir: resolve(root, ".async", "sources")
404
507
  };
405
508
  }
406
- main().catch((error) => {
407
- console.error(error instanceof Error ? error.message : String(error));
408
- process.exitCode = 1;
409
- });
509
+ if (process.argv[1] && pathToFileURL(process.argv[1]).href === import.meta.url) {
510
+ runPipelineCli({ args: process.argv.slice(2) }).then((result) => {
511
+ process.exitCode = result.code;
512
+ }).catch((error) => {
513
+ console.error(error instanceof Error ? error.message : String(error));
514
+ process.exitCode = 1;
515
+ });
516
+ }
410
517
  //# sourceMappingURL=cli.js.map