@jujulego/jill 2.5.2 → 3.0.0-alpha.10
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 +1 -2
- package/bin/jill.js +1 -0
- package/dist/flat-job-tree.js +81 -0
- package/dist/flat-job-tree.js.map +1 -0
- package/dist/inked.js +66 -0
- package/dist/inked.js.map +1 -0
- package/dist/instrument.js +9 -0
- package/dist/instrument.js.map +1 -0
- package/dist/job-command-execute.ink.js +530 -0
- package/dist/job-command-execute.ink.js.map +1 -0
- package/dist/job-plan.js +133 -0
- package/dist/job-plan.js.map +1 -0
- package/dist/list.ink.js +54 -0
- package/dist/list.ink.js.map +1 -0
- package/dist/main.js +1969 -38
- package/dist/main.js.map +1 -1
- package/dist/planner.service.js +63 -0
- package/dist/planner.service.js.map +1 -0
- package/dist/tree.ink.js +189 -0
- package/dist/tree.ink.js.map +1 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +58 -54
- package/dist/ajv.config.d.ts +0 -3
- package/dist/commands/each.d.ts +0 -25
- package/dist/commands/exec.d.ts +0 -26
- package/dist/commands/group.d.ts +0 -16
- package/dist/commands/list.d.ts +0 -30
- package/dist/commands/run.d.ts +0 -24
- package/dist/commands/tree.d.ts +0 -6
- package/dist/commons/context.service.d.ts +0 -23
- package/dist/commons/git.service.d.ts +0 -62
- package/dist/commons/logger/log.gateway.d.ts +0 -18
- package/dist/commons/logger/parameters.d.ts +0 -2
- package/dist/commons/logger/thread.gateway.d.ts +0 -13
- package/dist/commons/logger/types.d.ts +0 -2
- package/dist/commons/logger.service.d.ts +0 -1
- package/dist/config/config-loader.d.ts +0 -4
- package/dist/config/config-options.d.ts +0 -5
- package/dist/config/types.d.ts +0 -8
- package/dist/config/utils.d.ts +0 -5
- package/dist/constants.d.ts +0 -1
- package/dist/core.plugin-Bxu0Sx70.js +0 -642
- package/dist/core.plugin-Bxu0Sx70.js.map +0 -1
- package/dist/core.plugin.d.ts +0 -2
- package/dist/filters/affected.filter.d.ts +0 -12
- package/dist/filters/pipeline.d.ts +0 -11
- package/dist/filters/private.filter.d.ts +0 -7
- package/dist/filters/scripts.filter.d.ts +0 -8
- package/dist/index.d.ts +0 -45
- package/dist/index.js +0 -35
- package/dist/index.js.map +0 -1
- package/dist/ink-command-S3TpJLFi.js +0 -2082
- package/dist/ink-command-S3TpJLFi.js.map +0 -1
- package/dist/ink.config.d.ts +0 -3
- package/dist/inversify.config.d.ts +0 -4
- package/dist/jill.application-CGujSe1_.js +0 -639
- package/dist/jill.application-CGujSe1_.js.map +0 -1
- package/dist/jill.application.d.ts +0 -19
- package/dist/main.d.ts +0 -1
- package/dist/middlewares/load-project.d.ts +0 -21
- package/dist/middlewares/load-workspace.d.ts +0 -20
- package/dist/modules/command.d.ts +0 -20
- package/dist/modules/ink-command.d.ts +0 -11
- package/dist/modules/middleware.d.ts +0 -8
- package/dist/modules/module.d.ts +0 -7
- package/dist/modules/plugin-loader.service.d.ts +0 -10
- package/dist/modules/plugin.d.ts +0 -14
- package/dist/modules/service.d.ts +0 -8
- package/dist/modules/task-command.d.ts +0 -14
- package/dist/project/project.d.ts +0 -27
- package/dist/project/project.repository.d.ts +0 -15
- package/dist/project/types.d.ts +0 -1
- package/dist/project/workspace.d.ts +0 -41
- package/dist/tasks/command-task.d.ts +0 -15
- package/dist/tasks/errors.d.ts +0 -4
- package/dist/tasks/script-task.d.ts +0 -27
- package/dist/tasks/task-expression.service.d.ts +0 -25
- package/dist/tasks/task-manager.config.d.ts +0 -3
- package/dist/types.d.ts +0 -11
- package/dist/ui/hooks/useFlatTaskTree.d.ts +0 -14
- package/dist/ui/hooks/useIsVerbose.d.ts +0 -1
- package/dist/ui/hooks/useStdoutDimensions.d.ts +0 -4
- package/dist/ui/layout.d.ts +0 -5
- package/dist/ui/list.d.ts +0 -5
- package/dist/ui/static-logs.d.ts +0 -1
- package/dist/ui/task-name.d.ts +0 -5
- package/dist/ui/task-spinner.d.ts +0 -5
- package/dist/ui/task-tree-completed.d.ts +0 -5
- package/dist/ui/task-tree-full-spinner.d.ts +0 -5
- package/dist/ui/task-tree-scrollable-spinner.d.ts +0 -5
- package/dist/ui/task-tree-spinner.d.ts +0 -5
- package/dist/ui/task-tree-stats.d.ts +0 -5
- package/dist/ui/workspace-tree.d.ts +0 -8
- package/dist/utils/events.d.ts +0 -3
- package/dist/utils/exit.d.ts +0 -4
- package/dist/utils/import.d.ts +0 -4
- package/dist/utils/json.d.ts +0 -2
- package/dist/utils/streams.d.ts +0 -3
- package/dist/utils/string.d.ts +0 -2
- package/dist/utils/worker-cache.d.ts +0 -4
- package/dist/workspace-tree-CyjZrimj.js +0 -1120
- package/dist/workspace-tree-CyjZrimj.js.map +0 -1
package/dist/main.js
CHANGED
|
@@ -1,46 +1,1977 @@
|
|
|
1
|
-
import {
|
|
1
|
+
;{try{(function(){var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="d71a4689-40f0-48b6-8162-43fd470f2d95",e._sentryDebugIdIdentifier="sentry-dbid-d71a4689-40f0-48b6-8162-43fd470f2d95");})();}catch(e){}};!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{};e.SENTRY_RELEASE={id:"3.0.0-alpha.10"};}catch(e){}}();import { token$, inject$, asyncScope$ } from '@kyrielle/injector';
|
|
2
|
+
import { startInactiveSpan, startSpan, getActiveSpan, updateSpanName, getRootSpan, captureException } from '@sentry/node';
|
|
3
|
+
import { filter$, waitFor$, pipe$, map$, collect$, asyncIterator$, var$, flow$ } from 'kyrielle';
|
|
4
|
+
import process$1 from 'node:process';
|
|
2
5
|
import { hideBin } from 'yargs/helpers';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import '@swc/helpers/_/
|
|
6
|
-
import '
|
|
7
|
-
import '
|
|
8
|
-
import '
|
|
9
|
-
import '
|
|
10
|
-
import 'node:
|
|
11
|
-
import '
|
|
12
|
-
import '
|
|
13
|
-
import '
|
|
14
|
-
import '
|
|
15
|
-
import '
|
|
16
|
-
import '
|
|
17
|
-
import '
|
|
18
|
-
import '
|
|
19
|
-
import '
|
|
20
|
-
import '
|
|
21
|
-
import '
|
|
22
|
-
import '
|
|
23
|
-
import '
|
|
24
|
-
import '
|
|
25
|
-
import '@jujulego/quick-tag';
|
|
26
|
-
import 'chalk-template';
|
|
6
|
+
import { scheduler$, isWorkloadEnded, WorkloadState, spawn$, parallelFlow$, fallbackFlow$, sequenceFlow$ } from '@jujulego/tasks';
|
|
7
|
+
import { logger$, withTimestamp, LogLevel, withLabel, qLogDelay, LogGateway, logDelay$, toStderr } from '@kyrielle/logger';
|
|
8
|
+
import { _ } from '@swc/helpers/_/_apply_decs_2203_r';
|
|
9
|
+
import { text } from 'node:stream/consumers';
|
|
10
|
+
import fs from 'node:fs';
|
|
11
|
+
import { PathScurry } from 'path-scurry';
|
|
12
|
+
import assert from 'assert';
|
|
13
|
+
import { Writable } from 'node:stream';
|
|
14
|
+
import moo from 'moo';
|
|
15
|
+
import path from 'node:path';
|
|
16
|
+
import { Glob } from 'glob';
|
|
17
|
+
import normalize from 'normalize-package-data';
|
|
18
|
+
import { satisfies, compare, parse } from 'semver';
|
|
19
|
+
import { spawn } from 'node:child_process';
|
|
20
|
+
import chalk from 'chalk';
|
|
21
|
+
import slugify from 'slugify';
|
|
22
|
+
import yargs from 'yargs';
|
|
23
|
+
import { qjson, qprop, q$, defineQuickFormat, qerror, qarg, qwrap } from '@jujulego/quick-tag';
|
|
24
|
+
import Ajv from 'ajv';
|
|
25
|
+
import os from 'node:os';
|
|
26
|
+
import { cosmiconfig, defaultLoaders } from 'cosmiconfig';
|
|
27
|
+
import { chalkTemplateStderr } from 'chalk-template';
|
|
27
28
|
|
|
28
|
-
//
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
// Filter
|
|
30
|
+
function hasSomeScript$(scripts) {
|
|
31
|
+
return filter$((wks)=>{
|
|
32
|
+
if (!wks.manifest.scripts) {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
return scripts.some((script)=>script in wks.manifest.scripts);
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
function hasEveryScript$(scripts) {
|
|
39
|
+
return filter$((wks)=>{
|
|
40
|
+
if (!wks.manifest.scripts) {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
return scripts.every((script)=>script in wks.manifest.scripts);
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Tokens
|
|
48
|
+
const CONFIG = token$('Config', async ()=>{
|
|
49
|
+
const { ConfigService } = await Promise.resolve().then(function () { return config_service; });
|
|
50
|
+
return waitFor$(inject$(ConfigService, asyncScope$()).config$);
|
|
51
|
+
});
|
|
52
|
+
const CWD = token$('cwd', ()=>process$1.cwd());
|
|
53
|
+
const LOGGER = token$('Logger', ()=>logger$(withTimestamp()));
|
|
54
|
+
const PATH_SCURRY = token$('PathScurry', ()=>new PathScurry('/', {
|
|
55
|
+
fs
|
|
56
|
+
}));
|
|
57
|
+
const SCHEDULER = token$('Scheduler', async ()=>{
|
|
58
|
+
const config = await inject$(CONFIG, asyncScope$());
|
|
59
|
+
const logger = inject$(LOGGER);
|
|
60
|
+
const scheduler = scheduler$({
|
|
61
|
+
strength: config.jobs
|
|
62
|
+
});
|
|
63
|
+
scheduler.events$.on('started', (job)=>{
|
|
64
|
+
logger.verbose(`job "${job.label}" started`);
|
|
65
|
+
});
|
|
66
|
+
scheduler.events$.on('ended', (job)=>{
|
|
67
|
+
logger.verbose(`job "${job.label}" ended in state ${job.state()}`);
|
|
68
|
+
});
|
|
69
|
+
// Task instrumentation
|
|
70
|
+
scheduler.events$.on('added', (job)=>{
|
|
71
|
+
const jobSpan = startInactiveSpan({
|
|
72
|
+
op: 'job',
|
|
73
|
+
name: job.label,
|
|
74
|
+
attributes: {
|
|
75
|
+
'job.id': job.id,
|
|
76
|
+
'job.type': job.type,
|
|
77
|
+
'job.weight': job.weight
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
// Status spans
|
|
81
|
+
let stateSpan;
|
|
82
|
+
let subscription;
|
|
83
|
+
job.state$.subscribe({
|
|
84
|
+
start: (sub)=>{
|
|
85
|
+
subscription = sub;
|
|
86
|
+
},
|
|
87
|
+
next: (state)=>{
|
|
88
|
+
stateSpan?.end();
|
|
89
|
+
if (isWorkloadEnded(state)) {
|
|
90
|
+
jobSpan.setAttribute('job.final_state', state);
|
|
91
|
+
jobSpan.setAttribute('job.duration', job.duration().seconds());
|
|
92
|
+
jobSpan.setStatus({
|
|
93
|
+
code: state === WorkloadState.Succeeded ? 1 : 2
|
|
94
|
+
});
|
|
95
|
+
jobSpan.end();
|
|
96
|
+
subscription.unsubscribe();
|
|
97
|
+
} else {
|
|
98
|
+
stateSpan = startInactiveSpan({
|
|
99
|
+
op: 'job.state',
|
|
100
|
+
name: state,
|
|
101
|
+
parentSpan: jobSpan
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
return scheduler;
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
function trace(fun, opts) {
|
|
111
|
+
const name = typeof opts === 'string' ? opts : opts.name;
|
|
112
|
+
const op = typeof opts === 'object' ? opts.op : undefined;
|
|
113
|
+
const use = typeof opts === 'object' && opts.use || ((_, r)=>r);
|
|
114
|
+
return function(...args) {
|
|
115
|
+
return pipe$(startSpan({
|
|
116
|
+
name,
|
|
117
|
+
op
|
|
118
|
+
}, ()=>fun.call(this, ...args)), (r)=>use(name, r));
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
function instrument(opts) {
|
|
122
|
+
return (target, context)=>{
|
|
123
|
+
const name = typeof opts === 'string' ? opts : opts?.name ?? context.name.toString();
|
|
124
|
+
const use = typeof opts === 'object' ? opts.use : (_, r)=>r;
|
|
125
|
+
return trace(target, {
|
|
126
|
+
name,
|
|
127
|
+
use
|
|
128
|
+
});
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
function traceImport(name, loader) {
|
|
132
|
+
return startSpan({
|
|
133
|
+
name: name,
|
|
134
|
+
op: 'resource.script'
|
|
135
|
+
}, loader);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
class ClientError extends Error {
|
|
139
|
+
name = 'ClientError';
|
|
140
|
+
constructor(message){
|
|
141
|
+
super(message);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
class TaskExpressionError extends ClientError {
|
|
145
|
+
name = 'TaskExpressionError';
|
|
146
|
+
}
|
|
147
|
+
class TaskSyntaxError extends ClientError {
|
|
148
|
+
name = 'TaskSyntaxError';
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function logStreamedLines(logger, level) {
|
|
152
|
+
let leftover = '';
|
|
153
|
+
return new Writable({
|
|
154
|
+
write (chunk, _, cb) {
|
|
155
|
+
assert(chunk instanceof Buffer);
|
|
156
|
+
const data = leftover + chunk.toString('utf-8');
|
|
157
|
+
const lines = data.split(/\r?\n/);
|
|
158
|
+
leftover = lines.pop() ?? '';
|
|
159
|
+
for (const line of lines){
|
|
160
|
+
logger.log(level, line);
|
|
161
|
+
}
|
|
162
|
+
cb();
|
|
163
|
+
},
|
|
164
|
+
final (cb) {
|
|
165
|
+
if (leftover !== '') {
|
|
166
|
+
logger.log(level, leftover);
|
|
167
|
+
}
|
|
168
|
+
cb();
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
var _dec$2, _dec1$1, _dec2, _initProto$2;
|
|
174
|
+
_dec$2 = instrument('GitService.isAffected'), _dec1$1 = instrument('GitService.listBranches'), _dec2 = instrument('GitService.listTags');
|
|
175
|
+
class GitService {
|
|
176
|
+
static{
|
|
177
|
+
({ e: [_initProto$2] } = _(this, [
|
|
178
|
+
[
|
|
179
|
+
_dec$2,
|
|
180
|
+
2,
|
|
181
|
+
"isAffected"
|
|
182
|
+
],
|
|
183
|
+
[
|
|
184
|
+
_dec1$1,
|
|
185
|
+
2,
|
|
186
|
+
"listBranches"
|
|
187
|
+
],
|
|
188
|
+
[
|
|
189
|
+
_dec2,
|
|
190
|
+
2,
|
|
191
|
+
"listTags"
|
|
192
|
+
]
|
|
193
|
+
], []));
|
|
194
|
+
}
|
|
195
|
+
// Attributes
|
|
196
|
+
_scheduler = (_initProto$2(this), inject$(SCHEDULER));
|
|
197
|
+
_logger = inject$(LOGGER);
|
|
198
|
+
// Methods
|
|
199
|
+
/**
|
|
200
|
+
* Runs a git command inside
|
|
201
|
+
*/ async command(cmd, args, opts = {}) {
|
|
202
|
+
const { logger = this._logger, ...props } = opts;
|
|
203
|
+
// Create job
|
|
204
|
+
const job = spawn$('git', [
|
|
205
|
+
cmd,
|
|
206
|
+
...args
|
|
207
|
+
], props);
|
|
208
|
+
job.stdout.pipe(logStreamedLines(logger, LogLevel.debug));
|
|
209
|
+
job.stderr.pipe(logStreamedLines(logger, LogLevel.warning));
|
|
210
|
+
(await this._scheduler).register(job);
|
|
211
|
+
return job;
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Runs git branch
|
|
215
|
+
*/ branch(args, opts) {
|
|
216
|
+
return this.command('branch', args, opts);
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Runs git diff
|
|
220
|
+
*/ diff(args, opts) {
|
|
221
|
+
return this.command('diff', args, opts);
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Runs git tag
|
|
225
|
+
*/ tag(args, opts) {
|
|
226
|
+
return this.command('tag', args, opts);
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Uses git diff to detect if given files have been affected since given reference
|
|
230
|
+
*/ async isAffected(reference, files = [], opts) {
|
|
231
|
+
const job = await this.diff([
|
|
232
|
+
'--quiet',
|
|
233
|
+
reference,
|
|
234
|
+
'--',
|
|
235
|
+
...files
|
|
236
|
+
], opts);
|
|
237
|
+
await waitFor$(pipe$(job.state$, filter$(isWorkloadEnded)));
|
|
238
|
+
if (job.exitCode() === 0) {
|
|
239
|
+
return false;
|
|
240
|
+
}
|
|
241
|
+
if (job.exitCode() === 1) {
|
|
242
|
+
return true;
|
|
243
|
+
}
|
|
244
|
+
throw new ClientError(`Error "git diff" command failed (exit code ${job.exitCode()})`);
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* List git branches
|
|
248
|
+
*/ async listBranches(args = [], opts) {
|
|
249
|
+
const job = await this.branch([
|
|
250
|
+
'-l',
|
|
251
|
+
...args
|
|
252
|
+
], opts);
|
|
253
|
+
const output = await text(job.stdout);
|
|
254
|
+
return pipe$(output.split(/\r?\n/), map$((line)=>line.replace(/^[ *] /, '')), filter$((line)=>!!line), collect$());
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* List git tags
|
|
258
|
+
*/ async listTags(args = [], opts) {
|
|
259
|
+
const job = await this.tag([
|
|
260
|
+
'-l',
|
|
261
|
+
...args
|
|
262
|
+
], opts);
|
|
263
|
+
const output = await text(job.stdout);
|
|
264
|
+
return output.split(/\r?\n/).filter((line)=>!!line);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// Filter
|
|
269
|
+
function isAffected$(opts) {
|
|
270
|
+
return (origin)=>{
|
|
271
|
+
return asyncIterator$(async function*() {
|
|
272
|
+
for await (const wks of origin){
|
|
273
|
+
const revision = await formatRevision(opts, wks);
|
|
274
|
+
if (await wks.isAffected(revision)) {
|
|
275
|
+
yield wks;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}());
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
async function formatRevision(opts, workspace) {
|
|
282
|
+
const git = inject$(GitService);
|
|
283
|
+
const logger = inject$(LOGGER).child(withLabel(workspace.name));
|
|
284
|
+
// Format revision
|
|
285
|
+
let result = opts.format;
|
|
286
|
+
result = result.replace(/(?<!\\)((?:\\\\)*)%name/g, `$1${workspace.name}`);
|
|
287
|
+
result = result.replace(/\\(.)/g, '$1');
|
|
288
|
+
// Ask git to complete it
|
|
289
|
+
const sortArgs = opts.sort ? [
|
|
290
|
+
'--sort',
|
|
291
|
+
opts.sort
|
|
292
|
+
] : [];
|
|
293
|
+
// - search in branches
|
|
294
|
+
if (result.includes('*')) {
|
|
295
|
+
const branches = await git.listBranches([
|
|
296
|
+
...sortArgs,
|
|
297
|
+
result
|
|
298
|
+
], {
|
|
299
|
+
cwd: workspace.root,
|
|
300
|
+
logger: logger
|
|
301
|
+
});
|
|
302
|
+
if (branches.length > 0) {
|
|
303
|
+
result = branches[branches.length - 1];
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
// - search in tags
|
|
307
|
+
if (result.includes('*')) {
|
|
308
|
+
const tags = await git.listTags([
|
|
309
|
+
...sortArgs,
|
|
310
|
+
result
|
|
311
|
+
], {
|
|
312
|
+
cwd: workspace.root,
|
|
313
|
+
logger: logger
|
|
314
|
+
});
|
|
315
|
+
if (tags.length > 0) {
|
|
316
|
+
result = tags[tags.length - 1];
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
if (result !== opts.format) {
|
|
320
|
+
logger.verbose(`resolved ${opts.format} into ${result}`);
|
|
321
|
+
}
|
|
322
|
+
if (result.includes('*')) {
|
|
323
|
+
logger.warning(`no revision found matching ${result}, using fallback ${opts.fallback}`);
|
|
324
|
+
return opts.fallback;
|
|
325
|
+
}
|
|
326
|
+
return result;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// Filter
|
|
330
|
+
function isPrivate$(value) {
|
|
331
|
+
return filter$((wks)=>(wks.manifest.private ?? false) === value);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
var _dec$1, _dec1, _initProto$1;
|
|
335
|
+
_dec$1 = instrument('TaskParserService.parse'), _dec1 = instrument('TaskParserService.buildJob');
|
|
336
|
+
// Service
|
|
337
|
+
class TaskParserService {
|
|
338
|
+
static{
|
|
339
|
+
({ e: [_initProto$1] } = _(this, [
|
|
340
|
+
[
|
|
341
|
+
_dec$1,
|
|
342
|
+
2,
|
|
343
|
+
"parse"
|
|
344
|
+
],
|
|
345
|
+
[
|
|
346
|
+
_dec1,
|
|
347
|
+
2,
|
|
348
|
+
"buildJob"
|
|
349
|
+
]
|
|
350
|
+
], []));
|
|
351
|
+
}
|
|
352
|
+
// Attributes
|
|
353
|
+
_logger = (_initProto$1(this), inject$(LOGGER).child(withLabel('task-parser')));
|
|
354
|
+
// Statics
|
|
355
|
+
static isTaskNode(node) {
|
|
356
|
+
return 'script' in node;
|
|
357
|
+
}
|
|
358
|
+
static _sequenceOperatorWarn = true;
|
|
359
|
+
// Methods
|
|
360
|
+
_lexer() {
|
|
361
|
+
return moo.states({
|
|
362
|
+
task: {
|
|
363
|
+
lparen: '(',
|
|
364
|
+
whitespace: /[ \t]+/,
|
|
365
|
+
script: {
|
|
366
|
+
match: /[-_:a-zA-Z0-9]+/,
|
|
367
|
+
push: 'operatorOrArgument'
|
|
368
|
+
},
|
|
369
|
+
string: [
|
|
370
|
+
{
|
|
371
|
+
match: /'(?:\\['\\]|[^\r\n'\\])+'/,
|
|
372
|
+
push: 'operator',
|
|
373
|
+
value: (x)=>x.slice(1, -1).replace(/\\(['\\])/g, '$1')
|
|
374
|
+
},
|
|
375
|
+
{
|
|
376
|
+
match: /"(?:\\["\\]|[^\r\n"\\])+"/,
|
|
377
|
+
push: 'operator',
|
|
378
|
+
value: (x)=>x.slice(1, -1).replace(/\\(["\\])/g, '$1')
|
|
379
|
+
}
|
|
380
|
+
]
|
|
381
|
+
},
|
|
382
|
+
operator: {
|
|
383
|
+
rparen: ')',
|
|
384
|
+
whitespace: /[ \t]+/,
|
|
385
|
+
operator: {
|
|
386
|
+
match: [
|
|
387
|
+
'->',
|
|
388
|
+
'&&',
|
|
389
|
+
'//',
|
|
390
|
+
'||'
|
|
391
|
+
],
|
|
392
|
+
pop: 1
|
|
393
|
+
}
|
|
394
|
+
},
|
|
395
|
+
operatorOrArgument: {
|
|
396
|
+
rparen: ')',
|
|
397
|
+
whitespace: /[ \t]+/,
|
|
398
|
+
operator: {
|
|
399
|
+
match: [
|
|
400
|
+
'->',
|
|
401
|
+
'&&',
|
|
402
|
+
'//',
|
|
403
|
+
'||'
|
|
404
|
+
],
|
|
405
|
+
pop: 1
|
|
406
|
+
},
|
|
407
|
+
argument: [
|
|
408
|
+
{
|
|
409
|
+
match: /[-_:a-zA-Z0-9]+/
|
|
410
|
+
},
|
|
411
|
+
{
|
|
412
|
+
match: /'(?:\\['\\]|[^\r\n'\\])+'/,
|
|
413
|
+
value: (x)=>x.slice(1, -1).replace(/\\(['\\])/g, '$1')
|
|
414
|
+
},
|
|
415
|
+
{
|
|
416
|
+
match: /"(?:\\["\\]|[^\r\n"\\])+"/,
|
|
417
|
+
value: (x)=>x.slice(1, -1).replace(/\\(["\\])/g, '$1')
|
|
418
|
+
}
|
|
419
|
+
]
|
|
420
|
+
}
|
|
421
|
+
});
|
|
422
|
+
}
|
|
423
|
+
_nextNode(lexer, i = 0) {
|
|
424
|
+
let node = null;
|
|
425
|
+
for (const token of lexer){
|
|
426
|
+
// Ignore whitespaces
|
|
427
|
+
if (token.type === 'whitespace') {
|
|
428
|
+
continue;
|
|
429
|
+
}
|
|
430
|
+
// rparen = end of group
|
|
431
|
+
if (token.type === 'rparen') {
|
|
432
|
+
break;
|
|
433
|
+
}
|
|
434
|
+
// Handle argument
|
|
435
|
+
if (token.type === 'argument') {
|
|
436
|
+
if (!node) {
|
|
437
|
+
throw new TaskSyntaxError(lexer.formatError(token, 'Unexpected argument'));
|
|
438
|
+
} else if (TaskParserService.isTaskNode(node)) {
|
|
439
|
+
node.args.push(token.value);
|
|
440
|
+
} else {
|
|
441
|
+
const lastTask = node.tasks[node.tasks.length - 1];
|
|
442
|
+
if (!lastTask || !TaskParserService.isTaskNode(lastTask)) {
|
|
443
|
+
throw new TaskSyntaxError(lexer.formatError(token, 'Unexpected argument'));
|
|
444
|
+
} else {
|
|
445
|
+
lastTask.args.push(token.value);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
continue;
|
|
449
|
+
}
|
|
450
|
+
// Handle operator
|
|
451
|
+
if (token.type === 'operator') {
|
|
452
|
+
const operator = token.value;
|
|
453
|
+
if (!node) {
|
|
454
|
+
throw new TaskSyntaxError(lexer.formatError(token, 'Unexpected operator'));
|
|
455
|
+
} else if (TaskParserService.isTaskNode(node)) {
|
|
456
|
+
node = {
|
|
457
|
+
operator,
|
|
458
|
+
tasks: [
|
|
459
|
+
node
|
|
460
|
+
]
|
|
461
|
+
};
|
|
462
|
+
continue;
|
|
463
|
+
} else {
|
|
464
|
+
if (node.operator !== operator) {
|
|
465
|
+
node = {
|
|
466
|
+
operator,
|
|
467
|
+
tasks: [
|
|
468
|
+
node
|
|
469
|
+
]
|
|
470
|
+
};
|
|
471
|
+
}
|
|
472
|
+
continue;
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
// Build "child"
|
|
476
|
+
let child;
|
|
477
|
+
if (token.type === 'script') {
|
|
478
|
+
child = {
|
|
479
|
+
script: token.value,
|
|
480
|
+
args: []
|
|
481
|
+
};
|
|
482
|
+
} else if (token.type === 'string') {
|
|
483
|
+
const [script, ...args] = token.value.split(/ +/);
|
|
484
|
+
child = {
|
|
485
|
+
script,
|
|
486
|
+
args
|
|
487
|
+
};
|
|
488
|
+
} else if (token.type === 'lparen') {
|
|
489
|
+
const res = this._nextNode(lexer, i + 1);
|
|
490
|
+
if (!res) {
|
|
491
|
+
throw new TaskSyntaxError(lexer.formatError(token, 'Empty group found'));
|
|
492
|
+
}
|
|
493
|
+
child = res;
|
|
494
|
+
} else {
|
|
495
|
+
throw new TaskSyntaxError(lexer.formatError(token, 'Unexpected token'));
|
|
496
|
+
}
|
|
497
|
+
if (!node) {
|
|
498
|
+
node = child;
|
|
499
|
+
} else if (TaskParserService.isTaskNode(node)) {
|
|
500
|
+
throw new TaskSyntaxError(lexer.formatError(token, 'Unexpected token, expected an operator'));
|
|
501
|
+
} else {
|
|
502
|
+
node.tasks.push(child);
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
return node;
|
|
506
|
+
}
|
|
507
|
+
parse(expr) {
|
|
508
|
+
const lexer = this._lexer().reset(expr);
|
|
509
|
+
const tree = {
|
|
510
|
+
roots: []
|
|
511
|
+
};
|
|
512
|
+
while(true){
|
|
513
|
+
const node = this._nextNode(lexer);
|
|
514
|
+
if (node) {
|
|
515
|
+
tree.roots.push(node);
|
|
516
|
+
} else {
|
|
517
|
+
break;
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
return tree;
|
|
521
|
+
}
|
|
522
|
+
*extractScripts(node) {
|
|
523
|
+
if ('roots' in node) {
|
|
524
|
+
for (const child of node.roots){
|
|
525
|
+
yield* this.extractScripts(child);
|
|
526
|
+
}
|
|
527
|
+
} else if (TaskParserService.isTaskNode(node)) {
|
|
528
|
+
yield node.script;
|
|
529
|
+
} else {
|
|
530
|
+
for (const child of node.tasks){
|
|
531
|
+
yield* this.extractScripts(child);
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
async buildJob(node, workspace, opts) {
|
|
536
|
+
if (TaskParserService.isTaskNode(node)) {
|
|
537
|
+
const job = await workspace.run(node.script, node.args, opts);
|
|
538
|
+
if (!job) {
|
|
539
|
+
throw new TaskExpressionError(`Workspace ${workspace.name} have no ${node.script} script`);
|
|
540
|
+
}
|
|
541
|
+
return job;
|
|
542
|
+
} else {
|
|
543
|
+
let flow;
|
|
544
|
+
if (node.operator === '//') {
|
|
545
|
+
flow = parallelFlow$({
|
|
546
|
+
label: 'In parallel'
|
|
547
|
+
});
|
|
548
|
+
} else if (node.operator === '||') {
|
|
549
|
+
flow = fallbackFlow$({
|
|
550
|
+
label: 'Fallbacks'
|
|
551
|
+
});
|
|
552
|
+
} else {
|
|
553
|
+
if (node.operator === '->' && TaskParserService._sequenceOperatorWarn) {
|
|
554
|
+
this._logger.warn('Sequence operator -> is deprecated in favor of &&. It will be removed in a next major release.');
|
|
555
|
+
TaskParserService._sequenceOperatorWarn = true;
|
|
556
|
+
}
|
|
557
|
+
flow = sequenceFlow$({
|
|
558
|
+
label: 'In sequence'
|
|
559
|
+
});
|
|
560
|
+
}
|
|
561
|
+
for (const child of node.tasks){
|
|
562
|
+
flow.push(await this.buildJob(child, workspace, opts));
|
|
563
|
+
}
|
|
564
|
+
return flow;
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
function mutex$() {
|
|
570
|
+
const count$ = var$(0);
|
|
571
|
+
return {
|
|
572
|
+
async acquire () {
|
|
573
|
+
let cnt = count$.defer();
|
|
574
|
+
while(cnt > 0){
|
|
575
|
+
cnt = await waitFor$(count$);
|
|
576
|
+
}
|
|
577
|
+
count$.mutate(cnt + 1);
|
|
578
|
+
},
|
|
579
|
+
release () {
|
|
580
|
+
const cnt = count$.defer();
|
|
581
|
+
if (cnt > 0) {
|
|
582
|
+
count$.mutate(cnt - 1);
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
};
|
|
586
|
+
}
|
|
587
|
+
async function with$(lock, fn) {
|
|
31
588
|
try {
|
|
32
|
-
await
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
589
|
+
await lock.acquire();
|
|
590
|
+
return await fn();
|
|
591
|
+
} finally{
|
|
592
|
+
lock.release();
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
function command$(workspace, cmd, args, opts = {}) {
|
|
597
|
+
const { superCommand, logger = inject$(LOGGER), ...rest } = opts;
|
|
598
|
+
// Apply super command
|
|
599
|
+
if (superCommand) {
|
|
600
|
+
if (typeof superCommand === 'string') {
|
|
601
|
+
args = [
|
|
602
|
+
cmd,
|
|
603
|
+
...args
|
|
604
|
+
];
|
|
605
|
+
cmd = superCommand;
|
|
606
|
+
} else if (superCommand.length) {
|
|
607
|
+
args = [
|
|
608
|
+
...superCommand.slice(1),
|
|
609
|
+
cmd,
|
|
610
|
+
...args
|
|
611
|
+
];
|
|
612
|
+
cmd = superCommand[0];
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
// Prepare job
|
|
616
|
+
const job = spawn$(cmd, args, {
|
|
617
|
+
...rest,
|
|
618
|
+
cwd: workspace.root,
|
|
619
|
+
env: {
|
|
620
|
+
FORCE_COLOR: '1',
|
|
621
|
+
...rest.env
|
|
622
|
+
}
|
|
623
|
+
});
|
|
624
|
+
job.stdout.pipe(logStreamedLines(logger, LogLevel.info));
|
|
625
|
+
job.stderr.pipe(logStreamedLines(logger, LogLevel.info));
|
|
626
|
+
return job;
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
// Utils
|
|
630
|
+
function capitalize(txt) {
|
|
631
|
+
return txt.charAt(0).toUpperCase() + txt.substring(1).toLowerCase();
|
|
632
|
+
}
|
|
633
|
+
function splitCommandLine(line) {
|
|
634
|
+
line = line.trim();
|
|
635
|
+
const parts = [];
|
|
636
|
+
let current_cote = '';
|
|
637
|
+
let last = 0;
|
|
638
|
+
for(let i = 1; i < line.length; ++i){
|
|
639
|
+
const c = line[i];
|
|
640
|
+
if (current_cote) {
|
|
641
|
+
if (c === current_cote) {
|
|
642
|
+
current_cote = '';
|
|
643
|
+
}
|
|
36
644
|
} else {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
645
|
+
if ([
|
|
646
|
+
'"',
|
|
647
|
+
'\''
|
|
648
|
+
].includes(c)) {
|
|
649
|
+
current_cote = c;
|
|
650
|
+
} else if (c === ' ') {
|
|
651
|
+
parts.push(line.slice(last, i));
|
|
652
|
+
last = i + 1;
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
parts.push(line.slice(last));
|
|
657
|
+
return parts;
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
async function runScript$(workspace, script, args, opts = {}) {
|
|
661
|
+
// Run script itself
|
|
662
|
+
const jobs = [
|
|
663
|
+
await planScript$(workspace, script, args, opts)
|
|
664
|
+
];
|
|
665
|
+
if (!jobs[0]) {
|
|
666
|
+
throw new ScriptNotFound(`No script ${script} in ${workspace.name}`);
|
|
667
|
+
}
|
|
668
|
+
// Run hooks
|
|
669
|
+
if (opts.runHooks) {
|
|
670
|
+
jobs.unshift(await planScript$(workspace, `pre${script}`, [], opts));
|
|
671
|
+
jobs.push(await planScript$(workspace, `post${script}`, [], opts));
|
|
672
|
+
}
|
|
673
|
+
// Prepare workflow
|
|
674
|
+
const flow = pipe$(jobs, filter$((job)=>job !== null), collect$(sequenceFlow$({
|
|
675
|
+
label: script,
|
|
676
|
+
type: 'script'
|
|
677
|
+
})));
|
|
678
|
+
return {
|
|
679
|
+
...flow,
|
|
680
|
+
script,
|
|
681
|
+
workspace
|
|
682
|
+
};
|
|
683
|
+
}
|
|
684
|
+
// Utils
|
|
685
|
+
async function planScript$(workspace, script, args, opts) {
|
|
686
|
+
// Load script
|
|
687
|
+
const line = workspace?.getScript(script);
|
|
688
|
+
if (!line) {
|
|
689
|
+
return null;
|
|
690
|
+
}
|
|
691
|
+
// Parse script
|
|
692
|
+
const [command, ...commandArgs] = splitCommandLine(line);
|
|
693
|
+
if (!command) {
|
|
694
|
+
return null;
|
|
695
|
+
}
|
|
696
|
+
if (command === 'jill') {
|
|
697
|
+
const argv = commandArgs.map((arg)=>arg.replace(/^["'](.+)["']$/, '$1'));
|
|
698
|
+
const { PlannerService } = await import('./planner.service.js');
|
|
699
|
+
const plannerService = inject$(PlannerService);
|
|
700
|
+
const job = await plannerService.plan(argv, workspace.root);
|
|
701
|
+
if (job) {
|
|
702
|
+
return job;
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
// Run command
|
|
706
|
+
const pm = await workspace.project.packageManager();
|
|
707
|
+
return command$(workspace, command, [
|
|
708
|
+
...commandArgs,
|
|
709
|
+
...args
|
|
710
|
+
], {
|
|
711
|
+
logger: opts.logger,
|
|
712
|
+
superCommand: pm === 'yarn' && command !== 'yarn' ? [
|
|
713
|
+
'yarn',
|
|
714
|
+
'exec'
|
|
715
|
+
] : undefined
|
|
716
|
+
});
|
|
717
|
+
}
|
|
718
|
+
class ScriptNotFound extends ClientError {
|
|
719
|
+
name = 'ScriptNotFound';
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
// Utils
|
|
723
|
+
async function* combine(...generators) {
|
|
724
|
+
for (const gen of generators){
|
|
725
|
+
yield* gen;
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
class Workspace {
|
|
730
|
+
manifest;
|
|
731
|
+
project;
|
|
732
|
+
// Attributes
|
|
733
|
+
_affectedCache = new Map();
|
|
734
|
+
_logger;
|
|
735
|
+
_git = inject$(GitService);
|
|
736
|
+
_root;
|
|
737
|
+
_jobs = new Map();
|
|
738
|
+
// Constructor
|
|
739
|
+
constructor(root, manifest, project){
|
|
740
|
+
this.manifest = manifest;
|
|
741
|
+
this.project = project;
|
|
742
|
+
this._root = root;
|
|
743
|
+
this._logger = inject$(LOGGER).child(withLabel(manifest.name));
|
|
744
|
+
}
|
|
745
|
+
// Methods
|
|
746
|
+
async _buildDependencies(job, opts) {
|
|
747
|
+
const generators = [];
|
|
748
|
+
switch(opts.buildDeps ?? 'all'){
|
|
749
|
+
case 'all':
|
|
750
|
+
generators.unshift(this.devDependencies());
|
|
751
|
+
// eslint-disable-next no-fallthrough
|
|
752
|
+
case 'prod':
|
|
753
|
+
generators.unshift(this.dependencies());
|
|
754
|
+
}
|
|
755
|
+
// Build deps
|
|
756
|
+
for await (const dep of combine(...generators)){
|
|
757
|
+
const build = await dep.build(opts);
|
|
758
|
+
if (build) {
|
|
759
|
+
job.dependsOn(build);
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
async *_loadDependencies(dependencies, kind) {
|
|
764
|
+
for (const [dep, range] of Object.entries(dependencies)){
|
|
765
|
+
const ws = await this.project.workspace(dep);
|
|
766
|
+
if (ws) {
|
|
767
|
+
if (ws._satisfies(this, range)) {
|
|
768
|
+
yield ws;
|
|
769
|
+
} else {
|
|
770
|
+
this._logger.warning(`ignoring ${kind} ${ws.reference} as it does not match requirement ${range}`);
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
async _isAffected(reference) {
|
|
776
|
+
const isAffected = await this._git.isAffected(reference, [
|
|
777
|
+
this.root
|
|
778
|
+
], {
|
|
779
|
+
cwd: this.project.root,
|
|
780
|
+
logger: this._logger
|
|
781
|
+
});
|
|
782
|
+
if (isAffected) {
|
|
783
|
+
return true;
|
|
784
|
+
}
|
|
785
|
+
// Test dependencies
|
|
786
|
+
const proms = [];
|
|
787
|
+
for await (const dep of combine(this.dependencies(), this.devDependencies())){
|
|
788
|
+
proms.push(dep.isAffected(reference));
|
|
789
|
+
}
|
|
790
|
+
const results = await Promise.all(proms);
|
|
791
|
+
return results.some((r)=>r);
|
|
792
|
+
}
|
|
793
|
+
_satisfies(from, range) {
|
|
794
|
+
if (range.startsWith('file:')) {
|
|
795
|
+
return path.resolve(from.root, range.substring(5)) === this.root;
|
|
796
|
+
}
|
|
797
|
+
if (range.startsWith('workspace:')) {
|
|
798
|
+
range = range.substring(10);
|
|
799
|
+
}
|
|
800
|
+
return !this.version || satisfies(this.version, range);
|
|
801
|
+
}
|
|
802
|
+
async isAffected(reference) {
|
|
803
|
+
let isAffected = this._affectedCache.get(reference);
|
|
804
|
+
if (!isAffected) {
|
|
805
|
+
isAffected = this._isAffected(reference);
|
|
806
|
+
this._affectedCache.set(reference, isAffected);
|
|
807
|
+
}
|
|
808
|
+
return await isAffected;
|
|
809
|
+
}
|
|
810
|
+
async *dependencies() {
|
|
811
|
+
if (!this.manifest.dependencies) return;
|
|
812
|
+
for await (const ws of this._loadDependencies(this.manifest.dependencies, 'dependency')){
|
|
813
|
+
yield ws;
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
async *devDependencies() {
|
|
817
|
+
if (!this.manifest.devDependencies) return;
|
|
818
|
+
for await (const ws of this._loadDependencies(this.manifest.devDependencies, 'devDependency')){
|
|
819
|
+
yield ws;
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
async build(opts = {}) {
|
|
823
|
+
const script = opts.buildScript ?? 'build';
|
|
824
|
+
const job = await this.run(script, [], opts);
|
|
825
|
+
if (!job) {
|
|
826
|
+
this._logger.warning(`will not be built (no "${script}" script found)`);
|
|
827
|
+
}
|
|
828
|
+
return job;
|
|
829
|
+
}
|
|
830
|
+
async exec(command, args = [], opts = {}) {
|
|
831
|
+
const pm = await this.project.packageManager();
|
|
832
|
+
const job = command$(this, command, args, {
|
|
833
|
+
...opts,
|
|
834
|
+
logger: this._logger.child(withLabel(`${this.name}$${command}`)),
|
|
835
|
+
superCommand: pm === 'yarn' ? [
|
|
836
|
+
'yarn',
|
|
837
|
+
'exec'
|
|
838
|
+
] : undefined
|
|
839
|
+
});
|
|
840
|
+
await this._buildDependencies(job, opts);
|
|
841
|
+
return job;
|
|
842
|
+
}
|
|
843
|
+
getScript(script) {
|
|
844
|
+
const { scripts = {} } = this.manifest;
|
|
845
|
+
return scripts[script] || null;
|
|
846
|
+
}
|
|
847
|
+
async run(script, args = [], opts = {}) {
|
|
848
|
+
// Script not found
|
|
849
|
+
if (!this.getScript(script)) {
|
|
850
|
+
return null;
|
|
851
|
+
}
|
|
852
|
+
// Create task if it doesn't exist yet
|
|
853
|
+
let job = this._jobs.get(script);
|
|
854
|
+
if (!job) {
|
|
855
|
+
const config = await inject$(CONFIG, asyncScope$());
|
|
856
|
+
job = await runScript$(this, script, args, {
|
|
857
|
+
...opts,
|
|
858
|
+
logger: this._logger.child(withLabel(`${this.name}#${script}`)),
|
|
859
|
+
runHooks: config.hooks
|
|
860
|
+
});
|
|
861
|
+
await this._buildDependencies(job, opts);
|
|
862
|
+
this._jobs.set(script, job);
|
|
863
|
+
}
|
|
864
|
+
return job;
|
|
865
|
+
}
|
|
866
|
+
toJSON() {
|
|
867
|
+
return {
|
|
868
|
+
name: this.name,
|
|
869
|
+
version: this.version,
|
|
870
|
+
root: this.root
|
|
871
|
+
};
|
|
872
|
+
}
|
|
873
|
+
// Properties
|
|
874
|
+
get name() {
|
|
875
|
+
return this.manifest.name;
|
|
876
|
+
}
|
|
877
|
+
get reference() {
|
|
878
|
+
return this.version ? `${this.name}@${this.version}` : this.name;
|
|
879
|
+
}
|
|
880
|
+
get root() {
|
|
881
|
+
return path.resolve(this.project.root, this._root);
|
|
882
|
+
}
|
|
883
|
+
get version() {
|
|
884
|
+
return this.manifest.version;
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
class Project {
|
|
889
|
+
// Attributes
|
|
890
|
+
_isFullyLoaded = false;
|
|
891
|
+
_lock = mutex$();
|
|
892
|
+
_mainWorkspace;
|
|
893
|
+
_packageManager;
|
|
894
|
+
_workspaceGlob;
|
|
895
|
+
_names = new Map();
|
|
896
|
+
_logger = inject$(LOGGER).child(withLabel('project'));
|
|
897
|
+
_root;
|
|
898
|
+
_scurry = inject$(PATH_SCURRY);
|
|
899
|
+
_workspaces = new Map();
|
|
900
|
+
// Constructor
|
|
901
|
+
constructor(root, opts = {}){
|
|
902
|
+
this._root = root;
|
|
903
|
+
if (opts.packageManager) {
|
|
904
|
+
this._logger.verbose`Forced use of ${opts.packageManager} in ${root}`;
|
|
905
|
+
this._packageManager = opts.packageManager;
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
// Methods
|
|
909
|
+
async _loadManifest(dir) {
|
|
910
|
+
const file = path.resolve(this.root, dir, 'package.json');
|
|
911
|
+
const relative = path.relative(this.root, path.dirname(file));
|
|
912
|
+
const logger = this._logger.child(withLabel(relative ? `project@${relative}` : 'project'));
|
|
913
|
+
logger.debug('loading package.json ...');
|
|
914
|
+
const data = await fs.promises.readFile(file, 'utf-8');
|
|
915
|
+
const mnf = JSON.parse(data);
|
|
916
|
+
normalize(mnf, (msg)=>logger.verbose(msg));
|
|
917
|
+
return mnf;
|
|
918
|
+
}
|
|
919
|
+
_loadWorkspace(dir) {
|
|
920
|
+
return with$(this._lock, async ()=>{
|
|
921
|
+
let wks = this._workspaces.get(dir);
|
|
922
|
+
if (!wks) {
|
|
923
|
+
const manifest = await this._loadManifest(dir);
|
|
924
|
+
wks = new Workspace(dir, manifest, this);
|
|
925
|
+
this._workspaces.set(dir, wks);
|
|
926
|
+
this._names.set(wks.name, wks);
|
|
927
|
+
}
|
|
928
|
+
return wks;
|
|
929
|
+
});
|
|
930
|
+
}
|
|
931
|
+
async currentWorkspace(cwd = inject$(CWD, asyncScope$())) {
|
|
932
|
+
let workspace = null;
|
|
933
|
+
cwd = path.resolve(cwd);
|
|
934
|
+
for await (const wks of this.workspaces()){
|
|
935
|
+
if (cwd.startsWith(wks.root)) {
|
|
936
|
+
workspace = wks;
|
|
937
|
+
if (wks.root !== this.root) return wks;
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
return workspace;
|
|
941
|
+
}
|
|
942
|
+
async mainWorkspace() {
|
|
943
|
+
if (!this._mainWorkspace) {
|
|
944
|
+
const manifest = await this._loadManifest('.');
|
|
945
|
+
this._mainWorkspace = new Workspace('.', manifest, this);
|
|
946
|
+
this._names.set(this._mainWorkspace.name, this._mainWorkspace);
|
|
947
|
+
}
|
|
948
|
+
return this._mainWorkspace;
|
|
949
|
+
}
|
|
950
|
+
async packageManager() {
|
|
951
|
+
if (!this._packageManager) {
|
|
952
|
+
this._logger.debug`searching lockfile in ${this.root}`;
|
|
953
|
+
const files = await this._scurry.readdir(this.root, {
|
|
954
|
+
withFileTypes: false
|
|
955
|
+
});
|
|
956
|
+
if (files.includes('yarn.lock')) {
|
|
957
|
+
this._logger.debug`detected yarn in ${this.root}`;
|
|
958
|
+
this._packageManager = 'yarn';
|
|
959
|
+
} else if (files.includes('package-lock.json')) {
|
|
960
|
+
this._logger.debug`detected npm in ${this.root}`;
|
|
961
|
+
this._packageManager = 'npm';
|
|
962
|
+
} else {
|
|
963
|
+
this._logger.debug`no package manager recognized in ${this.root}, defaults to npm`;
|
|
964
|
+
this._packageManager = 'npm';
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
return this._packageManager;
|
|
968
|
+
}
|
|
969
|
+
async workspace(name) {
|
|
970
|
+
// With current directory
|
|
971
|
+
if (!name) {
|
|
972
|
+
const dir = path.relative(this.root, inject$(CWD, asyncScope$()));
|
|
973
|
+
return this._loadWorkspace(dir);
|
|
974
|
+
}
|
|
975
|
+
// Try name index
|
|
976
|
+
const wks = this._names.get(name);
|
|
977
|
+
if (wks) {
|
|
978
|
+
return wks;
|
|
979
|
+
}
|
|
980
|
+
// Load workspaces
|
|
981
|
+
if (!this._isFullyLoaded) {
|
|
982
|
+
for await (const ws of this.workspaces()){
|
|
983
|
+
if (ws.name === name) {
|
|
984
|
+
return ws;
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
this._isFullyLoaded = true;
|
|
988
|
+
}
|
|
989
|
+
return null;
|
|
990
|
+
}
|
|
991
|
+
async *workspaces() {
|
|
992
|
+
const main = await this.mainWorkspace();
|
|
993
|
+
yield main;
|
|
994
|
+
if (this._isFullyLoaded) {
|
|
995
|
+
for (const wks of this._names.values()){
|
|
996
|
+
if (wks.name !== main.name) yield wks;
|
|
997
|
+
}
|
|
998
|
+
} else {
|
|
999
|
+
// Load child workspaces
|
|
1000
|
+
const patterns = main.manifest.workspaces ?? [];
|
|
1001
|
+
this._scurry.chdir(this.root);
|
|
1002
|
+
this._workspaceGlob ??= new Glob(patterns, {
|
|
1003
|
+
scurry: this._scurry,
|
|
1004
|
+
withFileTypes: true
|
|
1005
|
+
});
|
|
1006
|
+
for await (const dir of this._workspaceGlob){
|
|
1007
|
+
try {
|
|
1008
|
+
// Check if dir is a directory
|
|
1009
|
+
if (dir.isDirectory()) {
|
|
1010
|
+
yield await this._loadWorkspace(dir.fullpath());
|
|
1011
|
+
}
|
|
1012
|
+
} catch (error) {
|
|
1013
|
+
if (error.code === 'ENOENT') {
|
|
1014
|
+
continue;
|
|
1015
|
+
}
|
|
1016
|
+
throw error;
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
this._isFullyLoaded = true;
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
// Properties
|
|
1023
|
+
get root() {
|
|
1024
|
+
return path.resolve(this._root);
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
var _dec, _initProto;
|
|
1029
|
+
_dec = instrument();
|
|
1030
|
+
/**
|
|
1031
|
+
* Helps detecting projects folders
|
|
1032
|
+
*/ class ProjectsRepository {
|
|
1033
|
+
static{
|
|
1034
|
+
({ e: [_initProto] } = _(this, [
|
|
1035
|
+
[
|
|
1036
|
+
_dec,
|
|
1037
|
+
2,
|
|
1038
|
+
"searchProjectRoot"
|
|
1039
|
+
]
|
|
1040
|
+
], []));
|
|
1041
|
+
}
|
|
1042
|
+
// Attributes
|
|
1043
|
+
_cache = (_initProto(this), new Map());
|
|
1044
|
+
_logger = inject$(LOGGER).child(withLabel('projects'));
|
|
1045
|
+
_scurry = inject$(PATH_SCURRY);
|
|
1046
|
+
// Methods
|
|
1047
|
+
async isProjectRoot(dir) {
|
|
1048
|
+
this._logger.debug`testing ${dir}`;
|
|
1049
|
+
const files = await this._scurry.readdir(dir, {
|
|
1050
|
+
withFileTypes: false
|
|
1051
|
+
});
|
|
1052
|
+
return {
|
|
1053
|
+
hasManifest: files.includes(MANIFEST),
|
|
1054
|
+
hasLockFile: LOCK_FILES.some((lock)=>files.includes(lock))
|
|
1055
|
+
};
|
|
1056
|
+
}
|
|
1057
|
+
async searchProjectRoot(directory) {
|
|
1058
|
+
directory = path.resolve(directory);
|
|
1059
|
+
// Test all ancestors
|
|
1060
|
+
let foundManifest = false;
|
|
1061
|
+
let projectRoot = directory;
|
|
1062
|
+
let dir = directory;
|
|
1063
|
+
let prev = dir;
|
|
1064
|
+
do {
|
|
1065
|
+
// Look for files
|
|
1066
|
+
const { hasManifest, hasLockFile } = await this.isProjectRoot(dir);
|
|
1067
|
+
if (hasManifest) {
|
|
1068
|
+
projectRoot = dir;
|
|
1069
|
+
foundManifest = true;
|
|
1070
|
+
}
|
|
1071
|
+
if (hasLockFile) {
|
|
1072
|
+
break;
|
|
1073
|
+
}
|
|
1074
|
+
prev = dir;
|
|
1075
|
+
dir = path.dirname(dir);
|
|
1076
|
+
}while (prev !== dir);
|
|
1077
|
+
// Log it
|
|
1078
|
+
if (foundManifest) {
|
|
1079
|
+
this._logger.verbose`project root found at ${projectRoot}`;
|
|
1080
|
+
} else {
|
|
1081
|
+
this._logger.verbose`project root not found, keeping ${projectRoot}`;
|
|
1082
|
+
}
|
|
1083
|
+
return projectRoot;
|
|
1084
|
+
}
|
|
1085
|
+
getProject(root, opts) {
|
|
1086
|
+
let project = this._cache.get(root);
|
|
1087
|
+
if (!project) {
|
|
1088
|
+
project = new Project(root, opts);
|
|
1089
|
+
this._cache.set(root, project);
|
|
1090
|
+
}
|
|
1091
|
+
return project;
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
1094
|
+
// Constants
|
|
1095
|
+
const MANIFEST = 'package.json';
|
|
1096
|
+
const LOCK_FILES = [
|
|
1097
|
+
'package-lock.json',
|
|
1098
|
+
'yarn.lock'
|
|
1099
|
+
];
|
|
1100
|
+
|
|
1101
|
+
/**
|
|
1102
|
+
* Adds arguments to load a project.
|
|
1103
|
+
*/ function withProject(parser) {
|
|
1104
|
+
return parser.option('project', {
|
|
1105
|
+
alias: 'p',
|
|
1106
|
+
type: 'string',
|
|
1107
|
+
default: '',
|
|
1108
|
+
defaultDescription: 'current working directory',
|
|
1109
|
+
description: 'Project root directory',
|
|
1110
|
+
normalize: true
|
|
1111
|
+
}).option('package-manager', {
|
|
1112
|
+
choices: [
|
|
1113
|
+
'yarn',
|
|
1114
|
+
'npm'
|
|
1115
|
+
],
|
|
1116
|
+
type: 'string',
|
|
1117
|
+
description: 'Force package manager'
|
|
1118
|
+
}).middleware((args)=>startSpan({
|
|
1119
|
+
name: 'project',
|
|
1120
|
+
op: 'cli.middleware'
|
|
1121
|
+
}, async ()=>{
|
|
1122
|
+
const repository = inject$(ProjectsRepository);
|
|
1123
|
+
const directory = path.resolve(inject$(CWD, asyncScope$()), args.project);
|
|
1124
|
+
args.project = await repository.searchProjectRoot(directory);
|
|
1125
|
+
}));
|
|
1126
|
+
}
|
|
1127
|
+
/**
|
|
1128
|
+
* Loads a project, based on arguments.
|
|
1129
|
+
*/ function loadProject(args) {
|
|
1130
|
+
const repository = inject$(ProjectsRepository);
|
|
1131
|
+
return repository.getProject(args.project, {
|
|
1132
|
+
packageManager: args.packageManager
|
|
1133
|
+
});
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
function pipeline$() {
|
|
1137
|
+
const steps = [];
|
|
1138
|
+
return {
|
|
1139
|
+
add (step) {
|
|
1140
|
+
steps.push(step);
|
|
1141
|
+
return this;
|
|
1142
|
+
},
|
|
1143
|
+
build () {
|
|
1144
|
+
return (value)=>steps.reduce((val, step)=>step(val), value);
|
|
1145
|
+
}
|
|
1146
|
+
};
|
|
1147
|
+
}
|
|
1148
|
+
|
|
1149
|
+
// Command
|
|
1150
|
+
const command$5 = {
|
|
1151
|
+
command: 'each <expr>',
|
|
1152
|
+
describe: 'Run a task expression in many workspace, after having built all theirs dependencies.',
|
|
1153
|
+
builder: (parser)=>withProject(parser).positional('expr', {
|
|
1154
|
+
type: 'string',
|
|
1155
|
+
demandOption: true,
|
|
1156
|
+
desc: 'Script or task expression'
|
|
1157
|
+
}).option('affected', {
|
|
1158
|
+
alias: 'a',
|
|
1159
|
+
type: 'string',
|
|
1160
|
+
coerce: (rev)=>rev === '' ? 'master' : rev,
|
|
1161
|
+
group: 'Filters:',
|
|
1162
|
+
desc: 'Print only affected workspaces towards given git revision. If no revision is given, it will check towards master. Replaces %name by workspace name.'
|
|
1163
|
+
}).option('affected-rev-fallback', {
|
|
1164
|
+
type: 'string',
|
|
1165
|
+
default: 'master',
|
|
1166
|
+
group: 'Filters:',
|
|
1167
|
+
desc: 'Fallback revision, used if no revision matching the given format is found'
|
|
1168
|
+
}).option('affected-rev-sort', {
|
|
1169
|
+
type: 'string',
|
|
1170
|
+
group: 'Filters:',
|
|
1171
|
+
desc: 'Sort applied to git tag / git branch command'
|
|
1172
|
+
}).option('allow-no-workspaces', {
|
|
1173
|
+
type: 'boolean',
|
|
1174
|
+
default: false,
|
|
1175
|
+
desc: 'Allow no matching workspaces. Without it jill will exit with code 1 if no workspace matches'
|
|
1176
|
+
}).option('build-script', {
|
|
1177
|
+
default: 'build',
|
|
1178
|
+
desc: 'Script to use to build dependencies'
|
|
1179
|
+
}).option('deps-mode', {
|
|
1180
|
+
alias: 'd',
|
|
1181
|
+
choice: [
|
|
1182
|
+
'all',
|
|
1183
|
+
'prod',
|
|
1184
|
+
'none'
|
|
1185
|
+
],
|
|
1186
|
+
default: 'all',
|
|
1187
|
+
desc: 'Dependency selection mode:\n' + ' - all = dependencies AND devDependencies\n' + ' - prod = dependencies\n' + ' - none = nothing'
|
|
1188
|
+
}).option('private', {
|
|
1189
|
+
type: 'boolean',
|
|
1190
|
+
group: 'Filters:',
|
|
1191
|
+
describe: 'Print only private workspaces'
|
|
1192
|
+
})// Config
|
|
1193
|
+
.strict(false).parserConfiguration({
|
|
1194
|
+
'unknown-options-as-args': true
|
|
1195
|
+
}),
|
|
1196
|
+
async prepare (args) {
|
|
1197
|
+
// Extract expression
|
|
1198
|
+
const expr = args._.map((arg)=>arg.toString());
|
|
1199
|
+
if (expr[0] === 'each') {
|
|
1200
|
+
expr.splice(0, 1);
|
|
1201
|
+
}
|
|
1202
|
+
expr.unshift(args.expr);
|
|
1203
|
+
// Prepare filters
|
|
1204
|
+
let filters = pipeline$();
|
|
1205
|
+
if (args.private !== undefined) {
|
|
1206
|
+
filters = filters.add(isPrivate$(args.private));
|
|
1207
|
+
}
|
|
1208
|
+
if (args.affected !== undefined) {
|
|
1209
|
+
filters = filters.add(isAffected$({
|
|
1210
|
+
format: args.affected,
|
|
1211
|
+
fallback: args.affectedRevFallback,
|
|
1212
|
+
sort: args.affectedRevSort
|
|
1213
|
+
}));
|
|
1214
|
+
}
|
|
1215
|
+
// Parse task expression
|
|
1216
|
+
const taskParser = inject$(TaskParserService);
|
|
1217
|
+
const tree = taskParser.parse(expr.join(' '));
|
|
1218
|
+
const scripts = Array.from(taskParser.extractScripts(tree));
|
|
1219
|
+
// Load workspaces
|
|
1220
|
+
const project = loadProject(args);
|
|
1221
|
+
const workspaces = await waitFor$(pipe$(asyncIterator$(project.workspaces()), hasEveryScript$(scripts), filters.build(), collect$()));
|
|
1222
|
+
workspaces.sort((a, b)=>a.name.localeCompare(b.name));
|
|
1223
|
+
if (workspaces.length === 0) {
|
|
1224
|
+
return;
|
|
1225
|
+
}
|
|
1226
|
+
// Prepare tasks
|
|
1227
|
+
const flow = parallelFlow$({
|
|
1228
|
+
label: '[hidden]'
|
|
1229
|
+
});
|
|
1230
|
+
for (const wks of workspaces){
|
|
1231
|
+
flow.push(await taskParser.buildJob(tree.roots[0], wks, {
|
|
1232
|
+
buildScript: args.buildScript,
|
|
1233
|
+
buildDeps: args.depsMode
|
|
1234
|
+
}));
|
|
1235
|
+
}
|
|
1236
|
+
return flow;
|
|
1237
|
+
}
|
|
1238
|
+
};
|
|
1239
|
+
|
|
1240
|
+
/**
|
|
1241
|
+
* Adds arguments to load a workspace.
|
|
1242
|
+
*/ function withWorkspace(parser) {
|
|
1243
|
+
return withProject(parser).option('workspace', {
|
|
1244
|
+
alias: 'w',
|
|
1245
|
+
type: 'string',
|
|
1246
|
+
desc: 'Workspace to use'
|
|
1247
|
+
});
|
|
1248
|
+
}
|
|
1249
|
+
/**
|
|
1250
|
+
* Loads a workspace, based on arguments.
|
|
1251
|
+
*/ async function loadWorkspace(args) {
|
|
1252
|
+
const logger = inject$(LOGGER);
|
|
1253
|
+
const project = loadProject(args);
|
|
1254
|
+
let workspace;
|
|
1255
|
+
if (args.workspace) {
|
|
1256
|
+
logger.debug(`loading workspace "${args.workspace}"`);
|
|
1257
|
+
workspace = await project.workspace(args.workspace);
|
|
1258
|
+
} else if (inject$(CWD, asyncScope$()).startsWith(project.root)) {
|
|
1259
|
+
logger.debug('loading workspace containing current directory');
|
|
1260
|
+
workspace = await project.currentWorkspace(inject$(CWD, asyncScope$()));
|
|
1261
|
+
} else {
|
|
1262
|
+
logger.debug('loading main workspace');
|
|
1263
|
+
workspace = await project.mainWorkspace();
|
|
1264
|
+
}
|
|
1265
|
+
if (!workspace) {
|
|
1266
|
+
throw new WorkspaceNotFound(args.workspace || '.');
|
|
1267
|
+
}
|
|
1268
|
+
return workspace;
|
|
1269
|
+
}
|
|
1270
|
+
class WorkspaceNotFound extends ClientError {
|
|
1271
|
+
name = 'WorkspaceNotFound';
|
|
1272
|
+
constructor(workspace){
|
|
1273
|
+
super(`Workspace "${workspace}" not found`);
|
|
1274
|
+
}
|
|
1275
|
+
}
|
|
1276
|
+
|
|
1277
|
+
// Command
|
|
1278
|
+
const command$4 = {
|
|
1279
|
+
command: 'exec <command>',
|
|
1280
|
+
aliases: [
|
|
1281
|
+
'$0'
|
|
1282
|
+
],
|
|
1283
|
+
describe: 'Run command inside workspace, after all its dependencies has been built.',
|
|
1284
|
+
builder: (args)=>withWorkspace(args).positional('command', {
|
|
1285
|
+
type: 'string',
|
|
1286
|
+
demandOption: true
|
|
1287
|
+
}).option('build-script', {
|
|
1288
|
+
default: 'build',
|
|
1289
|
+
desc: 'Script to use to build dependencies'
|
|
1290
|
+
}).option('deps-mode', {
|
|
1291
|
+
alias: 'd',
|
|
1292
|
+
choice: [
|
|
1293
|
+
'all',
|
|
1294
|
+
'prod',
|
|
1295
|
+
'none'
|
|
1296
|
+
],
|
|
1297
|
+
default: 'all',
|
|
1298
|
+
desc: 'Dependency selection mode:\n' + ' - all = dependencies AND devDependencies\n' + ' - prod = dependencies\n' + ' - none = nothing'
|
|
1299
|
+
})// Documentation
|
|
1300
|
+
.example('jill exec eslint', '').example('jill exec eslint --env-info', 'Unknown arguments are passed down to command. Here it will run "eslint --env-info"').example('jill exec eslint -- -v', 'You can use -- to stop argument parsing. Here it will run "eslint -v"')// Config
|
|
1301
|
+
.strict(false).parserConfiguration({
|
|
1302
|
+
'unknown-options-as-args': true
|
|
1303
|
+
}),
|
|
1304
|
+
async prepare (args) {
|
|
1305
|
+
const workspace = await loadWorkspace(args);
|
|
1306
|
+
// Extract arguments
|
|
1307
|
+
const rest = args._.map((arg)=>arg.toString());
|
|
1308
|
+
if (rest[0] === 'exec') {
|
|
1309
|
+
rest.splice(0, 1);
|
|
1310
|
+
}
|
|
1311
|
+
// Run script in workspace
|
|
1312
|
+
return await workspace.exec(args.command, rest, {
|
|
1313
|
+
buildScript: args.buildScript,
|
|
1314
|
+
buildDeps: args.depsMode
|
|
1315
|
+
});
|
|
1316
|
+
},
|
|
1317
|
+
async execute (args, arg) {
|
|
1318
|
+
const job = arg;
|
|
1319
|
+
if (job.dependencies().length > 0) {
|
|
1320
|
+
const dependencies = pipe$(job.dependencies(), collect$(parallelFlow$({
|
|
1321
|
+
label: 'build dependencies'
|
|
1322
|
+
})));
|
|
1323
|
+
// Run dependencies first with spinners
|
|
1324
|
+
const { JobCommandExecuteInk } = await traceImport('JobCommandExecuteInk', ()=>import('./job-command-execute.ink.js'));
|
|
1325
|
+
await JobCommandExecuteInk({
|
|
1326
|
+
job: dependencies,
|
|
1327
|
+
verbose: [
|
|
1328
|
+
'verbose',
|
|
1329
|
+
'debug'
|
|
1330
|
+
].includes(args.verbose)
|
|
1331
|
+
});
|
|
1332
|
+
if (dependencies.state() !== WorkloadState.Succeeded) {
|
|
1333
|
+
return;
|
|
41
1334
|
}
|
|
42
|
-
|
|
1335
|
+
} else {
|
|
1336
|
+
const logger = inject$(LOGGER);
|
|
1337
|
+
logger.verbose('No dependency to build');
|
|
43
1338
|
}
|
|
1339
|
+
await startSpan({
|
|
1340
|
+
op: 'subprocess',
|
|
1341
|
+
name: [
|
|
1342
|
+
job.cmd,
|
|
1343
|
+
...job.args
|
|
1344
|
+
].join(' ')
|
|
1345
|
+
}, async ()=>{
|
|
1346
|
+
const child = spawn(job.cmd, job.args, {
|
|
1347
|
+
stdio: 'inherit',
|
|
1348
|
+
cwd: job.cwd,
|
|
1349
|
+
env: {
|
|
1350
|
+
...process$1.env,
|
|
1351
|
+
...job.env
|
|
1352
|
+
},
|
|
1353
|
+
shell: true,
|
|
1354
|
+
windowsHide: true
|
|
1355
|
+
});
|
|
1356
|
+
process$1.exitCode = await new Promise((resolve)=>{
|
|
1357
|
+
child.on('close', (code)=>{
|
|
1358
|
+
resolve(code ?? 0);
|
|
1359
|
+
});
|
|
1360
|
+
});
|
|
1361
|
+
});
|
|
1362
|
+
}
|
|
1363
|
+
};
|
|
1364
|
+
|
|
1365
|
+
// Utils
|
|
1366
|
+
function printJson(data, stream = process.stdout) {
|
|
1367
|
+
if (stream.isTTY) {
|
|
1368
|
+
stream.write(JSON.stringify(data, null, 2));
|
|
1369
|
+
} else {
|
|
1370
|
+
stream.write(JSON.stringify(data));
|
|
44
1371
|
}
|
|
45
|
-
}
|
|
1372
|
+
}
|
|
1373
|
+
|
|
1374
|
+
// Command
|
|
1375
|
+
const command$3 = {
|
|
1376
|
+
command: 'list',
|
|
1377
|
+
aliases: [
|
|
1378
|
+
'ls'
|
|
1379
|
+
],
|
|
1380
|
+
describe: 'List project workspaces',
|
|
1381
|
+
builder: (parser)=>withProject(parser).option('affected', {
|
|
1382
|
+
alias: 'a',
|
|
1383
|
+
type: 'string',
|
|
1384
|
+
coerce: (rev)=>rev === '' ? 'master' : rev,
|
|
1385
|
+
group: 'Filters:',
|
|
1386
|
+
desc: `Print only affected workspaces towards given git revision. If no revision is given, it will check towards master. Replaces ${chalk.bold('%name')} by workspace name.`
|
|
1387
|
+
}).option('affected-rev-fallback', {
|
|
1388
|
+
type: 'string',
|
|
1389
|
+
default: 'master',
|
|
1390
|
+
group: 'Filters:',
|
|
1391
|
+
desc: 'Fallback revision, used if no revision matching the given format is found'
|
|
1392
|
+
}).option('affected-rev-sort', {
|
|
1393
|
+
type: 'string',
|
|
1394
|
+
group: 'Filters:',
|
|
1395
|
+
desc: 'Sort applied to git tag / git branch command'
|
|
1396
|
+
}).option('attribute', {
|
|
1397
|
+
alias: [
|
|
1398
|
+
'attr',
|
|
1399
|
+
'attrs'
|
|
1400
|
+
],
|
|
1401
|
+
type: 'array',
|
|
1402
|
+
choices: [
|
|
1403
|
+
'name',
|
|
1404
|
+
'version',
|
|
1405
|
+
'root',
|
|
1406
|
+
'slug'
|
|
1407
|
+
],
|
|
1408
|
+
group: 'Format:',
|
|
1409
|
+
default: [],
|
|
1410
|
+
description: 'Select printed attributes',
|
|
1411
|
+
defaultDescription: '"name" only'
|
|
1412
|
+
}).option('headers', {
|
|
1413
|
+
type: 'boolean',
|
|
1414
|
+
group: 'Format:',
|
|
1415
|
+
default: false,
|
|
1416
|
+
desc: 'Prints columns headers'
|
|
1417
|
+
}).option('long', {
|
|
1418
|
+
alias: 'l',
|
|
1419
|
+
type: 'boolean',
|
|
1420
|
+
group: 'Format:',
|
|
1421
|
+
default: false,
|
|
1422
|
+
desc: 'Prints name, version and root of all workspaces'
|
|
1423
|
+
}).option('json', {
|
|
1424
|
+
type: 'boolean',
|
|
1425
|
+
group: 'Format:',
|
|
1426
|
+
default: false,
|
|
1427
|
+
desc: 'Prints data as a JSON array'
|
|
1428
|
+
}).option('private', {
|
|
1429
|
+
type: 'boolean',
|
|
1430
|
+
group: 'Filters:',
|
|
1431
|
+
describe: 'Print only private workspaces'
|
|
1432
|
+
}).option('sort-by', {
|
|
1433
|
+
alias: 's',
|
|
1434
|
+
type: 'array',
|
|
1435
|
+
choices: [
|
|
1436
|
+
'name',
|
|
1437
|
+
'version',
|
|
1438
|
+
'root',
|
|
1439
|
+
'slug'
|
|
1440
|
+
],
|
|
1441
|
+
group: 'Sort:',
|
|
1442
|
+
default: [],
|
|
1443
|
+
description: 'Sort output by given attribute',
|
|
1444
|
+
defaultDescription: 'first attribute'
|
|
1445
|
+
}).option('sort-order', {
|
|
1446
|
+
alias: [
|
|
1447
|
+
'o',
|
|
1448
|
+
'order'
|
|
1449
|
+
],
|
|
1450
|
+
type: 'string',
|
|
1451
|
+
choices: [
|
|
1452
|
+
'asc',
|
|
1453
|
+
'desc'
|
|
1454
|
+
],
|
|
1455
|
+
default: 'asc',
|
|
1456
|
+
group: 'Sort:',
|
|
1457
|
+
desc: 'Sort order'
|
|
1458
|
+
}).option('with-script', {
|
|
1459
|
+
type: 'array',
|
|
1460
|
+
string: true,
|
|
1461
|
+
group: 'Filters:',
|
|
1462
|
+
desc: 'Print only workspaces having the given script'
|
|
1463
|
+
}).middleware((argv)=>{
|
|
1464
|
+
// Compute attributes
|
|
1465
|
+
if (!argv.attribute.length) {
|
|
1466
|
+
if (argv.json) {
|
|
1467
|
+
argv.attrs = argv.attr = argv.attribute = [
|
|
1468
|
+
'name',
|
|
1469
|
+
'version',
|
|
1470
|
+
'slug',
|
|
1471
|
+
'root'
|
|
1472
|
+
];
|
|
1473
|
+
} else if (argv.long) {
|
|
1474
|
+
argv.attrs = argv.attr = argv.attribute = [
|
|
1475
|
+
'name',
|
|
1476
|
+
'version',
|
|
1477
|
+
'root'
|
|
1478
|
+
];
|
|
1479
|
+
} else if (argv.sortBy?.length) {
|
|
1480
|
+
argv.attrs = argv.attr = argv.attribute = argv.sortBy;
|
|
1481
|
+
} else {
|
|
1482
|
+
argv.attrs = argv.attr = argv.attribute = [
|
|
1483
|
+
'name'
|
|
1484
|
+
];
|
|
1485
|
+
}
|
|
1486
|
+
}
|
|
1487
|
+
}, true).check((argv)=>{
|
|
1488
|
+
if (argv.attribute.length > 0 && argv['sort-by']?.length) {
|
|
1489
|
+
const miss = argv['sort-by'].filter((attr)=>!argv.attribute.includes(attr));
|
|
1490
|
+
if (miss.length > 0) {
|
|
1491
|
+
throw new ClientError(`Cannot sort by non printed attributes. Missing ${miss.join(', ')}.`);
|
|
1492
|
+
}
|
|
1493
|
+
}
|
|
1494
|
+
if (!argv['sort-by']?.length && argv.attribute.length > 0) {
|
|
1495
|
+
argv['sort-by'] = argv.sortBy = argv.s = [
|
|
1496
|
+
argv.attribute[0]
|
|
1497
|
+
];
|
|
1498
|
+
}
|
|
1499
|
+
return true;
|
|
1500
|
+
}),
|
|
1501
|
+
async handler (args) {
|
|
1502
|
+
// Prepare filters
|
|
1503
|
+
let filters = pipeline$();
|
|
1504
|
+
if (args.private !== undefined) {
|
|
1505
|
+
filters = filters.add(isPrivate$(args.private));
|
|
1506
|
+
}
|
|
1507
|
+
if (args.withScript) {
|
|
1508
|
+
filters = filters.add(hasSomeScript$(args.withScript));
|
|
1509
|
+
}
|
|
1510
|
+
if (args.affected !== undefined) {
|
|
1511
|
+
filters = filters.add(isAffected$({
|
|
1512
|
+
format: args.affected,
|
|
1513
|
+
fallback: args.affectedRevFallback,
|
|
1514
|
+
sort: args.affectedRevSort
|
|
1515
|
+
}));
|
|
1516
|
+
}
|
|
1517
|
+
// Load workspaces
|
|
1518
|
+
const project = loadProject(args);
|
|
1519
|
+
const workspaces = await waitFor$(pipe$(asyncIterator$(project.workspaces()), filters.build(), map$(buildExtractor(args)), collect$()));
|
|
1520
|
+
if (args.sortBy.length > 0) {
|
|
1521
|
+
workspaces.sort(buildComparator(args));
|
|
1522
|
+
}
|
|
1523
|
+
if (args.json) {
|
|
1524
|
+
printJson(workspaces);
|
|
1525
|
+
} else if (workspaces.length > 0) {
|
|
1526
|
+
for (const data of workspaces){
|
|
1527
|
+
if (data.root) {
|
|
1528
|
+
data.root = path.relative(process.cwd(), data.root) || '.';
|
|
1529
|
+
}
|
|
1530
|
+
}
|
|
1531
|
+
const { default: ListInk } = await traceImport('ListInk', ()=>import('./list.ink.js'));
|
|
1532
|
+
await ListInk({
|
|
1533
|
+
attributes: args.attribute,
|
|
1534
|
+
headers: args.headers,
|
|
1535
|
+
workspaces
|
|
1536
|
+
});
|
|
1537
|
+
}
|
|
1538
|
+
}
|
|
1539
|
+
};
|
|
1540
|
+
const EXTRACTORS = {
|
|
1541
|
+
name: (wks)=>wks.name,
|
|
1542
|
+
version: (wks, json)=>wks.manifest.version || (json ? undefined : chalk.grey('unset')),
|
|
1543
|
+
root: (wks)=>wks.root,
|
|
1544
|
+
slug: (wks)=>slugify(wks.name)
|
|
1545
|
+
};
|
|
1546
|
+
const COMPARATORS = {
|
|
1547
|
+
name: (a = '', b = '')=>a.localeCompare(b),
|
|
1548
|
+
version: (a, b)=>compare(parse(a) ?? '0.0.0', parse(b) ?? '0.0.0'),
|
|
1549
|
+
root: (a = '', b = '')=>a.localeCompare(b),
|
|
1550
|
+
slug: (a = '', b = '')=>a.localeCompare(b)
|
|
1551
|
+
};
|
|
1552
|
+
function buildExtractor(args) {
|
|
1553
|
+
return (wks)=>{
|
|
1554
|
+
const data = {};
|
|
1555
|
+
for (const attr of args.attribute){
|
|
1556
|
+
data[attr] = EXTRACTORS[attr](wks, args.json);
|
|
1557
|
+
}
|
|
1558
|
+
return data;
|
|
1559
|
+
};
|
|
1560
|
+
}
|
|
1561
|
+
function buildComparator(args) {
|
|
1562
|
+
const factor = args.sortOrder === 'asc' ? 1 : -1;
|
|
1563
|
+
return (a, b)=>{
|
|
1564
|
+
for (const attr of args.sortBy){
|
|
1565
|
+
const diff = COMPARATORS[attr](a[attr], b[attr]);
|
|
1566
|
+
if (diff !== 0) {
|
|
1567
|
+
return diff * factor;
|
|
1568
|
+
}
|
|
1569
|
+
}
|
|
1570
|
+
return 0;
|
|
1571
|
+
};
|
|
1572
|
+
}
|
|
1573
|
+
|
|
1574
|
+
// Command
|
|
1575
|
+
const command$2 = {
|
|
1576
|
+
command: 'run <expr>',
|
|
1577
|
+
describe: 'Run a task expression in a workspace, after having built all its dependencies.',
|
|
1578
|
+
builder: (parser)=>withWorkspace(parser).positional('expr', {
|
|
1579
|
+
type: 'string',
|
|
1580
|
+
demandOption: true,
|
|
1581
|
+
desc: 'Script or task expression'
|
|
1582
|
+
}).option('build-script', {
|
|
1583
|
+
default: 'build',
|
|
1584
|
+
desc: 'Script to use to build dependencies'
|
|
1585
|
+
}).option('deps-mode', {
|
|
1586
|
+
alias: 'd',
|
|
1587
|
+
choice: [
|
|
1588
|
+
'all',
|
|
1589
|
+
'prod',
|
|
1590
|
+
'none'
|
|
1591
|
+
],
|
|
1592
|
+
default: 'all',
|
|
1593
|
+
desc: 'Dependency selection mode:\n' + ' - all = dependencies AND devDependencies\n' + ' - prod = dependencies\n' + ' - none = nothing'
|
|
1594
|
+
})// Config
|
|
1595
|
+
.strict(false).parserConfiguration({
|
|
1596
|
+
'unknown-options-as-args': true
|
|
1597
|
+
}),
|
|
1598
|
+
async prepare (args) {
|
|
1599
|
+
const workspace = await loadWorkspace(args);
|
|
1600
|
+
// Extract expression
|
|
1601
|
+
const expr = args._.map((arg)=>arg.toString());
|
|
1602
|
+
if (expr[0] === 'run') {
|
|
1603
|
+
expr.splice(0, 1);
|
|
1604
|
+
}
|
|
1605
|
+
expr.unshift(args.expr);
|
|
1606
|
+
// Parse task expression
|
|
1607
|
+
const taskParser = inject$(TaskParserService);
|
|
1608
|
+
const tree = taskParser.parse(expr.join(' '));
|
|
1609
|
+
return await taskParser.buildJob(tree.roots[0], workspace, {
|
|
1610
|
+
buildScript: args.buildScript,
|
|
1611
|
+
buildDeps: args.depsMode
|
|
1612
|
+
});
|
|
1613
|
+
}
|
|
1614
|
+
};
|
|
1615
|
+
|
|
1616
|
+
// Command
|
|
1617
|
+
const command$1 = {
|
|
1618
|
+
command: 'tree',
|
|
1619
|
+
describe: 'Print workspace dependency tree',
|
|
1620
|
+
builder: withWorkspace,
|
|
1621
|
+
async handler (args) {
|
|
1622
|
+
const workspace = await loadWorkspace(args);
|
|
1623
|
+
const { default: TreeInk } = await traceImport('TreeInk', ()=>import('./tree.ink.js'));
|
|
1624
|
+
await TreeInk({
|
|
1625
|
+
workspace
|
|
1626
|
+
});
|
|
1627
|
+
}
|
|
1628
|
+
};
|
|
1629
|
+
|
|
1630
|
+
var version = "3.0.0-alpha.10";
|
|
1631
|
+
|
|
1632
|
+
// Utils
|
|
1633
|
+
function dynamicImport(filepath) {
|
|
1634
|
+
return import(/* webpackIgnore: true */ process.platform === 'win32' ? `file://${filepath}` : filepath);
|
|
1635
|
+
}
|
|
1636
|
+
|
|
1637
|
+
const ConfigExplorer = token$('ConfigExplorer', ()=>cosmiconfig('jill', {
|
|
1638
|
+
searchStrategy: 'global',
|
|
1639
|
+
loaders: {
|
|
1640
|
+
'.cjs': (filepath)=>dynamicImport(filepath).then((mod)=>mod.default),
|
|
1641
|
+
'.js': (filepath)=>dynamicImport(filepath).then((mod)=>mod.default),
|
|
1642
|
+
'.json': defaultLoaders['.json'],
|
|
1643
|
+
'.yaml': defaultLoaders['.yaml'],
|
|
1644
|
+
'.yml': defaultLoaders['.yml'],
|
|
1645
|
+
noExt: defaultLoaders.noExt
|
|
1646
|
+
}
|
|
1647
|
+
}));
|
|
1648
|
+
|
|
1649
|
+
var $schema = "http://json-schema.org/draft-07/schema";
|
|
1650
|
+
var type = "object";
|
|
1651
|
+
var additionalProperties = false;
|
|
1652
|
+
var properties = {
|
|
1653
|
+
hooks: {
|
|
1654
|
+
type: "boolean",
|
|
1655
|
+
"default": true,
|
|
1656
|
+
description: "Instructs jill to run hook scripts."
|
|
1657
|
+
},
|
|
1658
|
+
jobs: {
|
|
1659
|
+
type: "number",
|
|
1660
|
+
"default": 0,
|
|
1661
|
+
description: "Number of allowed parallel tasks, defaults to CPU number."
|
|
1662
|
+
}
|
|
1663
|
+
};
|
|
1664
|
+
var schema = {
|
|
1665
|
+
$schema: $schema,
|
|
1666
|
+
type: type,
|
|
1667
|
+
additionalProperties: additionalProperties,
|
|
1668
|
+
properties: properties
|
|
1669
|
+
};
|
|
1670
|
+
|
|
1671
|
+
/**
|
|
1672
|
+
* Loads and make configuration accessible
|
|
1673
|
+
*/ class ConfigService {
|
|
1674
|
+
// Attributes
|
|
1675
|
+
_filepath;
|
|
1676
|
+
_config = var$();
|
|
1677
|
+
_logger = inject$(LOGGER).child(withLabel('config'));
|
|
1678
|
+
_explorer = inject$(ConfigExplorer);
|
|
1679
|
+
// Constructor
|
|
1680
|
+
constructor(state = {}){
|
|
1681
|
+
if (state.filepath) {
|
|
1682
|
+
this._filepath = state.filepath;
|
|
1683
|
+
}
|
|
1684
|
+
if (state.config) {
|
|
1685
|
+
this._config.mutate(state.config);
|
|
1686
|
+
}
|
|
1687
|
+
}
|
|
1688
|
+
// Methods
|
|
1689
|
+
_validateConfig(config) {
|
|
1690
|
+
// Validate config
|
|
1691
|
+
const ajv = new Ajv({
|
|
1692
|
+
allErrors: true,
|
|
1693
|
+
useDefaults: true,
|
|
1694
|
+
logger: this._logger.child(withLabel('ajv')),
|
|
1695
|
+
strict: process$1.env.NODE_ENV === 'development' ? 'log' : true
|
|
1696
|
+
});
|
|
1697
|
+
const validator = ajv.compile(schema);
|
|
1698
|
+
if (!validator(config)) {
|
|
1699
|
+
const errors = ajv.errorsText(validator.errors, {
|
|
1700
|
+
separator: '\n- ',
|
|
1701
|
+
dataVar: 'config'
|
|
1702
|
+
});
|
|
1703
|
+
this._logger.error(`errors in config file:\n- ${errors}`);
|
|
1704
|
+
throw new Error('Error in config file');
|
|
1705
|
+
}
|
|
1706
|
+
// Correct jobs value
|
|
1707
|
+
if (config.jobs <= 0) {
|
|
1708
|
+
Object.assign(config, {
|
|
1709
|
+
jobs: Math.max(os.availableParallelism() - 1, 1)
|
|
1710
|
+
});
|
|
1711
|
+
}
|
|
1712
|
+
this._logger.debug`loaded config:\n${qjson(config, {
|
|
1713
|
+
pretty: true
|
|
1714
|
+
})}`;
|
|
1715
|
+
return config;
|
|
1716
|
+
}
|
|
1717
|
+
async searchConfig() {
|
|
1718
|
+
const loaded = await this._explorer.search(inject$(CWD, asyncScope$()));
|
|
1719
|
+
if (loaded) {
|
|
1720
|
+
this._logger.verbose`loaded file ${loaded.filepath}`;
|
|
1721
|
+
this._filepath = loaded.filepath;
|
|
1722
|
+
}
|
|
1723
|
+
const config = this._validateConfig(loaded?.config ?? {});
|
|
1724
|
+
this._config.mutate(config);
|
|
1725
|
+
}
|
|
1726
|
+
async loadConfig(filepath) {
|
|
1727
|
+
const loaded = await this._explorer.load(filepath);
|
|
1728
|
+
if (loaded) {
|
|
1729
|
+
this._logger.verbose`loaded file ${loaded.filepath}`;
|
|
1730
|
+
this._filepath = loaded.filepath;
|
|
1731
|
+
}
|
|
1732
|
+
const config = this._validateConfig(loaded?.config ?? {});
|
|
1733
|
+
this._config.mutate(config);
|
|
1734
|
+
}
|
|
1735
|
+
// Attributes
|
|
1736
|
+
get baseDir() {
|
|
1737
|
+
return this._filepath ? path.dirname(this._filepath) : inject$(CWD, asyncScope$());
|
|
1738
|
+
}
|
|
1739
|
+
get config$() {
|
|
1740
|
+
return this._config;
|
|
1741
|
+
}
|
|
1742
|
+
get config() {
|
|
1743
|
+
return this._config.defer();
|
|
1744
|
+
}
|
|
1745
|
+
get state() {
|
|
1746
|
+
return {
|
|
1747
|
+
filepath: this._filepath,
|
|
1748
|
+
config: this.config
|
|
1749
|
+
};
|
|
1750
|
+
}
|
|
1751
|
+
}
|
|
1752
|
+
|
|
1753
|
+
var config_service = /*#__PURE__*/Object.freeze({
|
|
1754
|
+
__proto__: null,
|
|
1755
|
+
ConfigService: ConfigService
|
|
1756
|
+
});
|
|
1757
|
+
|
|
1758
|
+
/**
|
|
1759
|
+
* Adds configuration arguments and loading.
|
|
1760
|
+
*/ function withConfig(parser) {
|
|
1761
|
+
return parser.option('config-file', {
|
|
1762
|
+
alias: 'c',
|
|
1763
|
+
type: 'string',
|
|
1764
|
+
description: 'Configuration file'
|
|
1765
|
+
}).middleware((args)=>startSpan({
|
|
1766
|
+
name: 'config',
|
|
1767
|
+
op: 'cli.middleware'
|
|
1768
|
+
}, async ()=>{
|
|
1769
|
+
const configService = inject$(ConfigService, asyncScope$());
|
|
1770
|
+
if (args.configFile) {
|
|
1771
|
+
await configService.loadConfig(args.configFile);
|
|
1772
|
+
} else {
|
|
1773
|
+
await configService.searchConfig();
|
|
1774
|
+
}
|
|
1775
|
+
}));
|
|
1776
|
+
}
|
|
1777
|
+
|
|
1778
|
+
const LEVEL_COLORS = {
|
|
1779
|
+
[LogLevel.debug]: 'grey',
|
|
1780
|
+
[LogLevel.verbose]: 'blue',
|
|
1781
|
+
[LogLevel.info]: 'reset',
|
|
1782
|
+
[LogLevel.warning]: 'yellow',
|
|
1783
|
+
[LogLevel.error]: 'red'
|
|
1784
|
+
};
|
|
1785
|
+
const logColor = defineQuickFormat((level)=>LEVEL_COLORS[level])(qprop('level'));
|
|
1786
|
+
const logFormat = qwrap(chalkTemplateStderr).fun`#?:${qprop('label')}{grey [${q$}]} ?#{${logColor} ${qprop('message')} {grey +${qLogDelay(qarg())}}#?:${qerror(qprop('error'))}${os.EOL}${q$}?#}`;
|
|
1787
|
+
|
|
1788
|
+
/**
|
|
1789
|
+
* Adds logger related arguments.
|
|
1790
|
+
*/ function withLogger(parser) {
|
|
1791
|
+
return parser.option('verbose', {
|
|
1792
|
+
alias: 'v',
|
|
1793
|
+
default: 'info',
|
|
1794
|
+
type: 'count',
|
|
1795
|
+
description: 'Set verbosity level',
|
|
1796
|
+
coerce: (cnt)=>VERBOSITY_LEVEL[Math.min(cnt, 2)]
|
|
1797
|
+
}).middleware((args)=>startSpan({
|
|
1798
|
+
name: 'logger',
|
|
1799
|
+
op: 'cli.middleware'
|
|
1800
|
+
}, ()=>{
|
|
1801
|
+
const logLevel = args.verbose ? LogLevel[args.verbose] : LogLevel.info;
|
|
1802
|
+
const logGateway = inject$(LogGateway);
|
|
1803
|
+
flow$(inject$(LOGGER), filter$((log)=>log.level >= logLevel), logDelay$(), logGateway);
|
|
1804
|
+
logGateway.connect('console', toStderr(logFormat));
|
|
1805
|
+
}));
|
|
1806
|
+
}
|
|
1807
|
+
// Utils
|
|
1808
|
+
const VERBOSITY_LEVEL = {
|
|
1809
|
+
1: 'verbose',
|
|
1810
|
+
2: 'debug'
|
|
1811
|
+
};
|
|
1812
|
+
|
|
1813
|
+
// Utils
|
|
1814
|
+
function cliParser() {
|
|
1815
|
+
return pipe$(yargs().scriptName('jill').version(version).demandCommand().recommendCommands(), withLogger, withConfig);
|
|
1816
|
+
}
|
|
1817
|
+
|
|
1818
|
+
function commandName(module) {
|
|
1819
|
+
if (!module.command) {
|
|
1820
|
+
return '[unknown]';
|
|
1821
|
+
}
|
|
1822
|
+
if (typeof module.command === 'string') {
|
|
1823
|
+
return module.command;
|
|
1824
|
+
}
|
|
1825
|
+
return module.command[0];
|
|
1826
|
+
}
|
|
1827
|
+
|
|
1828
|
+
function command(module) {
|
|
1829
|
+
const name = commandName(module);
|
|
1830
|
+
const handler = trace(module.handler, {
|
|
1831
|
+
name: commandName(module),
|
|
1832
|
+
op: 'cli.handler'
|
|
1833
|
+
});
|
|
1834
|
+
return (parser)=>parser.command({
|
|
1835
|
+
...module,
|
|
1836
|
+
async handler (args) {
|
|
1837
|
+
const activeSpan = getActiveSpan();
|
|
1838
|
+
if (activeSpan) {
|
|
1839
|
+
updateSpanName(getRootSpan(activeSpan), `jill ${name}`);
|
|
1840
|
+
}
|
|
1841
|
+
await handler(args);
|
|
1842
|
+
}
|
|
1843
|
+
});
|
|
1844
|
+
}
|
|
1845
|
+
|
|
1846
|
+
function jobCommandPlan(module, job$) {
|
|
1847
|
+
if ('prepare' in module) {
|
|
1848
|
+
const prepare = trace(module.prepare, {
|
|
1849
|
+
name: commandName(module),
|
|
1850
|
+
op: 'cli.prepare'
|
|
1851
|
+
});
|
|
1852
|
+
return command({
|
|
1853
|
+
...module,
|
|
1854
|
+
builder (base) {
|
|
1855
|
+
const parser = withPlanMode(base);
|
|
1856
|
+
if (module.builder) {
|
|
1857
|
+
return module.builder(parser);
|
|
1858
|
+
} else {
|
|
1859
|
+
return parser;
|
|
1860
|
+
}
|
|
1861
|
+
},
|
|
1862
|
+
async handler (args) {
|
|
1863
|
+
job$.mutate(await prepare(args) ?? null);
|
|
1864
|
+
}
|
|
1865
|
+
});
|
|
1866
|
+
} else {
|
|
1867
|
+
return command({
|
|
1868
|
+
...module,
|
|
1869
|
+
handler: ()=>undefined
|
|
1870
|
+
});
|
|
1871
|
+
}
|
|
1872
|
+
}
|
|
1873
|
+
// Utils
|
|
1874
|
+
function withPlanMode(parser) {
|
|
1875
|
+
return parser.option('plan', {
|
|
1876
|
+
type: 'boolean',
|
|
1877
|
+
default: false,
|
|
1878
|
+
describe: 'Only prints tasks to be run'
|
|
1879
|
+
}).option('plan-mode', {
|
|
1880
|
+
type: 'string',
|
|
1881
|
+
desc: 'Plan output mode',
|
|
1882
|
+
choices: [
|
|
1883
|
+
'json',
|
|
1884
|
+
'list'
|
|
1885
|
+
],
|
|
1886
|
+
default: 'list'
|
|
1887
|
+
});
|
|
1888
|
+
}
|
|
1889
|
+
|
|
1890
|
+
function jobCommandExecute(module) {
|
|
1891
|
+
const prepare = trace(module.prepare, {
|
|
1892
|
+
name: commandName(module),
|
|
1893
|
+
op: 'cli.prepare'
|
|
1894
|
+
});
|
|
1895
|
+
const execute = module.execute && trace(module.execute, {
|
|
1896
|
+
name: commandName(module),
|
|
1897
|
+
op: 'cli.execute'
|
|
1898
|
+
});
|
|
1899
|
+
return command({
|
|
1900
|
+
...module,
|
|
1901
|
+
builder (base) {
|
|
1902
|
+
const parser = withPlanMode(base);
|
|
1903
|
+
if (module.builder) {
|
|
1904
|
+
return module.builder(parser);
|
|
1905
|
+
} else {
|
|
1906
|
+
return parser;
|
|
1907
|
+
}
|
|
1908
|
+
},
|
|
1909
|
+
async handler (args) {
|
|
1910
|
+
const job = await prepare(args) ?? null;
|
|
1911
|
+
if (!job) {
|
|
1912
|
+
const logger = inject$(LOGGER);
|
|
1913
|
+
logger.warning('No task found');
|
|
1914
|
+
process$1.exitCode = 1;
|
|
1915
|
+
return;
|
|
1916
|
+
}
|
|
1917
|
+
if (args.plan) {
|
|
1918
|
+
const { jobPlan } = await traceImport('printPlan', ()=>import('./job-plan.js'));
|
|
1919
|
+
jobPlan(job);
|
|
1920
|
+
} else {
|
|
1921
|
+
if (execute) {
|
|
1922
|
+
await execute(args, job);
|
|
1923
|
+
} else {
|
|
1924
|
+
const { JobCommandExecuteInk } = await traceImport('JobCommandExecuteInk', ()=>import('./job-command-execute.ink.js'));
|
|
1925
|
+
await JobCommandExecuteInk({
|
|
1926
|
+
job,
|
|
1927
|
+
verbose: [
|
|
1928
|
+
'verbose',
|
|
1929
|
+
'debug'
|
|
1930
|
+
].includes(args.verbose)
|
|
1931
|
+
});
|
|
1932
|
+
}
|
|
1933
|
+
}
|
|
1934
|
+
}
|
|
1935
|
+
});
|
|
1936
|
+
}
|
|
1937
|
+
|
|
1938
|
+
// Bootstrap
|
|
1939
|
+
const argv = hideBin(process$1.argv);
|
|
1940
|
+
const parser = pipe$(cliParser(), jobCommandExecute(command$5), jobCommandExecute(command$4), command(command$3), jobCommandExecute(command$2), command(command$1));
|
|
1941
|
+
void startSpan({
|
|
1942
|
+
name: 'jill',
|
|
1943
|
+
op: 'cli.main',
|
|
1944
|
+
startTime: 0,
|
|
1945
|
+
attributes: {
|
|
1946
|
+
'cli.argv': argv
|
|
1947
|
+
}
|
|
1948
|
+
}, async ()=>{
|
|
1949
|
+
try {
|
|
1950
|
+
startInactiveSpan({
|
|
1951
|
+
name: 'bootstrap',
|
|
1952
|
+
op: 'cli.bootstrap',
|
|
1953
|
+
startTime: 0
|
|
1954
|
+
}).end();
|
|
1955
|
+
return await parser.wrap(parser.terminalWidth()).fail((msg, err)=>{
|
|
1956
|
+
const logger = inject$(LOGGER);
|
|
1957
|
+
if (msg) {
|
|
1958
|
+
logger.error(msg);
|
|
1959
|
+
} else if (err instanceof ClientError) {
|
|
1960
|
+
logger.error(err.message);
|
|
1961
|
+
} else {
|
|
1962
|
+
captureException(err, {
|
|
1963
|
+
tags: {
|
|
1964
|
+
handled: false
|
|
1965
|
+
}
|
|
1966
|
+
});
|
|
1967
|
+
logger.error(err.message);
|
|
1968
|
+
}
|
|
1969
|
+
process$1.exitCode = 1;
|
|
1970
|
+
}).parseAsync(argv);
|
|
1971
|
+
} catch {
|
|
1972
|
+
// Already handled
|
|
1973
|
+
}
|
|
1974
|
+
});
|
|
1975
|
+
|
|
1976
|
+
export { CWD as C, LOGGER as L, SCHEDULER as S, ConfigService as a, cliParser as b, capitalize as c, command$5 as d, command$4 as e, command$3 as f, command$2 as g, command$1 as h, instrument as i, jobCommandPlan as j, logFormat as l };
|
|
46
1977
|
//# sourceMappingURL=main.js.map
|