hammerkit 1.6.0 → 1.7.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.
- package/README.md +4 -4
- package/build.schema.json +49 -2
- package/dist/cache/backends/local-cache-backend.js +58 -1
- package/dist/cache/backends/registry/registry-client.js +264 -0
- package/dist/cache/backends/registry/registry-credentials.js +81 -0
- package/dist/cache/backends/registry/registry-reference.js +36 -0
- package/dist/cache/backends/registry-cache-backend.js +145 -0
- package/dist/cache/backends/s3-cache-backend.js +99 -46
- package/dist/cache/cache-backend-registry.js +2 -0
- package/dist/cache/cache-inventory.js +84 -0
- package/dist/cache/cache-sync.js +77 -0
- package/dist/cache/explain.js +214 -0
- package/dist/cache/last-resolved.js +48 -0
- package/dist/cache/read-only.js +15 -0
- package/dist/cache/retention.js +61 -0
- package/dist/cli.js +43 -0
- package/dist/docker/using-container.js +5 -1
- package/dist/executer/archive-task-entry.js +21 -0
- package/dist/executer/await-completed-dependencies.js +32 -14
- package/dist/executer/cache-metadata.js +9 -2
- package/dist/executer/docker-service.js +2 -1
- package/dist/executer/docker-task.js +2 -1
- package/dist/executer/dry-run.js +73 -0
- package/dist/executer/event-cache.js +6 -5
- package/dist/executer/execute-command.js +21 -3
- package/dist/executer/execute-work-service.js +10 -1
- package/dist/executer/execute-work-task.js +95 -25
- package/dist/executer/execute-work-tree.js +26 -1
- package/dist/executer/get-scheduler-execute-result.js +4 -1
- package/dist/executer/lazy-resolver.js +15 -3
- package/dist/executer/reset-work-tree.js +2 -0
- package/dist/executer/run-summary.js +102 -0
- package/dist/executer/scheduler/enqueue-next.js +15 -60
- package/dist/executer/scheduler/state-key.js +78 -0
- package/dist/kubernetes/await-running-state.js +73 -19
- package/dist/log.js +16 -6
- package/dist/logging/live-logger.js +2 -1
- package/dist/optimizer/calculate-checksum.js +8 -2
- package/dist/optimizer/get-work-cache-stats.js +66 -16
- package/dist/optimizer/work-task-cache-description.js +34 -5
- package/dist/planner/project-root.js +26 -0
- package/dist/planner/utils/append-work-service.js +2 -1
- package/dist/planner/utils/append-work-task.js +7 -1
- package/dist/planner/utils/parse-work-source.js +35 -30
- package/dist/planner/utils/portable-path.js +30 -0
- package/dist/planner/work-cache-id.js +2 -2
- package/dist/planner/work-graph-serializer.js +103 -0
- package/dist/planner/work-instance-id.js +15 -0
- package/dist/planner/work-runtime-docker.js +53 -15
- package/dist/planner/work-runtime-kubernetes.js +35 -3
- package/dist/planner/work-runtime-local.js +9 -3
- package/dist/planner/work-service-id.js +16 -4
- package/dist/program.js +235 -1
- package/dist/schema/build-file-base-task-schema.js +4 -0
- package/dist/schema/cache-schema.js +21 -2
- package/dist/schema/duration-schema.js +18 -0
- package/dist/schema/reference-parser.js +3 -0
- package/dist/schema/size-schema.js +18 -0
- package/dist/schema/work-scope-parser.js +15 -2
- package/dist/utils/units.js +87 -0
- package/package.json +12 -1
- package/.agents/FEEDBACK.md +0 -74
- package/.agents/skills/before-commit/SKILL.md +0 -101
- package/.agents/skills/do-the-work/SKILL.md +0 -64
- package/.agents/skills/fix-ci/SKILL.md +0 -98
- package/.agents/skills/speckit-git-commit/SKILL.md +0 -53
- package/.agents/skills/speckit-git-feature/SKILL.md +0 -72
- package/.agents/skills/speckit-git-initialize/SKILL.md +0 -54
- package/.agents/skills/speckit-git-remote/SKILL.md +0 -50
- package/.agents/skills/speckit-git-validate/SKILL.md +0 -54
- package/.devcontainer/Dockerfile +0 -5
- package/.devcontainer/devcontainer.json +0 -40
- package/.gitbook.yaml +0 -5
- package/.specify/extensions/.registry +0 -37
- package/.specify/extensions/git/README.md +0 -100
- package/.specify/extensions/git/commands/speckit.git.commit.md +0 -48
- package/.specify/extensions/git/commands/speckit.git.feature.md +0 -67
- package/.specify/extensions/git/commands/speckit.git.initialize.md +0 -49
- package/.specify/extensions/git/commands/speckit.git.remote.md +0 -45
- package/.specify/extensions/git/commands/speckit.git.validate.md +0 -49
- package/.specify/extensions/git/config-template.yml +0 -62
- package/.specify/extensions/git/extension.yml +0 -140
- package/.specify/extensions/git/git-config.yml +0 -62
- package/.specify/extensions.yml +0 -149
- package/.specify/feature.json +0 -3
- package/.specify/init-options.json +0 -10
- package/.specify/integration.json +0 -15
- package/.specify/integrations/claude.manifest.json +0 -16
- package/.specify/integrations/speckit.manifest.json +0 -17
- package/.specify/memory/constitution.md +0 -162
- package/.specify/templates/checklist-template.md +0 -40
- package/.specify/templates/constitution-template.md +0 -50
- package/.specify/templates/plan-template.md +0 -113
- package/.specify/templates/spec-template.md +0 -131
- package/.specify/templates/tasks-template.md +0 -252
- package/.specify/workflows/speckit/workflow.yml +0 -77
- package/.specify/workflows/workflow-registry.json +0 -13
- package/AGENTS.md +0 -94
- package/Dockerfile +0 -32
- package/FOLLOWUPS.md +0 -34
- package/SUMMARY.md +0 -68
- package/VALUES.md +0 -53
- package/dist/cache/auto-pull.spec.js +0 -123
- package/dist/cache/backends/local-cache-backend.spec.js +0 -72
- package/dist/cache/backends/s3-cache-backend.spec.js +0 -118
- package/dist/cache/cache-backend-registry.spec.js +0 -22
- package/dist/cache/resolve-cache.spec.js +0 -52
- package/dist/cli.spec.js +0 -23
- package/dist/console/get-logger.spec.js +0 -23
- package/dist/docker/get-default-platform.spec.js +0 -29
- package/dist/docker/longest-common-prefix.spec.js +0 -26
- package/dist/docker/package.spec.js +0 -145
- package/dist/docker/remove-container.spec.js +0 -32
- package/dist/docker/using-container.spec.js +0 -90
- package/dist/environment/merge-environment-variables.spec.js +0 -24
- package/dist/environment/replace-env-variables.spec.js +0 -47
- package/dist/executer/abort.spec.js +0 -18
- package/dist/executer/environment-mock.spec.js +0 -22
- package/dist/executer/execute.spec.js +0 -51
- package/dist/executer/get-service-containers.spec.js +0 -59
- package/dist/executer/get-service-env-hints.spec.js +0 -65
- package/dist/executer/label-values.spec.js +0 -61
- package/dist/executer/lazy-resolver.spec.js +0 -20
- package/dist/executer/process-manager.spec.js +0 -54
- package/dist/executer/scheduler/enqueue-next.spec.js +0 -136
- package/dist/executer/state-resolver.spec.js +0 -37
- package/dist/executer/state.spec.js +0 -66
- package/dist/executer/states.spec.js +0 -14
- package/dist/file/get-file-context.spec.js +0 -178
- package/dist/file/move-files.spec.js +0 -48
- package/dist/kubernetes/ensure-http-route.spec.js +0 -70
- package/dist/kubernetes/ensure-namespace.spec.js +0 -39
- package/dist/kubernetes/resolve-pod-name.spec.js +0 -81
- package/dist/log.spec.js +0 -131
- package/dist/logging/grouped-logger.spec.js +0 -84
- package/dist/logging/interactive-logger.spec.js +0 -57
- package/dist/logging/live-logger.spec.js +0 -37
- package/dist/logging/loggers.spec.js +0 -154
- package/dist/optimizer/calculate-checksum.spec.js +0 -34
- package/dist/optimizer/get-cache-directory.spec.js +0 -59
- package/dist/optimizer/get-work-cache-stats.spec.js +0 -79
- package/dist/optimizer/work-task-cache-description.spec.js +0 -45
- package/dist/parser/default-build-file.spec.js +0 -48
- package/dist/parser/parse-label-arguments.spec.js +0 -27
- package/dist/parser/read-build-file.spec.js +0 -89
- package/dist/parser/read-env-file.spec.js +0 -42
- package/dist/planner/utils/append-work-service.spec.js +0 -71
- package/dist/planner/utils/get-container-user.spec.js +0 -30
- package/dist/planner/utils/get-default-kube-config.spec.js +0 -11
- package/dist/planner/utils/normalize-path.spec.js +0 -24
- package/dist/planner/utils/parse-work-command.spec.js +0 -21
- package/dist/planner/utils/parse-work-generate.spec.js +0 -45
- package/dist/planner/utils/parse-work-mount.spec.js +0 -62
- package/dist/planner/utils/parse-work-mounts.spec.js +0 -19
- package/dist/planner/utils/parse-work-port.spec.js +0 -24
- package/dist/planner/utils/parse-work-ports.spec.js +0 -24
- package/dist/planner/utils/parse-work-source.spec.js +0 -58
- package/dist/planner/utils/parse-work-volume.spec.js +0 -18
- package/dist/planner/utils/plan-work-tasks.spec.js +0 -30
- package/dist/planner/utils/plan-work-volume.spec.js +0 -33
- package/dist/planner/utils/split-name.spec.js +0 -16
- package/dist/planner/utils/template-value.spec.js +0 -14
- package/dist/planner/validate.spec.js +0 -251
- package/dist/planner/work-cache-id.spec.js +0 -173
- package/dist/planner/work-runtime-local.spec.js +0 -193
- package/dist/program-commands.spec.js +0 -84
- package/dist/program.spec.js +0 -43
- package/dist/schema/build-file-schema.spec.js +0 -61
- package/dist/schema/doc-build-files.spec.js +0 -223
- package/dist/schema/parse-error.spec.js +0 -18
- package/dist/schema/reference-parser.spec.js +0 -83
- package/dist/schema/schema-parser.spec.js +0 -80
- package/dist/schema/type-guards.spec.js +0 -23
- package/dist/schema/work-scope-parser.spec.js +0 -66
- package/dist/service/update-service-status.spec.js +0 -27
- package/dist/testing/ensure-local-registry.js +0 -134
- package/dist/testing/example-test-suite.js +0 -63
- package/dist/testing/expect.js +0 -82
- package/dist/testing/get-test-suite.js +0 -9
- package/dist/testing/http-get.js +0 -21
- package/dist/testing/integration/cache.spec.js +0 -49
- package/dist/testing/integration/cancellation.spec.js +0 -45
- package/dist/testing/integration/clean.spec.js +0 -43
- package/dist/testing/integration/cmd.spec.js +0 -15
- package/dist/testing/integration/concurrency.spec.js +0 -19
- package/dist/testing/integration/docker-package-registry.spec.js +0 -44
- package/dist/testing/integration/docker-service-crash.spec.js +0 -36
- package/dist/testing/integration/docker.spec.js +0 -18
- package/dist/testing/integration/env.spec.js +0 -48
- package/dist/testing/integration/environment.spec.js +0 -97
- package/dist/testing/integration/error.spec.js +0 -19
- package/dist/testing/integration/export.spec.js +0 -22
- package/dist/testing/integration/extend.spec.js +0 -29
- package/dist/testing/integration/glob.spec.js +0 -40
- package/dist/testing/integration/include.spec.js +0 -31
- package/dist/testing/integration/invalid.spec.js +0 -17
- package/dist/testing/integration/invalid_loop.spec.js +0 -56
- package/dist/testing/integration/k8s-state-cache.spec.js +0 -41
- package/dist/testing/integration/kubernetes-service.spec.js +0 -148
- package/dist/testing/integration/kubernetes.spec.js +0 -99
- package/dist/testing/integration/labels.spec.js +0 -42
- package/dist/testing/integration/local.spec.js +0 -14
- package/dist/testing/integration/monorepo.spec.js +0 -15
- package/dist/testing/integration/multi-line.spec.js +0 -17
- package/dist/testing/integration/reference.spec.js +0 -21
- package/dist/testing/integration/services.spec.js +0 -38
- package/dist/testing/integration/store.spec.js +0 -52
- package/dist/testing/integration/unknown.spec.js +0 -28
- package/dist/testing/integration/validate.spec.js +0 -160
- package/dist/testing/integration/watch-dependency.spec.js +0 -33
- package/dist/testing/integration/watch.spec.js +0 -45
- package/dist/testing/requires-kubernetes.js +0 -18
- package/dist/testing/requires-linux-containers.js +0 -18
- package/dist/testing/requires-windows-containers.js +0 -18
- package/dist/testing/test-case.js +0 -75
- package/dist/testing/test-streams.js +0 -28
- package/dist/testing/test-suite-setup.js +0 -3
- package/dist/testing/test-suite.js +0 -3
- package/dist/testing/testing-timeout.js +0 -32
- package/dist/utils/abort-event.spec.js +0 -46
- package/dist/utils/buffer-context.spec.js +0 -38
- package/dist/utils/ci.spec.js +0 -53
- package/dist/utils/debouncer.spec.js +0 -58
- package/dist/utils/emitter.spec.js +0 -34
- package/dist/utils/empty-writable.spec.js +0 -18
- package/dist/utils/environment-config.spec.js +0 -26
- package/dist/utils/fail-never.spec.js +0 -9
- package/dist/utils/sleep.spec.js +0 -22
- package/dist/version.spec.js +0 -12
- package/specs/build-file/caches/spec.md +0 -114
- package/specs/build-file/environment-variables/spec.md +0 -82
- package/specs/build-file/includes/spec.md +0 -60
- package/specs/build-file/references/spec.md +0 -56
- package/specs/build-file/spec.md +0 -62
- package/specs/cli/clean/spec.md +0 -85
- package/specs/cli/down/spec.md +0 -85
- package/specs/cli/execute/spec.md +0 -101
- package/specs/cli/init/spec.md +0 -64
- package/specs/cli/ls/spec.md +0 -66
- package/specs/cli/package/spec.md +0 -110
- package/specs/cli/spec.md +0 -73
- package/specs/cli/store-restore/spec.md +0 -74
- package/specs/cli/up/spec.md +0 -80
- package/specs/cli/validate/spec.md +0 -70
- package/specs/labels/spec.md +0 -91
- package/specs/service/container/spec.md +0 -120
- package/specs/service/kubernetes/spec.md +0 -97
- package/specs/service/spec.md +0 -105
- package/specs/task/caching/spec.md +0 -107
- package/specs/task/container/spec.md +0 -118
- package/specs/task/dependencies/spec.md +0 -70
- package/specs/task/extending/spec.md +0 -74
- package/specs/task/kubernetes/spec.md +0 -125
- package/specs/task/needs/spec.md +0 -59
- package/specs/task/spec.md +0 -87
- package/specs/task/watching/spec.md +0 -69
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.computeStateKey = exports.combineStateKeys = exports.resolveEffective = void 0;
|
|
4
|
+
const crypto_1 = require("crypto");
|
|
5
|
+
const get_work_cache_stats_1 = require("../../optimizer/get-work-cache-stats");
|
|
6
|
+
// The state-key engine is shared by the scheduler's hit/miss decision
|
|
7
|
+
// (`checkCacheState`) and the read-only cache-explain engine, so a prediction
|
|
8
|
+
// can never diverge from the decision it predicts — there is one implementation.
|
|
9
|
+
function resolveEffective(item, fallbackMethod) {
|
|
10
|
+
const declared = item.data.caching;
|
|
11
|
+
if (declared.implicit) {
|
|
12
|
+
return { ...declared, method: fallbackMethod };
|
|
13
|
+
}
|
|
14
|
+
return declared;
|
|
15
|
+
}
|
|
16
|
+
exports.resolveEffective = resolveEffective;
|
|
17
|
+
// A task whose inputs cannot be observed cannot be proven up to date, so it
|
|
18
|
+
// always runs, and so does everything depending on it (specs/task SC-001). That
|
|
19
|
+
// is a task that declares no `src` of its own — the sources it inherits from its
|
|
20
|
+
// dependencies say nothing about the files its own commands read — and a task
|
|
21
|
+
// whose `src` entries all match no file (a typo, a moved path). Services are not
|
|
22
|
+
// affected.
|
|
23
|
+
function isOwnProvable(item, unmatched) {
|
|
24
|
+
// unmatched ⊆ declared, so this is also false when nothing is declared
|
|
25
|
+
return unmatched.length < item.data.src.filter((src) => !src.inherited).length;
|
|
26
|
+
}
|
|
27
|
+
// Fold a task's own state key together with the state keys of its dependencies.
|
|
28
|
+
// A change anywhere in the dependency subtree must change the resulting key, so
|
|
29
|
+
// that a downstream task is invalidated when an upstream source changes — even
|
|
30
|
+
// if the downstream task's own sources are untouched.
|
|
31
|
+
function combineStateKeys(ownKey, depKeys) {
|
|
32
|
+
if (depKeys.length === 0) {
|
|
33
|
+
return ownKey;
|
|
34
|
+
}
|
|
35
|
+
// Sort so the key is independent of dependency declaration order.
|
|
36
|
+
const sorted = [...depKeys].sort();
|
|
37
|
+
return (0, crypto_1.createHash)('md5')
|
|
38
|
+
.update([ownKey, ...sorted].join(','))
|
|
39
|
+
.digest('hex');
|
|
40
|
+
}
|
|
41
|
+
exports.combineStateKeys = combineStateKeys;
|
|
42
|
+
// `none` only disables the cache *check* for a task; its sources still influence
|
|
43
|
+
// what depends on it, so a concrete method is always used for the hash itself.
|
|
44
|
+
function concreteMethod(resolved, defaultCacheMethod) {
|
|
45
|
+
return resolved.method === 'none' ? defaultCacheMethod : resolved.method;
|
|
46
|
+
}
|
|
47
|
+
// Compute an item's effective state key from its source files plus the effective
|
|
48
|
+
// state keys of its dependencies, recursively. This is derived purely from the
|
|
49
|
+
// work tree and the filesystem, so it does not depend on scheduler/run state —
|
|
50
|
+
// important because it runs before dependencies are awaited.
|
|
51
|
+
async function resolveEffectiveStateKey(item, defaultCacheMethod, environment, memo) {
|
|
52
|
+
const existing = memo.get(item.id());
|
|
53
|
+
if (existing !== undefined) {
|
|
54
|
+
return existing;
|
|
55
|
+
}
|
|
56
|
+
const { stateKey, provable } = await computeStateKey(item, defaultCacheMethod, environment, memo);
|
|
57
|
+
const effective = { stateKey, provable };
|
|
58
|
+
memo.set(item.id(), effective);
|
|
59
|
+
return effective;
|
|
60
|
+
}
|
|
61
|
+
// Compute the combined state key for a single item together with the source
|
|
62
|
+
// stats it was derived from, sharing one filesystem walk with callers that also
|
|
63
|
+
// need the stats (the scheduler, the explain engine).
|
|
64
|
+
async function computeStateKey(item, defaultCacheMethod, environment, memo = new Map()) {
|
|
65
|
+
const resolved = resolveEffective(item, defaultCacheMethod);
|
|
66
|
+
const { stats, unmatched } = await (0, get_work_cache_stats_1.getWorkItemCacheStats)(item, environment);
|
|
67
|
+
const ownKey = (0, get_work_cache_stats_1.getStateKey)(stats, concreteMethod(resolved, defaultCacheMethod));
|
|
68
|
+
const depKeys = [];
|
|
69
|
+
let provable = isOwnProvable(item, unmatched);
|
|
70
|
+
for (const dep of item.deps) {
|
|
71
|
+
const effective = await resolveEffectiveStateKey(dep, defaultCacheMethod, environment, memo);
|
|
72
|
+
depKeys.push(effective.stateKey);
|
|
73
|
+
provable = provable && effective.provable;
|
|
74
|
+
}
|
|
75
|
+
return { stateKey: combineStateKeys(ownKey, depKeys), stats, resolved, provable, unmatched };
|
|
76
|
+
}
|
|
77
|
+
exports.computeStateKey = computeStateKey;
|
|
78
|
+
//# sourceMappingURL=state-key.js.map
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.awaitDeployRunningState = exports.
|
|
3
|
+
exports.deleteJobAndWait = exports.awaitDeployRunningState = exports.deleteJob = exports.awaitJobCompletion = exports.awaitRunningState = void 0;
|
|
4
|
+
const abort_1 = require("../executer/abort");
|
|
5
|
+
const abort_event_1 = require("../utils/abort-event");
|
|
6
|
+
const apply_1 = require("./apply");
|
|
4
7
|
function awaitRunningState(instance, env, name, phase) {
|
|
5
8
|
return new Promise((resolve, reject) => {
|
|
6
9
|
const req = instance.watch.watch(`/api/v1/namespaces/${env.namespace}/pods`, {}, (type, obj) => {
|
|
@@ -22,27 +25,56 @@ function awaitRunningState(instance, env, name, phase) {
|
|
|
22
25
|
});
|
|
23
26
|
}
|
|
24
27
|
exports.awaitRunningState = awaitRunningState;
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
28
|
+
// Wait for a job to finish by polling its status: resolves once it succeeded,
|
|
29
|
+
// rejects when it failed (its `Failed` condition, i.e. retries are exhausted).
|
|
30
|
+
// On abort the job is deleted — with its pods — and an AbortError is thrown, so
|
|
31
|
+
// a cancelled or timed-out task leaves nothing running on the cluster.
|
|
32
|
+
async function awaitJobCompletion(instance, env, name, abort, pollInterval = 1000) {
|
|
33
|
+
for (;;) {
|
|
34
|
+
if (abort.aborted) {
|
|
35
|
+
await deleteJob(instance, env, name);
|
|
36
|
+
throw new abort_1.AbortError();
|
|
37
|
+
}
|
|
38
|
+
const job = await instance.batchApi.readNamespacedJobStatus(name, env.namespace);
|
|
39
|
+
const status = job.body.status;
|
|
40
|
+
if (status?.succeeded) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
const failed = status?.conditions?.find((c) => c.type === 'Failed' && c.status === 'True');
|
|
44
|
+
if (failed) {
|
|
45
|
+
throw new Error(`job ${name} failed: ${failed.message ?? failed.reason ?? 'unknown reason'}`);
|
|
46
|
+
}
|
|
47
|
+
await sleep(pollInterval, abort);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
exports.awaitJobCompletion = awaitJobCompletion;
|
|
51
|
+
function sleep(ms, abort) {
|
|
52
|
+
if (abort.aborted) {
|
|
53
|
+
return Promise.resolve();
|
|
54
|
+
}
|
|
55
|
+
return new Promise((resolve) => {
|
|
56
|
+
const timer = setTimeout(() => {
|
|
57
|
+
listener.close();
|
|
58
|
+
resolve();
|
|
59
|
+
}, ms);
|
|
60
|
+
// not aborted yet, so this callback can only fire later, after assignment
|
|
61
|
+
const listener = (0, abort_event_1.listenOnAbort)(abort, () => {
|
|
62
|
+
clearTimeout(timer);
|
|
63
|
+
resolve();
|
|
42
64
|
});
|
|
43
65
|
});
|
|
44
66
|
}
|
|
45
|
-
|
|
67
|
+
async function deleteJob(instance, env, name) {
|
|
68
|
+
try {
|
|
69
|
+
await instance.batchApi.deleteNamespacedJob(name, env.namespace, undefined, undefined, 0, undefined, 'Background');
|
|
70
|
+
}
|
|
71
|
+
catch (e) {
|
|
72
|
+
if ((0, apply_1.statusCodeOf)(e) !== 404) {
|
|
73
|
+
throw e;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
exports.deleteJob = deleteJob;
|
|
46
78
|
function awaitDeployRunningState(instance, env, name) {
|
|
47
79
|
return new Promise((resolve, reject) => {
|
|
48
80
|
const req = instance.watch.watch(`/apis/apps/v1/namespaces/${env.namespace}/deployments`, {}, (type, obj) => {
|
|
@@ -68,4 +100,26 @@ function awaitDeployRunningState(instance, env, name) {
|
|
|
68
100
|
});
|
|
69
101
|
}
|
|
70
102
|
exports.awaitDeployRunningState = awaitDeployRunningState;
|
|
103
|
+
// Delete a job and wait until it is gone: its name is reused for the task's next
|
|
104
|
+
// command (and the next run with the same state), and creating it while the old
|
|
105
|
+
// one is still being deleted would pick up the old job.
|
|
106
|
+
async function deleteJobAndWait(instance, env, name, abort, pollInterval = 500) {
|
|
107
|
+
await deleteJob(instance, env, name);
|
|
108
|
+
for (;;) {
|
|
109
|
+
try {
|
|
110
|
+
await instance.batchApi.readNamespacedJob(name, env.namespace);
|
|
111
|
+
}
|
|
112
|
+
catch (e) {
|
|
113
|
+
if ((0, apply_1.statusCodeOf)(e) === 404) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
throw e;
|
|
117
|
+
}
|
|
118
|
+
if (abort.aborted) {
|
|
119
|
+
throw new abort_1.AbortError();
|
|
120
|
+
}
|
|
121
|
+
await sleep(pollInterval, abort);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
exports.deleteJobAndWait = deleteJobAndWait;
|
|
71
125
|
//# sourceMappingURL=await-running-state.js.map
|
package/dist/log.js
CHANGED
|
@@ -109,9 +109,14 @@ async function printWorkTreeResult(schedulerState, env) {
|
|
|
109
109
|
for (const task of (0, plan_work_tasks_1.iterateWorkTasks)(schedulerState)) {
|
|
110
110
|
let message = `${formatDate(new Date())} ${getType('task')} ${getNodeName(task.name, maxNodeNameLength)} - ${getStateText(task.state.current)}`;
|
|
111
111
|
if (task.state.current.type === 'completed') {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
112
|
+
if (task.state.current.skipped) {
|
|
113
|
+
message += ' [SKIPPED]';
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
message += ` in ${task.state.current.duration}ms`;
|
|
117
|
+
if (task.state.current.cached) {
|
|
118
|
+
message += ' [CACHED]';
|
|
119
|
+
}
|
|
115
120
|
}
|
|
116
121
|
}
|
|
117
122
|
if (task.state.current.type === 'crash') {
|
|
@@ -263,9 +268,14 @@ function writeWorkTreeStatus(schedulerState, env, ticker) {
|
|
|
263
268
|
}
|
|
264
269
|
}
|
|
265
270
|
else if (task.state.current.type === 'completed') {
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
271
|
+
if (task.state.current.skipped) {
|
|
272
|
+
message += ' [SKIPPED]';
|
|
273
|
+
}
|
|
274
|
+
else {
|
|
275
|
+
message += ` in ${task.state.current.duration}ms`;
|
|
276
|
+
if (task.state.current.cached) {
|
|
277
|
+
message += ' [CACHED]';
|
|
278
|
+
}
|
|
269
279
|
}
|
|
270
280
|
}
|
|
271
281
|
else if (task.state.current.type === 'error') {
|
|
@@ -4,11 +4,12 @@ exports.liveLogger = void 0;
|
|
|
4
4
|
const log_1 = require("../log");
|
|
5
5
|
function liveLogger(state, env) {
|
|
6
6
|
const maxNodeNameLength = (0, log_1.getWorkItemMaxLength)(state.current);
|
|
7
|
-
env.status.on((log) => {
|
|
7
|
+
const listener = env.status.on((log) => {
|
|
8
8
|
(0, log_1.writeWorkItemLogToConsole)(env, log, maxNodeNameLength);
|
|
9
9
|
});
|
|
10
10
|
return {
|
|
11
11
|
async complete(evt, env) {
|
|
12
|
+
listener.close();
|
|
12
13
|
await (0, log_1.printWorkTreeResult)(evt.state, env);
|
|
13
14
|
},
|
|
14
15
|
};
|
|
@@ -2,9 +2,15 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.calculateChecksum = void 0;
|
|
4
4
|
const crypto_1 = require("crypto");
|
|
5
|
+
// Hashes the raw bytes: decoding to text first would map every invalid UTF-8
|
|
6
|
+
// sequence to U+FFFD, so two different binary files could share a checksum.
|
|
7
|
+
// For UTF-8 text the result is unchanged.
|
|
5
8
|
async function calculateChecksum(environment, path) {
|
|
6
|
-
const
|
|
7
|
-
|
|
9
|
+
const hash = (0, crypto_1.createHash)('sha1');
|
|
10
|
+
for await (const chunk of environment.file.readStream(path)) {
|
|
11
|
+
hash.update(chunk);
|
|
12
|
+
}
|
|
13
|
+
return hash.digest('hex');
|
|
8
14
|
}
|
|
9
15
|
exports.calculateChecksum = calculateChecksum;
|
|
10
16
|
//# sourceMappingURL=calculate-checksum.js.map
|
|
@@ -1,45 +1,95 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getStateKey = exports.getWorkCacheStats = void 0;
|
|
3
|
+
exports.getStateKey = exports.getWorkItemCacheStats = exports.getWorkCacheStats = void 0;
|
|
4
4
|
const calculate_checksum_1 = require("./calculate-checksum");
|
|
5
5
|
const path_1 = require("path");
|
|
6
6
|
const crypto_1 = require("crypto");
|
|
7
|
-
|
|
7
|
+
function isExcluded(path, excluded) {
|
|
8
|
+
return excluded.some((excludedPath) => path === excludedPath || path.startsWith(excludedPath + path_1.sep));
|
|
9
|
+
}
|
|
10
|
+
async function addWorkCacheStats(result, cwd, path, matcher, context, root, excluded) {
|
|
11
|
+
if (isExcluded(path, excluded)) {
|
|
12
|
+
return 0;
|
|
13
|
+
}
|
|
8
14
|
const exists = await context.file.exists(path);
|
|
9
15
|
if (!exists) {
|
|
10
|
-
return;
|
|
16
|
+
return 0;
|
|
11
17
|
}
|
|
12
18
|
const stats = await context.file.stats(path);
|
|
13
19
|
if (stats.type === 'file') {
|
|
14
|
-
if (matcher(path)) {
|
|
15
|
-
|
|
16
|
-
result.files[(0, path_1.relative)(cwd, path)] = { lastModified: stats.lastModified, checksum };
|
|
20
|
+
if (!matcher(path)) {
|
|
21
|
+
return 0;
|
|
17
22
|
}
|
|
23
|
+
const checksum = await (0, calculate_checksum_1.calculateChecksum)(context, path);
|
|
24
|
+
// '/'-separated on every OS, so a Windows host keys a container task like Linux
|
|
25
|
+
result.files[(0, path_1.relative)(cwd, path).split(path_1.sep).join(path_1.posix.sep)] = { lastModified: stats.lastModified, checksum };
|
|
26
|
+
return 1;
|
|
18
27
|
}
|
|
19
28
|
else if (stats.type === 'directory') {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
29
|
+
// A glob such as src/**/*.ts never matches a directory itself, so a
|
|
30
|
+
// directory is walked when files inside it could match.
|
|
31
|
+
if (!root && !matcher(path, true)) {
|
|
32
|
+
return 0;
|
|
33
|
+
}
|
|
34
|
+
let matched = 0;
|
|
35
|
+
for (const file of await context.file.listFiles(path)) {
|
|
36
|
+
matched += await addWorkCacheStats(result, cwd, (0, path_1.join)(path, file), matcher, context, false, excluded);
|
|
26
37
|
}
|
|
38
|
+
return matched;
|
|
27
39
|
}
|
|
40
|
+
return 0;
|
|
28
41
|
}
|
|
29
42
|
async function getWorkCacheStats(work, environment) {
|
|
43
|
+
return (await collectSourceStats(work, environment, [])).stats;
|
|
44
|
+
}
|
|
45
|
+
exports.getWorkCacheStats = getWorkCacheStats;
|
|
46
|
+
// `excluded` paths are left out of the walk, see getWorkItemCacheStats. A src
|
|
47
|
+
// entry inside an excluded path is represented there, so it is never unmatched.
|
|
48
|
+
async function collectSourceStats(work, environment, excluded) {
|
|
30
49
|
const result = {
|
|
31
50
|
created: new Date(),
|
|
32
51
|
files: {},
|
|
33
52
|
};
|
|
53
|
+
const unmatched = [];
|
|
34
54
|
for (const src of work.src) {
|
|
35
|
-
|
|
55
|
+
const matcher = (file, partial) => src.matcher(file, work.cwd, partial);
|
|
56
|
+
const matched = await addWorkCacheStats(result, work.cwd, src.absolutePath, matcher, environment, true, excluded);
|
|
57
|
+
if (matched === 0 && !src.inherited && !isExcluded(src.absolutePath, excluded)) {
|
|
58
|
+
unmatched.push(src);
|
|
59
|
+
}
|
|
36
60
|
}
|
|
37
|
-
return result;
|
|
61
|
+
return { stats: result, unmatched };
|
|
62
|
+
}
|
|
63
|
+
// The source stats of a task or service, leaving out everything its (transitive)
|
|
64
|
+
// dependencies generate. Those outputs are already represented by the
|
|
65
|
+
// dependencies' task ids and state keys — and hashing them would key the item
|
|
66
|
+
// differently before and after its dependencies ran, since the key is computed
|
|
67
|
+
// before they do.
|
|
68
|
+
function getWorkItemCacheStats(item, environment) {
|
|
69
|
+
return collectSourceStats(item.data, environment, dependencyOutputs(item));
|
|
70
|
+
}
|
|
71
|
+
exports.getWorkItemCacheStats = getWorkItemCacheStats;
|
|
72
|
+
function dependencyOutputs(item) {
|
|
73
|
+
const outputs = new Set();
|
|
74
|
+
const visited = new Set();
|
|
75
|
+
const visit = (dep) => {
|
|
76
|
+
if (visited.has(dep)) {
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
visited.add(dep);
|
|
80
|
+
for (const generate of dep.data.generates) {
|
|
81
|
+
outputs.add(generate.path);
|
|
82
|
+
}
|
|
83
|
+
dep.deps.forEach(visit);
|
|
84
|
+
};
|
|
85
|
+
item.deps.forEach(visit);
|
|
86
|
+
return [...outputs];
|
|
38
87
|
}
|
|
39
|
-
exports.getWorkCacheStats = getWorkCacheStats;
|
|
40
88
|
function getStateKey(stats, cacheMethod) {
|
|
41
89
|
const contents = [];
|
|
42
|
-
|
|
90
|
+
// Sorted: the walk follows the filesystem's listing order, which differs
|
|
91
|
+
// between machines (ext4 and overlayfs list in hash order).
|
|
92
|
+
for (const key of Object.keys(stats.files).sort()) {
|
|
43
93
|
if (cacheMethod === 'checksum') {
|
|
44
94
|
contents.push(`${key}:${stats.files[key].checksum}`);
|
|
45
95
|
}
|
|
@@ -1,14 +1,27 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getWorkTaskCacheDescription = void 0;
|
|
3
|
+
exports.storedCacheDescription = exports.getWorkTaskCacheDescription = void 0;
|
|
4
4
|
const os_1 = require("os");
|
|
5
|
+
const crypto_1 = require("crypto");
|
|
5
6
|
const replace_env_variables_1 = require("../environment/replace-env-variables");
|
|
6
|
-
|
|
7
|
+
const portable_path_1 = require("../planner/utils/portable-path");
|
|
8
|
+
// Every path is made project-relative (portablePath) so the description, and the
|
|
9
|
+
// task id hashed from it, is identical wherever the project is checked out.
|
|
10
|
+
//
|
|
11
|
+
// `deps` holds the dependencies' task ids, so a change to a dependency's
|
|
12
|
+
// definition (command, env, image, …) gives every task depending on it a new id.
|
|
13
|
+
// The state key only folds in the dependencies' *sources*.
|
|
14
|
+
function getWorkTaskCacheDescription(item) {
|
|
15
|
+
const task = item.data;
|
|
16
|
+
const portable = (path) => (0, portable_path_1.portablePath)(task.projectRoot, path);
|
|
7
17
|
const envs = (0, replace_env_variables_1.getEnvironmentVariables)(task.envs);
|
|
8
18
|
return {
|
|
9
19
|
shell: task.shell ?? undefined,
|
|
10
20
|
platform: task.type === 'container-task' ? task.image : (0, os_1.platform)(),
|
|
11
|
-
|
|
21
|
+
// outputs hold native binaries, and docker pulls images for the host
|
|
22
|
+
// architecture, so container tasks are keyed by it too
|
|
23
|
+
arch: process.arch,
|
|
24
|
+
generates: task.generates.map((g) => portable(g.path)).sort() ?? undefined,
|
|
12
25
|
src: task.src.map((s) => s.source).sort() ?? undefined,
|
|
13
26
|
envs: Object.keys(envs)
|
|
14
27
|
.sort()
|
|
@@ -16,10 +29,26 @@ function getWorkTaskCacheDescription(task) {
|
|
|
16
29
|
map[key] = envs[key];
|
|
17
30
|
return map;
|
|
18
31
|
}, {}) ?? undefined,
|
|
19
|
-
cmds: task.cmds.map((c) => ({ cmd: c.cmd, cwd: c.cwd })),
|
|
32
|
+
cmds: task.cmds.map((c) => ({ cmd: c.cmd, cwd: portable(c.cwd) })),
|
|
20
33
|
mounts: task.type === 'container-task' ? task.mounts.map((m) => m.mount).sort() : undefined,
|
|
21
|
-
cwd: task.cwd,
|
|
34
|
+
cwd: portable(task.cwd),
|
|
35
|
+
deps: item.deps.length > 0 ? item.deps.map((dep) => dep.id()).sort() : undefined,
|
|
22
36
|
};
|
|
23
37
|
}
|
|
24
38
|
exports.getWorkTaskCacheDescription = getWorkTaskCacheDescription;
|
|
39
|
+
// The form written to disk: description.json in a cache entry (pushed to remote
|
|
40
|
+
// backends, readable by everyone who can pull) and the local explain record.
|
|
41
|
+
// Env is where tokens get passed, so each value is stored as a digest; the task
|
|
42
|
+
// id is still hashed from the values themselves.
|
|
43
|
+
function storedCacheDescription(description) {
|
|
44
|
+
if (!description.envs) {
|
|
45
|
+
return description;
|
|
46
|
+
}
|
|
47
|
+
const envs = {};
|
|
48
|
+
for (const [key, value] of Object.entries(description.envs)) {
|
|
49
|
+
envs[key] = `sha256:${(0, crypto_1.createHash)('sha256').update(value).digest('hex')}`;
|
|
50
|
+
}
|
|
51
|
+
return { ...description, envs };
|
|
52
|
+
}
|
|
53
|
+
exports.storedCacheDescription = storedCacheDescription;
|
|
25
54
|
//# sourceMappingURL=work-task-cache-description.js.map
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.findProjectRoot = void 0;
|
|
4
|
+
const path_1 = require("path");
|
|
5
|
+
// The project root anchors cache identity: every path that participates in a
|
|
6
|
+
// task or service id is expressed relative to it, so the same checkout yields the
|
|
7
|
+
// same ids wherever it lives (an agent sandbox, a CI runner, a laptop). The git
|
|
8
|
+
// root is used when there is one, so the root does not move with the build file
|
|
9
|
+
// the CLI was invoked with (`-f apps/web/.hammerkit.yaml` vs the repo root);
|
|
10
|
+
// otherwise the directory of the main build file.
|
|
11
|
+
async function findProjectRoot(buildFileDirectory, environment) {
|
|
12
|
+
let current = buildFileDirectory;
|
|
13
|
+
for (;;) {
|
|
14
|
+
// `.git` is a directory in a normal clone and a file in a worktree/submodule
|
|
15
|
+
if (await environment.file.exists((0, path_1.join)(current, '.git'))) {
|
|
16
|
+
return current;
|
|
17
|
+
}
|
|
18
|
+
const parent = (0, path_1.dirname)(current);
|
|
19
|
+
if (parent === current) {
|
|
20
|
+
return buildFileDirectory;
|
|
21
|
+
}
|
|
22
|
+
current = parent;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
exports.findProjectRoot = findProjectRoot;
|
|
26
|
+
//# sourceMappingURL=project-root.js.map
|
|
@@ -51,6 +51,7 @@ function parseService(workTree, service, environment, context) {
|
|
|
51
51
|
const envs = (0, replace_env_variables_1.buildEnvironmentVariables)(service.envs, environment, context);
|
|
52
52
|
const workService = {
|
|
53
53
|
cwd: service.cwd,
|
|
54
|
+
projectRoot: context.projectRoot,
|
|
54
55
|
name: service.relativeName,
|
|
55
56
|
ports: (0, parse_work_ports_1.parseWorkPorts)(service.schema, envs),
|
|
56
57
|
scope: service.scope,
|
|
@@ -94,7 +95,7 @@ function parseService(workTree, service, environment, context) {
|
|
|
94
95
|
}
|
|
95
96
|
: null,
|
|
96
97
|
envs: (0, replace_env_variables_1.buildEnvironmentVariables)(service.envs, environment, context),
|
|
97
|
-
image: service.schema.image,
|
|
98
|
+
image: (0, template_value_1.templateValue)(service.schema.image, envs),
|
|
98
99
|
cwd: service.cwd,
|
|
99
100
|
cmd: service.schema.cmd ? (0, parse_work_command_1.parseWorkCommand)(service.cwd, service.schema.cmd, envs) : null,
|
|
100
101
|
volumes: (0, parse_work_volume_1.parseWorkVolumes)(service.cwd, service.schema.volumes, envs),
|
|
@@ -16,11 +16,15 @@ const replace_env_variables_1 = require("../../environment/replace-env-variables
|
|
|
16
16
|
const lazy_resolver_1 = require("../../executer/lazy-resolver");
|
|
17
17
|
const get_work_runtime_1 = require("./get-work-runtime");
|
|
18
18
|
const resolve_cache_1 = require("../../cache/resolve-cache");
|
|
19
|
+
const units_1 = require("../../utils/units");
|
|
19
20
|
function appendWorkTask(workTree, cwd, referenceTask, environment, context) {
|
|
20
21
|
const task = parseTask(cwd, referenceTask, environment, context);
|
|
21
22
|
if (!workTree.tasks[task.name]) {
|
|
22
23
|
const workItem = {
|
|
23
|
-
|
|
24
|
+
// A dependency cycle is rejected before anything runs (checkForLoop), but
|
|
25
|
+
// the id may be asked for earlier; re-entry yields a placeholder instead of
|
|
26
|
+
// recursing forever.
|
|
27
|
+
id: (0, lazy_resolver_1.lazyResolver)(() => (0, work_cache_id_1.getWorkTaskId)(workItem), () => `cycle:${task.name}`),
|
|
24
28
|
name: task.name,
|
|
25
29
|
data: task,
|
|
26
30
|
status: environment.status.from(task),
|
|
@@ -53,6 +57,7 @@ function parseTask(cwd, task, environment, context) {
|
|
|
53
57
|
description: (0, template_value_1.templateValue)(task.schema.description || '', envs).trim(),
|
|
54
58
|
name: task.relativeName,
|
|
55
59
|
cwd,
|
|
60
|
+
projectRoot: context.projectRoot,
|
|
56
61
|
cmds: (0, parse_work_command_1.parseWorkCommands)(cwd, task.schema.cmds || [], envs),
|
|
57
62
|
src: (0, parse_work_source_1.parseWorkSource)(cwd, task.schema.src, envs),
|
|
58
63
|
generates: (0, parse_work_generate_1.parseWorkGenerate)(cwd, task.schema, envs),
|
|
@@ -61,6 +66,7 @@ function parseTask(cwd, task, environment, context) {
|
|
|
61
66
|
caching: (0, resolve_cache_1.resolveCache)(task.schema.cache ?? null, context.caches, task.relativeName),
|
|
62
67
|
shell: task.schema.shell ? (0, template_value_1.templateValue)(task.schema.shell, envs) : '/bin/sh',
|
|
63
68
|
continuous: task.schema.continuous ?? false,
|
|
69
|
+
timeout: task.schema.timeout ? (0, units_1.parseDuration)(task.schema.timeout) : null,
|
|
64
70
|
};
|
|
65
71
|
if ((0, build_file_task_schema_1.isBuildFileContainerTaskSchema)(task.schema)) {
|
|
66
72
|
return {
|
|
@@ -15,38 +15,25 @@ function parseWorkSource(cwd, sources, envs) {
|
|
|
15
15
|
return result;
|
|
16
16
|
}
|
|
17
17
|
for (const source of sources) {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
const prefixSource = source.substring(0, wildcardIndex);
|
|
35
|
-
const absolutePath = (0, path_1.join)(cwd, (0, template_value_1.templateValue)(prefixSource, envs));
|
|
36
|
-
result.push({
|
|
37
|
-
matcher: (file, cwd) => {
|
|
38
|
-
const matcher = new minimatch_1.Minimatch(toPosix((0, path_1.join)(cwd, source)), { dot: true });
|
|
39
|
-
return matcher.match(toPosix(file));
|
|
40
|
-
},
|
|
41
|
-
inherited: null,
|
|
42
|
-
source,
|
|
43
|
-
absolutePath,
|
|
44
|
-
isFile: false,
|
|
45
|
-
});
|
|
46
|
-
}
|
|
18
|
+
// env variables are substituted before matching, so `$DIR/*.ts` globs the
|
|
19
|
+
// directory the task actually reads; `source` keeps the declared form.
|
|
20
|
+
const pattern = path_1.posix.normalize(toPosix((0, template_value_1.templateValue)(source, envs)));
|
|
21
|
+
const matcher = new minimatch_1.Minimatch(pattern, { dot: true });
|
|
22
|
+
if (isGlob(matcher)) {
|
|
23
|
+
// Walk from the literal directory prefix and match paths relative to the
|
|
24
|
+
// declaring task's cwd — which stays correct when the source is inherited
|
|
25
|
+
// by a dependant in another directory.
|
|
26
|
+
const prefix = literalPrefix(pattern);
|
|
27
|
+
result.push({
|
|
28
|
+
matcher: (file, _cwd, partial) => matcher.match(toPosix((0, path_1.relative)(cwd, file)), partial),
|
|
29
|
+
inherited: null,
|
|
30
|
+
source,
|
|
31
|
+
absolutePath: prefix ? (0, path_1.join)(cwd, prefix) : cwd,
|
|
32
|
+
isFile: false,
|
|
33
|
+
});
|
|
47
34
|
}
|
|
48
35
|
else {
|
|
49
|
-
const absolutePath = (0, path_1.join)(cwd,
|
|
36
|
+
const absolutePath = (0, path_1.join)(cwd, pattern);
|
|
50
37
|
result.push({
|
|
51
38
|
matcher: (file) => file.startsWith(absolutePath),
|
|
52
39
|
absolutePath,
|
|
@@ -59,6 +46,24 @@ function parseWorkSource(cwd, sources, envs) {
|
|
|
59
46
|
return result;
|
|
60
47
|
}
|
|
61
48
|
exports.parseWorkSource = parseWorkSource;
|
|
49
|
+
// minimatch parses a literal path into a single set of plain strings; anything
|
|
50
|
+
// else (wildcards, ?, [...], {a,b}, extglobs) is a glob.
|
|
51
|
+
function isGlob(matcher) {
|
|
52
|
+
return matcher.set.length !== 1 || matcher.set[0].some((part) => typeof part !== 'string');
|
|
53
|
+
}
|
|
54
|
+
// The leading directories of a glob that contain no glob syntax — where the
|
|
55
|
+
// walk for matching files starts.
|
|
56
|
+
function literalPrefix(pattern) {
|
|
57
|
+
const segments = pattern.split('/');
|
|
58
|
+
const literal = [];
|
|
59
|
+
for (const segment of segments.slice(0, -1)) {
|
|
60
|
+
if (/[*?[\]{}()!+@]/.test(segment)) {
|
|
61
|
+
break;
|
|
62
|
+
}
|
|
63
|
+
literal.push(segment);
|
|
64
|
+
}
|
|
65
|
+
return literal.join('/');
|
|
66
|
+
}
|
|
62
67
|
function createSource(absolutePath) {
|
|
63
68
|
return {
|
|
64
69
|
matcher: (file) => file.startsWith(absolutePath),
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.portablePath = void 0;
|
|
4
|
+
const os_1 = require("os");
|
|
5
|
+
const path_1 = require("path");
|
|
6
|
+
const toPosix = (value) => (path_1.sep === path_1.posix.sep ? value : value.split(path_1.sep).join(path_1.posix.sep));
|
|
7
|
+
function within(base, path) {
|
|
8
|
+
const rel = (0, path_1.relative)(base, path);
|
|
9
|
+
if (rel.startsWith('..') || (0, path_1.isAbsolute)(rel)) {
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
return toPosix(rel);
|
|
13
|
+
}
|
|
14
|
+
// Express an absolute path in a machine-independent form for cache identity:
|
|
15
|
+
// relative to the project root when inside it, relative to the home directory
|
|
16
|
+
// (`~/…`) when inside that, and unchanged otherwise (a fixed system path such as
|
|
17
|
+
// `/var/run/docker.sock` is already the same on every machine).
|
|
18
|
+
function portablePath(projectRoot, path) {
|
|
19
|
+
const inRoot = within(projectRoot, path);
|
|
20
|
+
if (inRoot !== null) {
|
|
21
|
+
return inRoot.length > 0 ? inRoot : '.';
|
|
22
|
+
}
|
|
23
|
+
const inHome = within((0, os_1.homedir)(), path);
|
|
24
|
+
if (inHome !== null) {
|
|
25
|
+
return inHome.length > 0 ? `~/${inHome}` : '~';
|
|
26
|
+
}
|
|
27
|
+
return toPosix(path);
|
|
28
|
+
}
|
|
29
|
+
exports.portablePath = portablePath;
|
|
30
|
+
//# sourceMappingURL=portable-path.js.map
|
|
@@ -3,8 +3,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.getWorkTaskId = void 0;
|
|
4
4
|
const crypto_1 = require("crypto");
|
|
5
5
|
const work_task_cache_description_1 = require("../optimizer/work-task-cache-description");
|
|
6
|
-
function getWorkTaskId(
|
|
7
|
-
const description = (0, work_task_cache_description_1.getWorkTaskCacheDescription)(
|
|
6
|
+
function getWorkTaskId(item) {
|
|
7
|
+
const description = (0, work_task_cache_description_1.getWorkTaskCacheDescription)(item);
|
|
8
8
|
const jsonData = JSON.stringify(description);
|
|
9
9
|
return (0, crypto_1.createHash)('sha1').update(jsonData).digest('hex');
|
|
10
10
|
}
|