hammerkit 1.5.1 → 1.6.0-alpha.1

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 (480) hide show
  1. package/.agents/FEEDBACK.md +74 -0
  2. package/.agents/skills/before-commit/SKILL.md +101 -0
  3. package/.agents/skills/do-the-work/SKILL.md +64 -0
  4. package/.agents/skills/fix-ci/SKILL.md +98 -0
  5. package/.agents/skills/speckit-git-commit/SKILL.md +53 -0
  6. package/.agents/skills/speckit-git-feature/SKILL.md +72 -0
  7. package/.agents/skills/speckit-git-initialize/SKILL.md +54 -0
  8. package/.agents/skills/speckit-git-remote/SKILL.md +50 -0
  9. package/.agents/skills/speckit-git-validate/SKILL.md +54 -0
  10. package/.gitbook.yaml +5 -0
  11. package/.specify/extensions/.registry +37 -0
  12. package/.specify/extensions/git/README.md +100 -0
  13. package/.specify/extensions/git/commands/speckit.git.commit.md +48 -0
  14. package/.specify/extensions/git/commands/speckit.git.feature.md +67 -0
  15. package/.specify/extensions/git/commands/speckit.git.initialize.md +49 -0
  16. package/.specify/extensions/git/commands/speckit.git.remote.md +45 -0
  17. package/.specify/extensions/git/commands/speckit.git.validate.md +49 -0
  18. package/.specify/extensions/git/config-template.yml +62 -0
  19. package/.specify/extensions/git/extension.yml +140 -0
  20. package/.specify/extensions/git/git-config.yml +62 -0
  21. package/.specify/extensions.yml +149 -0
  22. package/.specify/feature.json +3 -0
  23. package/.specify/init-options.json +10 -0
  24. package/.specify/integration.json +15 -0
  25. package/.specify/integrations/claude.manifest.json +16 -0
  26. package/.specify/integrations/speckit.manifest.json +17 -0
  27. package/.specify/memory/constitution.md +162 -0
  28. package/.specify/templates/checklist-template.md +40 -0
  29. package/.specify/templates/constitution-template.md +50 -0
  30. package/.specify/templates/plan-template.md +113 -0
  31. package/.specify/templates/spec-template.md +131 -0
  32. package/.specify/templates/tasks-template.md +252 -0
  33. package/.specify/workflows/speckit/workflow.yml +77 -0
  34. package/.specify/workflows/workflow-registry.json +13 -0
  35. package/AGENTS.md +94 -0
  36. package/Dockerfile +3 -3
  37. package/FOLLOWUPS.md +34 -0
  38. package/README.md +9 -7
  39. package/SUMMARY.md +30 -5
  40. package/VALUES.md +53 -0
  41. package/build.schema.json +542 -408
  42. package/dist/cache/auto-pull.spec.js +123 -0
  43. package/dist/cache/backends/local-cache-backend.js +56 -0
  44. package/dist/cache/backends/local-cache-backend.spec.js +72 -0
  45. package/dist/cache/backends/s3-cache-backend.js +124 -0
  46. package/dist/cache/backends/s3-cache-backend.spec.js +118 -0
  47. package/dist/cache/cache-backend-registry.js +21 -0
  48. package/dist/cache/cache-backend-registry.spec.js +22 -0
  49. package/dist/cache/cache-backend.js +3 -0
  50. package/dist/cache/resolve-cache.js +60 -0
  51. package/dist/cache/resolve-cache.spec.js +52 -0
  52. package/dist/cli.js +112 -82
  53. package/dist/cli.spec.js +20 -2
  54. package/dist/console/get-logger.spec.js +23 -0
  55. package/dist/docker/get-default-platform.spec.js +29 -0
  56. package/dist/docker/longest-common-prefix.spec.js +26 -0
  57. package/dist/docker/package.js +283 -0
  58. package/dist/docker/package.spec.js +145 -0
  59. package/dist/docker/pull.js +4 -4
  60. package/dist/docker/remove-container.spec.js +32 -0
  61. package/dist/docker/stream.js +3 -3
  62. package/dist/docker/using-container.js +29 -11
  63. package/dist/docker/using-container.spec.js +90 -0
  64. package/dist/environment/merge-environment-variables.js +21 -0
  65. package/dist/environment/merge-environment-variables.spec.js +24 -0
  66. package/dist/environment/replace-env-variables.js +43 -10
  67. package/dist/environment/replace-env-variables.spec.js +47 -0
  68. package/dist/executer/abort.spec.js +18 -0
  69. package/dist/executer/await-completed-dependencies.js +56 -0
  70. package/dist/executer/cache-metadata.js +11 -0
  71. package/dist/executer/check-readiness.js +1 -1
  72. package/dist/{planner/work-node-port.js → executer/container-bind.js} +1 -1
  73. package/dist/executer/docker-service.js +110 -86
  74. package/dist/executer/docker-task.js +132 -0
  75. package/dist/executer/environment-mock.js +3 -4
  76. package/dist/executer/environment-mock.spec.js +22 -0
  77. package/dist/executer/event-cache.js +60 -127
  78. package/dist/executer/execute-command.js +4 -3
  79. package/dist/executer/execute-docker.js +32 -14
  80. package/dist/executer/execute-work-service.js +61 -0
  81. package/dist/executer/execute-work-task.js +107 -0
  82. package/dist/executer/execute-work-tree.js +27 -0
  83. package/dist/executer/execute.spec.js +14 -10
  84. package/dist/executer/execution-steps.js +47 -31
  85. package/dist/executer/get-container-binds.js +19 -0
  86. package/dist/executer/get-docker-executor.js +12 -12
  87. package/dist/executer/get-scheduler-down-result.js +12 -0
  88. package/dist/executer/get-scheduler-execute-result.js +28 -0
  89. package/dist/executer/get-scheduler-up-result.js +12 -0
  90. package/dist/executer/get-service-containers.js +14 -0
  91. package/dist/executer/get-service-containers.spec.js +59 -0
  92. package/dist/executer/get-service-env-hints.js +35 -0
  93. package/dist/executer/get-service-env-hints.spec.js +65 -0
  94. package/dist/executer/kubernetes-service.js +116 -48
  95. package/dist/executer/label-values.js +29 -46
  96. package/dist/executer/label-values.spec.js +61 -0
  97. package/dist/executer/lazy-resolver.js +16 -0
  98. package/dist/executer/lazy-resolver.spec.js +20 -0
  99. package/dist/executer/local-task.js +44 -0
  100. package/dist/executer/process-manager.js +16 -70
  101. package/dist/executer/process-manager.spec.js +39 -76
  102. package/dist/executer/reset-work-tree.js +30 -0
  103. package/dist/executer/scheduler/check-for-loop.js +17 -7
  104. package/dist/executer/scheduler/{scheduler-state.js → dependency-state.js} +1 -1
  105. package/dist/executer/scheduler/enqueue-next.js +83 -19
  106. package/dist/executer/scheduler/enqueue-next.spec.js +136 -0
  107. package/dist/executer/scheduler/{node-state.js → need-state.js} +1 -1
  108. package/dist/{parser/build-file.js → executer/scheduler/task-state.js} +1 -1
  109. package/dist/executer/set-user-permission.js +1 -1
  110. package/dist/executer/state-resolver.js +61 -0
  111. package/dist/executer/state-resolver.spec.js +37 -0
  112. package/dist/executer/state.js +37 -31
  113. package/dist/executer/state.spec.js +66 -0
  114. package/dist/executer/states.spec.js +14 -0
  115. package/dist/executer/watch-loop.js +68 -0
  116. package/dist/executer/watch-state-key.js +44 -0
  117. package/dist/executer/work-scope.js +1 -5
  118. package/dist/file/get-file-context.js +24 -3
  119. package/dist/file/get-file-context.spec.js +178 -0
  120. package/dist/file/move-files.js +2 -2
  121. package/dist/file/move-files.spec.js +48 -0
  122. package/dist/index.js +7 -8
  123. package/dist/json-schema.js +12 -0
  124. package/dist/kubernetes/apply.js +34 -0
  125. package/dist/kubernetes/await-running-state.js +71 -0
  126. package/dist/kubernetes/ensure-http-route.js +53 -0
  127. package/dist/kubernetes/ensure-http-route.spec.js +70 -0
  128. package/dist/kubernetes/ensure-ingress.js +54 -0
  129. package/dist/kubernetes/ensure-kubernetes-deployment-exists.js +105 -0
  130. package/dist/kubernetes/ensure-kubernetes-persistent-volume-claim-exists.js +40 -0
  131. package/dist/kubernetes/ensure-kubernetes-service-exists.js +36 -0
  132. package/dist/kubernetes/ensure-namespace.js +43 -0
  133. package/dist/kubernetes/ensure-namespace.spec.js +39 -0
  134. package/dist/kubernetes/ensure-persistent-data.js +158 -0
  135. package/dist/kubernetes/get-service-ip.js +11 -0
  136. package/dist/kubernetes/kubernetes-instance.js +48 -0
  137. package/dist/kubernetes/remove-persistent-data.js +19 -0
  138. package/dist/kubernetes/resolve-pod-name.js +42 -0
  139. package/dist/kubernetes/resolve-pod-name.spec.js +81 -0
  140. package/dist/kubernetes/resources.js +12 -0
  141. package/dist/kubernetes/restore-kubernetes-data.js +42 -0
  142. package/dist/kubernetes/store-kubernetes-data.js +39 -0
  143. package/dist/kubernetes/volumes.js +64 -0
  144. package/dist/log.js +69 -69
  145. package/dist/log.spec.js +131 -0
  146. package/dist/logging/grouped-logger.js +17 -15
  147. package/dist/logging/grouped-logger.spec.js +84 -0
  148. package/dist/logging/interactive-logger.js +1 -1
  149. package/dist/logging/interactive-logger.spec.js +57 -0
  150. package/dist/logging/live-logger.js +2 -2
  151. package/dist/logging/live-logger.spec.js +37 -0
  152. package/dist/logging/loggers.spec.js +154 -0
  153. package/dist/optimizer/calculate-checksum.spec.js +34 -0
  154. package/dist/optimizer/get-cache-directory.js +6 -6
  155. package/dist/optimizer/get-cache-directory.spec.js +59 -0
  156. package/dist/optimizer/get-work-cache-stats.js +53 -0
  157. package/dist/optimizer/get-work-cache-stats.spec.js +79 -0
  158. package/dist/{parser/build-file-task.js → optimizer/work-cache-stats.js} +1 -1
  159. package/dist/optimizer/work-task-cache-description.js +25 -0
  160. package/dist/optimizer/work-task-cache-description.spec.js +45 -0
  161. package/dist/parser/default-build-file.js +14 -7
  162. package/dist/parser/default-build-file.spec.js +48 -0
  163. package/dist/parser/parse-label-arguments.spec.js +27 -0
  164. package/dist/parser/read-build-file.js +2 -13
  165. package/dist/parser/read-build-file.spec.js +89 -0
  166. package/dist/parser/read-env-file.js +4 -5
  167. package/dist/parser/read-env-file.spec.js +42 -0
  168. package/dist/planner/utils/append-work-dependencies.js +55 -0
  169. package/dist/planner/utils/append-work-environment.js +22 -0
  170. package/dist/planner/utils/append-work-needs.js +18 -0
  171. package/dist/planner/utils/append-work-service.js +107 -0
  172. package/dist/planner/utils/append-work-service.spec.js +71 -0
  173. package/dist/planner/utils/append-work-task.js +81 -0
  174. package/dist/planner/utils/get-container-user.js +14 -0
  175. package/dist/planner/utils/get-container-user.spec.js +30 -0
  176. package/dist/planner/utils/get-default-kube-config.js +10 -0
  177. package/dist/planner/utils/get-default-kube-config.spec.js +11 -0
  178. package/dist/planner/utils/get-work-runtime.js +44 -0
  179. package/dist/planner/utils/normalize-path.js +1 -1
  180. package/dist/planner/utils/normalize-path.spec.js +24 -0
  181. package/dist/planner/utils/parse-work-command.js +80 -0
  182. package/dist/planner/utils/parse-work-command.spec.js +21 -0
  183. package/dist/planner/utils/parse-work-generate.js +38 -0
  184. package/dist/planner/utils/parse-work-generate.spec.js +45 -0
  185. package/dist/planner/utils/{parse-work-node-mount.js → parse-work-mount.js} +10 -7
  186. package/dist/planner/utils/{parse-work-node-mount.spec.js → parse-work-mount.spec.js} +14 -11
  187. package/dist/planner/utils/parse-work-mounts.js +13 -0
  188. package/dist/planner/utils/parse-work-mounts.spec.js +19 -0
  189. package/dist/planner/utils/{parse-work-node-port.js → parse-work-port.js} +11 -8
  190. package/dist/planner/utils/parse-work-port.spec.js +24 -0
  191. package/dist/planner/utils/parse-work-ports.js +10 -0
  192. package/dist/planner/utils/parse-work-ports.spec.js +24 -0
  193. package/dist/planner/utils/parse-work-source.js +72 -0
  194. package/dist/planner/utils/parse-work-source.spec.js +58 -0
  195. package/dist/planner/utils/parse-work-volume.js +45 -0
  196. package/dist/planner/utils/parse-work-volume.spec.js +18 -0
  197. package/dist/planner/utils/plan-work-tasks.js +41 -0
  198. package/dist/planner/utils/plan-work-tasks.spec.js +30 -0
  199. package/dist/planner/utils/plan-work-volume.js +1 -0
  200. package/dist/planner/utils/plan-work-volume.spec.js +33 -0
  201. package/dist/planner/utils/split-name.js +2 -2
  202. package/dist/planner/utils/split-name.spec.js +16 -0
  203. package/dist/planner/utils/template-value.js +1 -2
  204. package/dist/planner/utils/template-value.spec.js +14 -0
  205. package/dist/planner/validate.js +71 -38
  206. package/dist/planner/validate.spec.js +251 -0
  207. package/dist/planner/work-cache-id.js +12 -0
  208. package/dist/planner/work-cache-id.spec.js +173 -0
  209. package/dist/planner/work-command.js +3 -0
  210. package/dist/planner/work-environment.js +3 -0
  211. package/dist/planner/work-healthcheck.js +3 -0
  212. package/dist/planner/{work-node-status.js → work-item-status.js} +16 -62
  213. package/dist/{parser/build-file-service.js → planner/work-item-validation.js} +1 -1
  214. package/dist/planner/work-item.js +16 -0
  215. package/dist/{optimizer/work-node-cache-stats.js → planner/work-kubernetes-selector.js} +1 -1
  216. package/dist/planner/{work-nodes.js → work-port.js} +1 -1
  217. package/dist/planner/work-runtime-docker.js +224 -0
  218. package/dist/planner/work-runtime-kubernetes.js +264 -0
  219. package/dist/planner/work-runtime-local.js +109 -0
  220. package/dist/planner/work-runtime-local.spec.js +193 -0
  221. package/dist/planner/work-service-id.js +15 -5
  222. package/dist/planner/work-source.js +3 -0
  223. package/dist/planner/work-task.js +8 -0
  224. package/dist/program-commands.spec.js +84 -0
  225. package/dist/program.js +219 -73
  226. package/dist/program.spec.js +34 -11
  227. package/dist/run-program.js +28 -0
  228. package/dist/{executer/process.js → runtime/runtime.js} +1 -1
  229. package/dist/schema/build-file-base-task-schema.js +27 -0
  230. package/dist/schema/build-file-container-service-schema.js +28 -0
  231. package/dist/schema/build-file-container-task-schema.js +13 -0
  232. package/dist/schema/build-file-environment-schema-ingress.js +14 -0
  233. package/dist/schema/build-file-environment-schema.js +23 -0
  234. package/dist/schema/build-file-kubernetes-service-schema.js +17 -0
  235. package/dist/schema/build-file-kubernetes-service-selector-schema.js +9 -0
  236. package/dist/schema/build-file-local-platform-schema.js +9 -0
  237. package/dist/schema/build-file-local-task-schema.js +12 -0
  238. package/dist/schema/build-file-need-schema.js +12 -0
  239. package/dist/schema/build-file-schema.js +23 -0
  240. package/dist/schema/build-file-schema.spec.js +61 -0
  241. package/dist/schema/build-file-service-container-healthcheck.js +8 -0
  242. package/dist/schema/build-file-service-schema.js +15 -0
  243. package/dist/schema/build-file-task-command-schema.js +12 -0
  244. package/dist/schema/build-file-task-schema.js +12 -0
  245. package/dist/schema/build-file-volume-schema.js +14 -0
  246. package/dist/schema/cache-schema.js +31 -0
  247. package/dist/schema/doc-build-files.spec.js +223 -0
  248. package/dist/schema/envs-schema.js +6 -0
  249. package/dist/schema/labels-schema.js +6 -0
  250. package/dist/schema/parse-context.js +3 -0
  251. package/dist/schema/parse-error.js +12 -0
  252. package/dist/schema/parse-error.spec.js +18 -0
  253. package/dist/schema/reference-parser.js +209 -0
  254. package/dist/schema/reference-parser.spec.js +83 -0
  255. package/dist/schema/schema-parser.js +66 -0
  256. package/dist/schema/schema-parser.spec.js +80 -0
  257. package/dist/schema/type-guards.spec.js +23 -0
  258. package/dist/schema/work-scope-parser.js +77 -0
  259. package/dist/schema/work-scope-parser.spec.js +66 -0
  260. package/dist/service/update-service-status.js +11 -0
  261. package/dist/service/update-service-status.spec.js +27 -0
  262. package/dist/testing/ensure-local-registry.js +134 -0
  263. package/dist/testing/example-test-suite.js +16 -18
  264. package/dist/testing/expect.js +46 -22
  265. package/dist/testing/http-get.js +21 -0
  266. package/dist/testing/integration/cache.spec.js +13 -14
  267. package/dist/testing/integration/cancellation.spec.js +14 -9
  268. package/dist/testing/integration/clean.spec.js +14 -11
  269. package/dist/testing/integration/cmd.spec.js +1 -1
  270. package/dist/testing/integration/concurrency.spec.js +3 -3
  271. package/dist/testing/integration/docker-package-registry.spec.js +44 -0
  272. package/dist/testing/integration/docker-service-crash.spec.js +36 -0
  273. package/dist/testing/integration/docker.spec.js +4 -3
  274. package/dist/testing/integration/env.spec.js +9 -8
  275. package/dist/testing/integration/environment.spec.js +97 -0
  276. package/dist/testing/integration/error.spec.js +5 -4
  277. package/dist/testing/integration/export.spec.js +22 -0
  278. package/dist/testing/integration/extend.spec.js +9 -12
  279. package/dist/testing/integration/glob.spec.js +12 -20
  280. package/dist/testing/integration/include.spec.js +3 -3
  281. package/dist/testing/integration/invalid.spec.js +2 -3
  282. package/dist/testing/integration/invalid_loop.spec.js +49 -6
  283. package/dist/testing/integration/k8s-state-cache.spec.js +41 -0
  284. package/dist/testing/integration/kubernetes-service.spec.js +148 -0
  285. package/dist/testing/integration/kubernetes.spec.js +93 -9
  286. package/dist/testing/integration/labels.spec.js +6 -11
  287. package/dist/testing/integration/local.spec.js +1 -1
  288. package/dist/testing/integration/monorepo.spec.js +4 -4
  289. package/dist/testing/integration/multi-line.spec.js +3 -3
  290. package/dist/testing/integration/reference.spec.js +4 -5
  291. package/dist/testing/integration/services.spec.js +22 -4
  292. package/dist/testing/integration/store.spec.js +15 -16
  293. package/dist/testing/integration/unknown.spec.js +20 -18
  294. package/dist/testing/integration/validate.spec.js +143 -26
  295. package/dist/testing/integration/watch-dependency.spec.js +11 -8
  296. package/dist/testing/integration/watch.spec.js +15 -16
  297. package/dist/testing/requires-kubernetes.js +18 -0
  298. package/dist/testing/requires-linux-containers.js +18 -0
  299. package/dist/testing/requires-windows-containers.js +18 -0
  300. package/dist/testing/test-case.js +75 -0
  301. package/dist/testing/test-streams.js +28 -0
  302. package/dist/testing/testing-timeout.js +32 -0
  303. package/dist/utils/abort-event.js +10 -4
  304. package/dist/utils/abort-event.spec.js +46 -0
  305. package/dist/utils/buffer-context.js +44 -0
  306. package/dist/utils/buffer-context.spec.js +38 -0
  307. package/dist/utils/ci.spec.js +53 -0
  308. package/dist/utils/emitter.spec.js +34 -0
  309. package/dist/utils/empty-writable.spec.js +18 -0
  310. package/dist/utils/environment-config.js +4 -1
  311. package/dist/utils/environment-config.spec.js +26 -0
  312. package/dist/utils/fail-never.spec.js +9 -0
  313. package/dist/version.js +9 -0
  314. package/dist/version.spec.js +12 -0
  315. package/package.json +41 -23
  316. package/specs/build-file/caches/spec.md +114 -0
  317. package/specs/build-file/environment-variables/spec.md +82 -0
  318. package/specs/build-file/includes/spec.md +60 -0
  319. package/specs/build-file/references/spec.md +56 -0
  320. package/specs/build-file/spec.md +62 -0
  321. package/specs/cli/clean/spec.md +85 -0
  322. package/specs/cli/down/spec.md +85 -0
  323. package/specs/cli/execute/spec.md +101 -0
  324. package/specs/cli/init/spec.md +64 -0
  325. package/specs/cli/ls/spec.md +66 -0
  326. package/specs/cli/package/spec.md +110 -0
  327. package/specs/cli/spec.md +73 -0
  328. package/specs/cli/store-restore/spec.md +74 -0
  329. package/specs/cli/up/spec.md +80 -0
  330. package/specs/cli/validate/spec.md +70 -0
  331. package/specs/labels/spec.md +91 -0
  332. package/specs/service/container/spec.md +120 -0
  333. package/specs/service/kubernetes/spec.md +97 -0
  334. package/specs/service/spec.md +105 -0
  335. package/specs/task/caching/spec.md +107 -0
  336. package/specs/task/container/spec.md +118 -0
  337. package/specs/task/dependencies/spec.md +70 -0
  338. package/specs/task/extending/spec.md +74 -0
  339. package/specs/task/kubernetes/spec.md +125 -0
  340. package/specs/task/needs/spec.md +59 -0
  341. package/specs/task/spec.md +87 -0
  342. package/specs/task/watching/spec.md +69 -0
  343. package/dist/console/console-context-mock.js +0 -14
  344. package/dist/environment/get-process-env.js +0 -15
  345. package/dist/executer/create-scheduler-state.js +0 -32
  346. package/dist/executer/docker-node.js +0 -123
  347. package/dist/executer/hierarchy-scheduler.js +0 -94
  348. package/dist/executer/local-node.js +0 -57
  349. package/dist/executer/schedule.js +0 -137
  350. package/dist/executer/start-node.js +0 -67
  351. package/dist/executer/watch-node.js +0 -56
  352. package/dist/executer/watch-service.js +0 -54
  353. package/dist/file/get-file-context-mock.js +0 -187
  354. package/dist/file/get-file-context-mock.spec.js +0 -56
  355. package/dist/get-work-context.js +0 -29
  356. package/dist/optimizer/get-work-node-cache-stats.js +0 -104
  357. package/dist/optimizer/read-work-node-cache.js +0 -19
  358. package/dist/optimizer/work-node-cache-description.js +0 -32
  359. package/dist/optimizer/write-work-node-cache.js +0 -21
  360. package/dist/parser/build-file-task-command.js +0 -3
  361. package/dist/parser/build-file-task-platform.js +0 -3
  362. package/dist/parser/build-file-task-source.js +0 -3
  363. package/dist/parser/get-build-file.js +0 -9
  364. package/dist/parser/parse-boolean.js +0 -29
  365. package/dist/parser/parse-boolean.spec.js +0 -48
  366. package/dist/parser/parse-build-file-references.js +0 -35
  367. package/dist/parser/parse-build-file-services.js +0 -58
  368. package/dist/parser/parse-build-file-task-command.js +0 -36
  369. package/dist/parser/parse-build-file-task-source.js +0 -63
  370. package/dist/parser/parse-build-file.js +0 -125
  371. package/dist/parser/parse-context.js +0 -13
  372. package/dist/parser/parse-envs.js +0 -29
  373. package/dist/parser/parse-healthcheck.js +0 -28
  374. package/dist/parser/parse-service-selector.js +0 -39
  375. package/dist/parser/parse-string-array.js +0 -20
  376. package/dist/parser/parse-string-map.js +0 -30
  377. package/dist/parser/parse-string.js +0 -20
  378. package/dist/planner/utils/get-container-mounts.js +0 -24
  379. package/dist/planner/utils/parse-work-service-volume.js +0 -23
  380. package/dist/planner/utils/plan-work-command.js +0 -21
  381. package/dist/planner/utils/plan-work-dependency.js +0 -36
  382. package/dist/planner/utils/plan-work-node.js +0 -293
  383. package/dist/planner/utils/plan-work-nodes.js +0 -78
  384. package/dist/planner/utils/plan-work-tree.js +0 -15
  385. package/dist/planner/work-context.js +0 -23
  386. package/dist/planner/work-node-command.js +0 -3
  387. package/dist/planner/work-node-id.js +0 -12
  388. package/dist/planner/work-node-id.spec.js +0 -106
  389. package/dist/planner/work-node-path.js +0 -2
  390. package/dist/planner/work-node-source.js +0 -3
  391. package/dist/planner/work-node-validation.js +0 -3
  392. package/dist/planner/work-node.js +0 -8
  393. package/dist/planner/work-service-volume.js +0 -3
  394. package/dist/planner/work-services.js +0 -3
  395. package/dist/start-watch-processes.js +0 -28
  396. package/dist/testing/cli.js +0 -73
  397. package/dist/testing/create-build-file.js +0 -9
  398. package/dist/testing/get-logger.js +0 -23
  399. package/dist/testing/get-node.js +0 -12
  400. package/dist/testing/get-test-context.js +0 -19
  401. package/dist/testing/integration/program.spec.js +0 -25
  402. package/dist/testing/lazy.js +0 -17
  403. package/dist/testing/start-watch-processes.js +0 -29
  404. package/dist/testing/test-suite-options.js +0 -3
  405. package/dist/utils/abortable-function.js +0 -71
  406. package/logs/cache-console.log +0 -0
  407. package/logs/cache-status.log +0 -6
  408. package/logs/cache-stout.log +0 -0
  409. package/logs/cancellation-console.log +0 -0
  410. package/logs/cancellation-status.log +0 -3
  411. package/logs/cancellation-stout.log +0 -3
  412. package/logs/clean-console.log +0 -0
  413. package/logs/clean-status.log +0 -37
  414. package/logs/clean-stout.log +0 -95
  415. package/logs/cmd-console.log +0 -0
  416. package/logs/cmd-status.log +0 -8
  417. package/logs/cmd-stout.log +0 -9
  418. package/logs/concurrency-console.log +0 -0
  419. package/logs/concurrency-status.log +0 -14
  420. package/logs/concurrency-stout.log +0 -88
  421. package/logs/docker-console.log +0 -0
  422. package/logs/docker-status.log +0 -14
  423. package/logs/docker-stout.log +0 -39
  424. package/logs/env-console.log +0 -1
  425. package/logs/env-status.log +0 -1
  426. package/logs/env-stout.log +0 -0
  427. package/logs/error-console.log +0 -0
  428. package/logs/error-status.log +0 -10
  429. package/logs/error-stout.log +0 -36
  430. package/logs/extend-console.log +0 -0
  431. package/logs/extend-status.log +0 -1
  432. package/logs/extend-stout.log +0 -0
  433. package/logs/glob-console.log +0 -28
  434. package/logs/glob-status.log +0 -23
  435. package/logs/glob-stout.log +0 -52
  436. package/logs/hello-world-node-console.log +0 -0
  437. package/logs/hello-world-node-status.log +0 -37
  438. package/logs/hello-world-node-stout.log +0 -41
  439. package/logs/include-console.log +0 -0
  440. package/logs/include-status.log +0 -3
  441. package/logs/include-stout.log +0 -0
  442. package/logs/invalid-console.log +0 -0
  443. package/logs/invalid-status.log +0 -1
  444. package/logs/invalid-stout.log +0 -0
  445. package/logs/invalid_loop-console.log +0 -0
  446. package/logs/invalid_loop-status.log +0 -1
  447. package/logs/invalid_loop-stout.log +0 -11
  448. package/logs/labels-console.log +0 -0
  449. package/logs/labels-status.log +0 -1
  450. package/logs/labels-stout.log +0 -0
  451. package/logs/local-console.log +0 -0
  452. package/logs/local-status.log +0 -7
  453. package/logs/local-stout.log +0 -8
  454. package/logs/monorepo-console.log +0 -0
  455. package/logs/monorepo-status.log +0 -152
  456. package/logs/monorepo-stout.log +0 -2354
  457. package/logs/multi-line-console.log +0 -0
  458. package/logs/multi-line-status.log +0 -1
  459. package/logs/multi-line-stout.log +0 -0
  460. package/logs/reference-console.log +0 -0
  461. package/logs/reference-status.log +0 -5
  462. package/logs/reference-stout.log +0 -0
  463. package/logs/services-console.log +0 -0
  464. package/logs/services-status.log +0 -129
  465. package/logs/services-stout.log +0 -639
  466. package/logs/store-restore-console.log +0 -0
  467. package/logs/store-restore-status.log +0 -40
  468. package/logs/store-restore-stout.log +0 -97
  469. package/logs/unknown-console.log +0 -3
  470. package/logs/unknown-status.log +0 -1
  471. package/logs/unknown-stout.log +0 -0
  472. package/logs/validate-console.log +0 -0
  473. package/logs/validate-status.log +0 -2
  474. package/logs/validate-stout.log +0 -0
  475. package/logs/watch-console.log +0 -0
  476. package/logs/watch-dependency-console.log +0 -0
  477. package/logs/watch-dependency-status.log +0 -176
  478. package/logs/watch-dependency-stout.log +0 -779
  479. package/logs/watch-status.log +0 -100
  480. package/logs/watch-stout.log +0 -349
