hammerkit 1.6.0 → 1.8.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 +7 -7
- package/build.schema.json +59 -15
- 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 +17 -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 +18 -3
- 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-docker.js +18 -2
- package/dist/executer/execute-work-service.js +10 -1
- package/dist/executer/execute-work-task.js +111 -26
- package/dist/executer/execute-work-tree.js +26 -1
- package/dist/executer/execution-steps.js +3 -8
- package/dist/executer/get-container-binds.js +13 -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/index.js +14 -1
- package/dist/kubernetes/apply.js +6 -7
- package/dist/kubernetes/await-running-state.js +100 -60
- package/dist/kubernetes/ensure-http-route.js +7 -10
- package/dist/kubernetes/ensure-namespace.js +9 -10
- package/dist/kubernetes/ensure-persistent-data.js +6 -5
- package/dist/kubernetes/get-service-ip.js +2 -2
- package/dist/kubernetes/kubernetes-instance.js +0 -4
- package/dist/kubernetes/remove-persistent-data.js +16 -6
- package/dist/kubernetes/resolve-pod-name.js +7 -7
- 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-environment.js +1 -1
- package/dist/planner/utils/append-work-service.js +2 -1
- package/dist/planner/utils/append-work-task.js +7 -1
- package/dist/planner/utils/get-outputs-to-reset.js +20 -0
- package/dist/planner/utils/parse-work-generate.js +2 -2
- 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 +58 -15
- package/dist/planner/work-runtime-kubernetes.js +95 -45
- package/dist/planner/work-runtime-local.js +31 -8
- 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/build-file-environment-schema-http-route.js +15 -0
- package/dist/schema/build-file-environment-schema.js +8 -2
- package/dist/schema/build-file-schema.js +1 -1
- package/dist/schema/cache-schema.js +21 -2
- package/dist/schema/duration-schema.js +18 -0
- package/dist/schema/envs-schema.js +1 -1
- 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 +20 -9
- 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-ingress.js +0 -54
- 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-environment-schema-ingress.js +0 -14
- 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
|
@@ -10,6 +10,7 @@ const sleep_1 = require("../utils/sleep");
|
|
|
10
10
|
const version_1 = require("../version");
|
|
11
11
|
const log_1 = require("../log");
|
|
12
12
|
const ensure_kubernetes_persistent_volume_claim_exists_1 = require("./ensure-kubernetes-persistent-volume-claim-exists");
|
|
13
|
+
const stream_1 = require("stream");
|
|
13
14
|
// A previous run that crashed between creating an upload/download pod and its
|
|
14
15
|
// `finally` cleanup leaves a pod behind under the same deterministic name. apply()
|
|
15
16
|
// would then patch that stale pod in place — and if it is stuck in a terminal
|
|
@@ -17,7 +18,7 @@ const ensure_kubernetes_persistent_volume_claim_exists_1 = require("./ensure-kub
|
|
|
17
18
|
// and wait for it to disappear before scheduling a fresh one.
|
|
18
19
|
async function removeStalePod(instance, namespace, name) {
|
|
19
20
|
try {
|
|
20
|
-
await instance.coreApi.deleteNamespacedPod(name, namespace,
|
|
21
|
+
await instance.coreApi.deleteNamespacedPod({ name, namespace, gracePeriodSeconds: 0 });
|
|
21
22
|
}
|
|
22
23
|
catch (e) {
|
|
23
24
|
if ((0, apply_1.statusCodeOf)(e) === 404) {
|
|
@@ -27,7 +28,7 @@ async function removeStalePod(instance, namespace, name) {
|
|
|
27
28
|
}
|
|
28
29
|
for (let attempt = 0; attempt < 30; attempt++) {
|
|
29
30
|
try {
|
|
30
|
-
await instance.coreApi.readNamespacedPod(name, namespace);
|
|
31
|
+
await instance.coreApi.readNamespacedPod({ name, namespace });
|
|
31
32
|
}
|
|
32
33
|
catch (e) {
|
|
33
34
|
if ((0, apply_1.statusCodeOf)(e) === 404) {
|
|
@@ -100,7 +101,7 @@ async function getPodForPersistence(instance, env, service, persistence, type, f
|
|
|
100
101
|
}
|
|
101
102
|
finally {
|
|
102
103
|
service.status.console('stdout', `delete pod ${name}`);
|
|
103
|
-
await instance.coreApi.deleteNamespacedPod(name, env.namespace);
|
|
104
|
+
await instance.coreApi.deleteNamespacedPod({ name, namespace: env.namespace });
|
|
104
105
|
}
|
|
105
106
|
}
|
|
106
107
|
exports.getPodForPersistence = getPodForPersistence;
|
|
@@ -122,12 +123,12 @@ async function ensurePersistentData(instance, env, environment, service, persist
|
|
|
122
123
|
continue;
|
|
123
124
|
}
|
|
124
125
|
const fileItem = (0, path_1.basename)(source.localPath);
|
|
125
|
-
const data = (0, tar_1.create)({
|
|
126
|
+
const data = stream_1.Readable.from((0, tar_1.create)({
|
|
126
127
|
cwd: (0, path_1.dirname)(source.localPath),
|
|
127
128
|
filter(path) {
|
|
128
129
|
return source.matcher(path, source.localPath);
|
|
129
130
|
},
|
|
130
|
-
}, [fileItem]);
|
|
131
|
+
}, [fileItem]));
|
|
131
132
|
service.status.console('stdout', `upload ${source.localPath}`);
|
|
132
133
|
const targetPath = `/dev/hammerkit/${service.id()}`;
|
|
133
134
|
const res = await instance.exec.exec(env.namespace, name, 'volume', ['tar', 'xf', '-', '-C', targetPath], process.stdout, process.stderr, data, false, (status) => {
|
|
@@ -4,8 +4,8 @@ exports.getServiceIp = void 0;
|
|
|
4
4
|
const resources_1 = require("./resources");
|
|
5
5
|
async function getServiceIp(instance, env, workService) {
|
|
6
6
|
const name = (0, resources_1.getResourceName)(workService);
|
|
7
|
-
const svc = await instance.coreApi.readNamespacedService(name, env.namespace);
|
|
8
|
-
return svc.
|
|
7
|
+
const svc = await instance.coreApi.readNamespacedService({ name, namespace: env.namespace });
|
|
8
|
+
return svc.spec?.clusterIP ?? null;
|
|
9
9
|
}
|
|
10
10
|
exports.getServiceIp = getServiceIp;
|
|
11
11
|
//# sourceMappingURL=get-service-ip.js.map
|
|
@@ -16,14 +16,10 @@ function createKubernetesInstances(kubernetes) {
|
|
|
16
16
|
const coreApi = kc.makeApiClient(client_node_1.CoreV1Api);
|
|
17
17
|
const batchApi = kc.makeApiClient(client_node_1.BatchV1Api);
|
|
18
18
|
const appsApi = kc.makeApiClient(client_node_1.AppsV1Api);
|
|
19
|
-
const networkingApi = kc.makeApiClient(client_node_1.NetworkingV1Api);
|
|
20
19
|
const objectApi = kc.makeApiClient(client_node_1.KubernetesObjectApi);
|
|
21
20
|
const exec = new client_node_1.Exec(kc);
|
|
22
|
-
const watch = new client_node_1.Watch(kc);
|
|
23
21
|
return {
|
|
24
22
|
objectApi,
|
|
25
|
-
networkingApi,
|
|
26
|
-
watch,
|
|
27
23
|
exec,
|
|
28
24
|
appsApi,
|
|
29
25
|
coreApi,
|
|
@@ -2,16 +2,26 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.removePersistentData = void 0;
|
|
4
4
|
async function removePersistentData(instance, kubernetes, task) {
|
|
5
|
-
const
|
|
6
|
-
|
|
5
|
+
const labelSelector = `hammerkit.dev/id=${task.id()}`;
|
|
6
|
+
const configmaps = await instance.coreApi.listNamespacedConfigMap({ namespace: kubernetes.namespace, labelSelector });
|
|
7
|
+
for (const configMap of configmaps.items) {
|
|
7
8
|
if (configMap.metadata?.name) {
|
|
8
|
-
await instance.coreApi.deleteNamespacedConfigMap(
|
|
9
|
+
await instance.coreApi.deleteNamespacedConfigMap({
|
|
10
|
+
name: configMap.metadata.name,
|
|
11
|
+
namespace: kubernetes.namespace,
|
|
12
|
+
});
|
|
9
13
|
}
|
|
10
14
|
}
|
|
11
|
-
const pvcs = await instance.coreApi.listNamespacedPersistentVolumeClaim(
|
|
12
|
-
|
|
15
|
+
const pvcs = await instance.coreApi.listNamespacedPersistentVolumeClaim({
|
|
16
|
+
namespace: kubernetes.namespace,
|
|
17
|
+
labelSelector,
|
|
18
|
+
});
|
|
19
|
+
for (const pvc of pvcs.items) {
|
|
13
20
|
if (pvc.metadata?.name) {
|
|
14
|
-
await instance.coreApi.deleteNamespacedPersistentVolumeClaim(
|
|
21
|
+
await instance.coreApi.deleteNamespacedPersistentVolumeClaim({
|
|
22
|
+
name: pvc.metadata.name,
|
|
23
|
+
namespace: kubernetes.namespace,
|
|
24
|
+
});
|
|
15
25
|
}
|
|
16
26
|
}
|
|
17
27
|
}
|
|
@@ -7,9 +7,9 @@ function formatSelector(matchLabels) {
|
|
|
7
7
|
.join(',');
|
|
8
8
|
}
|
|
9
9
|
async function findReadyPod(coreApi, namespace, labelSelector) {
|
|
10
|
-
const pods = await coreApi.listNamespacedPod(namespace,
|
|
11
|
-
const ready = pods.
|
|
12
|
-
const pod = ready ?? pods.
|
|
10
|
+
const pods = await coreApi.listNamespacedPod({ namespace, labelSelector });
|
|
11
|
+
const ready = pods.items.find((p) => p.status?.conditions?.some((c) => c.type === 'Ready' && c.status === 'True'));
|
|
12
|
+
const pod = ready ?? pods.items[0];
|
|
13
13
|
if (!pod?.metadata?.name) {
|
|
14
14
|
throw new Error(`no pod found for selector ${labelSelector} in namespace ${namespace}`);
|
|
15
15
|
}
|
|
@@ -21,16 +21,16 @@ async function resolvePodName(coreApi, appsApi, namespace, selector) {
|
|
|
21
21
|
return selector.name;
|
|
22
22
|
}
|
|
23
23
|
if (type === 'service') {
|
|
24
|
-
const svc = await coreApi.readNamespacedService(selector.name, namespace);
|
|
25
|
-
const matchLabels = svc.
|
|
24
|
+
const svc = await coreApi.readNamespacedService({ name: selector.name, namespace });
|
|
25
|
+
const matchLabels = svc.spec?.selector;
|
|
26
26
|
if (!matchLabels || Object.keys(matchLabels).length === 0) {
|
|
27
27
|
throw new Error(`service ${selector.name} has no selector`);
|
|
28
28
|
}
|
|
29
29
|
return findReadyPod(coreApi, namespace, formatSelector(matchLabels));
|
|
30
30
|
}
|
|
31
31
|
if (type === 'deployment') {
|
|
32
|
-
const deploy = await appsApi.readNamespacedDeployment(selector.name, namespace);
|
|
33
|
-
const matchLabels = deploy.
|
|
32
|
+
const deploy = await appsApi.readNamespacedDeployment({ name: selector.name, namespace });
|
|
33
|
+
const matchLabels = deploy.spec?.selector?.matchLabels;
|
|
34
34
|
if (!matchLabels) {
|
|
35
35
|
throw new Error(`deployment ${selector.name} has no matchLabels`);
|
|
36
36
|
}
|
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
|
|
@@ -13,7 +13,7 @@ function appendWorkEnvironment(referenced) {
|
|
|
13
13
|
type: 'kubernetes',
|
|
14
14
|
context: referenced.schema.kubernetes.context,
|
|
15
15
|
kubeConfig: referenced.schema.kubernetes.kubeconfig,
|
|
16
|
-
|
|
16
|
+
httpRoutes: referenced.schema.kubernetes.httpRoutes || [],
|
|
17
17
|
namespace: referenced.schema.kubernetes.namespace ?? 'default',
|
|
18
18
|
};
|
|
19
19
|
}
|
|
@@ -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 {
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getOutputsToReset = void 0;
|
|
4
|
+
const path_1 = require("path");
|
|
5
|
+
function contains(parent, path) {
|
|
6
|
+
const rel = (0, path_1.relative)(parent, path);
|
|
7
|
+
return rel === '' || (!rel.startsWith('..') && !(0, path_1.isAbsolute)(rel));
|
|
8
|
+
}
|
|
9
|
+
// The outputs a run empties before the task's commands: its own outputs with
|
|
10
|
+
// resetOnChange (the default), except one that is a source or contains one
|
|
11
|
+
// (`package-lock.json` declared both ways) or contains the task's directory.
|
|
12
|
+
// Emptying those would delete what the task reads.
|
|
13
|
+
function getOutputsToReset(task) {
|
|
14
|
+
return task.generates.filter((generate) => generate.resetOnChange &&
|
|
15
|
+
!generate.inherited &&
|
|
16
|
+
!contains(generate.path, task.cwd) &&
|
|
17
|
+
!task.src.some((src) => contains(generate.path, src.absolutePath)));
|
|
18
|
+
}
|
|
19
|
+
exports.getOutputsToReset = getOutputsToReset;
|
|
20
|
+
//# sourceMappingURL=get-outputs-to-reset.js.map
|
|
@@ -24,12 +24,12 @@ function parseWorkGenerate(cwd, schema, envs) {
|
|
|
24
24
|
exports.parseWorkGenerate = parseWorkGenerate;
|
|
25
25
|
function mapGenerate(generate) {
|
|
26
26
|
if (typeof generate === 'string') {
|
|
27
|
-
return { path: generate, resetOnChange:
|
|
27
|
+
return { path: generate, resetOnChange: true, export: false };
|
|
28
28
|
}
|
|
29
29
|
else {
|
|
30
30
|
return {
|
|
31
31
|
path: generate.path,
|
|
32
|
-
resetOnChange: generate.resetOnChange ??
|
|
32
|
+
resetOnChange: generate.resetOnChange ?? true,
|
|
33
33
|
export: generate.export ?? false,
|
|
34
34
|
};
|
|
35
35
|
}
|
|
@@ -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
|
}
|