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.
Files changed (277) hide show
  1. package/README.md +7 -7
  2. package/build.schema.json +59 -15
  3. package/dist/cache/backends/local-cache-backend.js +58 -1
  4. package/dist/cache/backends/registry/registry-client.js +264 -0
  5. package/dist/cache/backends/registry/registry-credentials.js +81 -0
  6. package/dist/cache/backends/registry/registry-reference.js +36 -0
  7. package/dist/cache/backends/registry-cache-backend.js +145 -0
  8. package/dist/cache/backends/s3-cache-backend.js +99 -46
  9. package/dist/cache/cache-backend-registry.js +2 -0
  10. package/dist/cache/cache-inventory.js +84 -0
  11. package/dist/cache/cache-sync.js +77 -0
  12. package/dist/cache/explain.js +214 -0
  13. package/dist/cache/last-resolved.js +48 -0
  14. package/dist/cache/read-only.js +15 -0
  15. package/dist/cache/retention.js +61 -0
  16. package/dist/cli.js +43 -0
  17. package/dist/docker/using-container.js +17 -1
  18. package/dist/executer/archive-task-entry.js +21 -0
  19. package/dist/executer/await-completed-dependencies.js +32 -14
  20. package/dist/executer/cache-metadata.js +9 -2
  21. package/dist/executer/docker-service.js +2 -1
  22. package/dist/executer/docker-task.js +18 -3
  23. package/dist/executer/dry-run.js +73 -0
  24. package/dist/executer/event-cache.js +6 -5
  25. package/dist/executer/execute-command.js +21 -3
  26. package/dist/executer/execute-docker.js +18 -2
  27. package/dist/executer/execute-work-service.js +10 -1
  28. package/dist/executer/execute-work-task.js +111 -26
  29. package/dist/executer/execute-work-tree.js +26 -1
  30. package/dist/executer/execution-steps.js +3 -8
  31. package/dist/executer/get-container-binds.js +13 -1
  32. package/dist/executer/get-scheduler-execute-result.js +4 -1
  33. package/dist/executer/lazy-resolver.js +15 -3
  34. package/dist/executer/reset-work-tree.js +2 -0
  35. package/dist/executer/run-summary.js +102 -0
  36. package/dist/executer/scheduler/enqueue-next.js +15 -60
  37. package/dist/executer/scheduler/state-key.js +78 -0
  38. package/dist/index.js +14 -1
  39. package/dist/kubernetes/apply.js +6 -7
  40. package/dist/kubernetes/await-running-state.js +100 -60
  41. package/dist/kubernetes/ensure-http-route.js +7 -10
  42. package/dist/kubernetes/ensure-namespace.js +9 -10
  43. package/dist/kubernetes/ensure-persistent-data.js +6 -5
  44. package/dist/kubernetes/get-service-ip.js +2 -2
  45. package/dist/kubernetes/kubernetes-instance.js +0 -4
  46. package/dist/kubernetes/remove-persistent-data.js +16 -6
  47. package/dist/kubernetes/resolve-pod-name.js +7 -7
  48. package/dist/log.js +16 -6
  49. package/dist/logging/live-logger.js +2 -1
  50. package/dist/optimizer/calculate-checksum.js +8 -2
  51. package/dist/optimizer/get-work-cache-stats.js +66 -16
  52. package/dist/optimizer/work-task-cache-description.js +34 -5
  53. package/dist/planner/project-root.js +26 -0
  54. package/dist/planner/utils/append-work-environment.js +1 -1
  55. package/dist/planner/utils/append-work-service.js +2 -1
  56. package/dist/planner/utils/append-work-task.js +7 -1
  57. package/dist/planner/utils/get-outputs-to-reset.js +20 -0
  58. package/dist/planner/utils/parse-work-generate.js +2 -2
  59. package/dist/planner/utils/parse-work-source.js +35 -30
  60. package/dist/planner/utils/portable-path.js +30 -0
  61. package/dist/planner/work-cache-id.js +2 -2
  62. package/dist/planner/work-graph-serializer.js +103 -0
  63. package/dist/planner/work-instance-id.js +15 -0
  64. package/dist/planner/work-runtime-docker.js +58 -15
  65. package/dist/planner/work-runtime-kubernetes.js +95 -45
  66. package/dist/planner/work-runtime-local.js +31 -8
  67. package/dist/planner/work-service-id.js +16 -4
  68. package/dist/program.js +235 -1
  69. package/dist/schema/build-file-base-task-schema.js +4 -0
  70. package/dist/schema/build-file-environment-schema-http-route.js +15 -0
  71. package/dist/schema/build-file-environment-schema.js +8 -2
  72. package/dist/schema/build-file-schema.js +1 -1
  73. package/dist/schema/cache-schema.js +21 -2
  74. package/dist/schema/duration-schema.js +18 -0
  75. package/dist/schema/envs-schema.js +1 -1
  76. package/dist/schema/reference-parser.js +3 -0
  77. package/dist/schema/size-schema.js +18 -0
  78. package/dist/schema/work-scope-parser.js +15 -2
  79. package/dist/utils/units.js +87 -0
  80. package/package.json +20 -9
  81. package/.agents/FEEDBACK.md +0 -74
  82. package/.agents/skills/before-commit/SKILL.md +0 -101
  83. package/.agents/skills/do-the-work/SKILL.md +0 -64
  84. package/.agents/skills/fix-ci/SKILL.md +0 -98
  85. package/.agents/skills/speckit-git-commit/SKILL.md +0 -53
  86. package/.agents/skills/speckit-git-feature/SKILL.md +0 -72
  87. package/.agents/skills/speckit-git-initialize/SKILL.md +0 -54
  88. package/.agents/skills/speckit-git-remote/SKILL.md +0 -50
  89. package/.agents/skills/speckit-git-validate/SKILL.md +0 -54
  90. package/.devcontainer/Dockerfile +0 -5
  91. package/.devcontainer/devcontainer.json +0 -40
  92. package/.gitbook.yaml +0 -5
  93. package/.specify/extensions/.registry +0 -37
  94. package/.specify/extensions/git/README.md +0 -100
  95. package/.specify/extensions/git/commands/speckit.git.commit.md +0 -48
  96. package/.specify/extensions/git/commands/speckit.git.feature.md +0 -67
  97. package/.specify/extensions/git/commands/speckit.git.initialize.md +0 -49
  98. package/.specify/extensions/git/commands/speckit.git.remote.md +0 -45
  99. package/.specify/extensions/git/commands/speckit.git.validate.md +0 -49
  100. package/.specify/extensions/git/config-template.yml +0 -62
  101. package/.specify/extensions/git/extension.yml +0 -140
  102. package/.specify/extensions/git/git-config.yml +0 -62
  103. package/.specify/extensions.yml +0 -149
  104. package/.specify/feature.json +0 -3
  105. package/.specify/init-options.json +0 -10
  106. package/.specify/integration.json +0 -15
  107. package/.specify/integrations/claude.manifest.json +0 -16
  108. package/.specify/integrations/speckit.manifest.json +0 -17
  109. package/.specify/memory/constitution.md +0 -162
  110. package/.specify/templates/checklist-template.md +0 -40
  111. package/.specify/templates/constitution-template.md +0 -50
  112. package/.specify/templates/plan-template.md +0 -113
  113. package/.specify/templates/spec-template.md +0 -131
  114. package/.specify/templates/tasks-template.md +0 -252
  115. package/.specify/workflows/speckit/workflow.yml +0 -77
  116. package/.specify/workflows/workflow-registry.json +0 -13
  117. package/AGENTS.md +0 -94
  118. package/Dockerfile +0 -32
  119. package/FOLLOWUPS.md +0 -34
  120. package/SUMMARY.md +0 -68
  121. package/VALUES.md +0 -53
  122. package/dist/cache/auto-pull.spec.js +0 -123
  123. package/dist/cache/backends/local-cache-backend.spec.js +0 -72
  124. package/dist/cache/backends/s3-cache-backend.spec.js +0 -118
  125. package/dist/cache/cache-backend-registry.spec.js +0 -22
  126. package/dist/cache/resolve-cache.spec.js +0 -52
  127. package/dist/cli.spec.js +0 -23
  128. package/dist/console/get-logger.spec.js +0 -23
  129. package/dist/docker/get-default-platform.spec.js +0 -29
  130. package/dist/docker/longest-common-prefix.spec.js +0 -26
  131. package/dist/docker/package.spec.js +0 -145
  132. package/dist/docker/remove-container.spec.js +0 -32
  133. package/dist/docker/using-container.spec.js +0 -90
  134. package/dist/environment/merge-environment-variables.spec.js +0 -24
  135. package/dist/environment/replace-env-variables.spec.js +0 -47
  136. package/dist/executer/abort.spec.js +0 -18
  137. package/dist/executer/environment-mock.spec.js +0 -22
  138. package/dist/executer/execute.spec.js +0 -51
  139. package/dist/executer/get-service-containers.spec.js +0 -59
  140. package/dist/executer/get-service-env-hints.spec.js +0 -65
  141. package/dist/executer/label-values.spec.js +0 -61
  142. package/dist/executer/lazy-resolver.spec.js +0 -20
  143. package/dist/executer/process-manager.spec.js +0 -54
  144. package/dist/executer/scheduler/enqueue-next.spec.js +0 -136
  145. package/dist/executer/state-resolver.spec.js +0 -37
  146. package/dist/executer/state.spec.js +0 -66
  147. package/dist/executer/states.spec.js +0 -14
  148. package/dist/file/get-file-context.spec.js +0 -178
  149. package/dist/file/move-files.spec.js +0 -48
  150. package/dist/kubernetes/ensure-http-route.spec.js +0 -70
  151. package/dist/kubernetes/ensure-ingress.js +0 -54
  152. package/dist/kubernetes/ensure-namespace.spec.js +0 -39
  153. package/dist/kubernetes/resolve-pod-name.spec.js +0 -81
  154. package/dist/log.spec.js +0 -131
  155. package/dist/logging/grouped-logger.spec.js +0 -84
  156. package/dist/logging/interactive-logger.spec.js +0 -57
  157. package/dist/logging/live-logger.spec.js +0 -37
  158. package/dist/logging/loggers.spec.js +0 -154
  159. package/dist/optimizer/calculate-checksum.spec.js +0 -34
  160. package/dist/optimizer/get-cache-directory.spec.js +0 -59
  161. package/dist/optimizer/get-work-cache-stats.spec.js +0 -79
  162. package/dist/optimizer/work-task-cache-description.spec.js +0 -45
  163. package/dist/parser/default-build-file.spec.js +0 -48
  164. package/dist/parser/parse-label-arguments.spec.js +0 -27
  165. package/dist/parser/read-build-file.spec.js +0 -89
  166. package/dist/parser/read-env-file.spec.js +0 -42
  167. package/dist/planner/utils/append-work-service.spec.js +0 -71
  168. package/dist/planner/utils/get-container-user.spec.js +0 -30
  169. package/dist/planner/utils/get-default-kube-config.spec.js +0 -11
  170. package/dist/planner/utils/normalize-path.spec.js +0 -24
  171. package/dist/planner/utils/parse-work-command.spec.js +0 -21
  172. package/dist/planner/utils/parse-work-generate.spec.js +0 -45
  173. package/dist/planner/utils/parse-work-mount.spec.js +0 -62
  174. package/dist/planner/utils/parse-work-mounts.spec.js +0 -19
  175. package/dist/planner/utils/parse-work-port.spec.js +0 -24
  176. package/dist/planner/utils/parse-work-ports.spec.js +0 -24
  177. package/dist/planner/utils/parse-work-source.spec.js +0 -58
  178. package/dist/planner/utils/parse-work-volume.spec.js +0 -18
  179. package/dist/planner/utils/plan-work-tasks.spec.js +0 -30
  180. package/dist/planner/utils/plan-work-volume.spec.js +0 -33
  181. package/dist/planner/utils/split-name.spec.js +0 -16
  182. package/dist/planner/utils/template-value.spec.js +0 -14
  183. package/dist/planner/validate.spec.js +0 -251
  184. package/dist/planner/work-cache-id.spec.js +0 -173
  185. package/dist/planner/work-runtime-local.spec.js +0 -193
  186. package/dist/program-commands.spec.js +0 -84
  187. package/dist/program.spec.js +0 -43
  188. package/dist/schema/build-file-environment-schema-ingress.js +0 -14
  189. package/dist/schema/build-file-schema.spec.js +0 -61
  190. package/dist/schema/doc-build-files.spec.js +0 -223
  191. package/dist/schema/parse-error.spec.js +0 -18
  192. package/dist/schema/reference-parser.spec.js +0 -83
  193. package/dist/schema/schema-parser.spec.js +0 -80
  194. package/dist/schema/type-guards.spec.js +0 -23
  195. package/dist/schema/work-scope-parser.spec.js +0 -66
  196. package/dist/service/update-service-status.spec.js +0 -27
  197. package/dist/testing/ensure-local-registry.js +0 -134
  198. package/dist/testing/example-test-suite.js +0 -63
  199. package/dist/testing/expect.js +0 -82
  200. package/dist/testing/get-test-suite.js +0 -9
  201. package/dist/testing/http-get.js +0 -21
  202. package/dist/testing/integration/cache.spec.js +0 -49
  203. package/dist/testing/integration/cancellation.spec.js +0 -45
  204. package/dist/testing/integration/clean.spec.js +0 -43
  205. package/dist/testing/integration/cmd.spec.js +0 -15
  206. package/dist/testing/integration/concurrency.spec.js +0 -19
  207. package/dist/testing/integration/docker-package-registry.spec.js +0 -44
  208. package/dist/testing/integration/docker-service-crash.spec.js +0 -36
  209. package/dist/testing/integration/docker.spec.js +0 -18
  210. package/dist/testing/integration/env.spec.js +0 -48
  211. package/dist/testing/integration/environment.spec.js +0 -97
  212. package/dist/testing/integration/error.spec.js +0 -19
  213. package/dist/testing/integration/export.spec.js +0 -22
  214. package/dist/testing/integration/extend.spec.js +0 -29
  215. package/dist/testing/integration/glob.spec.js +0 -40
  216. package/dist/testing/integration/include.spec.js +0 -31
  217. package/dist/testing/integration/invalid.spec.js +0 -17
  218. package/dist/testing/integration/invalid_loop.spec.js +0 -56
  219. package/dist/testing/integration/k8s-state-cache.spec.js +0 -41
  220. package/dist/testing/integration/kubernetes-service.spec.js +0 -148
  221. package/dist/testing/integration/kubernetes.spec.js +0 -99
  222. package/dist/testing/integration/labels.spec.js +0 -42
  223. package/dist/testing/integration/local.spec.js +0 -14
  224. package/dist/testing/integration/monorepo.spec.js +0 -15
  225. package/dist/testing/integration/multi-line.spec.js +0 -17
  226. package/dist/testing/integration/reference.spec.js +0 -21
  227. package/dist/testing/integration/services.spec.js +0 -38
  228. package/dist/testing/integration/store.spec.js +0 -52
  229. package/dist/testing/integration/unknown.spec.js +0 -28
  230. package/dist/testing/integration/validate.spec.js +0 -160
  231. package/dist/testing/integration/watch-dependency.spec.js +0 -33
  232. package/dist/testing/integration/watch.spec.js +0 -45
  233. package/dist/testing/requires-kubernetes.js +0 -18
  234. package/dist/testing/requires-linux-containers.js +0 -18
  235. package/dist/testing/requires-windows-containers.js +0 -18
  236. package/dist/testing/test-case.js +0 -75
  237. package/dist/testing/test-streams.js +0 -28
  238. package/dist/testing/test-suite-setup.js +0 -3
  239. package/dist/testing/test-suite.js +0 -3
  240. package/dist/testing/testing-timeout.js +0 -32
  241. package/dist/utils/abort-event.spec.js +0 -46
  242. package/dist/utils/buffer-context.spec.js +0 -38
  243. package/dist/utils/ci.spec.js +0 -53
  244. package/dist/utils/debouncer.spec.js +0 -58
  245. package/dist/utils/emitter.spec.js +0 -34
  246. package/dist/utils/empty-writable.spec.js +0 -18
  247. package/dist/utils/environment-config.spec.js +0 -26
  248. package/dist/utils/fail-never.spec.js +0 -9
  249. package/dist/utils/sleep.spec.js +0 -22
  250. package/dist/version.spec.js +0 -12
  251. package/specs/build-file/caches/spec.md +0 -114
  252. package/specs/build-file/environment-variables/spec.md +0 -82
  253. package/specs/build-file/includes/spec.md +0 -60
  254. package/specs/build-file/references/spec.md +0 -56
  255. package/specs/build-file/spec.md +0 -62
  256. package/specs/cli/clean/spec.md +0 -85
  257. package/specs/cli/down/spec.md +0 -85
  258. package/specs/cli/execute/spec.md +0 -101
  259. package/specs/cli/init/spec.md +0 -64
  260. package/specs/cli/ls/spec.md +0 -66
  261. package/specs/cli/package/spec.md +0 -110
  262. package/specs/cli/spec.md +0 -73
  263. package/specs/cli/store-restore/spec.md +0 -74
  264. package/specs/cli/up/spec.md +0 -80
  265. package/specs/cli/validate/spec.md +0 -70
  266. package/specs/labels/spec.md +0 -91
  267. package/specs/service/container/spec.md +0 -120
  268. package/specs/service/kubernetes/spec.md +0 -97
  269. package/specs/service/spec.md +0 -105
  270. package/specs/task/caching/spec.md +0 -107
  271. package/specs/task/container/spec.md +0 -118
  272. package/specs/task/dependencies/spec.md +0 -70
  273. package/specs/task/extending/spec.md +0 -74
  274. package/specs/task/kubernetes/spec.md +0 -125
  275. package/specs/task/needs/spec.md +0 -59
  276. package/specs/task/spec.md +0 -87
  277. 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, undefined, undefined, 0);
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.body.spec?.clusterIP ?? null;
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 configmaps = await instance.coreApi.listNamespacedConfigMap(kubernetes.namespace, undefined, undefined, undefined, undefined, `hammerkit.dev/id=${task.id()}`);
6
- for (const configMap of configmaps.body.items) {
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(configMap.metadata.name, kubernetes.namespace);
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(kubernetes.namespace, undefined, undefined, undefined, undefined, `hammerkit.dev/id=${task.id()}`);
12
- for (const pvc of pvcs.body.items) {
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(pvc.metadata.name, kubernetes.namespace);
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, undefined, undefined, undefined, undefined, labelSelector);
11
- const ready = pods.body.items.find((p) => p.status?.conditions?.some((c) => c.type === 'Ready' && c.status === 'True'));
12
- const pod = ready ?? pods.body.items[0];
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.body.spec?.selector;
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.body.spec?.selector?.matchLabels;
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
- message += ` in ${task.state.current.duration}ms`;
113
- if (task.state.current.cached) {
114
- message += ' [CACHED]';
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
- message += ` in ${task.state.current.duration}ms`;
267
- if (task.state.current.cached) {
268
- message += ' [CACHED]';
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 input = await environment.file.read(path);
7
- return (0, crypto_1.createHash)('sha1').update(input).digest('hex');
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
- async function addWorkCacheStats(result, cwd, path, matcher, context) {
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
- const checksum = await (0, calculate_checksum_1.calculateChecksum)(context, path);
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
- const files = await context.file.listFiles(path);
21
- for (const file of files) {
22
- const subPath = (0, path_1.join)(path, file);
23
- if (matcher(subPath)) {
24
- await addWorkCacheStats(result, cwd, (0, path_1.join)(path, file), matcher, context);
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
- await addWorkCacheStats(result, work.cwd, src.absolutePath, (file) => src.matcher(file, work.cwd), environment);
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
- for (const key of Object.keys(stats.files)) {
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
- function getWorkTaskCacheDescription(task) {
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
- generates: task.generates.map((g) => g.path).sort() ?? undefined,
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
- ingresses: referenced.schema.kubernetes.ingresses || [],
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
- id: (0, lazy_resolver_1.lazyResolver)(() => (0, work_cache_id_1.getWorkTaskId)(task)),
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: false, export: false };
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 ?? false,
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
- const wildcardIndex = source.indexOf('*');
19
- if (wildcardIndex >= 0) {
20
- if (wildcardIndex === 0) {
21
- const absolutePath = cwd;
22
- result.push({
23
- matcher: (file, cwd) => {
24
- const matcher = new minimatch_1.Minimatch(source, { dot: true });
25
- return matcher.match(toPosix((0, path_1.relative)(cwd, file)));
26
- },
27
- inherited: null,
28
- source,
29
- absolutePath,
30
- isFile: false,
31
- });
32
- }
33
- else {
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, (0, template_value_1.templateValue)(source, envs));
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(task) {
7
- const description = (0, work_task_cache_description_1.getWorkTaskCacheDescription)(task);
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
  }