@laurence79/wireit 0.14.13-shared-cache.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +1062 -0
  3. package/bin/wireit.js +9 -0
  4. package/lib/analyzer.js +1600 -0
  5. package/lib/caching/cache.js +7 -0
  6. package/lib/caching/github-actions-cache.js +832 -0
  7. package/lib/caching/local-cache.js +78 -0
  8. package/lib/caching/shared-cache.js +256 -0
  9. package/lib/cli-options.js +495 -0
  10. package/lib/cli.js +177 -0
  11. package/lib/config.js +18 -0
  12. package/lib/error.js +160 -0
  13. package/lib/event.js +7 -0
  14. package/lib/execution/base.js +108 -0
  15. package/lib/execution/no-command.js +32 -0
  16. package/lib/execution/service.js +1017 -0
  17. package/lib/execution/standard.js +683 -0
  18. package/lib/executor.js +249 -0
  19. package/lib/fingerprint.js +164 -0
  20. package/lib/ide.js +583 -0
  21. package/lib/language-server.js +135 -0
  22. package/lib/logging/combination-logger.js +41 -0
  23. package/lib/logging/debug-logger.js +43 -0
  24. package/lib/logging/logger.js +38 -0
  25. package/lib/logging/metrics-logger.js +108 -0
  26. package/lib/logging/quiet/run-tracker.js +597 -0
  27. package/lib/logging/quiet/stack-map.js +41 -0
  28. package/lib/logging/quiet/writeover-line.js +197 -0
  29. package/lib/logging/quiet-logger.js +78 -0
  30. package/lib/logging/simple-logger.js +296 -0
  31. package/lib/logging/watch-logger.js +81 -0
  32. package/lib/script-child-process.js +270 -0
  33. package/lib/util/ast.js +71 -0
  34. package/lib/util/async-cache.js +24 -0
  35. package/lib/util/copy.js +120 -0
  36. package/lib/util/deferred.js +35 -0
  37. package/lib/util/delete.js +120 -0
  38. package/lib/util/dispose.js +16 -0
  39. package/lib/util/fs.js +258 -0
  40. package/lib/util/glob.js +255 -0
  41. package/lib/util/line-monitor.js +69 -0
  42. package/lib/util/manifest.js +31 -0
  43. package/lib/util/optimize-mkdirs.js +55 -0
  44. package/lib/util/package-json-reader.js +61 -0
  45. package/lib/util/package-json.js +179 -0
  46. package/lib/util/script-data-dir.js +19 -0
  47. package/lib/util/shuffle.js +16 -0
  48. package/lib/util/unreachable.js +12 -0
  49. package/lib/util/windows.js +87 -0
  50. package/lib/util/worker-pool.js +61 -0
  51. package/lib/watcher.js +396 -0
  52. package/package.json +470 -0
  53. package/schema.json +132 -0
  54. package/wireit.svg +1 -0
