@kitsi/cli 0.0.15 → 0.0.16
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/dist/bin/commands/cache/clear.js +1 -1
- package/dist/bin/commands/cache/clear.js.map +1 -1
- package/dist/bin/commands/cache/inspect.js +1 -1
- package/dist/bin/commands/cache/inspect.js.map +1 -1
- package/dist/bin/commands/cache/stats.js +1 -1
- package/dist/bin/commands/cache/stats.js.map +1 -1
- package/dist/bin/commands/graph.d.ts.map +1 -1
- package/dist/bin/commands/graph.js +1 -2
- package/dist/bin/commands/graph.js.map +1 -1
- package/dist/bin/commands/list.d.ts.map +1 -1
- package/dist/bin/commands/list.js +1 -1
- package/dist/bin/commands/list.js.map +1 -1
- package/dist/bin/commands/show.d.ts.map +1 -1
- package/dist/bin/commands/show.js +1 -1
- package/dist/bin/commands/show.js.map +1 -1
- package/dist/bin/commands/validate.d.ts.map +1 -1
- package/dist/bin/commands/validate.js +1 -1
- package/dist/bin/commands/validate.js.map +1 -1
- package/dist/bin/commands/watch.js +1 -1
- package/dist/bin/commands/watch.js.map +1 -1
- package/dist/bin/errors/index.d.ts +1 -59
- package/dist/bin/errors/index.d.ts.map +1 -1
- package/dist/bin/errors/index.js +1 -65
- package/dist/bin/errors/index.js.map +1 -1
- package/dist/bin/output/base.d.ts +25 -0
- package/dist/bin/output/base.d.ts.map +1 -0
- package/dist/bin/output/base.js +39 -0
- package/dist/bin/output/base.js.map +1 -0
- package/dist/bin/output/llm.d.ts +2 -20
- package/dist/bin/output/llm.d.ts.map +1 -1
- package/dist/bin/output/llm.js +2 -36
- package/dist/bin/output/llm.js.map +1 -1
- package/dist/bin/output/runner.d.ts +2 -19
- package/dist/bin/output/runner.d.ts.map +1 -1
- package/dist/bin/output/runner.js +2 -34
- package/dist/bin/output/runner.js.map +1 -1
- package/dist/bin/pipeline-authoring.d.ts +28 -62
- package/dist/bin/pipeline-authoring.d.ts.map +1 -1
- package/dist/bin/pipeline-authoring.js +26 -28
- package/dist/bin/pipeline-authoring.js.map +1 -1
- package/dist/bin/pipeline.d.ts +1 -1
- package/dist/bin/pipeline.d.ts.map +1 -1
- package/dist/bin/pipeline.js +1 -2
- package/dist/bin/pipeline.js.map +1 -1
- package/dist/bin/prompts/parameters.d.ts +1 -1
- package/dist/bin/prompts/parameters.d.ts.map +1 -1
- package/dist/bin/services/execution-runner.d.ts +1 -2
- package/dist/bin/services/execution-runner.d.ts.map +1 -1
- package/dist/bin/services/execution-runner.js +1 -1
- package/dist/bin/services/execution-runner.js.map +1 -1
- package/dist/bin/services/input-collector.d.ts +1 -2
- package/dist/bin/services/input-collector.d.ts.map +1 -1
- package/dist/bin/services/input-collector.js +1 -1
- package/dist/bin/services/input-collector.js.map +1 -1
- package/dist/bin/services/plan-loader.d.ts +1 -2
- package/dist/bin/services/plan-loader.d.ts.map +1 -1
- package/dist/bin/services/plan-loader.js +1 -3
- package/dist/bin/services/plan-loader.js.map +1 -1
- package/dist/index.d.ts +1 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -4
- package/dist/index.js.map +1 -1
- package/package.json +2 -3
- package/dist/dsl/analysis.d.ts +0 -17
- package/dist/dsl/analysis.d.ts.map +0 -1
- package/dist/dsl/analysis.js +0 -186
- package/dist/dsl/analysis.js.map +0 -1
- package/dist/dsl/image.d.ts +0 -14
- package/dist/dsl/image.d.ts.map +0 -1
- package/dist/dsl/image.js +0 -14
- package/dist/dsl/image.js.map +0 -1
- package/dist/dsl/index.d.ts +0 -21
- package/dist/dsl/index.d.ts.map +0 -1
- package/dist/dsl/index.js +0 -12
- package/dist/dsl/index.js.map +0 -1
- package/dist/dsl/kit.d.ts +0 -20
- package/dist/dsl/kit.d.ts.map +0 -1
- package/dist/dsl/kit.js +0 -34
- package/dist/dsl/kit.js.map +0 -1
- package/dist/dsl/module.d.ts +0 -4
- package/dist/dsl/module.d.ts.map +0 -1
- package/dist/dsl/module.js +0 -4
- package/dist/dsl/module.js.map +0 -1
- package/dist/dsl/plan-repository.d.ts +0 -10
- package/dist/dsl/plan-repository.d.ts.map +0 -1
- package/dist/dsl/plan-repository.js +0 -24
- package/dist/dsl/plan-repository.js.map +0 -1
- package/dist/dsl/plan.d.ts +0 -46
- package/dist/dsl/plan.d.ts.map +0 -1
- package/dist/dsl/plan.js +0 -95
- package/dist/dsl/plan.js.map +0 -1
- package/dist/dsl/registry.d.ts +0 -6
- package/dist/dsl/registry.d.ts.map +0 -1
- package/dist/dsl/registry.js +0 -4
- package/dist/dsl/registry.js.map +0 -1
- package/dist/dsl/service.d.ts +0 -24
- package/dist/dsl/service.d.ts.map +0 -1
- package/dist/dsl/service.js +0 -20
- package/dist/dsl/service.js.map +0 -1
- package/dist/dsl/step.d.ts +0 -10
- package/dist/dsl/step.d.ts.map +0 -1
- package/dist/dsl/step.js +0 -17
- package/dist/dsl/step.js.map +0 -1
- package/dist/dsl/task.d.ts +0 -38
- package/dist/dsl/task.d.ts.map +0 -1
- package/dist/dsl/task.js +0 -66
- package/dist/dsl/task.js.map +0 -1
- package/dist/dsl/types.d.ts +0 -12
- package/dist/dsl/types.d.ts.map +0 -1
- package/dist/dsl/types.js +0 -2
- package/dist/dsl/types.js.map +0 -1
- package/dist/dsl/utils.d.ts +0 -2
- package/dist/dsl/utils.d.ts.map +0 -1
- package/dist/dsl/utils.js +0 -7
- package/dist/dsl/utils.js.map +0 -1
- package/dist/engine/cache/analyze.d.ts +0 -12
- package/dist/engine/cache/analyze.d.ts.map +0 -1
- package/dist/engine/cache/analyze.js +0 -103
- package/dist/engine/cache/analyze.js.map +0 -1
- package/dist/engine/cache/coordinator.d.ts +0 -99
- package/dist/engine/cache/coordinator.d.ts.map +0 -1
- package/dist/engine/cache/coordinator.js +0 -794
- package/dist/engine/cache/coordinator.js.map +0 -1
- package/dist/engine/cache/hasher.d.ts +0 -9
- package/dist/engine/cache/hasher.d.ts.map +0 -1
- package/dist/engine/cache/hasher.js +0 -50
- package/dist/engine/cache/hasher.js.map +0 -1
- package/dist/engine/cache/inference.d.ts +0 -3
- package/dist/engine/cache/inference.d.ts.map +0 -1
- package/dist/engine/cache/inference.js +0 -43
- package/dist/engine/cache/inference.js.map +0 -1
- package/dist/engine/cache/serialize.d.ts +0 -2
- package/dist/engine/cache/serialize.d.ts.map +0 -1
- package/dist/engine/cache/serialize.js +0 -21
- package/dist/engine/cache/serialize.js.map +0 -1
- package/dist/engine/cache/store.d.ts +0 -75
- package/dist/engine/cache/store.d.ts.map +0 -1
- package/dist/engine/cache/store.js +0 -208
- package/dist/engine/cache/store.js.map +0 -1
- package/dist/engine/executor/command-runner.d.ts +0 -26
- package/dist/engine/executor/command-runner.d.ts.map +0 -1
- package/dist/engine/executor/command-runner.js +0 -151
- package/dist/engine/executor/command-runner.js.map +0 -1
- package/dist/engine/executor/executor.d.ts +0 -6
- package/dist/engine/executor/executor.d.ts.map +0 -1
- package/dist/engine/executor/executor.js +0 -29
- package/dist/engine/executor/executor.js.map +0 -1
- package/dist/engine/executor/file-system.d.ts +0 -5
- package/dist/engine/executor/file-system.d.ts.map +0 -1
- package/dist/engine/executor/file-system.js +0 -50
- package/dist/engine/executor/file-system.js.map +0 -1
- package/dist/engine/executor/index.d.ts +0 -3
- package/dist/engine/executor/index.d.ts.map +0 -1
- package/dist/engine/executor/index.js +0 -2
- package/dist/engine/executor/index.js.map +0 -1
- package/dist/engine/executor/plan-runner.d.ts +0 -37
- package/dist/engine/executor/plan-runner.d.ts.map +0 -1
- package/dist/engine/executor/plan-runner.js +0 -427
- package/dist/engine/executor/plan-runner.js.map +0 -1
- package/dist/engine/executor/types.d.ts +0 -73
- package/dist/engine/executor/types.d.ts.map +0 -1
- package/dist/engine/executor/types.js +0 -2
- package/dist/engine/executor/types.js.map +0 -1
- package/dist/engine/executor/value-resolver.d.ts +0 -14
- package/dist/engine/executor/value-resolver.d.ts.map +0 -1
- package/dist/engine/executor/value-resolver.js +0 -63
- package/dist/engine/executor/value-resolver.js.map +0 -1
- package/dist/engine/handlers/annotate-handler.d.ts +0 -8
- package/dist/engine/handlers/annotate-handler.d.ts.map +0 -1
- package/dist/engine/handlers/annotate-handler.js +0 -8
- package/dist/engine/handlers/annotate-handler.js.map +0 -1
- package/dist/engine/handlers/artifact-download-handler.d.ts +0 -8
- package/dist/engine/handlers/artifact-download-handler.d.ts.map +0 -1
- package/dist/engine/handlers/artifact-download-handler.js +0 -38
- package/dist/engine/handlers/artifact-download-handler.js.map +0 -1
- package/dist/engine/handlers/artifact-upload-handler.d.ts +0 -8
- package/dist/engine/handlers/artifact-upload-handler.d.ts.map +0 -1
- package/dist/engine/handlers/artifact-upload-handler.js +0 -26
- package/dist/engine/handlers/artifact-upload-handler.js.map +0 -1
- package/dist/engine/handlers/bind-service-handler.d.ts +0 -8
- package/dist/engine/handlers/bind-service-handler.d.ts.map +0 -1
- package/dist/engine/handlers/bind-service-handler.js +0 -8
- package/dist/engine/handlers/bind-service-handler.js.map +0 -1
- package/dist/engine/handlers/cache-mount-handler.d.ts +0 -8
- package/dist/engine/handlers/cache-mount-handler.d.ts.map +0 -1
- package/dist/engine/handlers/cache-mount-handler.js +0 -16
- package/dist/engine/handlers/cache-mount-handler.js.map +0 -1
- package/dist/engine/handlers/command-handler.d.ts +0 -10
- package/dist/engine/handlers/command-handler.d.ts.map +0 -1
- package/dist/engine/handlers/command-handler.js +0 -11
- package/dist/engine/handlers/command-handler.js.map +0 -1
- package/dist/engine/handlers/emit-handler.d.ts +0 -8
- package/dist/engine/handlers/emit-handler.d.ts.map +0 -1
- package/dist/engine/handlers/emit-handler.js +0 -8
- package/dist/engine/handlers/emit-handler.js.map +0 -1
- package/dist/engine/handlers/env-handler.d.ts +0 -8
- package/dist/engine/handlers/env-handler.d.ts.map +0 -1
- package/dist/engine/handlers/env-handler.js +0 -9
- package/dist/engine/handlers/env-handler.js.map +0 -1
- package/dist/engine/handlers/handler.d.ts +0 -18
- package/dist/engine/handlers/handler.d.ts.map +0 -1
- package/dist/engine/handlers/handler.js +0 -2
- package/dist/engine/handlers/handler.js.map +0 -1
- package/dist/engine/handlers/http-handler.d.ts +0 -8
- package/dist/engine/handlers/http-handler.d.ts.map +0 -1
- package/dist/engine/handlers/http-handler.js +0 -77
- package/dist/engine/handlers/http-handler.js.map +0 -1
- package/dist/engine/handlers/pack-handler.d.ts +0 -8
- package/dist/engine/handlers/pack-handler.d.ts.map +0 -1
- package/dist/engine/handlers/pack-handler.js +0 -16
- package/dist/engine/handlers/pack-handler.js.map +0 -1
- package/dist/engine/handlers/registry.d.ts +0 -9
- package/dist/engine/handlers/registry.d.ts.map +0 -1
- package/dist/engine/handlers/registry.js +0 -14
- package/dist/engine/handlers/registry.js.map +0 -1
- package/dist/engine/handlers/report-handler.d.ts +0 -8
- package/dist/engine/handlers/report-handler.d.ts.map +0 -1
- package/dist/engine/handlers/report-handler.js +0 -13
- package/dist/engine/handlers/report-handler.js.map +0 -1
- package/dist/engine/handlers/summary-handler.d.ts +0 -8
- package/dist/engine/handlers/summary-handler.d.ts.map +0 -1
- package/dist/engine/handlers/summary-handler.js +0 -8
- package/dist/engine/handlers/summary-handler.js.map +0 -1
- package/dist/engine/handlers/unpack-handler.d.ts +0 -12
- package/dist/engine/handlers/unpack-handler.d.ts.map +0 -1
- package/dist/engine/handlers/unpack-handler.js +0 -31
- package/dist/engine/handlers/unpack-handler.js.map +0 -1
- package/dist/engine/handlers/use-handler.d.ts +0 -12
- package/dist/engine/handlers/use-handler.d.ts.map +0 -1
- package/dist/engine/handlers/use-handler.js +0 -29
- package/dist/engine/handlers/use-handler.js.map +0 -1
- package/dist/engine/index.d.ts +0 -5
- package/dist/engine/index.d.ts.map +0 -1
- package/dist/engine/index.js +0 -3
- package/dist/engine/index.js.map +0 -1
- package/dist/engine/providers/artifact.d.ts +0 -5
- package/dist/engine/providers/artifact.d.ts.map +0 -1
- package/dist/engine/providers/artifact.js +0 -2
- package/dist/engine/providers/artifact.js.map +0 -1
- package/dist/engine/providers/cache.d.ts +0 -6
- package/dist/engine/providers/cache.d.ts.map +0 -1
- package/dist/engine/providers/cache.js +0 -2
- package/dist/engine/providers/cache.js.map +0 -1
- package/dist/engine/providers/index.d.ts +0 -5
- package/dist/engine/providers/index.d.ts.map +0 -1
- package/dist/engine/providers/index.js +0 -2
- package/dist/engine/providers/index.js.map +0 -1
- package/dist/engine/providers/local.d.ts +0 -16
- package/dist/engine/providers/local.d.ts.map +0 -1
- package/dist/engine/providers/local.js +0 -81
- package/dist/engine/providers/local.js.map +0 -1
- package/dist/engine/providers/types.d.ts +0 -20
- package/dist/engine/providers/types.d.ts.map +0 -1
- package/dist/engine/providers/types.js +0 -2
- package/dist/engine/providers/types.js.map +0 -1
- package/dist/ops/annotate.d.ts +0 -32
- package/dist/ops/annotate.d.ts.map +0 -1
- package/dist/ops/annotate.js +0 -47
- package/dist/ops/annotate.js.map +0 -1
- package/dist/ops/artifact.d.ts +0 -58
- package/dist/ops/artifact.d.ts.map +0 -1
- package/dist/ops/artifact.js +0 -104
- package/dist/ops/artifact.js.map +0 -1
- package/dist/ops/base.d.ts +0 -22
- package/dist/ops/base.d.ts.map +0 -1
- package/dist/ops/base.js +0 -2
- package/dist/ops/base.js.map +0 -1
- package/dist/ops/bind.d.ts +0 -22
- package/dist/ops/bind.d.ts.map +0 -1
- package/dist/ops/bind.js +0 -38
- package/dist/ops/bind.js.map +0 -1
- package/dist/ops/bundle.d.ts +0 -53
- package/dist/ops/bundle.d.ts.map +0 -1
- package/dist/ops/bundle.js +0 -85
- package/dist/ops/bundle.js.map +0 -1
- package/dist/ops/cache.d.ts +0 -88
- package/dist/ops/cache.d.ts.map +0 -1
- package/dist/ops/cache.js +0 -144
- package/dist/ops/cache.js.map +0 -1
- package/dist/ops/env.d.ts +0 -22
- package/dist/ops/env.d.ts.map +0 -1
- package/dist/ops/env.js +0 -63
- package/dist/ops/env.js.map +0 -1
- package/dist/ops/errors.d.ts +0 -36
- package/dist/ops/errors.d.ts.map +0 -1
- package/dist/ops/errors.js +0 -41
- package/dist/ops/errors.js.map +0 -1
- package/dist/ops/http.d.ts +0 -52
- package/dist/ops/http.d.ts.map +0 -1
- package/dist/ops/http.js +0 -173
- package/dist/ops/http.js.map +0 -1
- package/dist/ops/index.d.ts +0 -34
- package/dist/ops/index.d.ts.map +0 -1
- package/dist/ops/index.js +0 -17
- package/dist/ops/index.js.map +0 -1
- package/dist/ops/operation.d.ts +0 -40
- package/dist/ops/operation.d.ts.map +0 -1
- package/dist/ops/operation.js +0 -14
- package/dist/ops/operation.js.map +0 -1
- package/dist/ops/output.d.ts +0 -58
- package/dist/ops/output.d.ts.map +0 -1
- package/dist/ops/output.js +0 -74
- package/dist/ops/output.js.map +0 -1
- package/dist/ops/param.d.ts +0 -4
- package/dist/ops/param.d.ts.map +0 -1
- package/dist/ops/param.js +0 -3
- package/dist/ops/param.js.map +0 -1
- package/dist/ops/ref.d.ts +0 -21
- package/dist/ops/ref.d.ts.map +0 -1
- package/dist/ops/ref.js +0 -23
- package/dist/ops/ref.js.map +0 -1
- package/dist/ops/report.d.ts +0 -24
- package/dist/ops/report.d.ts.map +0 -1
- package/dist/ops/report.js +0 -37
- package/dist/ops/report.js.map +0 -1
- package/dist/ops/secret.d.ts +0 -4
- package/dist/ops/secret.d.ts.map +0 -1
- package/dist/ops/secret.js +0 -2
- package/dist/ops/secret.js.map +0 -1
- package/dist/ops/shell.d.ts +0 -56
- package/dist/ops/shell.d.ts.map +0 -1
- package/dist/ops/shell.js +0 -127
- package/dist/ops/shell.js.map +0 -1
- package/dist/ops/summary.d.ts +0 -23
- package/dist/ops/summary.d.ts.map +0 -1
- package/dist/ops/summary.js +0 -36
- package/dist/ops/summary.js.map +0 -1
- package/dist/ops/types.d.ts +0 -23
- package/dist/ops/types.d.ts.map +0 -1
- package/dist/ops/types.js +0 -5
- package/dist/ops/types.js.map +0 -1
- package/dist/version.d.ts +0 -2
- package/dist/version.d.ts.map +0 -1
- package/dist/version.js +0 -2
- package/dist/version.js.map +0 -1
|
@@ -1,794 +0,0 @@
|
|
|
1
|
-
import { join } from 'node:path';
|
|
2
|
-
import { StepBuilder } from '../../dsl/step.js';
|
|
3
|
-
import { OpErrors } from '../../ops/errors.js';
|
|
4
|
-
import { isOperation } from '../../ops/operation.js';
|
|
5
|
-
import { isParamRef, isSecretRef, isValueRef } from '../../ops/types.js';
|
|
6
|
-
import { VERSION } from '../../version.js';
|
|
7
|
-
import { collectFiles, writeFiles } from '../executor/file-system.js';
|
|
8
|
-
import { analyzeTaskSteps } from './analyze.js';
|
|
9
|
-
import { hashFiles, hashNamedValues, hashObject, hashString } from './hasher.js';
|
|
10
|
-
import { inferInputsFromCommand, inferOutputsFromCommand } from './inference.js';
|
|
11
|
-
import { CacheStore } from './store.js';
|
|
12
|
-
export class CacheCoordinator {
|
|
13
|
-
workspace;
|
|
14
|
-
settings;
|
|
15
|
-
logger;
|
|
16
|
-
reporter;
|
|
17
|
-
resolver;
|
|
18
|
-
params;
|
|
19
|
-
getTaskState;
|
|
20
|
-
findTask;
|
|
21
|
-
executeTask;
|
|
22
|
-
cacheStore;
|
|
23
|
-
pendingOpSaves = new Map();
|
|
24
|
-
taskCacheState = new Map();
|
|
25
|
-
constructor(opts) {
|
|
26
|
-
this.workspace = opts.workspace;
|
|
27
|
-
this.settings = opts.settings ?? {};
|
|
28
|
-
this.logger = opts.logger;
|
|
29
|
-
this.reporter = opts.reporter;
|
|
30
|
-
this.resolver = opts.resolver;
|
|
31
|
-
this.params = opts.params;
|
|
32
|
-
this.getTaskState = opts.getTaskState;
|
|
33
|
-
this.findTask = opts.findTask;
|
|
34
|
-
this.executeTask = opts.executeTask;
|
|
35
|
-
const cacheEnabled = this.settings.enabled !== false;
|
|
36
|
-
const cacheRoot = this.settings.directory ?? join(this.workspace, '.kitsi', 'cache');
|
|
37
|
-
this.cacheStore = cacheEnabled ? new CacheStore(cacheRoot) : null;
|
|
38
|
-
}
|
|
39
|
-
async beforeTask(task, env, state) {
|
|
40
|
-
if (!this.shouldAttemptTaskCache(task))
|
|
41
|
-
return null;
|
|
42
|
-
this.ensureCacheStore(task);
|
|
43
|
-
if (!this.cacheAllowedForTask(task)) {
|
|
44
|
-
return this.handleCacheDisabled(task);
|
|
45
|
-
}
|
|
46
|
-
const decision = await this.computeTaskCacheDecision(task, env);
|
|
47
|
-
if (!decision.cacheable) {
|
|
48
|
-
return await this.handleUncacheableDecision(task, decision);
|
|
49
|
-
}
|
|
50
|
-
return await this.tryRestoreTaskCache(task, decision, state);
|
|
51
|
-
}
|
|
52
|
-
async afterStep(task) {
|
|
53
|
-
if (!this.cacheAllowedForTask(task))
|
|
54
|
-
return;
|
|
55
|
-
await this.flushPendingOpSaves(task.config.name);
|
|
56
|
-
}
|
|
57
|
-
async afterTask(task, state, decision) {
|
|
58
|
-
if (!this.cacheAllowedForTask(task))
|
|
59
|
-
return;
|
|
60
|
-
await this.flushPendingOpSaves(task.config.name);
|
|
61
|
-
if (!decision?.cacheable || decision.hit || !this.cacheStore)
|
|
62
|
-
return;
|
|
63
|
-
const outputFiles = decision.outputPatterns.length > 0
|
|
64
|
-
? await collectFiles(this.workspace, decision.outputPatterns)
|
|
65
|
-
: {};
|
|
66
|
-
const outputs = {};
|
|
67
|
-
for (const [key, value] of state.outputs.entries()) {
|
|
68
|
-
outputs[key] = value;
|
|
69
|
-
}
|
|
70
|
-
const meta = {
|
|
71
|
-
key: decision.key,
|
|
72
|
-
task: task.config.name,
|
|
73
|
-
createdAt: new Date().toISOString(),
|
|
74
|
-
components: decision.components,
|
|
75
|
-
inputs: decision.inputs,
|
|
76
|
-
outputs,
|
|
77
|
-
summaries: state.summaries,
|
|
78
|
-
bundles: Array.from(state.bundles.keys()),
|
|
79
|
-
};
|
|
80
|
-
await this.cacheStore.saveTask(task.config.name, decision.key, {
|
|
81
|
-
meta,
|
|
82
|
-
bundles: state.bundles,
|
|
83
|
-
outputFiles,
|
|
84
|
-
});
|
|
85
|
-
this.recordTaskCacheState(task.config.name, {
|
|
86
|
-
key: decision.key,
|
|
87
|
-
fingerprint: decision.key,
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
|
-
async tryExecuteOp(op, task, context, state, executeBaseOp, executeOp) {
|
|
91
|
-
if (op.kind === 'cache') {
|
|
92
|
-
const hit = await this.executeCacheOp(op, task, context, state);
|
|
93
|
-
return { handled: true, value: hit };
|
|
94
|
-
}
|
|
95
|
-
if (op.kind === 'cache-or') {
|
|
96
|
-
const hit = await this.executeCacheOrOp(op, task, context, state, executeOp);
|
|
97
|
-
return { handled: true, value: hit };
|
|
98
|
-
}
|
|
99
|
-
if (op.kind === 'command' && op.cache?.enabled === true) {
|
|
100
|
-
const result = await this.executeCachedCommand(op, task, context, state, executeBaseOp);
|
|
101
|
-
return { handled: true, value: result };
|
|
102
|
-
}
|
|
103
|
-
return null;
|
|
104
|
-
}
|
|
105
|
-
wasTaskCached(taskName) {
|
|
106
|
-
return this.taskCacheState.get(taskName)?.hit ?? false;
|
|
107
|
-
}
|
|
108
|
-
shouldAttemptTaskCache(task) {
|
|
109
|
-
if (this.settings.cacheOnly) {
|
|
110
|
-
return (task.config.cache !== undefined ||
|
|
111
|
-
task.config.cacheEnv.length > 0 ||
|
|
112
|
-
task.config.cacheBust !== undefined ||
|
|
113
|
-
task.config.inputs.length > 0 ||
|
|
114
|
-
Boolean(this.settings.inputs?.[task.config.name]?.length));
|
|
115
|
-
}
|
|
116
|
-
return true;
|
|
117
|
-
}
|
|
118
|
-
cacheAllowedForTask(task) {
|
|
119
|
-
if (!this.cacheStore)
|
|
120
|
-
return false;
|
|
121
|
-
if (this.settings.enabled === false)
|
|
122
|
-
return false;
|
|
123
|
-
if (task.config.cache?.enabled === false)
|
|
124
|
-
return false;
|
|
125
|
-
return true;
|
|
126
|
-
}
|
|
127
|
-
cacheExcludes() {
|
|
128
|
-
const defaults = ['.kitsi/**', '.git/**'];
|
|
129
|
-
const configExcludes = this.settings.exclude ?? [];
|
|
130
|
-
return [...defaults, ...configExcludes];
|
|
131
|
-
}
|
|
132
|
-
ensureCacheStore(task) {
|
|
133
|
-
if (this.settings.cacheOnly && !this.cacheStore) {
|
|
134
|
-
throw OpErrors.cache('Cache-only mode requires caching to be enabled')
|
|
135
|
-
.inTask(task.config.name)
|
|
136
|
-
.build();
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
handleCacheDisabled(task) {
|
|
140
|
-
if (this.settings.cacheOnly) {
|
|
141
|
-
throw OpErrors.cache(`Caching disabled for task "${task.config.name}"`)
|
|
142
|
-
.inTask(task.config.name)
|
|
143
|
-
.build();
|
|
144
|
-
}
|
|
145
|
-
return null;
|
|
146
|
-
}
|
|
147
|
-
async handleUncacheableDecision(task, decision) {
|
|
148
|
-
if (this.settings.cacheOnly) {
|
|
149
|
-
throw OpErrors.cache(decision.reason ?? 'Task cache is unavailable')
|
|
150
|
-
.inTask(task.config.name)
|
|
151
|
-
.build();
|
|
152
|
-
}
|
|
153
|
-
if (this.settings.debug) {
|
|
154
|
-
await this.emitCacheDebug(task.config.name, decision, 'SKIP');
|
|
155
|
-
}
|
|
156
|
-
return decision;
|
|
157
|
-
}
|
|
158
|
-
async tryRestoreTaskCache(task, decision, state) {
|
|
159
|
-
const entry = await this.cacheStore?.readTask(task.config.name, decision.key, decision.ttlMs);
|
|
160
|
-
const hit = Boolean(entry);
|
|
161
|
-
decision.hit = hit;
|
|
162
|
-
if (hit && entry) {
|
|
163
|
-
await this.restoreTaskCacheEntry(entry, state);
|
|
164
|
-
this.reporter?.emit('cache_hit', { cache: task.config.name, scope: 'task' });
|
|
165
|
-
this.recordTaskCacheState(task.config.name, {
|
|
166
|
-
key: decision.key,
|
|
167
|
-
hit: true,
|
|
168
|
-
fingerprint: decision.key,
|
|
169
|
-
});
|
|
170
|
-
}
|
|
171
|
-
else {
|
|
172
|
-
this.reporter?.emit('cache_miss', { cache: task.config.name, scope: 'task' });
|
|
173
|
-
this.recordTaskCacheState(task.config.name, { key: decision.key, hit: false });
|
|
174
|
-
if (this.settings.cacheOnly) {
|
|
175
|
-
throw OpErrors.cache(`Cache miss for task "${task.config.name}"`)
|
|
176
|
-
.inTask(task.config.name)
|
|
177
|
-
.build();
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
if (this.settings.debug) {
|
|
181
|
-
await this.emitCacheDebug(task.config.name, decision, hit ? 'HIT' : 'MISS');
|
|
182
|
-
}
|
|
183
|
-
return decision;
|
|
184
|
-
}
|
|
185
|
-
async restoreTaskCacheEntry(entry, state) {
|
|
186
|
-
if (!entry)
|
|
187
|
-
return;
|
|
188
|
-
state.outputs = new Map(Object.entries(entry.meta.outputs));
|
|
189
|
-
state.summaries = entry.meta.summaries ?? [];
|
|
190
|
-
state.bundles = entry.bundles;
|
|
191
|
-
if (Object.keys(entry.outputFiles).length > 0) {
|
|
192
|
-
await writeFiles(this.workspace, entry.outputFiles);
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
async flushPendingOpSaves(taskName) {
|
|
196
|
-
if (!this.cacheStore)
|
|
197
|
-
return;
|
|
198
|
-
const pending = this.pendingOpSaves.get(taskName);
|
|
199
|
-
if (!pending || pending.length === 0)
|
|
200
|
-
return;
|
|
201
|
-
this.pendingOpSaves.set(taskName, []);
|
|
202
|
-
for (const entry of pending) {
|
|
203
|
-
const existing = await this.cacheStore.readOp(entry.key, this.settings.ttlMs ?? null);
|
|
204
|
-
if (existing)
|
|
205
|
-
continue;
|
|
206
|
-
const files = await collectFiles(this.workspace, entry.paths);
|
|
207
|
-
await this.cacheStore.saveOp(entry.key, {
|
|
208
|
-
meta: {
|
|
209
|
-
key: entry.key,
|
|
210
|
-
createdAt: new Date().toISOString(),
|
|
211
|
-
kind: 'cache',
|
|
212
|
-
outputs: entry.paths,
|
|
213
|
-
},
|
|
214
|
-
files,
|
|
215
|
-
});
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
recordTaskCacheState(taskName, patch) {
|
|
219
|
-
const current = this.taskCacheState.get(taskName) ?? {};
|
|
220
|
-
const next = { ...current, ...patch };
|
|
221
|
-
this.taskCacheState.set(taskName, next);
|
|
222
|
-
return next;
|
|
223
|
-
}
|
|
224
|
-
async computeTaskCacheDecision(task, env) {
|
|
225
|
-
const analysis = analyzeTaskSteps(task, this.workspace);
|
|
226
|
-
const taskName = task.config.name;
|
|
227
|
-
if (analysis.dynamic) {
|
|
228
|
-
return {
|
|
229
|
-
key: '',
|
|
230
|
-
cacheable: false,
|
|
231
|
-
reason: 'Task uses runtime values to define operations',
|
|
232
|
-
components: {},
|
|
233
|
-
inputs: { patterns: [], files: {} },
|
|
234
|
-
outputPatterns: [],
|
|
235
|
-
};
|
|
236
|
-
}
|
|
237
|
-
const patterns = this.collectTaskInputPatterns(task, analysis, taskName);
|
|
238
|
-
const { inputPatterns, inputPatternsHash, inputs } = await this.resolveTaskInputs(taskName, patterns);
|
|
239
|
-
const envHash = this.hashCacheEnv(task, env);
|
|
240
|
-
const secrets = new Set(analysis.secrets);
|
|
241
|
-
const params = new Set(analysis.params);
|
|
242
|
-
const hasValueRefs = this.collectTaskRefs(task, secrets, params);
|
|
243
|
-
if (hasValueRefs) {
|
|
244
|
-
return {
|
|
245
|
-
key: '',
|
|
246
|
-
cacheable: false,
|
|
247
|
-
reason: 'Task env uses runtime values',
|
|
248
|
-
components: {},
|
|
249
|
-
inputs: { patterns: inputPatterns, files: inputs.files },
|
|
250
|
-
outputPatterns: task.config.cache?.outputs ?? [],
|
|
251
|
-
};
|
|
252
|
-
}
|
|
253
|
-
const secretsHash = hashString(Array.from(secrets).sort().join('\n'));
|
|
254
|
-
const paramsHash = this.hashParams(params);
|
|
255
|
-
const depsHash = await this.hashDependencies(task, analysis.dependencies);
|
|
256
|
-
const opsHash = hashObject(analysis.ops);
|
|
257
|
-
const imageRef = task.config.image?.config.ref ?? 'local';
|
|
258
|
-
const workdir = task.config.image?.config.workdir ?? '';
|
|
259
|
-
const envConfigHash = this.hashEnvConfig(task);
|
|
260
|
-
const outputPatterns = task.config.cache?.outputs ?? [];
|
|
261
|
-
const outputPatternsHash = hashString(outputPatterns.join('\n'));
|
|
262
|
-
const components = {
|
|
263
|
-
taskName,
|
|
264
|
-
image: imageRef,
|
|
265
|
-
workdir,
|
|
266
|
-
inputsHash: inputs.hash,
|
|
267
|
-
inputPatternsHash,
|
|
268
|
-
envHash,
|
|
269
|
-
envConfigHash,
|
|
270
|
-
secretsHash,
|
|
271
|
-
paramsHash,
|
|
272
|
-
depsHash,
|
|
273
|
-
opsHash,
|
|
274
|
-
cacheBust: this.buildCacheBust(task),
|
|
275
|
-
outputPatternsHash,
|
|
276
|
-
version: this.cacheVersion(),
|
|
277
|
-
};
|
|
278
|
-
const key = hashObject(components);
|
|
279
|
-
return {
|
|
280
|
-
key,
|
|
281
|
-
cacheable: true,
|
|
282
|
-
components,
|
|
283
|
-
inputs: { patterns: inputPatterns, files: inputs.files },
|
|
284
|
-
outputPatterns,
|
|
285
|
-
ttlMs: this.resolveTtl(task.config.cache?.ttl),
|
|
286
|
-
};
|
|
287
|
-
}
|
|
288
|
-
collectTaskInputPatterns(task, analysis, taskName) {
|
|
289
|
-
const explicitPatterns = new Set();
|
|
290
|
-
for (const pattern of task.config.inputs)
|
|
291
|
-
explicitPatterns.add(pattern);
|
|
292
|
-
for (const pattern of task.config.cache?.inputs ?? [])
|
|
293
|
-
explicitPatterns.add(pattern);
|
|
294
|
-
for (const pattern of analysis.inputs)
|
|
295
|
-
explicitPatterns.add(pattern);
|
|
296
|
-
for (const pattern of this.settings.inputs?.[taskName] ?? []) {
|
|
297
|
-
explicitPatterns.add(pattern);
|
|
298
|
-
}
|
|
299
|
-
if (explicitPatterns.size > 0)
|
|
300
|
-
return Array.from(explicitPatterns);
|
|
301
|
-
const inferred = new Set();
|
|
302
|
-
let hasUnknownCommand = false;
|
|
303
|
-
for (const signature of analysis.commandSignatures) {
|
|
304
|
-
const patterns = inferInputsFromCommand(signature);
|
|
305
|
-
if (patterns.length === 0) {
|
|
306
|
-
hasUnknownCommand = true;
|
|
307
|
-
continue;
|
|
308
|
-
}
|
|
309
|
-
for (const pattern of patterns)
|
|
310
|
-
inferred.add(pattern);
|
|
311
|
-
}
|
|
312
|
-
if (hasUnknownCommand) {
|
|
313
|
-
inferred.add('**/*');
|
|
314
|
-
}
|
|
315
|
-
return Array.from(inferred);
|
|
316
|
-
}
|
|
317
|
-
async resolveTaskInputs(taskName, patterns) {
|
|
318
|
-
const inputPatterns = patterns.length > 0 ? patterns : ['**/*'];
|
|
319
|
-
if (patterns.length === 0) {
|
|
320
|
-
this.logger.info(this.resolver.redactSecrets(`Cache inputs not detected for task "${taskName}". Consider declaring inputs.`));
|
|
321
|
-
}
|
|
322
|
-
const inputPatternsHash = hashString(inputPatterns.join('\n'));
|
|
323
|
-
const inputs = await hashFiles(this.workspace, inputPatterns, this.cacheExcludes());
|
|
324
|
-
return { inputPatterns, inputPatternsHash, inputs };
|
|
325
|
-
}
|
|
326
|
-
hashCacheEnv(task, env) {
|
|
327
|
-
const cacheEnv = new Set(task.config.cacheEnv);
|
|
328
|
-
for (const name of task.config.cache?.env ?? [])
|
|
329
|
-
cacheEnv.add(name);
|
|
330
|
-
const secretEnv = this.collectSecretEnvNames(task);
|
|
331
|
-
const envValues = {};
|
|
332
|
-
for (const name of cacheEnv) {
|
|
333
|
-
envValues[name] = secretEnv.has(name) ? '__secret__' : (env[name] ?? '__unset__');
|
|
334
|
-
}
|
|
335
|
-
return hashNamedValues(envValues);
|
|
336
|
-
}
|
|
337
|
-
buildCacheBust(task) {
|
|
338
|
-
const cacheBust = [this.settings.bust, task.config.cacheBust, task.config.cache?.key]
|
|
339
|
-
.filter(Boolean)
|
|
340
|
-
.join(':');
|
|
341
|
-
return cacheBust || '';
|
|
342
|
-
}
|
|
343
|
-
collectTaskRefs(task, secrets, params) {
|
|
344
|
-
let hasValueRef = false;
|
|
345
|
-
const collect = (value) => {
|
|
346
|
-
if (isSecretRef(value))
|
|
347
|
-
secrets.add(value.name);
|
|
348
|
-
if (isParamRef(value))
|
|
349
|
-
params.add(value.name);
|
|
350
|
-
if (isValueRef(value)) {
|
|
351
|
-
hasValueRef = true;
|
|
352
|
-
}
|
|
353
|
-
};
|
|
354
|
-
for (const value of Object.values(task.config.env)) {
|
|
355
|
-
collect(value);
|
|
356
|
-
}
|
|
357
|
-
for (const value of Object.values(task.config.image?.config.env ?? {})) {
|
|
358
|
-
collect(value);
|
|
359
|
-
}
|
|
360
|
-
return hasValueRef;
|
|
361
|
-
}
|
|
362
|
-
collectSecretEnvNames(task) {
|
|
363
|
-
const names = new Set();
|
|
364
|
-
for (const [key, value] of Object.entries(task.config.env)) {
|
|
365
|
-
if (isSecretRef(value))
|
|
366
|
-
names.add(key);
|
|
367
|
-
}
|
|
368
|
-
for (const [key, value] of Object.entries(task.config.image?.config.env ?? {})) {
|
|
369
|
-
if (isSecretRef(value))
|
|
370
|
-
names.add(key);
|
|
371
|
-
}
|
|
372
|
-
return names;
|
|
373
|
-
}
|
|
374
|
-
hashParams(params) {
|
|
375
|
-
const values = {};
|
|
376
|
-
for (const name of params) {
|
|
377
|
-
if (Object.prototype.hasOwnProperty.call(this.params, name)) {
|
|
378
|
-
values[name] = JSON.stringify(this.params[name]);
|
|
379
|
-
}
|
|
380
|
-
else {
|
|
381
|
-
values[name] = '__unset__';
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
return hashNamedValues(values);
|
|
385
|
-
}
|
|
386
|
-
hashEnvConfig(task) {
|
|
387
|
-
const normalize = (value) => {
|
|
388
|
-
if (typeof value === 'string' || typeof value === 'number')
|
|
389
|
-
return value;
|
|
390
|
-
if (isSecretRef(value))
|
|
391
|
-
return { kind: 'secret', name: value.name };
|
|
392
|
-
if (isParamRef(value))
|
|
393
|
-
return { kind: 'param', name: value.name };
|
|
394
|
-
if (isValueRef(value))
|
|
395
|
-
return { kind: 'value', name: value.name };
|
|
396
|
-
return String(value);
|
|
397
|
-
};
|
|
398
|
-
const taskEnv = {};
|
|
399
|
-
for (const [name, value] of Object.entries(task.config.env)) {
|
|
400
|
-
taskEnv[name] = normalize(value);
|
|
401
|
-
}
|
|
402
|
-
const imageEnv = {};
|
|
403
|
-
for (const [name, value] of Object.entries(task.config.image?.config.env ?? {})) {
|
|
404
|
-
imageEnv[name] = normalize(value);
|
|
405
|
-
}
|
|
406
|
-
return hashObject({ taskEnv, imageEnv });
|
|
407
|
-
}
|
|
408
|
-
async hashDependencies(task, discovered) {
|
|
409
|
-
const names = this.collectDependencyNames(task, discovered);
|
|
410
|
-
const fingerprints = {};
|
|
411
|
-
for (const name of names) {
|
|
412
|
-
fingerprints[name] = await this.resolveDependencyFingerprint(name);
|
|
413
|
-
}
|
|
414
|
-
return hashObject(fingerprints);
|
|
415
|
-
}
|
|
416
|
-
collectDependencyNames(task, discovered) {
|
|
417
|
-
const names = new Set(task.config.needs);
|
|
418
|
-
for (const name of discovered)
|
|
419
|
-
names.add(name);
|
|
420
|
-
return Array.from(names);
|
|
421
|
-
}
|
|
422
|
-
async resolveDependencyFingerprint(name) {
|
|
423
|
-
let state = this.getTaskState(name);
|
|
424
|
-
if (!state || state.status === 'pending' || state.status === 'running') {
|
|
425
|
-
const depTask = this.findTask(name);
|
|
426
|
-
if (depTask) {
|
|
427
|
-
await this.executeTask(depTask);
|
|
428
|
-
}
|
|
429
|
-
state = this.getTaskState(name);
|
|
430
|
-
}
|
|
431
|
-
if (!state)
|
|
432
|
-
return 'missing';
|
|
433
|
-
if (state.status === 'failed')
|
|
434
|
-
return 'failed';
|
|
435
|
-
const cacheState = this.taskCacheState.get(name);
|
|
436
|
-
if (cacheState?.key)
|
|
437
|
-
return cacheState.key;
|
|
438
|
-
if (!cacheState?.fingerprint) {
|
|
439
|
-
const fingerprint = this.computeStateFingerprint(state);
|
|
440
|
-
this.recordTaskCacheState(name, { fingerprint });
|
|
441
|
-
}
|
|
442
|
-
return this.taskCacheState.get(name)?.fingerprint ?? 'missing';
|
|
443
|
-
}
|
|
444
|
-
computeStateFingerprint(state) {
|
|
445
|
-
const outputs = {};
|
|
446
|
-
for (const [key, value] of state.outputs.entries()) {
|
|
447
|
-
outputs[key] = value;
|
|
448
|
-
}
|
|
449
|
-
const bundleHashes = {};
|
|
450
|
-
for (const [name, bundle] of state.bundles.entries()) {
|
|
451
|
-
const fileHashes = {};
|
|
452
|
-
for (const [path, content] of Object.entries(bundle.files)) {
|
|
453
|
-
fileHashes[path] = hashString(Buffer.from(content).toString('base64'));
|
|
454
|
-
}
|
|
455
|
-
bundleHashes[name] = hashObject(fileHashes);
|
|
456
|
-
}
|
|
457
|
-
return hashObject({ outputs, bundles: bundleHashes });
|
|
458
|
-
}
|
|
459
|
-
cacheVersion() {
|
|
460
|
-
const [major] = VERSION.split('.');
|
|
461
|
-
return major ?? VERSION;
|
|
462
|
-
}
|
|
463
|
-
resolveTtl(value) {
|
|
464
|
-
if (!value)
|
|
465
|
-
return this.settings.ttlMs ?? null;
|
|
466
|
-
const parsed = this.parseDuration(value);
|
|
467
|
-
return parsed ?? this.settings.ttlMs ?? null;
|
|
468
|
-
}
|
|
469
|
-
parseDuration(value) {
|
|
470
|
-
const match = value.trim().match(/^(\d+(?:\.\d+)?)(ms|s|m|h|d)$/);
|
|
471
|
-
if (!match)
|
|
472
|
-
return null;
|
|
473
|
-
const amount = Number(match[1]);
|
|
474
|
-
if (!Number.isFinite(amount))
|
|
475
|
-
return null;
|
|
476
|
-
const multipliers = {
|
|
477
|
-
ms: 1,
|
|
478
|
-
s: 1000,
|
|
479
|
-
m: 60_000,
|
|
480
|
-
h: 3_600_000,
|
|
481
|
-
d: 86_400_000,
|
|
482
|
-
};
|
|
483
|
-
const unit = match[2];
|
|
484
|
-
if (!unit)
|
|
485
|
-
return null;
|
|
486
|
-
return amount * (multipliers[unit] ?? 1);
|
|
487
|
-
}
|
|
488
|
-
async executeCacheOp(op, task, context, _state, recordPending = true) {
|
|
489
|
-
if (!this.cacheAllowedForTask(task)) {
|
|
490
|
-
if (this.settings.cacheOnly) {
|
|
491
|
-
throw OpErrors.cache(`Cache disabled for task "${task.config.name}"`)
|
|
492
|
-
.inTask(context.task)
|
|
493
|
-
.inStep(context.step)
|
|
494
|
-
.build();
|
|
495
|
-
}
|
|
496
|
-
return false;
|
|
497
|
-
}
|
|
498
|
-
const decision = await this.computeCacheOpDecision(op, context, task);
|
|
499
|
-
const entry = await this.cacheStore?.readOp(decision.key, this.settings.ttlMs ?? null);
|
|
500
|
-
if (entry) {
|
|
501
|
-
if (Object.keys(entry.files).length > 0) {
|
|
502
|
-
await writeFiles(context.workspace, entry.files);
|
|
503
|
-
}
|
|
504
|
-
this.reporter?.emit('cache_hit', { cache: op.name, scope: 'op' });
|
|
505
|
-
return true;
|
|
506
|
-
}
|
|
507
|
-
this.reporter?.emit('cache_miss', { cache: op.name, scope: 'op' });
|
|
508
|
-
if (this.settings.cacheOnly) {
|
|
509
|
-
throw OpErrors.cache(`Cache miss for "${op.name}"`)
|
|
510
|
-
.inTask(context.task)
|
|
511
|
-
.inStep(context.step)
|
|
512
|
-
.build();
|
|
513
|
-
}
|
|
514
|
-
if (recordPending) {
|
|
515
|
-
this.enqueuePendingSave(task.config.name, {
|
|
516
|
-
key: decision.key,
|
|
517
|
-
name: op.name,
|
|
518
|
-
paths: op.paths,
|
|
519
|
-
});
|
|
520
|
-
}
|
|
521
|
-
return false;
|
|
522
|
-
}
|
|
523
|
-
async executeCacheOrOp(op, task, context, state, executeOp) {
|
|
524
|
-
if (!this.cacheAllowedForTask(task)) {
|
|
525
|
-
if (this.settings.cacheOnly) {
|
|
526
|
-
throw OpErrors.cache(`Cache disabled for task "${task.config.name}"`)
|
|
527
|
-
.inTask(context.task)
|
|
528
|
-
.inStep(context.step)
|
|
529
|
-
.build();
|
|
530
|
-
}
|
|
531
|
-
await this.executeInline(op.compute, task, context, state, executeOp);
|
|
532
|
-
return false;
|
|
533
|
-
}
|
|
534
|
-
const cacheHit = await this.executeCacheOp(op, task, context, state, false);
|
|
535
|
-
if (cacheHit)
|
|
536
|
-
return true;
|
|
537
|
-
if (this.settings.cacheOnly) {
|
|
538
|
-
throw OpErrors.cache(`Cache miss for "${op.name}"`)
|
|
539
|
-
.inTask(context.task)
|
|
540
|
-
.inStep(context.step)
|
|
541
|
-
.build();
|
|
542
|
-
}
|
|
543
|
-
await this.executeInline(op.compute, task, context, state, executeOp);
|
|
544
|
-
await this.saveCacheOpSnapshot(op, context, task);
|
|
545
|
-
return false;
|
|
546
|
-
}
|
|
547
|
-
async executeCachedCommand(op, task, context, _state, executeBaseOp) {
|
|
548
|
-
if (!this.cacheAllowedForTask(task)) {
|
|
549
|
-
if (this.settings.cacheOnly) {
|
|
550
|
-
throw OpErrors.cache(`Cache disabled for task "${task.config.name}"`)
|
|
551
|
-
.inTask(context.task)
|
|
552
|
-
.inStep(context.step)
|
|
553
|
-
.build();
|
|
554
|
-
}
|
|
555
|
-
return executeBaseOp(op);
|
|
556
|
-
}
|
|
557
|
-
const decision = await this.computeCommandCacheDecision(op, context, task);
|
|
558
|
-
if (!decision) {
|
|
559
|
-
return executeBaseOp(op);
|
|
560
|
-
}
|
|
561
|
-
const entry = await this.cacheStore?.readOp(decision.key, this.settings.ttlMs ?? null);
|
|
562
|
-
if (entry) {
|
|
563
|
-
if (Object.keys(entry.files).length > 0) {
|
|
564
|
-
await writeFiles(context.workspace, entry.files);
|
|
565
|
-
}
|
|
566
|
-
this.reporter?.emit('cache_hit', { cache: decision.name, scope: 'command' });
|
|
567
|
-
return entry.result;
|
|
568
|
-
}
|
|
569
|
-
this.reporter?.emit('cache_miss', { cache: decision.name, scope: 'command' });
|
|
570
|
-
if (this.settings.cacheOnly) {
|
|
571
|
-
throw OpErrors.cache(`Cache miss for command "${decision.name}"`)
|
|
572
|
-
.inTask(context.task)
|
|
573
|
-
.inStep(context.step)
|
|
574
|
-
.build();
|
|
575
|
-
}
|
|
576
|
-
const result = await executeBaseOp(op);
|
|
577
|
-
if (this.shouldCacheCommandResult(op, result)) {
|
|
578
|
-
const files = await collectFiles(context.workspace, decision.outputs);
|
|
579
|
-
await this.cacheStore?.saveOp(decision.key, {
|
|
580
|
-
meta: {
|
|
581
|
-
key: decision.key,
|
|
582
|
-
createdAt: new Date().toISOString(),
|
|
583
|
-
kind: 'command',
|
|
584
|
-
command: decision.command,
|
|
585
|
-
inputs: decision.inputs,
|
|
586
|
-
outputs: decision.outputs,
|
|
587
|
-
},
|
|
588
|
-
result,
|
|
589
|
-
files,
|
|
590
|
-
});
|
|
591
|
-
}
|
|
592
|
-
return result;
|
|
593
|
-
}
|
|
594
|
-
async executeInline(compute, task, context, state, executeOp) {
|
|
595
|
-
const iterator = compute({ task, workspace: this.workspace });
|
|
596
|
-
const inlineStep = new StepBuilder(context.step, () => iterator, []);
|
|
597
|
-
let cursor = iterator.next();
|
|
598
|
-
while (!cursor.done) {
|
|
599
|
-
const op = this.toOp(cursor.value);
|
|
600
|
-
const result = await executeOp(op, task, inlineStep, context.env, state);
|
|
601
|
-
cursor = iterator.next(result);
|
|
602
|
-
}
|
|
603
|
-
}
|
|
604
|
-
toOp(value) {
|
|
605
|
-
if (!isOperation(value)) {
|
|
606
|
-
throw new Error('Step yielded a non-operation value');
|
|
607
|
-
}
|
|
608
|
-
return value.toOp();
|
|
609
|
-
}
|
|
610
|
-
async computeCacheOpDecision(op, context, task) {
|
|
611
|
-
if (op.paths.length === 0) {
|
|
612
|
-
throw OpErrors.cache(`Cache "${op.name}" has no paths`)
|
|
613
|
-
.inTask(context.task)
|
|
614
|
-
.inStep(context.step)
|
|
615
|
-
.build();
|
|
616
|
-
}
|
|
617
|
-
const fileHash = op.key?.files
|
|
618
|
-
? await hashFiles(this.workspace, op.key.files, this.cacheExcludes())
|
|
619
|
-
: { hash: hashString(''), files: {} };
|
|
620
|
-
const secretEnv = this.collectSecretEnvNames(task);
|
|
621
|
-
const envValues = {};
|
|
622
|
-
for (const name of op.key?.env ?? []) {
|
|
623
|
-
envValues[name] = secretEnv.has(name) ? '__secret__' : (context.env[name] ?? '__unset__');
|
|
624
|
-
}
|
|
625
|
-
const envHash = hashNamedValues(envValues);
|
|
626
|
-
const keyComponents = {
|
|
627
|
-
name: op.name,
|
|
628
|
-
paths: op.paths,
|
|
629
|
-
files: fileHash.hash,
|
|
630
|
-
filePatternsHash: hashString((op.key?.files ?? []).join('\n')),
|
|
631
|
-
env: envHash,
|
|
632
|
-
value: op.key?.value ?? '',
|
|
633
|
-
bust: this.settings.bust ?? '',
|
|
634
|
-
version: this.cacheVersion(),
|
|
635
|
-
};
|
|
636
|
-
return { key: hashObject(keyComponents) };
|
|
637
|
-
}
|
|
638
|
-
async saveCacheOpSnapshot(op, context, task) {
|
|
639
|
-
const decision = await this.computeCacheOpDecision(op, context, task);
|
|
640
|
-
const files = await collectFiles(this.workspace, op.paths);
|
|
641
|
-
await this.cacheStore?.saveOp(decision.key, {
|
|
642
|
-
meta: {
|
|
643
|
-
key: decision.key,
|
|
644
|
-
createdAt: new Date().toISOString(),
|
|
645
|
-
kind: 'cache',
|
|
646
|
-
outputs: op.paths,
|
|
647
|
-
},
|
|
648
|
-
files,
|
|
649
|
-
});
|
|
650
|
-
}
|
|
651
|
-
async computeCommandCacheDecision(op, context, task) {
|
|
652
|
-
const cacheOptions = op.cache;
|
|
653
|
-
if (!cacheOptions)
|
|
654
|
-
return null;
|
|
655
|
-
const { template, signature } = this.normalizeCommandTemplate(op.template);
|
|
656
|
-
const resolvedOutputs = this.resolveCommandOutputs(cacheOptions, signature);
|
|
657
|
-
if (resolvedOutputs.length === 0)
|
|
658
|
-
return null;
|
|
659
|
-
const uniqueInputs = this.resolveCommandInputs(cacheOptions, signature);
|
|
660
|
-
if (uniqueInputs.length === 0)
|
|
661
|
-
return null;
|
|
662
|
-
const inputs = await hashFiles(this.workspace, uniqueInputs, this.cacheExcludes());
|
|
663
|
-
const envHash = this.hashCommandEnv(cacheOptions, context.env, task);
|
|
664
|
-
const outputsHash = hashString(resolvedOutputs.join('\n'));
|
|
665
|
-
const keyComponents = {
|
|
666
|
-
task: context.task,
|
|
667
|
-
step: context.step,
|
|
668
|
-
capture: op.capture,
|
|
669
|
-
command: template,
|
|
670
|
-
inputs: inputs.hash,
|
|
671
|
-
inputPatternsHash: hashString(uniqueInputs.join('\n')),
|
|
672
|
-
outputsHash,
|
|
673
|
-
env: envHash,
|
|
674
|
-
value: cacheOptions.key?.value ?? '',
|
|
675
|
-
bust: this.settings.bust ?? '',
|
|
676
|
-
version: this.cacheVersion(),
|
|
677
|
-
};
|
|
678
|
-
return {
|
|
679
|
-
key: hashObject(keyComponents),
|
|
680
|
-
name: signature || `${context.task}:${context.step}`,
|
|
681
|
-
outputs: resolvedOutputs,
|
|
682
|
-
command: { capture: op.capture, template },
|
|
683
|
-
inputs: { patterns: uniqueInputs, files: inputs.files },
|
|
684
|
-
};
|
|
685
|
-
}
|
|
686
|
-
normalizeCommandTemplate(template) {
|
|
687
|
-
const normalized = template.map((part) => ({
|
|
688
|
-
type: part.type,
|
|
689
|
-
...(part.type === 'text' || part.type === 'interpolated'
|
|
690
|
-
? { value: part.value }
|
|
691
|
-
: part.type === 'secret'
|
|
692
|
-
? { name: part.name }
|
|
693
|
-
: { name: part.ref.name }),
|
|
694
|
-
}));
|
|
695
|
-
const signature = template
|
|
696
|
-
.map((part) => {
|
|
697
|
-
if (part.type === 'text')
|
|
698
|
-
return part.value;
|
|
699
|
-
if (part.type === 'interpolated')
|
|
700
|
-
return String(part.value);
|
|
701
|
-
if (part.type === 'secret')
|
|
702
|
-
return `<secret:${part.name}>`;
|
|
703
|
-
if (part.type === 'param')
|
|
704
|
-
return `<param:${part.ref.name}>`;
|
|
705
|
-
return `<value:${part.ref.name}>`;
|
|
706
|
-
})
|
|
707
|
-
.join('')
|
|
708
|
-
.replace(/\s+/g, ' ')
|
|
709
|
-
.trim();
|
|
710
|
-
return { template: normalized, signature };
|
|
711
|
-
}
|
|
712
|
-
resolveCommandOutputs(cacheOptions, signature) {
|
|
713
|
-
if (cacheOptions.outputs && cacheOptions.outputs.length > 0) {
|
|
714
|
-
return cacheOptions.outputs;
|
|
715
|
-
}
|
|
716
|
-
return inferOutputsFromCommand(signature);
|
|
717
|
-
}
|
|
718
|
-
resolveCommandInputs(cacheOptions, signature) {
|
|
719
|
-
const inputPatterns = [...(cacheOptions.inputs ?? []), ...(cacheOptions.key?.files ?? [])];
|
|
720
|
-
const resolvedInputs = inputPatterns.length > 0 ? inputPatterns : inferInputsFromCommand(signature);
|
|
721
|
-
return Array.from(new Set(resolvedInputs));
|
|
722
|
-
}
|
|
723
|
-
hashCommandEnv(cacheOptions, env, task) {
|
|
724
|
-
const envValues = {};
|
|
725
|
-
const envNames = new Set(cacheOptions.env ?? []);
|
|
726
|
-
for (const name of cacheOptions.key?.env ?? [])
|
|
727
|
-
envNames.add(name);
|
|
728
|
-
const secretEnv = this.collectSecretEnvNames(task);
|
|
729
|
-
for (const name of envNames) {
|
|
730
|
-
envValues[name] = secretEnv.has(name) ? '__secret__' : (env[name] ?? '__unset__');
|
|
731
|
-
}
|
|
732
|
-
return hashNamedValues(envValues);
|
|
733
|
-
}
|
|
734
|
-
shouldCacheCommandResult(op, result) {
|
|
735
|
-
if (op.capture === 'ok')
|
|
736
|
-
return result === true;
|
|
737
|
-
if (op.capture === 'code')
|
|
738
|
-
return result === 0;
|
|
739
|
-
if (op.capture === 'result') {
|
|
740
|
-
return Boolean(result && typeof result === 'object' && result.ok);
|
|
741
|
-
}
|
|
742
|
-
return true;
|
|
743
|
-
}
|
|
744
|
-
async emitCacheDebug(taskName, decision, status) {
|
|
745
|
-
const meta = await this.cacheStore?.latestTaskMeta(taskName);
|
|
746
|
-
const changes = this.diffInputFiles(decision.inputs.files, meta?.inputs.files ?? {});
|
|
747
|
-
const lines = [
|
|
748
|
-
`Task: ${taskName}`,
|
|
749
|
-
` Cache key: sha256:${decision.key}`,
|
|
750
|
-
' Components:',
|
|
751
|
-
...Object.entries(decision.components).map(([key, value]) => ` ${key}: sha256:${value}`),
|
|
752
|
-
` Status: ${status}${decision.reason ? ` (${decision.reason})` : ''}`,
|
|
753
|
-
];
|
|
754
|
-
if (changes.length > 0) {
|
|
755
|
-
lines.push(' Changed files:');
|
|
756
|
-
for (const change of changes) {
|
|
757
|
-
lines.push(` - ${change}`);
|
|
758
|
-
}
|
|
759
|
-
}
|
|
760
|
-
if (this.reporter) {
|
|
761
|
-
this.reporter.emit('cache_debug', { task: taskName, lines });
|
|
762
|
-
}
|
|
763
|
-
else {
|
|
764
|
-
for (const line of lines) {
|
|
765
|
-
this.logger.info(this.resolver.redactSecrets(line));
|
|
766
|
-
}
|
|
767
|
-
}
|
|
768
|
-
}
|
|
769
|
-
diffInputFiles(current, previous) {
|
|
770
|
-
const changes = [];
|
|
771
|
-
const currentKeys = new Set(Object.keys(current));
|
|
772
|
-
const previousKeys = new Set(Object.keys(previous));
|
|
773
|
-
for (const key of currentKeys) {
|
|
774
|
-
if (!previousKeys.has(key)) {
|
|
775
|
-
changes.push(`${key} (added)`);
|
|
776
|
-
}
|
|
777
|
-
else if (current[key] !== previous[key]) {
|
|
778
|
-
changes.push(`${key} (modified)`);
|
|
779
|
-
}
|
|
780
|
-
}
|
|
781
|
-
for (const key of previousKeys) {
|
|
782
|
-
if (!currentKeys.has(key)) {
|
|
783
|
-
changes.push(`${key} (removed)`);
|
|
784
|
-
}
|
|
785
|
-
}
|
|
786
|
-
return changes;
|
|
787
|
-
}
|
|
788
|
-
enqueuePendingSave(taskName, entry) {
|
|
789
|
-
const existing = this.pendingOpSaves.get(taskName) ?? [];
|
|
790
|
-
existing.push(entry);
|
|
791
|
-
this.pendingOpSaves.set(taskName, existing);
|
|
792
|
-
}
|
|
793
|
-
}
|
|
794
|
-
//# sourceMappingURL=coordinator.js.map
|