@visulima/task-runner 1.0.0-alpha.7 → 1.0.0-alpha.9
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/CHANGELOG.md +48 -0
- package/README.md +3 -1
- package/dist/index.d.ts +3093 -49
- package/dist/index.js +29 -19
- package/dist/packem_chunks/index.js +5593 -0
- package/dist/packem_shared/{Cache-CWaX_c8U.js → Cache-CbhoA268.js} +151 -10
- package/dist/packem_shared/{FileAccessTracker-CQ5Ot7Hd.js → FileAccessTracker-D8zIURPU.js} +1 -1
- package/dist/packem_shared/{FingerprintManager-CV7U4f4f.js → FingerprintManager-78DjwWQ4.js} +1 -1
- package/dist/packem_shared/HttpRemoteCache-BTXUBH7t.js +290 -0
- package/dist/packem_shared/INPUT_URI_SCHEMES-DRm76YI5.js +69 -0
- package/dist/packem_shared/{IncrementalFileHasher-BRS76-mb.js → IncrementalFileHasher-BBhVK491.js} +1 -1
- package/dist/packem_shared/ReapiRemoteCache-vgRxDMmu.js +1012 -0
- package/dist/packem_shared/{TaskOrchestrator-rf45vW5c.js → TaskOrchestrator-CdRaQhTO.js} +100 -11
- package/dist/packem_shared/{TrackedTaskExecutor-CFPpQfXF.js → TrackedTaskExecutor-CWSMfHAW.js} +2 -2
- package/dist/packem_shared/V2_ROOT-DKBLxKo4.js +14 -0
- package/dist/packem_shared/actionDigestForTaskHash-BRE-9MT6.js +121 -0
- package/dist/packem_shared/archive-CnggHWb-.js +152 -0
- package/dist/packem_shared/{buildForwardDependencyMap-DLPgKEto.js → buildForwardDependencyMap-0BJFMMPv.js} +1 -2
- package/dist/packem_shared/{collectFiles-ClXHnHhg.js → collectFiles-cc1gokGU.js} +2 -1
- package/dist/packem_shared/{computeTaskHash-DYqfrDGq.js → computeTaskHash-DHoBJ_-V.js} +10 -4
- package/dist/packem_shared/containsBlob-CwGB0a_q.js +125 -0
- package/dist/packem_shared/{createTaskGraph-B7nH0kY_.js → createTaskGraph-Bwl4hwAf.js} +23 -2
- package/dist/packem_shared/{defaultTaskRunner-Cp7jCmIl.js → defaultTaskRunner-BaX4ZbFv.js} +58 -15
- package/dist/packem_shared/{detectFrameworks-CeFzKE6J.js → detectFrameworks-D7nyTc-o.js} +1 -1
- package/dist/packem_shared/{detectScriptShell-CR-xXKA4.js → detectScriptShell-CzxCM9-t.js} +1 -1
- package/dist/packem_shared/digestBuffer-CPdI2E1d.js +48 -0
- package/dist/packem_shared/{expandArguments-0AwD2BIA.js → expandArguments-Ba-hHYff.js} +2 -1
- package/dist/packem_shared/expandTokensInString-Bb7nYehP.js +47 -0
- package/dist/packem_shared/{extractPackageName-BllKetnz.js → extractPackageName-CMHjqGj_.js} +2 -3
- package/dist/packem_shared/{generateRunSummary-BE1jnQ3H.js → generateRunSummary-Bah7CFay.js} +1 -1
- package/dist/packem_shared/getCurrentBranch-DVNikt0P.js +156 -0
- package/dist/packem_shared/getMainWorktreeRoot-iBqToQJ4.js +114 -0
- package/dist/packem_shared/{parseCommands-D-IgF8Zh.js → parseCommands-DDdIxaH5.js} +8 -3
- package/dist/packem_shared/resolveCacheMode-CsmHT_0o.js +21 -0
- package/dist/packem_shared/{runConcurrently-CmfC4r-f.js → runConcurrently-BCGQ9fJl.js} +1 -1
- package/dist/packem_shared/shell-quote-DWJJbt21.js +3 -0
- package/dist/packem_shared/{utils-zO0ZRgtf.js → utils-Bmnj-H2J.js} +4 -1
- package/index.js +556 -723
- package/package.json +26 -13
- package/dist/affected.d.ts +0 -82
- package/dist/archive.d.ts +0 -38
- package/dist/cache.d.ts +0 -138
- package/dist/chrome-trace.d.ts +0 -53
- package/dist/command-parser/expand-arguments.d.ts +0 -11
- package/dist/command-parser/expand-shortcut.d.ts +0 -15
- package/dist/command-parser/expand-wildcard.d.ts +0 -13
- package/dist/command-parser/index.d.ts +0 -18
- package/dist/command-parser/strip-quotes.d.ts +0 -6
- package/dist/concurrent-fallback.d.ts +0 -16
- package/dist/concurrent.d.ts +0 -23
- package/dist/default-task-runner.d.ts +0 -44
- package/dist/detect-shell.d.ts +0 -19
- package/dist/file-access-tracker.d.ts +0 -59
- package/dist/fingerprint.d.ts +0 -54
- package/dist/flow-controllers/index.d.ts +0 -7
- package/dist/flow-controllers/input-handler.d.ts +0 -44
- package/dist/flow-controllers/log-timings.d.ts +0 -18
- package/dist/flow-controllers/restart-process.d.ts +0 -21
- package/dist/flow-controllers/teardown.d.ts +0 -22
- package/dist/framework-inference.d.ts +0 -35
- package/dist/graph-visualizer.d.ts +0 -74
- package/dist/incremental-hasher.d.ts +0 -76
- package/dist/life-cycle.d.ts +0 -38
- package/dist/lockfile-hasher.d.ts +0 -73
- package/dist/log-reporter.d.ts +0 -34
- package/dist/native-binding.d.ts +0 -106
- package/dist/output-resolver.d.ts +0 -20
- package/dist/packem_shared/RemoteCache-DSU3lc87.js +0 -219
- package/dist/packem_shared/archive-UQHAnZUa.js +0 -102
- package/dist/project-constraints.d.ts +0 -9
- package/dist/remote-cache.d.ts +0 -100
- package/dist/run-summary.d.ts +0 -111
- package/dist/task-graph-utils.d.ts +0 -39
- package/dist/task-graph.d.ts +0 -22
- package/dist/task-hasher.d.ts +0 -104
- package/dist/task-orchestrator.d.ts +0 -38
- package/dist/task-scheduler.d.ts +0 -41
- package/dist/terminal-buffer.d.ts +0 -29
- package/dist/tracked-executor.d.ts +0 -46
- package/dist/types.d.ts +0 -757
- package/dist/utils.d.ts +0 -39
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import { d as resolveTaskCwd, a as createFailureResult, e as createXxh3Hasher } from './utils-
|
|
1
|
+
import { d as resolveTaskCwd, a as createFailureResult, e as createXxh3Hasher } from './utils-Bmnj-H2J.js';
|
|
2
2
|
import { resolve, join } from '@visulima/path';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
3
|
+
import { retrieveByTaskHash, storeByTaskHash } from './actionDigestForTaskHash-BRE-9MT6.js';
|
|
4
|
+
import { FingerprintManager } from './FingerprintManager-78DjwWQ4.js';
|
|
5
|
+
import { generateRunSummary, writeLastRunSummary, writeRunSummary } from './generateRunSummary-Bah7CFay.js';
|
|
6
|
+
import { computeTaskHash } from './computeTaskHash-DHoBJ_-V.js';
|
|
7
|
+
import { TrackedTaskExecutor } from './TrackedTaskExecutor-CWSMfHAW.js';
|
|
8
|
+
import { getCurrentBranch, evaluateWhen, explainWhen } from './getCurrentBranch-DVNikt0P.js';
|
|
7
9
|
|
|
8
10
|
const hashFingerprint = (fingerprint) => {
|
|
9
11
|
const hash = createXxh3Hasher();
|
|
@@ -25,6 +27,20 @@ const hashFingerprint = (fingerprint) => {
|
|
|
25
27
|
}
|
|
26
28
|
return hash.digest();
|
|
27
29
|
};
|
|
30
|
+
const detectWarnings = (patterns, output) => {
|
|
31
|
+
if (!patterns || patterns.length === 0 || !output) {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
for (const source of patterns) {
|
|
35
|
+
try {
|
|
36
|
+
if (new RegExp(source).test(output)) {
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
} catch {
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return false;
|
|
43
|
+
};
|
|
28
44
|
const createDeferred = () => {
|
|
29
45
|
let resolve2;
|
|
30
46
|
const promise = new Promise((r) => {
|
|
@@ -49,11 +65,14 @@ class TaskOrchestrator {
|
|
|
49
65
|
#cacheDiagnostics;
|
|
50
66
|
#resolveCommand;
|
|
51
67
|
#remoteCache;
|
|
68
|
+
#onRemoteUploadError;
|
|
52
69
|
#dryRun;
|
|
53
70
|
#summarize;
|
|
54
71
|
#taskGraph;
|
|
55
72
|
#results = /* @__PURE__ */ new Map();
|
|
56
73
|
#startTime;
|
|
74
|
+
#alwaysTasks;
|
|
75
|
+
#whenContext;
|
|
57
76
|
/** Tracks in-flight task promises so the execution loop can await them */
|
|
58
77
|
#inFlightTasks = /* @__PURE__ */ new Map();
|
|
59
78
|
/** Deferred that gets resolved whenever a task completes, waking the loop */
|
|
@@ -74,10 +93,15 @@ class TaskOrchestrator {
|
|
|
74
93
|
this.#cacheDiagnostics = options.cacheDiagnostics ?? false;
|
|
75
94
|
this.#resolveCommand = options.resolveCommand ?? void 0;
|
|
76
95
|
this.#remoteCache = options.remoteCache ?? void 0;
|
|
96
|
+
this.#onRemoteUploadError = options.onRemoteUploadError ?? void 0;
|
|
77
97
|
this.#dryRun = options.dryRun ?? false;
|
|
78
98
|
this.#summarize = options.summarize ?? false;
|
|
79
99
|
this.#taskGraph = options.taskGraph ?? void 0;
|
|
80
100
|
this.#startTime = Date.now();
|
|
101
|
+
this.#alwaysTasks = options.alwaysTasks ?? [];
|
|
102
|
+
this.#whenContext = options.whenContext ?? {
|
|
103
|
+
branch: getCurrentBranch(options.workspaceRoot)
|
|
104
|
+
};
|
|
81
105
|
if (this.#autoFingerprint) {
|
|
82
106
|
this.#fingerprintManager = new FingerprintManager(options.workspaceRoot);
|
|
83
107
|
this.#trackedExecutor = new TrackedTaskExecutor(options.workspaceRoot);
|
|
@@ -96,6 +120,9 @@ class TaskOrchestrator {
|
|
|
96
120
|
process.on("SIGTERM", signalHandler);
|
|
97
121
|
try {
|
|
98
122
|
await this.#executionLoop();
|
|
123
|
+
if (this.#alwaysTasks.length > 0 && !this.#aborted) {
|
|
124
|
+
await this.#runAlwaysTasks();
|
|
125
|
+
}
|
|
99
126
|
} finally {
|
|
100
127
|
process.removeListener("SIGINT", signalHandler);
|
|
101
128
|
process.removeListener("SIGTERM", signalHandler);
|
|
@@ -110,6 +137,40 @@ class TaskOrchestrator {
|
|
|
110
137
|
}
|
|
111
138
|
return this.#results;
|
|
112
139
|
}
|
|
140
|
+
/**
|
|
141
|
+
* Runs the configured `always: true` tasks sequentially after the
|
|
142
|
+
* main task graph completes. Each task's `when` clause is still
|
|
143
|
+
* honoured. Failures are recorded but never propagate — the whole
|
|
144
|
+
* point of an always-task is to fire regardless of upstream state.
|
|
145
|
+
*
|
|
146
|
+
* Always-tasks deliberately bypass `#processTask` /
|
|
147
|
+
* `#processTaskWithFingerprint`, which means **no cache lookup
|
|
148
|
+
* and no fingerprint check**. Cleanup / teardown / notification
|
|
149
|
+
* tasks are expected to run every invocation; serving them from
|
|
150
|
+
* cache would defeat the purpose.
|
|
151
|
+
*/
|
|
152
|
+
async #runAlwaysTasks() {
|
|
153
|
+
for (const task of this.#alwaysTasks) {
|
|
154
|
+
this.#lifeCycle.scheduleTask?.(task);
|
|
155
|
+
this.#lifeCycle.startTasks?.([task]);
|
|
156
|
+
let result;
|
|
157
|
+
if (this.#shouldSkipForWhen(task)) {
|
|
158
|
+
result = this.#whenSkipResult(task);
|
|
159
|
+
} else {
|
|
160
|
+
const startTime = Date.now();
|
|
161
|
+
try {
|
|
162
|
+
result = await this.#executeTask(task, startTime);
|
|
163
|
+
} catch (error) {
|
|
164
|
+
result = createFailureResult(task, error, startTime);
|
|
165
|
+
this.#results.set(task.id, result);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
this.#lifeCycle.endTasks?.([result]);
|
|
169
|
+
if (result.terminalOutput) {
|
|
170
|
+
this.#lifeCycle.printTaskTerminalOutput?.(result.task, result.status, result.terminalOutput);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
113
174
|
async #executionLoop() {
|
|
114
175
|
while (!this.#scheduler.isComplete() && !this.#aborted) {
|
|
115
176
|
const batch = this.#scheduler.getNextBatch();
|
|
@@ -130,7 +191,8 @@ class TaskOrchestrator {
|
|
|
130
191
|
}
|
|
131
192
|
this.#lifeCycle.startTasks?.(batch);
|
|
132
193
|
for (const task of batch) {
|
|
133
|
-
const
|
|
194
|
+
const startPromise = this.#shouldSkipForWhen(task) ? Promise.resolve(this.#whenSkipResult(task)) : this.#autoFingerprint ? this.#processTaskWithFingerprint(task) : this.#processTask(task);
|
|
195
|
+
const taskPromise = startPromise.catch((error) => {
|
|
134
196
|
const errorResult = createFailureResult(task, error, Date.now());
|
|
135
197
|
this.#results.set(task.id, errorResult);
|
|
136
198
|
return errorResult;
|
|
@@ -169,7 +231,7 @@ class TaskOrchestrator {
|
|
|
169
231
|
return this.#applyCachedResult(task, cachedResult, startTime);
|
|
170
232
|
}
|
|
171
233
|
if (this.#remoteCache) {
|
|
172
|
-
const retrieved = await this.#remoteCache
|
|
234
|
+
const retrieved = await retrieveByTaskHash(this.#remoteCache, hash, this.#cache.cacheDirectory);
|
|
173
235
|
if (retrieved) {
|
|
174
236
|
const remoteCached = await this.#cache.get(hash);
|
|
175
237
|
if (remoteCached) {
|
|
@@ -182,7 +244,7 @@ class TaskOrchestrator {
|
|
|
182
244
|
}
|
|
183
245
|
const result = await this.#executeTask(task, startTime);
|
|
184
246
|
if (result.code === 0 && task.cache !== false && task.hash && this.#remoteCache) {
|
|
185
|
-
this.#remoteCache
|
|
247
|
+
storeByTaskHash(this.#remoteCache, task.hash, this.#cache.cacheDirectory, this.#onRemoteUploadError).catch(() => {
|
|
186
248
|
});
|
|
187
249
|
}
|
|
188
250
|
return result;
|
|
@@ -217,7 +279,7 @@ class TaskOrchestrator {
|
|
|
217
279
|
return this.#executeTaskWithTracking(task, startTime);
|
|
218
280
|
}
|
|
219
281
|
async #applyCachedResult(task, cachedResult, startTime) {
|
|
220
|
-
const restored = await this.#cache.restoreOutputs(cachedResult.hash, task.outputs);
|
|
282
|
+
const restored = await this.#cache.restoreOutputs(cachedResult.hash, task.outputs, task.cacheRestore);
|
|
221
283
|
const status = restored ? "local-cache" : "local-cache-kept-existing";
|
|
222
284
|
const result = {
|
|
223
285
|
code: cachedResult.code,
|
|
@@ -236,16 +298,19 @@ class TaskOrchestrator {
|
|
|
236
298
|
captureOutput: this.#captureOutput,
|
|
237
299
|
cwd: resolveTaskCwd(this.#workspaceRoot, task)
|
|
238
300
|
});
|
|
301
|
+
const hadWarnings = code === 0 && detectWarnings(task.warningPattern, terminalOutput);
|
|
239
302
|
const result = {
|
|
240
303
|
code,
|
|
241
304
|
endTime: Date.now(),
|
|
305
|
+
hadWarnings: hadWarnings || void 0,
|
|
242
306
|
startTime,
|
|
243
307
|
status: code === 0 ? "success" : "failure",
|
|
244
308
|
task,
|
|
245
309
|
terminalOutput
|
|
246
310
|
};
|
|
247
311
|
this.#results.set(task.id, result);
|
|
248
|
-
|
|
312
|
+
const skipCacheOnWarning = hadWarnings && task.cacheOnWarning === false;
|
|
313
|
+
if (code === 0 && task.cache !== false && task.hash && !skipCacheOnWarning) {
|
|
249
314
|
const modified = await this.#detectSelfModifiedInputs(task);
|
|
250
315
|
if (modified.length > 0) {
|
|
251
316
|
result.selfModified = true;
|
|
@@ -336,16 +401,19 @@ class TaskOrchestrator {
|
|
|
336
401
|
this.#untrackedEnvVars
|
|
337
402
|
);
|
|
338
403
|
}
|
|
404
|
+
const hadWarnings = code === 0 && detectWarnings(task.warningPattern, terminalOutput);
|
|
339
405
|
const result = {
|
|
340
406
|
code,
|
|
341
407
|
endTime: Date.now(),
|
|
408
|
+
hadWarnings: hadWarnings || void 0,
|
|
342
409
|
startTime,
|
|
343
410
|
status: code === 0 ? "success" : "failure",
|
|
344
411
|
task,
|
|
345
412
|
terminalOutput
|
|
346
413
|
};
|
|
347
414
|
this.#results.set(task.id, result);
|
|
348
|
-
|
|
415
|
+
const skipCacheOnWarning = hadWarnings && task.cacheOnWarning === false;
|
|
416
|
+
if (code === 0 && task.cache !== false && fingerprint && !skipCacheOnWarning) {
|
|
349
417
|
const modified = this.#detectSelfModifiedFingerprint(fingerprint);
|
|
350
418
|
const emptyFingerprintReason = this.#describeEmptyFingerprint(fingerprint, usedRealTracker, trackerAccessCount);
|
|
351
419
|
if (modified.length > 0) {
|
|
@@ -413,6 +481,27 @@ class TaskOrchestrator {
|
|
|
413
481
|
this.#results.set(task.id, result);
|
|
414
482
|
return result;
|
|
415
483
|
}
|
|
484
|
+
#shouldSkipForWhen(task) {
|
|
485
|
+
if (!task.when) {
|
|
486
|
+
return false;
|
|
487
|
+
}
|
|
488
|
+
return !evaluateWhen(task.when, this.#whenContext);
|
|
489
|
+
}
|
|
490
|
+
#whenSkipResult(task) {
|
|
491
|
+
const reason = explainWhen(task.when, this.#whenContext);
|
|
492
|
+
const startTime = Date.now();
|
|
493
|
+
this.#lifeCycle.printWhenSkip?.(task, reason);
|
|
494
|
+
const result = {
|
|
495
|
+
code: 0,
|
|
496
|
+
endTime: startTime,
|
|
497
|
+
startTime,
|
|
498
|
+
status: "skipped",
|
|
499
|
+
task,
|
|
500
|
+
terminalOutput: reason ? `Skipped: ${reason}` : "Skipped by when clause"
|
|
501
|
+
};
|
|
502
|
+
this.#results.set(task.id, result);
|
|
503
|
+
return result;
|
|
504
|
+
}
|
|
416
505
|
}
|
|
417
506
|
|
|
418
507
|
export { TaskOrchestrator };
|
package/dist/packem_shared/{TrackedTaskExecutor-CFPpQfXF.js → TrackedTaskExecutor-CWSMfHAW.js}
RENAMED
|
@@ -27,8 +27,8 @@ const {
|
|
|
27
27
|
rm
|
|
28
28
|
} = __cjs_getBuiltinModule("node:fs/promises");
|
|
29
29
|
import { join } from '@visulima/path';
|
|
30
|
-
import { FileAccessTracker, generatePreloadScript } from './FileAccessTracker-
|
|
31
|
-
import { u as uniqueId } from './utils-
|
|
30
|
+
import { FileAccessTracker, generatePreloadScript } from './FileAccessTracker-D8zIURPU.js';
|
|
31
|
+
import { u as uniqueId } from './utils-Bmnj-H2J.js';
|
|
32
32
|
|
|
33
33
|
class TrackedTaskExecutor {
|
|
34
34
|
#tracker;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { join } from '@visulima/path';
|
|
2
|
+
|
|
3
|
+
const V2_ROOT = "v2";
|
|
4
|
+
const V2_CAS = "cas";
|
|
5
|
+
const V2_AC = "ac";
|
|
6
|
+
const V2_INDEX = "task-hash-index";
|
|
7
|
+
const V2_TMP = "tmp";
|
|
8
|
+
const shard = (hash) => hash.slice(0, 2);
|
|
9
|
+
const casBlobPath = (root, hash) => join(root, V2_ROOT, V2_CAS, shard(hash), hash);
|
|
10
|
+
const acEntryPath = (root, actionHash) => join(root, V2_ROOT, V2_AC, shard(actionHash), `${actionHash}.json`);
|
|
11
|
+
const taskHashIndexPath = (root, taskHash) => join(root, V2_ROOT, V2_INDEX, shard(taskHash), taskHash);
|
|
12
|
+
const tmpDirectory = (root) => join(root, V2_ROOT, V2_TMP);
|
|
13
|
+
|
|
14
|
+
export { V2_AC, V2_CAS, V2_INDEX, V2_ROOT, V2_TMP, acEntryPath, casBlobPath, shard, taskHashIndexPath, tmpDirectory };
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { createRequire as __cjs_createRequire } from "node:module";
|
|
2
|
+
|
|
3
|
+
const __cjs_require = __cjs_createRequire(import.meta.url);
|
|
4
|
+
|
|
5
|
+
const __cjs_getProcess = typeof globalThis !== "undefined" && typeof globalThis.process !== "undefined" ? globalThis.process : process;
|
|
6
|
+
|
|
7
|
+
const __cjs_getBuiltinModule = (module) => {
|
|
8
|
+
// Check if we're in Node.js and version supports getBuiltinModule
|
|
9
|
+
if (typeof __cjs_getProcess !== "undefined" && __cjs_getProcess.versions && __cjs_getProcess.versions.node) {
|
|
10
|
+
const [major, minor] = __cjs_getProcess.versions.node.split(".").map(Number);
|
|
11
|
+
// Node.js 20.16.0+ and 22.3.0+
|
|
12
|
+
if (major > 22 || (major === 22 && minor >= 3) || (major === 20 && minor >= 16)) {
|
|
13
|
+
return __cjs_getProcess.getBuiltinModule(module);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
// Fallback to createRequire
|
|
17
|
+
return __cjs_require(module);
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const {
|
|
21
|
+
createHash,
|
|
22
|
+
randomUUID
|
|
23
|
+
} = __cjs_getBuiltinModule("node:crypto");
|
|
24
|
+
const {
|
|
25
|
+
createReadStream
|
|
26
|
+
} = __cjs_getBuiltinModule("node:fs");
|
|
27
|
+
const {
|
|
28
|
+
mkdir,
|
|
29
|
+
rm,
|
|
30
|
+
stat
|
|
31
|
+
} = __cjs_getBuiltinModule("node:fs/promises");
|
|
32
|
+
import { join } from '@visulima/path';
|
|
33
|
+
import { e as extractTarGz, c as createTarGz } from './archive-CnggHWb-.js';
|
|
34
|
+
|
|
35
|
+
const TARBALL_OUTPUT_PATH = "vis-entry.tar.gz";
|
|
36
|
+
const actionDigestForTaskHash = (taskHash) => {
|
|
37
|
+
const key = `vis-task:${taskHash}`;
|
|
38
|
+
return {
|
|
39
|
+
hash: createHash("sha256").update(Buffer.from(key, "utf8")).digest("hex"),
|
|
40
|
+
sizeBytes: 0
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
const containsByTaskHash = async (backend, taskHash) => {
|
|
44
|
+
try {
|
|
45
|
+
return await backend.containsAction(actionDigestForTaskHash(taskHash));
|
|
46
|
+
} catch {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
const retrieveByTaskHash = async (backend, taskHash, localCacheDirectory) => {
|
|
51
|
+
const action = await backend.retrieveAction(actionDigestForTaskHash(taskHash));
|
|
52
|
+
if (action === null || action.outputFiles.length === 0) {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
const tarballEntry = action.outputFiles[0];
|
|
56
|
+
if (!tarballEntry) {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
const entryDirectory = join(localCacheDirectory, taskHash);
|
|
60
|
+
const archivePath = join(localCacheDirectory, `.download-${taskHash}-${randomUUID()}.tar.gz`);
|
|
61
|
+
try {
|
|
62
|
+
await mkdir(localCacheDirectory, { recursive: true });
|
|
63
|
+
const fetched = await backend.fetchBlob(tarballEntry.digest, archivePath);
|
|
64
|
+
if (!fetched) {
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
await mkdir(entryDirectory, { recursive: true });
|
|
68
|
+
await extractTarGz(archivePath, entryDirectory);
|
|
69
|
+
return true;
|
|
70
|
+
} catch {
|
|
71
|
+
await rm(entryDirectory, { force: true, recursive: true }).catch(() => {
|
|
72
|
+
});
|
|
73
|
+
return false;
|
|
74
|
+
} finally {
|
|
75
|
+
await rm(archivePath, { force: true }).catch(() => {
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
const storeByTaskHash = async (backend, taskHash, localCacheDirectory, onUploadError) => {
|
|
80
|
+
const entryDirectory = join(localCacheDirectory, taskHash);
|
|
81
|
+
const archivePath = join(localCacheDirectory, `.upload-${taskHash}-${randomUUID()}.tar.gz`);
|
|
82
|
+
try {
|
|
83
|
+
try {
|
|
84
|
+
await stat(join(entryDirectory, ".commit"));
|
|
85
|
+
} catch {
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
try {
|
|
89
|
+
await createTarGz(entryDirectory, archivePath);
|
|
90
|
+
const blobDigest = await digestFile(archivePath);
|
|
91
|
+
const result = {
|
|
92
|
+
exitCode: 0,
|
|
93
|
+
outputDirectories: [],
|
|
94
|
+
outputFiles: [{ digest: blobDigest, isExecutable: false, path: TARBALL_OUTPUT_PATH }]
|
|
95
|
+
};
|
|
96
|
+
const blob = {
|
|
97
|
+
digest: blobDigest,
|
|
98
|
+
open: () => Promise.resolve(createReadStream(archivePath))
|
|
99
|
+
};
|
|
100
|
+
return await backend.storeAction(actionDigestForTaskHash(taskHash), result, [blob]);
|
|
101
|
+
} catch (error) {
|
|
102
|
+
onUploadError?.(taskHash, error);
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
} finally {
|
|
106
|
+
await rm(archivePath, { force: true }).catch(() => {
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
const digestFile = async (path) => {
|
|
111
|
+
const hash = createHash("sha256");
|
|
112
|
+
let sizeBytes = 0;
|
|
113
|
+
for await (const chunk of createReadStream(path)) {
|
|
114
|
+
const buffer = chunk;
|
|
115
|
+
hash.update(buffer);
|
|
116
|
+
sizeBytes += buffer.byteLength;
|
|
117
|
+
}
|
|
118
|
+
return { hash: hash.digest("hex"), sizeBytes };
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
export { actionDigestForTaskHash, containsByTaskHash, retrieveByTaskHash, storeByTaskHash };
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { createRequire as __cjs_createRequire } from "node:module";
|
|
2
|
+
|
|
3
|
+
const __cjs_require = __cjs_createRequire(import.meta.url);
|
|
4
|
+
|
|
5
|
+
const __cjs_getProcess = typeof globalThis !== "undefined" && typeof globalThis.process !== "undefined" ? globalThis.process : process;
|
|
6
|
+
|
|
7
|
+
const __cjs_getBuiltinModule = (module) => {
|
|
8
|
+
// Check if we're in Node.js and version supports getBuiltinModule
|
|
9
|
+
if (typeof __cjs_getProcess !== "undefined" && __cjs_getProcess.versions && __cjs_getProcess.versions.node) {
|
|
10
|
+
const [major, minor] = __cjs_getProcess.versions.node.split(".").map(Number);
|
|
11
|
+
// Node.js 20.16.0+ and 22.3.0+
|
|
12
|
+
if (major > 22 || (major === 22 && minor >= 3) || (major === 20 && minor >= 16)) {
|
|
13
|
+
return __cjs_getProcess.getBuiltinModule(module);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
// Fallback to createRequire
|
|
17
|
+
return __cjs_require(module);
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const {
|
|
21
|
+
mkdir,
|
|
22
|
+
writeFile,
|
|
23
|
+
utimes,
|
|
24
|
+
readdir,
|
|
25
|
+
readFile,
|
|
26
|
+
stat
|
|
27
|
+
} = __cjs_getBuiltinModule("node:fs/promises");
|
|
28
|
+
const {
|
|
29
|
+
Readable
|
|
30
|
+
} = __cjs_getBuiltinModule("node:stream");
|
|
31
|
+
const {
|
|
32
|
+
pipeline
|
|
33
|
+
} = __cjs_getBuiltinModule("node:stream/promises");
|
|
34
|
+
const {
|
|
35
|
+
createGunzip,
|
|
36
|
+
createGzip,
|
|
37
|
+
createBrotliDecompress,
|
|
38
|
+
createBrotliCompress,
|
|
39
|
+
constants
|
|
40
|
+
} = __cjs_getBuiltinModule("node:zlib");
|
|
41
|
+
import { join, resolve, sep } from '@visulima/path';
|
|
42
|
+
import { parseTar, createTar } from 'nanotar';
|
|
43
|
+
|
|
44
|
+
const BROTLI_COMPRESS_OPTIONS = {
|
|
45
|
+
params: {
|
|
46
|
+
[constants.BROTLI_PARAM_MODE]: constants.BROTLI_MODE_TEXT,
|
|
47
|
+
[constants.BROTLI_PARAM_QUALITY]: 4
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
const collectEntries = async (sourceDirectory) => {
|
|
51
|
+
const entries = [];
|
|
52
|
+
const root = resolve(sourceDirectory);
|
|
53
|
+
const walk = async (absolute, relative) => {
|
|
54
|
+
const items = await readdir(absolute, { withFileTypes: true });
|
|
55
|
+
items.sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0);
|
|
56
|
+
for (const item of items) {
|
|
57
|
+
const childAbsolute = join(absolute, item.name);
|
|
58
|
+
const childRelative = relative === "" ? item.name : `${relative}/${item.name}`;
|
|
59
|
+
if (item.isDirectory()) {
|
|
60
|
+
await walk(childAbsolute, childRelative);
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
if (!item.isFile()) {
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
const [data, info] = await Promise.all([readFile(childAbsolute), stat(childAbsolute)]);
|
|
67
|
+
entries.push({
|
|
68
|
+
attrs: {
|
|
69
|
+
// eslint-disable-next-line no-bitwise -- POSIX file modes encode the rwx triplet in the low 12 bits; bitmask is the canonical extraction
|
|
70
|
+
mode: (info.mode & 4095).toString(8),
|
|
71
|
+
mtime: Math.floor(info.mtimeMs)
|
|
72
|
+
},
|
|
73
|
+
data,
|
|
74
|
+
name: childRelative
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
await walk(root, "");
|
|
79
|
+
return entries;
|
|
80
|
+
};
|
|
81
|
+
const safeJoinForExtract = (destinationDirectory, entryName) => {
|
|
82
|
+
if (entryName === "" || entryName === "." || entryName === "./") {
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
if (entryName.startsWith("/") || entryName.startsWith("\\") || /^[a-z]:[\\/]/i.test(entryName)) {
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
const resolvedDestination = resolve(destinationDirectory);
|
|
89
|
+
const resolvedEntry = resolve(resolvedDestination, entryName);
|
|
90
|
+
const prefix = resolvedDestination.endsWith(sep) ? resolvedDestination : `${resolvedDestination}${sep}`;
|
|
91
|
+
if (resolvedEntry !== resolvedDestination && !resolvedEntry.startsWith(prefix)) {
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
return resolvedEntry;
|
|
95
|
+
};
|
|
96
|
+
const writeTarEntries = async (entries, destinationDirectory, options = {}) => {
|
|
97
|
+
const preserveMtime = options.preserveMtime ?? true;
|
|
98
|
+
const preservePerms = options.preservePerms ?? true;
|
|
99
|
+
await mkdir(destinationDirectory, { recursive: true });
|
|
100
|
+
for (const entry of entries) {
|
|
101
|
+
if (entry.data === void 0) {
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
const writePath = safeJoinForExtract(destinationDirectory, entry.name);
|
|
105
|
+
if (writePath === null) {
|
|
106
|
+
throw new Error(`[task-runner] refusing to extract tar entry with unsafe path: ${entry.name}`);
|
|
107
|
+
}
|
|
108
|
+
await mkdir(join(writePath, ".."), { recursive: true });
|
|
109
|
+
const mode = preservePerms && entry.attrs?.mode !== void 0 ? Number.parseInt(entry.attrs.mode, 8) : void 0;
|
|
110
|
+
await writeFile(writePath, entry.data, mode === void 0 ? void 0 : { mode });
|
|
111
|
+
if (preserveMtime && entry.attrs?.mtime !== void 0) {
|
|
112
|
+
const mtime = new Date(entry.attrs.mtime * 1e3);
|
|
113
|
+
await utimes(writePath, mtime, mtime);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
const compressBuffer = async (buffer, transform, outputPath) => {
|
|
118
|
+
const { createWriteStream } = await import('node:fs');
|
|
119
|
+
await pipeline(Readable.from(Buffer.from(buffer)), transform, createWriteStream(outputPath));
|
|
120
|
+
};
|
|
121
|
+
const decompressBuffer = async (sourcePath, transform) => {
|
|
122
|
+
const { createReadStream } = await import('node:fs');
|
|
123
|
+
const chunks = [];
|
|
124
|
+
await pipeline(createReadStream(sourcePath), transform, async (source) => {
|
|
125
|
+
for await (const chunk of source) {
|
|
126
|
+
chunks.push(chunk);
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
return Buffer.concat(chunks);
|
|
130
|
+
};
|
|
131
|
+
const createTarGz = async (sourceDirectory, outputPath) => {
|
|
132
|
+
const entries = await collectEntries(sourceDirectory);
|
|
133
|
+
const tarBytes = createTar(entries);
|
|
134
|
+
await compressBuffer(tarBytes, createGzip(), outputPath);
|
|
135
|
+
};
|
|
136
|
+
const extractTarGz = async (archivePath, destinationDirectory, options) => {
|
|
137
|
+
const tarBuffer = await decompressBuffer(archivePath, createGunzip());
|
|
138
|
+
const entries = parseTar(tarBuffer);
|
|
139
|
+
await writeTarEntries(entries, destinationDirectory, options);
|
|
140
|
+
};
|
|
141
|
+
const createTarBrotli = async (sourceDirectory, outputPath) => {
|
|
142
|
+
const entries = await collectEntries(sourceDirectory);
|
|
143
|
+
const tarBytes = createTar(entries);
|
|
144
|
+
await compressBuffer(tarBytes, createBrotliCompress(BROTLI_COMPRESS_OPTIONS), outputPath);
|
|
145
|
+
};
|
|
146
|
+
const extractTarBrotli = async (archivePath, destinationDirectory, options) => {
|
|
147
|
+
const tarBuffer = await decompressBuffer(archivePath, createBrotliDecompress());
|
|
148
|
+
const entries = parseTar(tarBuffer);
|
|
149
|
+
await writeTarEntries(entries, destinationDirectory, options);
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
export { extractTarBrotli as a, createTarBrotli as b, createTarGz as c, extractTarGz as e };
|
|
@@ -33,8 +33,7 @@ const findProjectForFile = (filePath, projects) => {
|
|
|
33
33
|
let bestLength = 0;
|
|
34
34
|
for (const [name, config] of Object.entries(projects)) {
|
|
35
35
|
const { root } = config;
|
|
36
|
-
if ((filePath.startsWith(`${root}/`) || filePath === root) &&
|
|
37
|
-
root.length > bestLength) {
|
|
36
|
+
if ((filePath.startsWith(`${root}/`) || filePath === root) && root.length > bestLength) {
|
|
38
37
|
bestMatch = name;
|
|
39
38
|
bestLength = root.length;
|
|
40
39
|
}
|
|
@@ -18,5 +18,6 @@ const __cjs_getBuiltinModule = (module) => {
|
|
|
18
18
|
};
|
|
19
19
|
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
|
|
22
|
+
export { c as collectFiles, a as createFailureResult, h as hashFile, b as hashStrings, r as readPackageDeps, d as resolveTaskCwd, s as sortObjectKeys, u as uniqueId } from './utils-Bmnj-H2J.js';
|
|
22
23
|
import '@visulima/path';
|
|
@@ -24,11 +24,12 @@ const {
|
|
|
24
24
|
stat,
|
|
25
25
|
readFile
|
|
26
26
|
} = __cjs_getBuiltinModule("node:fs/promises");
|
|
27
|
-
import { b as hashStrings, s as sortObjectKeys, c as collectFiles, x as xxh3Hash, e as createXxh3Hasher } from './utils-
|
|
27
|
+
import { b as hashStrings, s as sortObjectKeys, c as collectFiles, x as xxh3Hash, e as createXxh3Hasher } from './utils-Bmnj-H2J.js';
|
|
28
28
|
import { join, resolve, relative } from '@visulima/path';
|
|
29
|
-
import { getFrameworkEnvVariables } from './detectFrameworks-
|
|
30
|
-
import { LockfileHasher } from './extractPackageName-
|
|
29
|
+
import { getFrameworkEnvVariables } from './detectFrameworks-D7nyTc-o.js';
|
|
30
|
+
import { LockfileHasher } from './extractPackageName-CMHjqGj_.js';
|
|
31
31
|
import { loadNativeBindings } from './isNativeAvailable-BpD28A6Z.js';
|
|
32
|
+
import { looksLikeInputUri, parseInputUri } from './INPUT_URI_SCHEMES-DRm76YI5.js';
|
|
32
33
|
|
|
33
34
|
const DEFAULT_GLOBAL_INPUTS = ["package-lock.json", "pnpm-lock.yaml", "yarn.lock", "tsconfig.base.json", "tsconfig.json", ".env"];
|
|
34
35
|
const IGNORED_DIRS = /* @__PURE__ */ new Set([".git", "coverage", "dist", "node_modules"]);
|
|
@@ -254,7 +255,12 @@ class InProcessTaskHasher {
|
|
|
254
255
|
const seen = /* @__PURE__ */ new Set();
|
|
255
256
|
for (const input of inputs) {
|
|
256
257
|
if (typeof input === "string") {
|
|
257
|
-
if (
|
|
258
|
+
if (looksLikeInputUri(input)) {
|
|
259
|
+
const parsed = parseInputUri(input);
|
|
260
|
+
if (parsed) {
|
|
261
|
+
result.push(parsed);
|
|
262
|
+
}
|
|
263
|
+
} else if (input.startsWith("{") || input.startsWith("!{")) {
|
|
258
264
|
result.push({ fileset: input });
|
|
259
265
|
} else if (input.startsWith("^")) {
|
|
260
266
|
continue;
|