@@ -0,0 +1,66 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const state_1 = require("./state");
4
+ describe('State', () => {
5
+ it('exposes the initial value via `current`', () => {
6
+ const s = new state_1.State(7);
7
+ expect(s.current).toBe(7);
8
+ });
9
+ it('set() updates current and returns the new value', () => {
10
+ const s = new state_1.State(0);
11
+ expect(s.set(3)).toBe(3);
12
+ expect(s.current).toBe(3);
13
+ });
14
+ it('notifies every registered listener on set()', () => {
15
+ const s = new state_1.State(0);
16
+ const a = vi.fn();
17
+ const b = vi.fn();
18
+ s.on('a', a);
19
+ s.on('b', b);
20
+ s.set(1);
21
+ expect(a).toHaveBeenCalledWith(1);
22
+ expect(b).toHaveBeenCalledWith(1);
23
+ });
24
+ it('close() on a listener handle stops further notifications', () => {
25
+ const s = new state_1.State(0);
26
+ const cb = vi.fn();
27
+ const handle = s.on('k', cb);
28
+ handle.close();
29
+ s.set(1);
30
+ expect(cb).not.toHaveBeenCalled();
31
+ });
32
+ it('close() on an already-removed listener is a no-op', () => {
33
+ const s = new state_1.State(0);
34
+ const handle = s.on('k', vi.fn());
35
+ handle.close();
36
+ expect(() => handle.close()).not.toThrow();
37
+ });
38
+ it('forwards sub-state changes to its own listeners', () => {
39
+ const sub = new state_1.State(0);
40
+ const parent = new state_1.State('p', { subStates: [sub] });
41
+ const cb = vi.fn();
42
+ parent.on('any', cb);
43
+ // emit a 'sub-state-forward' on the sub-state to trigger parent notification
44
+ sub.set(1); // sub.set notifies its listeners under whatever keys are registered
45
+ // The parent registered for 'sub-state-forward' on the sub; sub.set calls
46
+ // notifyListeners which dispatches to all listeners regardless of key, so
47
+ // the parent's forwarding listener fires and notifies parent listeners.
48
+ expect(cb).toHaveBeenCalledWith('p');
49
+ });
50
+ it('close() runs the onDestroy callback', () => {
51
+ const onDestroy = vi.fn();
52
+ const s = new state_1.State(0, { onDestroy });
53
+ s.close();
54
+ expect(onDestroy).toHaveBeenCalledTimes(1);
55
+ });
56
+ it('close() detaches sub-state forwarders', () => {
57
+ const sub = new state_1.State(0);
58
+ const parent = new state_1.State('p', { subStates: [sub] });
59
+ const cb = vi.fn();
60
+ parent.on('any', cb);
61
+ parent.close();
62
+ sub.set(1);
63
+ expect(cb).not.toHaveBeenCalled();
64
+ });
65
+ });
66
+ //# sourceMappingURL=state.spec.js.map
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const states_1 = require("./states");
4
+ describe('getDuration', () => {
5
+ it('returns the milliseconds elapsed since the given start date', () => {
6
+ const started = new Date(Date.now() - 250);
7
+ const d = (0, states_1.getDuration)(started);
8
+ expect(d).toBeGreaterThanOrEqual(250);
9
+ });
10
+ it('returns 0 (or very small) for a start date that is essentially now', () => {
11
+ expect((0, states_1.getDuration)(new Date())).toBeGreaterThanOrEqual(0);
12
+ });
13
+ });
14
+ //# sourceMappingURL=states.spec.js.map
@@ -0,0 +1,68 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.watchLoop = void 0;
4
+ const watch_state_key_1 = require("./watch-state-key");
5
+ const state_1 = require("./state");
6
+ const abort_event_1 = require("../utils/abort-event");
7
+ const state_resolver_1 = require("./state-resolver");
8
+ const abort_1 = require("./abort");
9
+ const enqueue_next_1 = require("./scheduler/enqueue-next");
10
+ async function watchLoop(work, environment, options, factory) {
11
+ const currentStats = await (0, enqueue_next_1.checkCacheState)(work, options.cacheDefault, environment);
12
+ (0, abort_1.checkForAbort)(environment.abortCtrl.signal);
13
+ // continuous lives on container-services and on tasks (both task types); a
14
+ // kubernetes-service has no such field. Read it wherever it exists.
15
+ const continuous = 'continuous' in work.data ? work.data.continuous : false;
16
+ const watchMode = options.watch && !options.daemon && !continuous;
17
+ const watchState = watchMode
18
+ ? (0, watch_state_key_1.watchStateKey)(work, currentStats, environment, options)
19
+ : new state_1.State(currentStats);
20
+ let currentRun = {
21
+ abortController: new AbortController(),
22
+ cacheState: watchState.current,
23
+ };
24
+ if (watchMode) {
25
+ watchState.on('state-key', (cacheState) => {
26
+ work.status.write('debug', 'sources changed, abort current run');
27
+ currentRun.abortController.abort();
28
+ currentRun = {
29
+ abortController: new AbortController(),
30
+ cacheState,
31
+ };
32
+ });
33
+ }
34
+ const abortListener = (0, abort_event_1.listenOnAbort)(environment.abortCtrl.signal, () => {
35
+ currentRun.abortController.abort();
36
+ watchState.close();
37
+ });
38
+ do {
39
+ const currentState = watchState.current;
40
+ try {
41
+ await factory(currentState, currentRun.abortController.signal, () => {
42
+ currentRun.abortController.abort();
43
+ });
44
+ }
45
+ catch (e) {
46
+ if (e instanceof abort_1.AbortError) {
47
+ work.state.set({
48
+ type: 'canceled',
49
+ stateKey: currentState.stateKey,
50
+ });
51
+ }
52
+ else {
53
+ throw e;
54
+ }
55
+ }
56
+ if (!watchMode) {
57
+ break;
58
+ }
59
+ if (currentState.stateKey !== watchState.current.stateKey) {
60
+ continue;
61
+ }
62
+ await (0, state_resolver_1.untilChanged)('watch-source-change', watchState, environment.abortCtrl.signal);
63
+ work.status.write('debug', 'sources changed');
64
+ } while (watchMode);
65
+ abortListener.close();
66
+ }
67
+ exports.watchLoop = watchLoop;
68
+ //# sourceMappingURL=watch-loop.js.map
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.watchStateKey = void 0;
4
+ const debouncer_1 = require("../utils/debouncer");
5
+ const path_1 = require("path");
6
+ const state_1 = require("./state");
7
+ const enqueue_next_1 = require("./scheduler/enqueue-next");
8
+ function watchStateKey(item, cacheStats, environment, options) {
9
+ const fileWatchers = [];
10
+ const state = new state_1.State(cacheStats, {
11
+ onDestroy: () => {
12
+ for (const fileWatcher of fileWatchers) {
13
+ fileWatcher.close();
14
+ }
15
+ debouncer.clear();
16
+ },
17
+ });
18
+ const debouncer = new debouncer_1.Debouncer(async () => {
19
+ if (environment.abortCtrl.signal.aborted) {
20
+ return;
21
+ }
22
+ const cacheState = await (0, enqueue_next_1.checkCacheState)(item, options.cacheDefault, environment);
23
+ if (state.current.stateKey !== cacheState.stateKey) {
24
+ state.set(cacheState);
25
+ }
26
+ }, 100);
27
+ for (const src of item.data.src) {
28
+ if (src.inherited) {
29
+ continue;
30
+ }
31
+ item.status.write('debug', `watch ${src.absolutePath} source`);
32
+ const watcher = environment.file.watch(src.absolutePath, async (fileName) => {
33
+ const absoluteFileName = (0, path_1.join)(src.absolutePath, fileName);
34
+ if (src.matcher(absoluteFileName, item.data.cwd)) {
35
+ item.status.write('debug', `source ${absoluteFileName} change for watched task ${item.name}`);
36
+ debouncer.bounce();
37
+ }
38
+ });
39
+ fileWatchers.push(watcher);
40
+ }
41
+ return state;
42
+ }
43
+ exports.watchStateKey = watchStateKey;
44
+ //# sourceMappingURL=watch-state-key.js.map
@@ -1,10 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.isContextTaskFilter = exports.emptyWorkLabelScope = void 0;
4
- function emptyWorkLabelScope() {
5
- return { excludeLabels: {}, filterLabels: {} };
6
- }
7
- exports.emptyWorkLabelScope = emptyWorkLabelScope;
3
+ exports.isContextTaskFilter = void 0;
8
4
  const isContextTaskFilter = (target) => 'taskName' in target;