@@ -0,0 +1,495 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2022 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ import * as os from 'os';
7
+ import * as pathlib from 'path';
8
+ import { SimpleLogger } from './logging/simple-logger.js';
9
+ import { Console } from './logging/logger.js';
10
+ import { MetricsLogger } from './logging/metrics-logger.js';
11
+ import { QuietCiLogger, QuietLogger } from './logging/quiet-logger.js';
12
+ import * as fs from './util/fs.js';
13
+ import { unreachable } from './util/unreachable.js';
14
+ export const packageDir = await (async () => {
15
+ // Recent versions of npm, and node --run, set environment variables to tell
16
+ // us the current package.json.
17
+ const packageJsonPath = process.env.npm_package_json ?? process.env.NODE_RUN_PACKAGE_JSON_PATH;
18
+ if (packageJsonPath) {
19
+ return pathlib.dirname(packageJsonPath);
20
+ }
21
+ // Older versions of npm, as well as yarn and pnpm, don't set this variable,
22
+ // so we have to find the nearest package.json by walking up the filesystem.
23
+ let maybePackageDir = process.cwd();
24
+ while (true) {
25
+ try {
26
+ await fs.stat(pathlib.join(maybePackageDir, 'package.json'));
27
+ return maybePackageDir;
28
+ }
29
+ catch (error) {
30
+ const { code } = error;
31
+ if (code !== 'ENOENT') {
32
+ throw code;
33
+ }
34
+ }
35
+ const parent = pathlib.dirname(maybePackageDir);
36
+ if (parent === maybePackageDir) {
37
+ // Reached the root of the filesystem, no package.json.
38
+ return undefined;
39
+ }
40
+ maybePackageDir = parent;
41
+ }
42
+ })();
43
+ export const getOptions = async () => {
44
+ // This environment variable is set by npm, yarn, and pnpm, and tells us which
45
+ // script is running.
46
+ const scriptName = process.env.npm_lifecycle_event ?? process.env['NODE_RUN_SCRIPT_NAME'];
47
+ // We need to handle "npx wireit" as a special case, because it sets
48
+ // "npm_lifecycle_event" to "npx". The "npm_execpath" will be "npx-cli.js",
49
+ // though, so we use that combination to detect this special case.
50
+ const launchedWithNpx = scriptName === 'npx' && process.env.npm_command === 'exec';
51
+ if (!packageDir || !scriptName || launchedWithNpx) {
52
+ const detail = [];
53
+ if (!packageDir) {
54
+ detail.push('Wireit could not find a package.json.');
55
+ }
56
+ if (!scriptName) {
57
+ detail.push('Wireit could not identify the script to run.');
58
+ }
59
+ if (launchedWithNpx) {
60
+ detail.push('Launching Wireit with npx is not supported.');
61
+ }
62
+ return {
63
+ ok: false,
64
+ error: {
65
+ type: 'failure',
66
+ reason: 'launched-incorrectly',
67
+ script: { packageDir: packageDir ?? process.cwd() },
68
+ detail: detail.join(' '),
69
+ },
70
+ };
71
+ }
72
+ const script = { packageDir, name: scriptName };
73
+ const numWorkersResult = (() => {
74
+ const workerString = process.env['WIREIT_PARALLEL'] ?? '';
75
+ // Many scripts will be IO blocked rather than CPU blocked, so running
76
+ // multiple scripts per CPU will help keep things moving.
77
+ const defaultValue = os.cpus().length * 2;
78
+ if (workerString.match(/^infinity$/i)) {
79
+ return { ok: true, value: Infinity };
80
+ }
81
+ if (workerString == null || workerString === '') {
82
+ return { ok: true, value: defaultValue };
83
+ }
84
+ const parsedInt = parseInt(workerString, 10);
85
+ if (Number.isNaN(parsedInt) || parsedInt <= 0) {
86
+ return {
87
+ ok: false,
88
+ error: {
89
+ reason: 'invalid-usage',
90
+ message: `Expected the WIREIT_PARALLEL env variable to be ` +
91
+ `a positive integer, got ${JSON.stringify(workerString)}`,
92
+ script,
93
+ type: 'failure',
94
+ },
95
+ };
96
+ }
97
+ return { ok: true, value: parsedInt };
98
+ })();
99
+ if (!numWorkersResult.ok) {
100
+ return numWorkersResult;
101
+ }
102
+ const cacheResult = (() => {
103
+ // All shared-cache config is namespaced WIREIT_CACHE_SHARED_* so it can't be
104
+ // read as applying to other backends.
105
+ const sharedDirRaw = process.env['WIREIT_CACHE_SHARED_DIR'];
106
+ const sharedReadonlyRaw = process.env['WIREIT_CACHE_SHARED_READONLY'];
107
+ const sharedVarsSet = sharedDirRaw !== undefined || sharedReadonlyRaw !== undefined;
108
+ const str = process.env['WIREIT_CACHE'];
109
+ let mode;
110
+ if (str === undefined) {
111
+ // The CI variable is a convention that is automatically set by GitHub
112
+ // Actions [0], Travis [1], and other CI (continuous integration)
113
+ // providers.
114
+ //
115
+ // [0] https://docs.github.com/en/actions/learn-github-actions/environment-variables#default-environment-variables
116
+ // [1] https://docs.travis-ci.com/user/environment-variables/#default-environment-variables
117
+ //
118
+ // If we're on CI, we don't want "local" caching, because anything we
119
+ // store locally will be lost when the VM shuts down.
120
+ //
121
+ // We also don't want "github", because (even if we also detected that
122
+ // we're specifically on GitHub) we should be cautious about using up
123
+ // storage quota, and instead require opt-in via WIREIT_CACHE=github.
124
+ const ci = process.env['CI'] === 'true';
125
+ mode = ci ? 'none' : 'local';
126
+ }
127
+ else if (str === 'local' ||
128
+ str === 'github' ||
129
+ str === 'none' ||
130
+ str === 'shared') {
131
+ mode = str;
132
+ }
133
+ else {
134
+ return {
135
+ ok: false,
136
+ error: {
137
+ reason: 'invalid-usage',
138
+ message: `Expected the WIREIT_CACHE env variable to be ` +
139
+ `"local", "github", "shared", or "none", got ${JSON.stringify(str)}`,
140
+ script,
141
+ type: 'failure',
142
+ },
143
+ };
144
+ }
145
+ if (mode !== 'shared') {
146
+ // A WIREIT_CACHE_SHARED_* variable set while the shared cache isn't
147
+ // selected is unambiguous misuse (the namespaced name makes that clear),
148
+ // so we error rather than silently ignoring it.
149
+ if (sharedVarsSet) {
150
+ return {
151
+ ok: false,
152
+ error: {
153
+ reason: 'invalid-usage',
154
+ message: `The WIREIT_CACHE_SHARED_* environment variables only apply ` +
155
+ `when WIREIT_CACHE=shared, but WIREIT_CACHE is ${str === undefined ? 'unset' : JSON.stringify(str)}.`,
156
+ script,
157
+ type: 'failure',
158
+ },
159
+ };
160
+ }
161
+ return { ok: true, value: { mode } };
162
+ }
163
+ let readonly = false;
164
+ if (sharedReadonlyRaw !== undefined) {
165
+ // Accept exactly "true"/"false"; error on anything else.
166
+ if (sharedReadonlyRaw === 'true') {
167
+ readonly = true;
168
+ }
169
+ else if (sharedReadonlyRaw === 'false') {
170
+ readonly = false;
171
+ }
172
+ else {
173
+ return {
174
+ ok: false,
175
+ error: {
176
+ reason: 'invalid-usage',
177
+ message: `Expected the WIREIT_CACHE_SHARED_READONLY env variable to be ` +
178
+ `"true" or "false", got ${JSON.stringify(sharedReadonlyRaw)}`,
179
+ script,
180
+ type: 'failure',
181
+ },
182
+ };
183
+ }
184
+ }
185
+ const dir = sharedDirRaw !== undefined && sharedDirRaw !== ''
186
+ ? sharedDirRaw
187
+ : pathlib.join(os.homedir(), '.wireit', 'cache');
188
+ return { ok: true, value: { mode, shared: { dir, readonly } } };
189
+ })();
190
+ if (!cacheResult.ok) {
191
+ return cacheResult;
192
+ }
193
+ const failureModeResult = (() => {
194
+ const str = process.env['WIREIT_FAILURES'];
195
+ if (!str) {
196
+ return { ok: true, value: 'no-new' };
197
+ }
198
+ if (str === 'no-new' || str === 'continue' || str === 'kill') {
199
+ return { ok: true, value: str };
200
+ }
201
+ return {
202
+ ok: false,
203
+ error: {
204
+ reason: 'invalid-usage',
205
+ message: `Expected the WIREIT_FAILURES env variable to be ` +
206
+ `"no-new", "continue", or "kill", got ${JSON.stringify(str)}`,
207
+ script,
208
+ type: 'failure',
209
+ },
210
+ };
211
+ })();
212
+ if (!failureModeResult.ok) {
213
+ return failureModeResult;
214
+ }
215
+ const agent = getNpmUserAgent();
216
+ const console = new Console(process.stdout, process.stderr);
217
+ const packageRoot = packageDir ?? process.cwd();
218
+ const loggerResult = (() => {
219
+ const str = process.env['WIREIT_LOGGER'];
220
+ if (!str) {
221
+ if (process.env.CI || !process.stdout.isTTY) {
222
+ return { ok: true, value: new QuietCiLogger(packageRoot, console) };
223
+ }
224
+ return { ok: true, value: new QuietLogger(packageRoot, console) };
225
+ }
226
+ if (str === 'quiet') {
227
+ return { ok: true, value: new QuietLogger(packageRoot, console) };
228
+ }
229
+ if (str === 'quiet-ci') {
230
+ return { ok: true, value: new QuietCiLogger(packageRoot, console) };
231
+ }
232
+ if (str === 'simple') {
233
+ return { ok: true, value: new SimpleLogger(packageRoot, console) };
234
+ }
235
+ if (str === 'metrics') {
236
+ return { ok: true, value: new MetricsLogger(packageRoot, console) };
237
+ }
238
+ return {
239
+ ok: false,
240
+ error: {
241
+ reason: 'invalid-usage',
242
+ message: `Expected the WIREIT_LOGGER env variable to be ` +
243
+ `"quiet", "simple", or "metrics", got ${JSON.stringify(str)}`,
244
+ script,
245
+ type: 'failure',
246
+ },
247
+ };
248
+ })();
249
+ if (!loggerResult.ok) {
250
+ return loggerResult;
251
+ }
252
+ let logger = loggerResult.value;
253
+ if (process.env['WIREIT_DEBUG_LOG_FILE']) {
254
+ const [{ DebugLogger }, { CombinationLogger }] = await Promise.all([
255
+ import('./logging/debug-logger.js'),
256
+ import('./logging/combination-logger.js'),
257
+ ]);
258
+ const debugLogStream = await fs.createWriteStream(process.env['WIREIT_DEBUG_LOG_FILE']);
259
+ const debugLogConsole = new Console(debugLogStream, debugLogStream, true);
260
+ logger = new CombinationLogger([logger, new DebugLogger(packageRoot, debugLogConsole)], console);
261
+ }
262
+ return {
263
+ ok: true,
264
+ value: {
265
+ script,
266
+ numWorkers: numWorkersResult.value,
267
+ cache: cacheResult.value.mode,
268
+ sharedCache: cacheResult.value.shared,
269
+ failureMode: failureModeResult.value,
270
+ agent,
271
+ logger,
272
+ ...getArgvOptions(script, agent),
273
+ },
274
+ };
275
+ };
276
+ /**
277
+ * Get options that are set as command-line arguments.
278
+ */
279
+ function getArgvOptions(script, agent) {
280
+ // The way command-line arguments are handled in npm, yarn, and pnpm are all
281
+ // different. Our goal here is for `<agent> --watch -- --extra` to behave the
282
+ // same in all agents.
283
+ switch (agent) {
284
+ case 'npm': {
285
+ // npm 6.14.17
286
+ // - Arguments before the "--" in "--flag" style turn into "npm_config_<flag>"
287
+ // environment variables.
288
+ // - Arguments before the "--" in "plain" style go to argv.
289
+ // - Arguments after "--" go to argv.
290
+ // - The "npm_config_argv" environment variable contains full details as JSON.
291
+ //
292
+ // npm 8.11.0
293
+ // - Like npm 6, except there is no "npm_config_argv" environment variable.
294
+ //
295
+ // npm 12 (https://github.com/npm/cli/pull/8071)
296
+ // - "npm run build --watch" is rejected as an unrecognized npm argument
297
+ // instead of being silently turned into "npm_config_watch", so we also
298
+ // accept WIREIT_WATCH=true as an escape hatch (see #1346).
299
+ return {
300
+ watch: process.env['npm_config_watch'] !== undefined
301
+ ? readWatchConfigFromEnv()
302
+ : readWatchConfigFromWireitWatchEnv(),
303
+ extraArgs: process.argv.slice(2),
304
+ };
305
+ }
306
+ case 'nodeRun': {
307
+ return parseRemainingArgs(process.argv.slice(2));
308
+ }
309
+ case 'yarnClassic': {
310
+ // yarn 1.22.18
311
+ // - If there is no "--", all arguments go to argv.
312
+ // - If there is a "--", arguments in "--flag" style before it are eaten,
313
+ // arguments in "plain" style before it go to argv, and all arguments after
314
+ // it go to argv. Also a warning is emitted saying "In a future version, any
315
+ // explicit "--" will be forwarded as-is to the scripts."
316
+ // - The "npm_config_argv" environment variable contains full details as JSON,
317
+ // but unlike npm 6, it reflects the first script in a chain of scripts, instead
318
+ // of the last.
319
+ return parseRemainingArgs(findRemainingArgsFromNpmConfigArgv(script, agent));
320
+ }
321
+ case 'yarnBerry':
322
+ case 'pnpm': {
323
+ // yarn 3.2.1
324
+ // - Arguments before the script name are yarn arguments and error if unknown.
325
+ // - Arguments after the script name go to argv.
326
+ // pnpm 7.1.7
327
+ // - Arguments before the script name are pnpm arguments and error if unknown.
328
+ // - Arguments after the script name go to argv.
329
+ return parseRemainingArgs(process.argv.slice(2));
330
+ }
331
+ default: {
332
+ throw new Error(`Unhandled npm agent: ${unreachable(agent)}`);
333
+ }
334
+ }
335
+ }
336
+ /**
337
+ * Try to find the npm user agent being used. If we can't detect it, assume npm.
338
+ */
339
+ function getNpmUserAgent() {
340
+ if (process.env['NODE_RUN_SCRIPT_NAME'] !== undefined) {
341
+ return 'nodeRun';
342
+ }
343
+ const userAgent = process.env['npm_config_user_agent'];
344
+ if (userAgent !== undefined) {
345
+ const match = userAgent.match(/^(npm|yarn|pnpm)\//);
346
+ if (match !== null) {
347
+ if (match[1] === 'yarn') {
348
+ return /^yarn\/[01]\./.test(userAgent) ? 'yarnClassic' : 'yarnBerry';
349
+ }
350
+ return match[1];
351
+ }
352
+ }
353
+ console.error('⚠️ Wireit could not identify the npm user agent, ' +
354
+ 'assuming it behaves like npm. ' +
355
+ 'Arguments may not be interpreted correctly.');
356
+ return 'npm';
357
+ }
358
+ /**
359
+ * Parses the `npm_config_argv` environment variable to find the command-line
360
+ * arguments that follow the main arguments. For example, given the result of
361
+ * `"yarn run build --watch -- --extra"`, return `["--watch", "--", "--extra"]`.
362
+ */
363
+ function findRemainingArgsFromNpmConfigArgv(script, agent) {
364
+ const configArgvStr = process.env['npm_config_argv'];
365
+ if (!configArgvStr) {
366
+ console.error('⚠️ The "npm_config_argv" environment variable was not set. ' +
367
+ 'Arguments may not be interpreted correctly.');
368
+ return [];
369
+ }
370
+ let configArgv;
371
+ try {
372
+ configArgv = JSON.parse(configArgvStr);
373
+ }
374
+ catch {
375
+ console.error('⚠️ Wireit could not parse the "npm_config_argv" ' +
376
+ 'environment variable as JSON. ' +
377
+ 'Arguments may not be interpreted correctly.');
378
+ return [];
379
+ }
380
+ // Since the "remain" and "cooked" arrays are unreliable in Yarn, the only
381
+ // reliable way to find the remaining args is to look for where the script
382
+ // name first appeared in the "original" array.
383
+ const scriptNameIdx = configArgv.original.indexOf(script.name);
384
+ if (scriptNameIdx === -1) {
385
+ // We're probably dealing with a recursive situation where one yarn 1.x
386
+ // script is calling another, such as `"watch": "yarn run build --watch"`.
387
+ //
388
+ // Usually we would handle this situation by looking at the original raw
389
+ // arguments provided by the "npm_config_argv" environment variable, but in
390
+ // the recursive case we can't do that, because due to
391
+ // https://github.com/yarnpkg/yarn/issues/8905 that variable reflects the
392
+ // first script in the chain, instead of the current script (unlike npm 6.x
393
+ // which does it correctly).
394
+ //
395
+ // So instead, we'll log a warning and at least handle the case where there
396
+ // is no "--" argument. If there is no "--" argument, then argv will contain
397
+ // all arguments. However, if there was a "--" argument, then all arguments
398
+ // before the "--" are lost, and argv only contains the arguments after the
399
+ // "--" (e.g. `yarn run build --watch` works fine, but `yarn run build
400
+ // --watch -- --extra` loses the `--watch`).
401
+ console.error('⚠️ Wireit could not find the script name in ' +
402
+ 'the "npm_config_argv" environment variable. ' +
403
+ 'Arguments may not be interpreted correctly. ' +
404
+ (agent === 'yarnClassic'
405
+ ? `See https://github.com/yarnpkg/yarn/issues/8905, ` +
406
+ `and please consider upgrading to yarn 3.x or switching to npm.`
407
+ : ''));
408
+ return process.argv.slice(2);
409
+ }
410
+ return configArgv.original.slice(scriptNameIdx + 1);
411
+ }
412
+ /**
413
+ * Given a list of remaining command-line arguments (the arguments after e.g.
414
+ * "yarn run build"), parse out the arguments that are Wireit options, warn
415
+ * about any unrecognized options, and return everything after a `"--"` argument
416
+ * as `extraArgs` to be passed down to the script.
417
+ */
418
+ function parseRemainingArgs(args) {
419
+ let watch = false;
420
+ let extraArgs = [];
421
+ const unrecognized = [];
422
+ for (let i = 0; i < args.length; i++) {
423
+ const arg = args[i];
424
+ if (arg === '--') {
425
+ extraArgs = args.slice(i + 1);
426
+ break;
427
+ }
428
+ else if (arg === '--watch') {
429
+ watch = readWatchConfigFromEnv();
430
+ }
431
+ else {
432
+ unrecognized.push(arg);
433
+ }
434
+ }
435
+ if (unrecognized.length > 0) {
436
+ console.error(`⚠️ Unrecognized Wireit argument(s): ` +
437
+ unrecognized.map((arg) => JSON.stringify(arg)).join(', ') +
438
+ `. To pass arguments to the script, use a double-dash, ` +
439
+ `e.g. "npm run build -- --extra".`);
440
+ }
441
+ // Also accept WIREIT_WATCH as an escape hatch (matches the npm branch behavior,
442
+ // useful for yarn/pnpm/node --run wrappers that scrub --watch from argv).
443
+ if (!watch) {
444
+ watch = readWatchConfigFromWireitWatchEnv();
445
+ }
446
+ return {
447
+ watch,
448
+ extraArgs,
449
+ };
450
+ }
451
+ const DEFAULT_WATCH_STRATEGY = { strategy: 'event' };
452
+ const DEFAULT_WATCH_INTERVAL = 500;
453
+ function readWatchConfigFromWireitWatchEnv() {
454
+ return process.env['WIREIT_WATCH']?.toLowerCase() === 'true'
455
+ ? readWatchConfigFromEnv()
456
+ : false;
457
+ }
458
+ /**
459
+ * Interpret the WIREIT_WATCH_* environment variables.
460
+ */
461
+ function readWatchConfigFromEnv() {
462
+ switch (process.env['WIREIT_WATCH_STRATEGY']) {
463
+ case 'event':
464
+ case '':
465
+ case undefined: {
466
+ return DEFAULT_WATCH_STRATEGY;
467
+ }
468
+ case 'poll': {
469
+ let interval = DEFAULT_WATCH_INTERVAL;
470
+ const intervalStr = process.env['WIREIT_WATCH_POLL_MS'];
471
+ if (intervalStr) {
472
+ const parsed = Number(intervalStr);
473
+ if (Number.isNaN(parsed) || parsed <= 0) {
474
+ console.error(`⚠️ Expected WIREIT_WATCH_POLL_MS to be a positive integer, ` +
475
+ `got ${JSON.stringify(intervalStr)}. Using default interval of ` +
476
+ `${DEFAULT_WATCH_INTERVAL}ms.`);
477
+ }
478
+ else {
479
+ interval = parsed;
480
+ }
481
+ }
482
+ return {
483
+ strategy: 'poll',
484
+ interval,
485
+ };
486
+ }
487
+ default: {
488
+ console.error(`⚠️ Unrecognized WIREIT_WATCH_STRATEGY: ` +
489
+ `${JSON.stringify(process.env['WIREIT_WATCH_STRATEGY'])}. ` +
490
+ `Using default strategy of ${DEFAULT_WATCH_STRATEGY.strategy}.`);
491
+ return DEFAULT_WATCH_STRATEGY;
492
+ }
493
+ }
494
+ }
495
+ //# sourceMappingURL=cli-options.js.map
package/lib/cli.js ADDED
@@ -0,0 +1,177 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2022 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {
7
+ if (value !== null && value !== void 0) {
8
+ if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
9
+ var dispose, inner;
10
+ if (async) {
11
+ if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
12
+ dispose = value[Symbol.asyncDispose];
13
+ }
14
+ if (dispose === void 0) {
15
+ if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
16
+ dispose = value[Symbol.dispose];
17
+ if (async) inner = dispose;
18
+ }
19
+ if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
20
+ if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } };
21
+ env.stack.push({ value: value, dispose: dispose, async: async });
22
+ }
23
+ else if (async) {
24
+ env.stack.push({ async: true });
25
+ }
26
+ return value;
27
+ };
28
+ var __disposeResources = (this && this.__disposeResources) || (function (SuppressedError) {
29
+ return function (env) {
30
+ function fail(e) {
31
+ env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
32
+ env.hasError = true;
33
+ }
34
+ var r, s = 0;
35
+ function next() {
36
+ while (r = env.stack.pop()) {
37
+ try {
38
+ if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);
39
+ if (r.dispose) {
40
+ var result = r.dispose.call(r.value);
41
+ if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); });
42
+ }
43
+ else s |= 1;
44
+ }
45
+ catch (e) {
46
+ fail(e);
47
+ }
48
+ }
49
+ if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();
50
+ if (env.hasError) throw env.error;
51
+ }
52
+ return next();
53
+ };
54
+ })(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
55
+ var e = new Error(message);
56
+ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
57
+ });
58
+ import { Analyzer } from './analyzer.js';
59
+ import { getOptions, packageDir } from './cli-options.js';
60
+ import { Executor } from './executor.js';
61
+ import { SimpleLogger } from './logging/simple-logger.js';
62
+ import { Console } from './logging/logger.js';
63
+ import { unreachable } from './util/unreachable.js';
64
+ import { WorkerPool } from './util/worker-pool.js';
65
+ const run = async (options) => {
66
+ const env_1 = { stack: [], error: void 0, hasError: false };
67
+ try {
68
+ const logger = __addDisposableResource(env_1, options.logger, false);
69
+ const workerPool = new WorkerPool(options.numWorkers);
70
+ let cache;
71
+ switch (options.cache) {
72
+ case 'local': {
73
+ // Import dynamically so that we import fewer unnecessary modules.
74
+ const { LocalCache } = await import('./caching/local-cache.js');
75
+ cache = new LocalCache();
76
+ break;
77
+ }
78
+ case 'github': {
79
+ const { GitHubActionsCache } = await import('./caching/github-actions-cache.js');
80
+ const cacheResult = await GitHubActionsCache.create(logger);
81
+ if (cacheResult.ok) {
82
+ cache = cacheResult.value;
83
+ }
84
+ else {
85
+ cache = undefined;
86
+ console.warn('⚠️ Error initializing GitHub cache. Caching is disabled for this run');
87
+ logger.log({
88
+ script: options.script,
89
+ ...cacheResult.error,
90
+ });
91
+ }
92
+ break;
93
+ }
94
+ case 'shared': {
95
+ const { SharedCache } = await import('./caching/shared-cache.js');
96
+ // cli-options guarantees sharedCache is set whenever the mode is 'shared'.
97
+ const sharedConfig = options.sharedCache;
98
+ // create() emits its own startup health line and returns undefined if the
99
+ // cache is unavailable (in which case caching is disabled, but the build
100
+ // still proceeds).
101
+ cache = await SharedCache.create(sharedConfig.dir, { readonly: sharedConfig.readonly }, logger);
102
+ break;
103
+ }
104
+ case 'none': {
105
+ cache = undefined;
106
+ break;
107
+ }
108
+ default: {
109
+ throw new Error(`Unhandled cache: ${unreachable(options.cache)}`);
110
+ }
111
+ }
112
+ if (options.watch) {
113
+ const { Watcher } = await import('./watcher.js');
114
+ const watcher = new Watcher(options.script, options.extraArgs, logger, workerPool, cache, options.failureMode, options.agent, options.watch);
115
+ process.on('SIGINT', () => {
116
+ watcher.abort();
117
+ });
118
+ process.on('SIGTERM', () => {
119
+ watcher.abort();
120
+ });
121
+ await watcher.watch();
122
+ return { ok: true, value: undefined };
123
+ }
124
+ else {
125
+ const analyzer = new Analyzer(options.agent, logger);
126
+ const { config } = await analyzer.analyze(options.script, options.extraArgs);
127
+ if (!config.ok) {
128
+ return config;
129
+ }
130
+ const executor = new Executor(config.value, logger, workerPool, cache, options.failureMode, undefined, false);
131
+ process.on('SIGINT', () => {
132
+ executor.abort();
133
+ });
134
+ process.on('SIGTERM', () => {
135
+ executor.abort();
136
+ });
137
+ const { persistentServices, errors } = await executor.execute();
138
+ if (persistentServices.size > 0) {
139
+ for (const service of persistentServices.values()) {
140
+ const result = await service.terminated;
141
+ if (!result.ok) {
142
+ errors.push(result.error);
143
+ }
144
+ }
145
+ }
146
+ logger.printMetrics();
147
+ return errors.length === 0
148
+ ? { ok: true, value: undefined }
149
+ : { ok: false, error: errors };
150
+ }
151
+ }
152
+ catch (e_1) {
153
+ env_1.error = e_1;
154
+ env_1.hasError = true;
155
+ }
156
+ finally {
157
+ __disposeResources(env_1);
158
+ }
159
+ };
160
+ const optionsResult = await getOptions();
161
+ if (!optionsResult.ok) {
162
+ // if we can't figure out our options, we can't figure out what logger
163
+ // we should use here, so just use the default logger.
164
+ const console = new Console(process.stderr, process.stderr);
165
+ const logger = new SimpleLogger(packageDir ?? process.cwd(), console);
166
+ logger.log(optionsResult.error);
167
+ process.exit(1);
168
+ }
169
+ const options = optionsResult.value;
170
+ const result = await run(options);
171
+ if (!result.ok) {
172
+ for (const failure of result.error) {
173
+ options.logger.log(failure);
174
+ }
175
+ process.exitCode = 1;
176
+ }
177
+ //# sourceMappingURL=cli.js.map
package/lib/config.js ADDED
@@ -0,0 +1,18 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2022 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ /**
7
+ * Convert a {@link ScriptReference} to a string that can be used as a key in a
8
+ * Set, Map, etc.
9
+ */
10
+ export const scriptReferenceToString = ({ packageDir, name, }) => JSON.stringify([packageDir, name]);
11
+ /**
12
+ * Inverse of {@link scriptReferenceToString}.
13
+ */
14
+ export const stringToScriptReference = (str) => {
15
+ const [packageDir, name] = JSON.parse(str);
16
+ return { packageDir, name };
17
+ };
18
+ //# sourceMappingURL=config.js.map