@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.
- package/README.md +3 -3
- package/dist/internal/core/cache.d.ts +5 -1
- package/dist/internal/core/cache.d.ts.map +1 -1
- package/dist/internal/core/cache.js +19 -9
- package/dist/internal/core/cache.js.map +1 -1
- package/dist/internal/core/index.d.ts +121 -3
- package/dist/internal/core/index.d.ts.map +1 -1
- package/dist/internal/core/index.js +105 -8
- package/dist/internal/core/index.js.map +1 -1
- package/dist/internal/core/runtime.js +1 -1
- package/dist/internal/core/runtime.js.map +1 -1
- package/dist/internal/lima/index.d.ts +1 -14
- package/dist/internal/lima/index.d.ts.map +1 -1
- package/dist/internal/lima/index.js +1 -64
- package/dist/internal/lima/index.js.map +1 -1
- package/dist/internal/node/cli.d.ts +9 -1
- package/dist/internal/node/cli.d.ts.map +1 -1
- package/dist/internal/node/cli.js +279 -172
- package/dist/internal/node/cli.js.map +1 -1
- package/dist/internal/node/doctor.js +2 -2
- package/dist/internal/node/doctor.js.map +1 -1
- package/dist/internal/node/github.d.ts +3 -1
- package/dist/internal/node/github.d.ts.map +1 -1
- package/dist/internal/node/github.js +31 -9
- package/dist/internal/node/github.js.map +1 -1
- package/dist/internal/node/index.d.ts +1 -0
- package/dist/internal/node/index.d.ts.map +1 -1
- package/dist/internal/node/index.js +1 -0
- package/dist/internal/node/index.js.map +1 -1
- package/dist/internal/node/runner.d.ts +98 -7
- package/dist/internal/node/runner.d.ts.map +1 -1
- package/dist/internal/node/runner.js +400 -73
- package/dist/internal/node/runner.js.map +1 -1
- package/dist/internal/node/store.d.ts +29 -5
- package/dist/internal/node/store.d.ts.map +1 -1
- package/dist/internal/node/store.js +225 -20
- package/dist/internal/node/store.js.map +1 -1
- 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
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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
|
-
|
|
30
|
-
|
|
92
|
+
catch (error) {
|
|
93
|
+
const message = `${error instanceof Error ? error.message : String(error)}\n`;
|
|
94
|
+
return { code: 1, stdout: "", stderr: message };
|
|
31
95
|
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
return;
|
|
96
|
+
}
|
|
97
|
+
async function dispatchCommand(commandName, args, context, program) {
|
|
98
|
+
if (commandName === "sync") {
|
|
99
|
+
return handleSyncCommand(args, context);
|
|
36
100
|
}
|
|
37
|
-
if (
|
|
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
|
-
|
|
44
|
-
|
|
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
|
-
|
|
52
|
-
|
|
53
|
-
return;
|
|
115
|
+
context.stderr(`${issue}\n`);
|
|
116
|
+
return 1;
|
|
54
117
|
}
|
|
55
|
-
|
|
56
|
-
return;
|
|
118
|
+
context.stdout("GitHub workflow is current.\n");
|
|
119
|
+
return 0;
|
|
57
120
|
}
|
|
58
121
|
if (subcommand === "run") {
|
|
59
|
-
const
|
|
60
|
-
const jobs = jobsForGitHubEvent(pipeline,
|
|
122
|
+
const eventContext = await readGitHubEventContext(context.workspace.env);
|
|
123
|
+
const jobs = jobsForGitHubEvent(context.pipeline, eventContext);
|
|
61
124
|
if (jobs.length === 0) {
|
|
62
|
-
|
|
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
|
-
|
|
69
|
-
const result = await runJob(pipeline, {
|
|
70
|
-
|
|
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
|
-
|
|
75
|
-
return;
|
|
137
|
+
return failed ? 1 : 0;
|
|
76
138
|
}
|
|
77
139
|
throw new Error(`Unknown github command "${subcommand}".`);
|
|
78
140
|
}
|
|
79
|
-
if (
|
|
80
|
-
|
|
81
|
-
for (const jobId of Object.keys(pipeline.jobs).sort())
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
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
|
-
|
|
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 (
|
|
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
|
-
|
|
104
|
-
return;
|
|
162
|
+
context.stdout(`${JSON.stringify(graph, null, 2)}\n`);
|
|
163
|
+
return 0;
|
|
105
164
|
}
|
|
106
165
|
if (format === "dot") {
|
|
107
|
-
|
|
166
|
+
context.stdout("digraph pipeline {\n");
|
|
108
167
|
for (const task of graph.tasks) {
|
|
109
168
|
if (task.dependsOn.length === 0)
|
|
110
|
-
|
|
111
|
-
for (const dependency of task.dependsOn)
|
|
112
|
-
|
|
113
|
-
}
|
|
169
|
+
context.stdout(` "${task.id}";\n`);
|
|
170
|
+
for (const dependency of task.dependsOn)
|
|
171
|
+
context.stdout(` "${dependency}" -> "${task.id}";\n`);
|
|
114
172
|
}
|
|
115
|
-
|
|
116
|
-
return;
|
|
173
|
+
context.stdout("}\n");
|
|
174
|
+
return 0;
|
|
117
175
|
}
|
|
118
176
|
throw new Error(`Unsupported graph format "${format}".`);
|
|
119
177
|
}
|
|
120
|
-
if (
|
|
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
|
-
|
|
130
|
-
return;
|
|
187
|
+
context.stdout(`${JSON.stringify(task, jsonReplacer, 2)}\n`);
|
|
188
|
+
return 0;
|
|
131
189
|
}
|
|
132
|
-
if (
|
|
133
|
-
|
|
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 (
|
|
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
|
-
|
|
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 (
|
|
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
|
-
|
|
175
|
-
return;
|
|
212
|
+
context.stdout(`${JSON.stringify(matrixForJob(context.pipeline, jobId))}\n`);
|
|
213
|
+
return 0;
|
|
176
214
|
}
|
|
177
|
-
if (
|
|
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
|
-
|
|
183
|
-
const result = await runJob(pipeline, {
|
|
184
|
-
|
|
185
|
-
|
|
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 (
|
|
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, {
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
232
|
-
|
|
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
|
-
|
|
242
|
-
|
|
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
|
-
|
|
301
|
+
context.stdout(`${line}\n`);
|
|
248
302
|
listed = true;
|
|
249
303
|
}
|
|
250
304
|
if (!listed)
|
|
251
|
-
|
|
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, {
|
|
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
|
-
|
|
287
|
-
|
|
288
|
-
return;
|
|
340
|
+
context.stderr(`${issue}\n`);
|
|
341
|
+
return 1;
|
|
289
342
|
}
|
|
290
|
-
|
|
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, {
|
|
353
|
+
const rendered = await renderGitHubWorkflow(context.pipeline, { cwd: context.cwd, configPath: context.configPath, ...paths });
|
|
301
354
|
if (subcommand === "list") {
|
|
302
|
-
|
|
303
|
-
|
|
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
|
-
|
|
309
|
-
|
|
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
|
-
|
|
317
|
-
|
|
318
|
-
return;
|
|
369
|
+
context.stderr(`${issue}\n`);
|
|
370
|
+
return 1;
|
|
319
371
|
}
|
|
320
|
-
|
|
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
|
-
|
|
330
|
-
|
|
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
|
-
|
|
340
|
-
|
|
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
|
-
|
|
348
|
-
|
|
349
|
-
return;
|
|
397
|
+
context.stderr(`${issue}\n`);
|
|
398
|
+
return 1;
|
|
350
399
|
}
|
|
351
|
-
|
|
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
|
-
|
|
407
|
-
|
|
408
|
-
|
|
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
|