9
5
  exports.isContextTaskFilter = isContextTaskFilter;
10
6
  //# sourceMappingURL=work-scope.js.map
@@ -5,6 +5,8 @@ const fs_1 = require("fs");
5
5
  const path_1 = require("path");
6
6
  const chokidar_1 = require("chokidar");
7
7
  function handleCallback(callback) {
8
+ //function handleCallback(callback: (cb: (err: Error | null) => void) => void): Promise<void>
9
+ //function handleCallback(callback: (cb: (err: Error | null, value?: any) => void) => void): Promise<any> {
8
10
  return new Promise((resolve, reject) => {
9
11
  callback((err, value) => {
10
12
  if (err) {
@@ -61,6 +63,9 @@ function getFileContext(cwd) {
61
63
  .on('close', () => resolve());
62
64
  });
63
65
  },
66
+ createWriteStream(path) {
67
+ return (0, fs_1.createWriteStream)(path);
68
+ },
64
69
  listFiles(path) {
65
70
  return handleCallback((cb) => (0, fs_1.readdir)(getAbsolutePath(cwd, path), cb));
66
71
  },
@@ -116,11 +121,27 @@ function getFileContext(cwd) {
116
121
  async remove(path) {
117
122
  const absolutePath = getAbsolutePath(cwd, path);
118
123
  if (await this.exists(absolutePath)) {
119
- return handleCallback((cb) => (0, fs_1.rm)(absolutePath, { recursive: true }, cb));
124
+ // On Windows a file/dir can briefly stay locked after a watcher or
125
+ // process releases it, making rmdir fail with EBUSY/EPERM. Node's
126
+ // built-in retry handles exactly those transient errors.
127
+ return handleCallback((cb) => (0, fs_1.rm)(absolutePath, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 }, cb));
120
128
  }
121
129
  },
122
130
  watch(path, callback) {
123
- const watcher = (0, chokidar_1.watch)(getAbsolutePath(cwd, path));
131
+ // Native fs events are unreliable on virtual filesystems and frequently
132
+ // miss single-file changes. This bites two platforms:
133
+ // - Windows CI, and
134
+ // - macOS + Docker Desktop, whose gRPC-FUSE/virtiofs share does not emit
135
+ // host FSEvents for files a *container* writes through a bind mount.
136
+ // The latter silently breaks --watch cascades between container tasks (a
137
+ // dependency regenerates an output but the dependent's watcher never
138
+ // fires). Poll on both so changes are detected deterministically regardless
139
+ // of who wrote the file. HAMMERKIT_WATCH_POLLING=true|false overrides the
140
+ // default (e.g. to force native events on a fast local FS).
141
+ const usePolling = process.env.HAMMERKIT_WATCH_POLLING !== undefined
142
+ ? process.env.HAMMERKIT_WATCH_POLLING === 'true'
143
+ : process.platform === 'win32' || process.platform === 'darwin';
144
+ const watcher = (0, chokidar_1.watch)(getAbsolutePath(cwd, path), { usePolling });
124
145
  watcher.on('add', (fileName) => {
125
146
  callback(fileName);
126
147
  });
@@ -132,7 +153,7 @@ function getFileContext(cwd) {
132
153
  });
133
154
  return {
134
155
  close() {
135
- watcher.close();
156
+ return watcher.close();
136
157
  },
137
158
  };
138
159
  },
@@ -0,0 +1,178 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const path_1 = require("path");
4
+ const os_1 = require("os");
5
+ const fs_1 = require("fs");
6
+ const stream_1 = require("stream");
7
+ const get_file_context_1 = require("./get-file-context");
8
+ describe('getFileContext', () => {
9
+ let cwd;
10
+ beforeEach(() => {
11
+ cwd = (0, fs_1.mkdtempSync)((0, path_1.join)((0, os_1.tmpdir)(), 'hammerkit-fc-'));
12
+ });
13
+ afterEach(() => {
14
+ // maxRetries handles Windows' transient ENOTEMPTY/EBUSY when a handle is
15
+ // released a beat after the test ends (mirrors getFileContext.remove). The
16
+ // stream tests below also await 'close' so this is only a safety net.
17
+ (0, fs_1.rmSync)(cwd, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 });
18
+ });
19
+ describe('stats', () => {
20
+ it('reports type "directory" for a directory', async () => {
21
+ const fc = (0, get_file_context_1.getFileContext)(cwd);
22
+ expect((await fc.stats('.')).type).toBe('directory');
23
+ });
24
+ it('reports type "file" with lastModified for a regular file', async () => {
25
+ (0, fs_1.writeFileSync)((0, path_1.join)(cwd, 'a.txt'), 'x');
26
+ const fc = (0, get_file_context_1.getFileContext)(cwd);
27
+ const s = await fc.stats('a.txt');
28
+ expect(s.type).toBe('file');
29
+ if (s.type === 'file') {
30
+ expect(s.lastModified).toBeGreaterThan(0);
31
+ }
32
+ });
33
+ it('rejects when the path does not exist', async () => {
34
+ const fc = (0, get_file_context_1.getFileContext)(cwd);
35
+ await expect(fc.stats('missing')).rejects.toThrow();
36
+ });
37
+ it('reports type "other" for non-file non-directory entries (symlink to missing)', async () => {
38
+ const link = (0, path_1.join)(cwd, 'link');
39
+ try {
40
+ (0, fs_1.symlinkSync)('/no/such/target', link);
41
+ }
42
+ catch {
43
+ // symlink may not be permitted on this platform — accept either outcome
44
+ return;
45
+ }
46
+ // statSync (used by stats()) follows symlinks; if the target is missing it errors.
47
+ // That is the existing behavior; we cover the rejection path here.
48
+ const fc = (0, get_file_context_1.getFileContext)(cwd);
49
+ await expect(fc.stats('link')).rejects.toThrow();
50
+ });
51
+ });
52
+ describe('exists', () => {
53
+ it('resolves true for an existing path and false otherwise', async () => {
54
+ const fc = (0, get_file_context_1.getFileContext)(cwd);
55
+ expect(await fc.exists('.')).toBe(true);
56
+ expect(await fc.exists('nope')).toBe(false);
57
+ });
58
+ });
59
+ describe('writeFile / read / appendFile', () => {
60
+ it('writes, reads, and appends file content', async () => {
61
+ const fc = (0, get_file_context_1.getFileContext)(cwd);
62
+ await fc.writeFile('f.txt', 'hello');
63
+ expect(await fc.read('f.txt')).toBe('hello');
64
+ await fc.appendFile('f.txt', ' world');
65
+ expect(await fc.read('f.txt')).toBe('hello world');
66
+ });
67
+ it('read rejects on missing file', async () => {
68
+ const fc = (0, get_file_context_1.getFileContext)(cwd);
69
+ await expect(fc.read('missing.txt')).rejects.toThrow();
70
+ });
71
+ });
72
+ describe('listFiles', () => {
73
+ it('returns the entries in a directory', async () => {
74
+ (0, fs_1.writeFileSync)((0, path_1.join)(cwd, 'a'), '');
75
+ (0, fs_1.writeFileSync)((0, path_1.join)(cwd, 'b'), '');
76
+ const fc = (0, get_file_context_1.getFileContext)(cwd);
77
+ expect((await fc.listFiles('.')).sort()).toEqual(['a', 'b']);
78
+ });
79
+ });
80
+ describe('createDirectory / remove', () => {
81
+ it('creates a directory recursively and removes it', async () => {
82
+ const fc = (0, get_file_context_1.getFileContext)(cwd);
83
+ await fc.createDirectory((0, path_1.join)('deep', 'inner'));
84
+ expect(await fc.exists((0, path_1.join)('deep', 'inner'))).toBe(true);
85
+ await fc.remove('deep');
86
+ expect(await fc.exists('deep')).toBe(false);
87
+ });
88
+ it('remove on a missing path is a no-op', async () => {
89
+ const fc = (0, get_file_context_1.getFileContext)(cwd);
90
+ await expect(fc.remove('nope')).resolves.toBeUndefined();
91
+ });
92
+ });
93
+ describe('copy', () => {
94
+ it('copies a file, creating the destination directory if needed', async () => {
95
+ const fc = (0, get_file_context_1.getFileContext)(cwd);
96
+ await fc.writeFile('src.txt', 'data');
97
+ await fc.copy('src.txt', (0, path_1.join)('dest', 'src.txt'));
98
+ expect((0, fs_1.readFileSync)((0, path_1.join)(cwd, 'dest', 'src.txt'), 'utf8')).toBe('data');
99
+ });
100
+ it('copies a directory recursively', async () => {
101
+ const fc = (0, get_file_context_1.getFileContext)(cwd);
102
+ (0, fs_1.mkdirSync)((0, path_1.join)(cwd, 'src', 'sub'), { recursive: true });
103
+ (0, fs_1.writeFileSync)((0, path_1.join)(cwd, 'src', 'a.txt'), 'a');
104
+ (0, fs_1.writeFileSync)((0, path_1.join)(cwd, 'src', 'sub', 'b.txt'), 'b');
105
+ await fc.copy('src', 'dest');
106
+ expect((0, fs_1.readFileSync)((0, path_1.join)(cwd, 'dest', 'a.txt'), 'utf8')).toBe('a');
107
+ expect((0, fs_1.readFileSync)((0, path_1.join)(cwd, 'dest', 'sub', 'b.txt'), 'utf8')).toBe('b');
108
+ });
109
+ it('copy of a missing source is a no-op', async () => {
110
+ const fc = (0, get_file_context_1.getFileContext)(cwd);
111
+ await expect(fc.copy('nope', 'dest')).resolves.toBeUndefined();
112
+ expect((0, fs_1.statSync)((0, path_1.join)(cwd))).toBeDefined(); // cwd still exists
113
+ });
114
+ it('copy preserves the destination directory when it already exists', async () => {
115
+ const fc = (0, get_file_context_1.getFileContext)(cwd);
116
+ await fc.writeFile('src.txt', 'x');
117
+ (0, fs_1.mkdirSync)((0, path_1.join)(cwd, 'dest'));
118
+ await fc.copy('src.txt', (0, path_1.join)('dest', 'src.txt'));
119
+ expect((0, fs_1.readFileSync)((0, path_1.join)(cwd, 'dest', 'src.txt'), 'utf8')).toBe('x');
120
+ });
121
+ });
122
+ describe('streams', () => {
123
+ it('writeStream pipes a Readable into a file', async () => {
124
+ const fc = (0, get_file_context_1.getFileContext)(cwd);
125
+ const target = (0, path_1.join)(cwd, 'out.bin');
126
+ await fc.writeStream(target, stream_1.Readable.from(['part-a', 'part-b']));
127
+ expect((0, fs_1.readFileSync)(target, 'utf8')).toBe('part-apart-b');
128
+ });
129
+ it('createWriteStream returns a writable stream', async () => {
130
+ const fc = (0, get_file_context_1.getFileContext)(cwd);
131
+ const target = (0, path_1.join)(cwd, 'wstream.txt');
132
+ const ws = fc.createWriteStream(target);
133
+ // Wait for 'close' (fd released), not just the end/'finish' callback (data
134
+ // flushed): on Windows an open write handle locks the file, so the afterEach
135
+ // rmSync would race it and fail with ENOTEMPTY.
136
+ await new Promise((resolve, reject) => {
137
+ ws.on('error', reject);
138
+ ws.on('close', () => resolve());
139
+ ws.end('payload');
140
+ });
141
+ expect((0, fs_1.readFileSync)(target, 'utf8')).toBe('payload');
142
+ });
143
+ it('readStream reads back what writeStream wrote', async () => {
144
+ const fc = (0, get_file_context_1.getFileContext)(cwd);
145
+ const target = (0, path_1.join)(cwd, 'r.txt');
146
+ (0, fs_1.writeFileSync)(target, 'abc');
147
+ const chunks = [];
148
+ await new Promise((resolve, reject) => {
149
+ const rs = fc.readStream(target);
150
+ rs.on('data', (c) => chunks.push(c));
151
+ rs.on('error', reject);
152
+ // 'close' (fd released) fires after 'end'; wait for it so an open read
153
+ // handle can't block the afterEach cleanup on Windows.
154
+ rs.on('close', resolve);
155
+ });
156
+ expect(Buffer.concat(chunks).toString()).toBe('abc');
157
+ });
158
+ });
159
+ describe('watch', () => {
160
+ it('returns a handle whose close() is callable; the polling override is honored', async () => {
161
+ const previous = process.env.HAMMERKIT_WATCH_POLLING;
162
+ process.env.HAMMERKIT_WATCH_POLLING = 'false';
163
+ try {
164
+ const fc = (0, get_file_context_1.getFileContext)(cwd);
165
+ const handle = fc.watch('.', () => undefined);
166
+ // close() returns a Promise on chokidar; await it
167
+ await Promise.resolve(handle.close());
168
+ }
169
+ finally {
170
+ if (previous === undefined)
171
+ delete process.env.HAMMERKIT_WATCH_POLLING;
172
+ else
173
+ process.env.HAMMERKIT_WATCH_POLLING = previous;
174
+ }
175
+ });
176
+ });
177
+ });
178
+ //# sourceMappingURL=get-file-context.spec.js.map
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.moveFiles = void 0;
4
- async function moveFiles(node, context, folder) {
4
+ async function moveFiles(item, context, folder) {
5
5
  const foldersToCopy = [];
6
6
  const addFolder = async (from, to) => {
7
7
  if (!(await context.file.exists(from))) {
@@ -19,7 +19,7 @@ async function moveFiles(node, context, folder) {
19
19
  if (await context.file.exists(folder.to)) {
20
20
  await context.file.remove(folder.to);
21
21
  }
22
- context.status.task(node).write('debug', `copy ${folder.from} to ${folder.to}`);
22
+ item.status.write('debug', `copy ${folder.from} to ${folder.to}`);
23
23
  await context.file.copy(folder.from, folder.to);
24
24
  }
25
25
  }
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const move_files_1 = require("./move-files");
4
+ function context(existing) {
5
+ const remove = vi.fn(async () => undefined);
6
+ const copy = vi.fn(async () => undefined);
7
+ const env = {
8
+ file: {
9
+ exists: vi.fn(async (p) => existing.includes(p)),
10
+ remove,
11
+ copy,
12
+ },
13
+ };
14
+ return { env, remove, copy };
15
+ }
16
+ const item = { status: { write: vi.fn() } };
17
+ function* once(from, to) {
18
+ yield { from, to };
19
+ }
20
+ describe('move-files', () => {
21
+ it('copies an existing folder and removes a pre-existing target first', async () => {
22
+ const { env, remove, copy } = context(['/a', '/out/a']);
23
+ await (0, move_files_1.moveFiles)(item, env, () => once('/a', '/out/a'));
24
+ expect(remove).toHaveBeenCalledWith('/out/a');
25
+ expect(copy).toHaveBeenCalledWith('/a', '/out/a');
26
+ });
27
+ it('does not remove the target when it does not exist yet', async () => {
28
+ const { env, remove, copy } = context(['/a']);
29
+ await (0, move_files_1.moveFiles)(item, env, () => once('/a', '/out/a'));
30
+ expect(remove).not.toHaveBeenCalled();
31
+ expect(copy).toHaveBeenCalledWith('/a', '/out/a');
32
+ });
33
+ it('skips folders whose source does not exist', async () => {
34
+ const { env, copy } = context([]);
35
+ await (0, move_files_1.moveFiles)(item, env, () => once('/missing', '/out/m'));
36
+ expect(copy).not.toHaveBeenCalled();
37
+ });
38
+ it('dedupes identical from/to pairs', async () => {
39
+ const { env, copy } = context(['/a']);
40
+ function* twice() {
41
+ yield { from: '/a', to: '/out/a' };
42
+ yield { from: '/a', to: '/out/a' };
43
+ }
44
+ await (0, move_files_1.moveFiles)(item, env, twice);
45
+ expect(copy).toHaveBeenCalledTimes(1);
46
+ });
47
+ });
48
+ //# sourceMappingURL=move-files.spec.js.map
package/dist/index.js CHANGED
@@ -1,27 +1,26 @@
1
1
  #!/usr/bin/env node
2
2
  "use strict";
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
- const program_1 = require("./program");
5
4
  const log_1 = require("./log");
6
5
  const get_file_context_1 = require("./file/get-file-context");
7
- const work_node_status_1 = require("./planner/work-node-status");
8
- const execute_docker_1 = require("./executer/execute-docker");
6
+ const work_item_status_1 = require("./planner/work-item-status");
9
7
  const empty_writable_1 = require("./utils/empty-writable");
8
+ const run_program_1 = require("./run-program");
10
9
  const abortCtrl = new AbortController();
11
10
  process.on('SIGINT', function () {
12
11
  abortCtrl.abort();
13
12
  });
14
- (0, program_1.getProgram)({
13
+ (0, run_program_1.runProgram)({
15
14
  cwd: process.cwd(),
16
15
  abortCtrl,
17
16
  processEnvs: process.env,
18
17
  file: (0, get_file_context_1.getFileContext)(process.cwd()),
19
18
  console: (0, log_1.consoleContext)(process.stdout),
20
- status: (0, work_node_status_1.statusConsole)((0, empty_writable_1.emptyWritable)()),
21
- docker: (0, execute_docker_1.getContainerCli)(),
19
+ status: (0, work_item_status_1.statusConsole)((0, empty_writable_1.emptyWritable)()),
22
20
  stdout: process.stdout,
21
+ stderr: process.stderr,
23
22
  stdoutColumns: process.stdout.columns,
24
- }, process.argv).then(({ program, args }) => {
25
- return program.parseAsync(args);
23
+ }, process.argv, false).catch(() => {
24
+ process.exit(1);
26
25
  });
27
26
  //# sourceMappingURL=index.js.map
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const zod_to_json_schema_1 = __importDefault(require("zod-to-json-schema"));
7
+ const fs_1 = require("fs");
8
+ const path_1 = require("path");
9
+ const build_file_schema_1 = require("./schema/build-file-schema");
10
+ const jsonSchema = (0, zod_to_json_schema_1.default)(build_file_schema_1.buildFileSchema, 'hammerkit');
11
+ (0, fs_1.writeFileSync)((0, path_1.join)(process.cwd(), 'build.schema.gen.json'), JSON.stringify(jsonSchema, null, 2));
12
+ //# sourceMappingURL=json-schema.js.map
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.apply = exports.statusCodeOf = void 0;
4
+ function statusCodeOf(e) {
5
+ const err = e;
6
+ return err?.statusCode ?? err?.response?.statusCode ?? err?.body?.code;
7
+ }
8
+ exports.statusCodeOf = statusCodeOf;
9
+ async function apply(instance, spec) {
10
+ try {
11
+ await instance.objectApi.read(spec);
12
+ const response = await instance.objectApi.patch(spec);
13
+ return response.body;
14
+ }
15
+ catch (e) {
16
+ try {
17
+ const response = await instance.objectApi.create(spec);
18
+ return response.body;
19
+ }
20
+ catch (createError) {
21
+ // Two work items can apply the same object concurrently (e.g. a volume
22
+ // shared between a task and a service that inherits its generates). If we
23
+ // lost the create race, the object now exists — read it back instead of
24
+ // failing the whole item.
25
+ if (statusCodeOf(createError) === 409) {
26
+ const response = await instance.objectApi.read(spec);
27
+ return response.body;
28
+ }
29
+ throw createError;
30
+ }
31
+ }
32
+ }
33
+ exports.apply = apply;
34
+ //# sourceMappingURL=apply.js.map
@@ -0,0 +1,71 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.awaitDeployRunningState = exports.awaitJobState = exports.awaitRunningState = void 0;
4
+ function awaitRunningState(instance, env, name, phase) {
5
+ return new Promise((resolve, reject) => {
6
+ const req = instance.watch.watch(`/api/v1/namespaces/${env.namespace}/pods`, {}, (type, obj) => {
7
+ if (obj.metadata.name === name && obj.metadata.namespace === env.namespace && obj.status.phase === phase) {
8
+ req.then((r) => r.abort());
9
+ resolve();
10
+ }
11
+ }, (err) => {
12
+ if (err.message === 'aborted') {
13
+ return;
14
+ }
15
+ if (err) {
16
+ reject(err);
17
+ }
18
+ else {
19
+ resolve();
20
+ }
21
+ });
22
+ });
23
+ }
24
+ exports.awaitRunningState = awaitRunningState;
25
+ function awaitJobState(instance, env, name) {
26
+ return new Promise((resolve, reject) => {
27
+ const req = instance.watch.watch(`/apis/batch/v1/namespaces/${env.namespace}/jobs/${name}`, {}, (type, obj) => {
28
+ if (obj && obj.metadata?.name === name && obj.metadata?.namespace === env.namespace && obj.status?.succeeded) {
29
+ req.then((r) => r.abort());
30
+ resolve();
31
+ }
32
+ }, (err) => {
33
+ if (err && err.message === 'aborted') {
34
+ return;
35
+ }
36
+ if (err) {
37
+ reject(err);
38
+ }
39
+ else {
40
+ resolve();
41
+ }
42
+ });
43
+ });
44
+ }
45
+ exports.awaitJobState = awaitJobState;
46
+ function awaitDeployRunningState(instance, env, name) {
47
+ return new Promise((resolve, reject) => {
48
+ const req = instance.watch.watch(`/apis/apps/v1/namespaces/${env.namespace}/deployments`, {}, (type, obj) => {
49
+ if ((obj &&
50
+ obj.metadata?.name === name &&
51
+ obj.metadata?.namespace === env.namespace &&
52
+ obj.status?.readyReplicas) ??
53
+ 0 > 0) {
54
+ req.then((r) => r.abort());
55
+ resolve();
56
+ }
57
+ }, (err) => {
58
+ if (err && err.message === 'aborted') {
59
+ return;
60
+ }
61
+ if (err) {
62
+ reject(err);
63
+ }
64
+ else {
65
+ resolve();
66
+ }
67
+ });
68
+ });
69
+ }
70
+ exports.awaitDeployRunningState = awaitDeployRunningState;
71
+ //# sourceMappingURL=await-running-state.js.map