hammerkit 1.6.0-alpha.3 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (256) hide show
  1. package/README.md +4 -4
  2. package/build.schema.json +49 -2
  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 +5 -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 +2 -1
  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-work-service.js +10 -1
  27. package/dist/executer/execute-work-task.js +95 -25
  28. package/dist/executer/execute-work-tree.js +26 -1
  29. package/dist/executer/get-scheduler-execute-result.js +4 -1
  30. package/dist/executer/lazy-resolver.js +15 -3
  31. package/dist/executer/reset-work-tree.js +2 -0
  32. package/dist/executer/run-summary.js +102 -0
  33. package/dist/executer/scheduler/enqueue-next.js +15 -60
  34. package/dist/executer/scheduler/state-key.js +78 -0
  35. package/dist/kubernetes/await-running-state.js +73 -19
  36. package/dist/log.js +16 -6
  37. package/dist/logging/live-logger.js +2 -1
  38. package/dist/optimizer/calculate-checksum.js +8 -2
  39. package/dist/optimizer/get-work-cache-stats.js +66 -16
  40. package/dist/optimizer/work-task-cache-description.js +34 -5
  41. package/dist/planner/project-root.js +26 -0
  42. package/dist/planner/utils/append-work-service.js +2 -1
  43. package/dist/planner/utils/append-work-task.js +7 -1
  44. package/dist/planner/utils/parse-work-source.js +35 -30
  45. package/dist/planner/utils/portable-path.js +30 -0
  46. package/dist/planner/work-cache-id.js +2 -2
  47. package/dist/planner/work-graph-serializer.js +103 -0
  48. package/dist/planner/work-instance-id.js +15 -0
  49. package/dist/planner/work-runtime-docker.js +53 -15
  50. package/dist/planner/work-runtime-kubernetes.js +35 -3
  51. package/dist/planner/work-runtime-local.js +9 -3
  52. package/dist/planner/work-service-id.js +16 -4
  53. package/dist/program.js +235 -1
  54. package/dist/schema/build-file-base-task-schema.js +4 -0
  55. package/dist/schema/cache-schema.js +21 -2
  56. package/dist/schema/duration-schema.js +18 -0
  57. package/dist/schema/reference-parser.js +3 -0
  58. package/dist/schema/size-schema.js +18 -0
  59. package/dist/schema/work-scope-parser.js +15 -2
  60. package/dist/utils/units.js +87 -0
  61. package/package.json +15 -4
  62. package/.agents/FEEDBACK.md +0 -74
  63. package/.agents/skills/before-commit/SKILL.md +0 -101
  64. package/.agents/skills/do-the-work/SKILL.md +0 -64
  65. package/.agents/skills/fix-ci/SKILL.md +0 -98
  66. package/.agents/skills/speckit-git-commit/SKILL.md +0 -53
  67. package/.agents/skills/speckit-git-feature/SKILL.md +0 -72
  68. package/.agents/skills/speckit-git-initialize/SKILL.md +0 -54
  69. package/.agents/skills/speckit-git-remote/SKILL.md +0 -50
  70. package/.agents/skills/speckit-git-validate/SKILL.md +0 -54
  71. package/.devcontainer/Dockerfile +0 -5
  72. package/.devcontainer/devcontainer.json +0 -40
  73. package/.gitbook.yaml +0 -5
  74. package/.specify/extensions/.registry +0 -37
  75. package/.specify/extensions/git/README.md +0 -100
  76. package/.specify/extensions/git/commands/speckit.git.commit.md +0 -48
  77. package/.specify/extensions/git/commands/speckit.git.feature.md +0 -67
  78. package/.specify/extensions/git/commands/speckit.git.initialize.md +0 -49
  79. package/.specify/extensions/git/commands/speckit.git.remote.md +0 -45
  80. package/.specify/extensions/git/commands/speckit.git.validate.md +0 -49
  81. package/.specify/extensions/git/config-template.yml +0 -62
  82. package/.specify/extensions/git/extension.yml +0 -140
  83. package/.specify/extensions/git/git-config.yml +0 -62
  84. package/.specify/extensions.yml +0 -149
  85. package/.specify/feature.json +0 -3
  86. package/.specify/init-options.json +0 -10
  87. package/.specify/integration.json +0 -15
  88. package/.specify/integrations/claude.manifest.json +0 -16
  89. package/.specify/integrations/speckit.manifest.json +0 -17
  90. package/.specify/memory/constitution.md +0 -162
  91. package/.specify/templates/checklist-template.md +0 -40
  92. package/.specify/templates/constitution-template.md +0 -50
  93. package/.specify/templates/plan-template.md +0 -113
  94. package/.specify/templates/spec-template.md +0 -131
  95. package/.specify/templates/tasks-template.md +0 -252
  96. package/.specify/workflows/speckit/workflow.yml +0 -77
  97. package/.specify/workflows/workflow-registry.json +0 -13
  98. package/AGENTS.md +0 -94
  99. package/Dockerfile +0 -25
  100. package/FOLLOWUPS.md +0 -34
  101. package/SUMMARY.md +0 -68
  102. package/VALUES.md +0 -53
  103. package/dist/cache/auto-pull.spec.js +0 -123
  104. package/dist/cache/backends/local-cache-backend.spec.js +0 -72
  105. package/dist/cache/backends/s3-cache-backend.spec.js +0 -118
  106. package/dist/cache/cache-backend-registry.spec.js +0 -22
  107. package/dist/cache/resolve-cache.spec.js +0 -52
  108. package/dist/cli.spec.js +0 -23
  109. package/dist/console/get-logger.spec.js +0 -23
  110. package/dist/docker/get-default-platform.spec.js +0 -29
  111. package/dist/docker/longest-common-prefix.spec.js +0 -26
  112. package/dist/docker/package.spec.js +0 -145
  113. package/dist/docker/remove-container.spec.js +0 -32
  114. package/dist/docker/using-container.spec.js +0 -90
  115. package/dist/environment/merge-environment-variables.spec.js +0 -24
  116. package/dist/environment/replace-env-variables.spec.js +0 -47
  117. package/dist/executer/abort.spec.js +0 -18
  118. package/dist/executer/environment-mock.spec.js +0 -22
  119. package/dist/executer/execute.spec.js +0 -51
  120. package/dist/executer/get-service-containers.spec.js +0 -59
  121. package/dist/executer/get-service-env-hints.spec.js +0 -65
  122. package/dist/executer/label-values.spec.js +0 -61
  123. package/dist/executer/lazy-resolver.spec.js +0 -20
  124. package/dist/executer/process-manager.spec.js +0 -54
  125. package/dist/executer/scheduler/enqueue-next.spec.js +0 -136
  126. package/dist/executer/state-resolver.spec.js +0 -37
  127. package/dist/executer/state.spec.js +0 -66
  128. package/dist/executer/states.spec.js +0 -14
  129. package/dist/file/get-file-context.spec.js +0 -178
  130. package/dist/file/move-files.spec.js +0 -48
  131. package/dist/kubernetes/ensure-http-route.spec.js +0 -70
  132. package/dist/kubernetes/ensure-namespace.spec.js +0 -39
  133. package/dist/kubernetes/resolve-pod-name.spec.js +0 -81
  134. package/dist/log.spec.js +0 -131
  135. package/dist/logging/grouped-logger.spec.js +0 -84
  136. package/dist/logging/interactive-logger.spec.js +0 -57
  137. package/dist/logging/live-logger.spec.js +0 -37
  138. package/dist/logging/loggers.spec.js +0 -154
  139. package/dist/optimizer/calculate-checksum.spec.js +0 -34
  140. package/dist/optimizer/get-cache-directory.spec.js +0 -59
  141. package/dist/optimizer/get-work-cache-stats.spec.js +0 -79
  142. package/dist/optimizer/work-task-cache-description.spec.js +0 -45
  143. package/dist/parser/default-build-file.spec.js +0 -48
  144. package/dist/parser/parse-label-arguments.spec.js +0 -27
  145. package/dist/parser/read-build-file.spec.js +0 -89
  146. package/dist/parser/read-env-file.spec.js +0 -42
  147. package/dist/planner/utils/append-work-service.spec.js +0 -71
  148. package/dist/planner/utils/get-container-user.spec.js +0 -30
  149. package/dist/planner/utils/get-default-kube-config.spec.js +0 -11
  150. package/dist/planner/utils/normalize-path.spec.js +0 -24
  151. package/dist/planner/utils/parse-work-command.spec.js +0 -21
  152. package/dist/planner/utils/parse-work-generate.spec.js +0 -45
  153. package/dist/planner/utils/parse-work-mount.spec.js +0 -62
  154. package/dist/planner/utils/parse-work-mounts.spec.js +0 -19
  155. package/dist/planner/utils/parse-work-port.spec.js +0 -24
  156. package/dist/planner/utils/parse-work-ports.spec.js +0 -24
  157. package/dist/planner/utils/parse-work-source.spec.js +0 -58
  158. package/dist/planner/utils/parse-work-volume.spec.js +0 -18
  159. package/dist/planner/utils/plan-work-tasks.spec.js +0 -30
  160. package/dist/planner/utils/plan-work-volume.spec.js +0 -33
  161. package/dist/planner/utils/split-name.spec.js +0 -16
  162. package/dist/planner/utils/template-value.spec.js +0 -14
  163. package/dist/planner/validate.spec.js +0 -251
  164. package/dist/planner/work-cache-id.spec.js +0 -173
  165. package/dist/planner/work-runtime-local.spec.js +0 -193
  166. package/dist/program-commands.spec.js +0 -84
  167. package/dist/program.spec.js +0 -43
  168. package/dist/schema/build-file-schema.spec.js +0 -61
  169. package/dist/schema/doc-build-files.spec.js +0 -223
  170. package/dist/schema/parse-error.spec.js +0 -18
  171. package/dist/schema/reference-parser.spec.js +0 -83
  172. package/dist/schema/schema-parser.spec.js +0 -80
  173. package/dist/schema/type-guards.spec.js +0 -23
  174. package/dist/schema/work-scope-parser.spec.js +0 -66
  175. package/dist/service/update-service-status.spec.js +0 -27
  176. package/dist/testing/ensure-local-registry.js +0 -134
  177. package/dist/testing/example-test-suite.js +0 -63
  178. package/dist/testing/expect.js +0 -82
  179. package/dist/testing/get-test-suite.js +0 -9
  180. package/dist/testing/http-get.js +0 -21
  181. package/dist/testing/integration/cache.spec.js +0 -49
  182. package/dist/testing/integration/cancellation.spec.js +0 -45
  183. package/dist/testing/integration/clean.spec.js +0 -43
  184. package/dist/testing/integration/cmd.spec.js +0 -15
  185. package/dist/testing/integration/concurrency.spec.js +0 -19
  186. package/dist/testing/integration/docker-package-registry.spec.js +0 -44
  187. package/dist/testing/integration/docker-service-crash.spec.js +0 -36
  188. package/dist/testing/integration/docker.spec.js +0 -17
  189. package/dist/testing/integration/env.spec.js +0 -48
  190. package/dist/testing/integration/environment.spec.js +0 -97
  191. package/dist/testing/integration/error.spec.js +0 -19
  192. package/dist/testing/integration/export.spec.js +0 -22
  193. package/dist/testing/integration/extend.spec.js +0 -29
  194. package/dist/testing/integration/glob.spec.js +0 -40
  195. package/dist/testing/integration/include.spec.js +0 -31
  196. package/dist/testing/integration/invalid.spec.js +0 -17
  197. package/dist/testing/integration/invalid_loop.spec.js +0 -56
  198. package/dist/testing/integration/k8s-state-cache.spec.js +0 -41
  199. package/dist/testing/integration/kubernetes-service.spec.js +0 -148
  200. package/dist/testing/integration/kubernetes.spec.js +0 -99
  201. package/dist/testing/integration/labels.spec.js +0 -42
  202. package/dist/testing/integration/local.spec.js +0 -14
  203. package/dist/testing/integration/monorepo.spec.js +0 -15
  204. package/dist/testing/integration/multi-line.spec.js +0 -17
  205. package/dist/testing/integration/reference.spec.js +0 -21
  206. package/dist/testing/integration/services.spec.js +0 -38
  207. package/dist/testing/integration/store.spec.js +0 -52
  208. package/dist/testing/integration/unknown.spec.js +0 -28
  209. package/dist/testing/integration/validate.spec.js +0 -160
  210. package/dist/testing/integration/watch-dependency.spec.js +0 -33
  211. package/dist/testing/integration/watch.spec.js +0 -45
  212. package/dist/testing/requires-kubernetes.js +0 -18
  213. package/dist/testing/requires-linux-containers.js +0 -18
  214. package/dist/testing/requires-windows-containers.js +0 -18
  215. package/dist/testing/test-case.js +0 -75
  216. package/dist/testing/test-streams.js +0 -28
  217. package/dist/testing/test-suite-setup.js +0 -3
  218. package/dist/testing/test-suite.js +0 -3
  219. package/dist/testing/testing-timeout.js +0 -32
  220. package/dist/utils/abort-event.spec.js +0 -46
  221. package/dist/utils/buffer-context.spec.js +0 -38
  222. package/dist/utils/ci.spec.js +0 -53
  223. package/dist/utils/debouncer.spec.js +0 -58
  224. package/dist/utils/emitter.spec.js +0 -34
  225. package/dist/utils/empty-writable.spec.js +0 -18
  226. package/dist/utils/environment-config.spec.js +0 -26
  227. package/dist/utils/fail-never.spec.js +0 -9
  228. package/dist/utils/sleep.spec.js +0 -22
  229. package/dist/version.spec.js +0 -12
  230. package/specs/build-file/caches/spec.md +0 -114
  231. package/specs/build-file/environment-variables/spec.md +0 -82
  232. package/specs/build-file/includes/spec.md +0 -60
  233. package/specs/build-file/references/spec.md +0 -56
  234. package/specs/build-file/spec.md +0 -62
  235. package/specs/cli/clean/spec.md +0 -85
  236. package/specs/cli/down/spec.md +0 -85
  237. package/specs/cli/execute/spec.md +0 -101
  238. package/specs/cli/init/spec.md +0 -64
  239. package/specs/cli/ls/spec.md +0 -66
  240. package/specs/cli/package/spec.md +0 -110
  241. package/specs/cli/spec.md +0 -73
  242. package/specs/cli/store-restore/spec.md +0 -74
  243. package/specs/cli/up/spec.md +0 -80
  244. package/specs/cli/validate/spec.md +0 -70
  245. package/specs/labels/spec.md +0 -91
  246. package/specs/service/container/spec.md +0 -120
  247. package/specs/service/kubernetes/spec.md +0 -97
  248. package/specs/service/spec.md +0 -105
  249. package/specs/task/caching/spec.md +0 -107
  250. package/specs/task/container/spec.md +0 -118
  251. package/specs/task/dependencies/spec.md +0 -70
  252. package/specs/task/extending/spec.md +0 -74
  253. package/specs/task/kubernetes/spec.md +0 -125
  254. package/specs/task/needs/spec.md +0 -59
  255. package/specs/task/spec.md +0 -87
  256. package/specs/task/watching/spec.md +0 -69
package/SUMMARY.md DELETED
@@ -1,68 +0,0 @@
1
- # Table of contents
2
-
3
- * [About](README.md)
4
- * [Why hammerkit](docs/why-hammerkit.md)
5
- * [Installation](docs/installation.md)
6
- * [Getting started](docs/getting-started.md)
7
- * [Tutorial](docs/tutorial.md)
8
- * [Concepts](docs/concepts.md)
9
- * [Build file](docs/build-file/README.md)
10
- * [Environment Variables](docs/build-file/environment-variables.md)
11
- * [References](docs/build-file/references.md)
12
- * [Includes](docs/build-file/includes.md)
13
- * [Caches](docs/build-file/caches.md)
14
- * [Reference](docs/build-file/reference.md)
15
- * [Task](docs/task/README.md)
16
- * [Dependencies](docs/task/dependencies.md)
17
- * [Needs](docs/task/needs.md)
18
- * [Caching](docs/task/caching.md)
19
- * [Run task in a container](docs/task/container.md)
20
- * [Run task on Kubernetes](docs/task/kubernetes.md)
21
- * [Watching](docs/task/watching.md)
22
- * [Extending](docs/task/extending.md)
23
- * [Service](docs/service/README.md)
24
- * [Container service](docs/service/container.md)
25
- * [Kubernetes service](docs/service/kubernetes.md)
26
- * [Labels](docs/labels/README.md)
27
- * [Guides](docs/guides/local-vs-container.md)
28
- * [Local vs container tasks](docs/guides/local-vs-container.md)
29
- * [Caching strategy in CI](docs/guides/ci-caching.md)
30
- * [Services & networking](docs/guides/services-networking.md)
31
- * [Monorepo layout](docs/guides/monorepo.md)
32
- * [Development workflow](docs/guides/development-workflow.md)
33
- * [CLI](docs/cli/README.md)
34
- * [Init](docs/cli/init.md)
35
- * [ls](docs/cli/ls.md)
36
- * [Execute](docs/cli/execute.md)
37
- * [Up](docs/cli/up.md)
38
- * [Down](docs/cli/down.md)
39
- * [Store / Restore](docs/cli/store-restore.md)
40
- * [Package](docs/cli/package.md)
41
- * [Clean](docs/cli/clean.md)
42
- * [Validate](docs/cli/validate.md)
43
- * [Recipes](docs/recipes.md)
44
- * [FAQ & Troubleshooting](docs/faq.md)
45
-
46
- ## External Links
47
-
48
- * [Docker Hub](https://hub.docker.com/r/no0dles/hammerkit)
49
- * [Homebrew Repository](https://github.com/no0dles/homebrew-hammerkit)
50
-
51
- ## Release Blog
52
-
53
- * [Release 1.4.0](docs/release-blog/release-1.4.0.md)
54
- * [Release 1.5.0](docs/release-blog/release-1.5.0.md)
55
- * [Release 1.6.0](docs/release-blog/release-1.6.0.md)
56
-
57
- ## Change Log
58
-
59
- * [Changelog 1.4.0](docs/change-log/change-log-1.4.0.md)
60
- * [Changelog 1.5.0](docs/change-log/change-log-1.5.0.md)
61
- * [Changelog 1.6.0](docs/change-log/change-log-1.6.0.md)
62
-
63
- ## Contribution
64
-
65
- * [Roadmap](docs/contribution/roadmap.md)
66
- * [Secret managers (planned)](docs/contribution/secret-managers.md)
67
- * [Publishing](docs/contribution/publish.md)
68
- * [Self-hosted runner](docs/contribution/self-hosted-runner.md)
package/VALUES.md DELETED
@@ -1,53 +0,0 @@
1
- # Values
2
-
3
- This file is the canonical statement of what hammerkit optimizes for and the conventions that follow from it. Contributors and AI agents should read it before non-trivial work. Day-to-day agent operating rules live in [AGENTS.md](AGENTS.md); durable corrections live in [.agents/FEEDBACK.md](.agents/FEEDBACK.md).
4
-
5
- ## Product values
6
-
7
- ### Local-first
8
-
9
- Hammerkit must be usable on a developer's laptop, not only in CI. A build tool that only runs on CI is broken.
10
-
11
- ### Platform-agnostic
12
-
13
- No coupling to GitHub Actions, GitLab CI, or any single provider. The same build runs anywhere a container does.
14
-
15
- ### One config, many tools
16
-
17
- A single hammerkit build file replaces N tool-specific configs. Reducing config sprawl is a feature, not a side effect.
18
-
19
- ### Caching reduces waste, not just time
20
-
21
- Smart, distributed caching saves compute — that matters for cost and footprint, not only build speed.
22
-
23
- ## Engineering values
24
-
25
- ### Stable foundation
26
-
27
- The CLI and build-file schema are contracts. Breaking them is a deliberate, versioned decision — never a refactor side effect.
28
-
29
- ### Simplicity & composability
30
-
31
- Prefer small, composable pieces over clever abstractions. If two features can be one, make them one.
32
-
33
- ### Pragmatic typing
34
-
35
- TS strict is on; `any` is permitted where it pulls its weight. Type purity is a means, not the goal.
36
-
37
- ### Tests carry their weight
38
-
39
- Quality and coverage are non-negotiable. Slow integration tests are accepted because they buy real confidence.
40
-
41
- ## Decision heuristics
42
-
43
- ### Fix root causes, not symptoms
44
-
45
- A red check is fixed by repairing what's broken — not by removing, muting, or weakening the check.
46
-
47
- ### Real integrations over mocks
48
-
49
- Where feasible, exercise real Docker / real registries / real Kubernetes. Mocks are a last resort, not a default.
50
-
51
- ### Add nothing speculative
52
-
53
- Features, abstractions, and config knobs land when a concrete need exists — not for hypothetical futures.
@@ -1,123 +0,0 @@
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 environment_mock_1 = require("../executer/environment-mock");
7
- const enqueue_next_1 = require("../executer/scheduler/enqueue-next");
8
- const local_cache_backend_1 = require("./backends/local-cache-backend");
9
- const state_1 = require("../executer/state");
10
- const cache_metadata_1 = require("../executer/cache-metadata");
11
- const get_cache_directory_1 = require("../optimizer/get-cache-directory");
12
- const get_work_cache_stats_1 = require("../optimizer/get-work-cache-stats");
13
- const work_task_cache_description_1 = require("../optimizer/work-task-cache-description");
14
- describe('checkCacheState auto-pull', () => {
15
- let remoteDir;
16
- let machineHome;
17
- let originalHome;
18
- beforeEach(() => {
19
- remoteDir = (0, fs_1.mkdtempSync)((0, path_1.join)((0, os_1.tmpdir)(), 'hammerkit-remote-'));
20
- machineHome = (0, fs_1.mkdtempSync)((0, path_1.join)((0, os_1.tmpdir)(), 'hammerkit-home-'));
21
- originalHome = process.env.HOME;
22
- if (process.platform === 'darwin') {
23
- // getHammerkitDirectory uses Library/Application Support on macOS
24
- process.env.HOME = machineHome;
25
- }
26
- else if (process.platform === 'win32') {
27
- process.env.APPDATA = machineHome;
28
- }
29
- else {
30
- process.env.HOME = machineHome;
31
- }
32
- });
33
- afterEach(() => {
34
- (0, fs_1.rmSync)(remoteDir, { recursive: true, force: true });
35
- (0, fs_1.rmSync)(machineHome, { recursive: true, force: true });
36
- if (originalHome !== undefined) {
37
- process.env.HOME = originalHome;
38
- }
39
- else {
40
- delete process.env.HOME;
41
- }
42
- });
43
- function makeBackend() {
44
- return (0, local_cache_backend_1.createLocalCacheBackend)({ type: 'local', path: remoteDir });
45
- }
46
- function makeWorkItem(cwd, resolved) {
47
- const storedStateKey = null;
48
- return {
49
- id: () => 'demo-task',
50
- name: 'demo-task',
51
- status: { write: vi.fn() },
52
- data: {
53
- type: 'local-task',
54
- name: 'demo-task',
55
- cwd,
56
- cmds: [],
57
- generates: [],
58
- src: [],
59
- envs: { variables: {}, replacements: [] },
60
- labels: {},
61
- shell: '/bin/sh',
62
- continuous: false,
63
- caching: resolved,
64
- description: null,
65
- scope: {},
66
- },
67
- needs: [],
68
- deps: [],
69
- requiredBy: [],
70
- state: new state_1.State({ type: 'pending', stateKey: null }),
71
- runtime: {
72
- async initialize() { },
73
- async remove() { },
74
- async execute() { },
75
- async stop() { },
76
- async archive() { },
77
- async restore() {
78
- // simulate a successful artifact restore
79
- },
80
- async currentStateKey() {
81
- return storedStateKey;
82
- },
83
- },
84
- };
85
- }
86
- it('reports cached:true on the second machine after the first pushed', async () => {
87
- // Machine A: produce cache + push to remote
88
- const machineAScratch = (0, fs_1.mkdtempSync)((0, path_1.join)((0, os_1.tmpdir)(), 'hammerkit-machine-a-'));
89
- const envA = (0, environment_mock_1.environmentMock)(machineAScratch);
90
- const backend = makeBackend();
91
- const resolved = { name: 'remote', method: 'checksum', backend, implicit: false };
92
- const itemA = makeWorkItem(machineAScratch, resolved);
93
- const stats = await (0, get_work_cache_stats_1.getWorkCacheStats)(itemA.data, envA);
94
- const stateKey = (0, get_work_cache_stats_1.getStateKey)(stats, 'checksum');
95
- await (0, cache_metadata_1.writeCacheMetadata)(envA, itemA.id(), stats, (0, work_task_cache_description_1.getWorkTaskCacheDescription)(itemA.data));
96
- const cacheDir = (0, get_cache_directory_1.getCacheDirectory)(itemA.id());
97
- await backend.push(itemA.id(), stateKey, cacheDir, envA);
98
- (0, fs_1.rmSync)(machineAScratch, { recursive: true, force: true });
99
- // Machine B (different HOME = fresh local cache): same task, same sources → same stateKey
100
- const machineBScratch = (0, fs_1.mkdtempSync)((0, path_1.join)((0, os_1.tmpdir)(), 'hammerkit-machine-b-'));
101
- const envB = (0, environment_mock_1.environmentMock)(machineBScratch);
102
- // simulate a *different* machine: reset the HOME again
103
- const machineBHome = (0, fs_1.mkdtempSync)((0, path_1.join)((0, os_1.tmpdir)(), 'hammerkit-home-b-'));
104
- process.env.HOME = machineBHome;
105
- const itemB = makeWorkItem(machineBScratch, resolved);
106
- const cacheState = await (0, enqueue_next_1.checkCacheState)(itemB, 'checksum', envB);
107
- expect(cacheState.stateKey).toBe(stateKey);
108
- expect(cacheState.cached).toBe(true);
109
- (0, fs_1.rmSync)(machineBScratch, { recursive: true, force: true });
110
- (0, fs_1.rmSync)(machineBHome, { recursive: true, force: true });
111
- });
112
- it('reports cached:false when remote has nothing', async () => {
113
- const scratch = (0, fs_1.mkdtempSync)((0, path_1.join)((0, os_1.tmpdir)(), 'hammerkit-machine-c-'));
114
- const env = (0, environment_mock_1.environmentMock)(scratch);
115
- const backend = makeBackend();
116
- const resolved = { name: 'remote', method: 'checksum', backend, implicit: false };
117
- const item = makeWorkItem(scratch, resolved);
118
- const cacheState = await (0, enqueue_next_1.checkCacheState)(item, 'checksum', env);
119
- expect(cacheState.cached).toBe(false);
120
- (0, fs_1.rmSync)(scratch, { recursive: true, force: true });
121
- });
122
- });
123
- //# sourceMappingURL=auto-pull.spec.js.map
@@ -1,72 +0,0 @@
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 local_cache_backend_1 = require("./local-cache-backend");
7
- const environment_mock_1 = require("../../executer/environment-mock");
8
- describe('local cache backend', () => {
9
- let root;
10
- let scratch;
11
- beforeEach(() => {
12
- root = (0, fs_1.mkdtempSync)((0, path_1.join)((0, os_1.tmpdir)(), 'hammerkit-local-cache-'));
13
- scratch = (0, fs_1.mkdtempSync)((0, path_1.join)((0, os_1.tmpdir)(), 'hammerkit-local-cache-scratch-'));
14
- });
15
- afterEach(() => {
16
- (0, fs_1.rmSync)(root, { recursive: true, force: true });
17
- (0, fs_1.rmSync)(scratch, { recursive: true, force: true });
18
- });
19
- it('reports no hit when nothing pushed yet', async () => {
20
- const env = (0, environment_mock_1.environmentMock)(scratch);
21
- const backend = (0, local_cache_backend_1.createLocalCacheBackend)({ type: 'local', path: root });
22
- expect(await backend.has('taskA', 'key1', env)).toBe(false);
23
- expect(await backend.pull('taskA', 'key1', (0, path_1.join)(scratch, 'pull'), env)).toBe(false);
24
- });
25
- it('round-trips a task through push and pull', async () => {
26
- const env = (0, environment_mock_1.environmentMock)(scratch);
27
- const backend = (0, local_cache_backend_1.createLocalCacheBackend)({ type: 'local', path: root });
28
- const sourceDir = (0, path_1.join)(scratch, 'src');
29
- await env.file.createDirectory(sourceDir);
30
- await env.file.writeFile((0, path_1.join)(sourceDir, 'stats.json'), '{"files":{}}');
31
- await env.file.writeFile((0, path_1.join)(sourceDir, 'description.json'), '{}');
32
- await env.file.writeFile((0, path_1.join)(sourceDir, 'dist-generates.tgz'), 'tarball-bytes');
33
- await backend.push('taskA', 'key1', sourceDir, env);
34
- expect(await backend.has('taskA', 'key1', env)).toBe(true);
35
- const into = (0, path_1.join)(scratch, 'pull');
36
- const ok = await backend.pull('taskA', 'key1', into, env);
37
- expect(ok).toBe(true);
38
- expect(await env.file.exists((0, path_1.join)(into, 'stats.json'))).toBe(true);
39
- expect(await env.file.exists((0, path_1.join)(into, 'description.json'))).toBe(true);
40
- expect(await env.file.exists((0, path_1.join)(into, 'dist-generates.tgz'))).toBe(true);
41
- expect(await env.file.read((0, path_1.join)(into, 'dist-generates.tgz'))).toBe('tarball-bytes');
42
- });
43
- it('isolates entries by stateKey', async () => {
44
- const env = (0, environment_mock_1.environmentMock)(scratch);
45
- const backend = (0, local_cache_backend_1.createLocalCacheBackend)({ type: 'local', path: root });
46
- const sourceDir = (0, path_1.join)(scratch, 'src');
47
- await env.file.createDirectory(sourceDir);
48
- await env.file.writeFile((0, path_1.join)(sourceDir, 'stats.json'), '{}');
49
- await backend.push('taskA', 'key1', sourceDir, env);
50
- expect(await backend.has('taskA', 'key2', env)).toBe(false);
51
- });
52
- it('clear removes every state key of a task without touching others', async () => {
53
- const env = (0, environment_mock_1.environmentMock)(scratch);
54
- const backend = (0, local_cache_backend_1.createLocalCacheBackend)({ type: 'local', path: root });
55
- const sourceDir = (0, path_1.join)(scratch, 'src');
56
- await env.file.createDirectory(sourceDir);
57
- await env.file.writeFile((0, path_1.join)(sourceDir, 'stats.json'), '{}');
58
- await backend.push('taskA', 'key1', sourceDir, env);
59
- await backend.push('taskA', 'key2', sourceDir, env);
60
- await backend.push('taskB', 'key1', sourceDir, env);
61
- await backend.clear('taskA', env);
62
- expect(await backend.has('taskA', 'key1', env)).toBe(false);
63
- expect(await backend.has('taskA', 'key2', env)).toBe(false);
64
- expect(await backend.has('taskB', 'key1', env)).toBe(true);
65
- });
66
- it('clear is a no-op when the task has nothing cached', async () => {
67
- const env = (0, environment_mock_1.environmentMock)(scratch);
68
- const backend = (0, local_cache_backend_1.createLocalCacheBackend)({ type: 'local', path: root });
69
- await expect(backend.clear('missing', env)).resolves.toBeUndefined();
70
- });
71
- });
72
- //# sourceMappingURL=local-cache-backend.spec.js.map
@@ -1,118 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const stream_1 = require("stream");
4
- // vi.hoisted: vi.mock is hoisted above this file's top-level code, so the factory
5
- // below can only reference variables that are themselves hoisted.
6
- const { sendMock } = vi.hoisted(() => ({ sendMock: vi.fn() }));
7
- vi.mock('@aws-sdk/client-s3', () => {
8
- class S3ClientMock {
9
- constructor() {
10
- this.send = sendMock;
11
- }
12
- }
13
- class GetObjectCommand {
14
- constructor(input) {
15
- this.input = input;
16
- }
17
- }
18
- class PutObjectCommand {
19
- constructor(input) {
20
- this.input = input;
21
- }
22
- }
23
- class HeadObjectCommand {
24
- constructor(input) {
25
- this.input = input;
26
- }
27
- }
28
- class ListObjectsV2Command {
29
- constructor(input) {
30
- this.input = input;
31
- }
32
- }
33
- return {
34
- S3Client: S3ClientMock,
35
- GetObjectCommand,
36
- PutObjectCommand,
37
- HeadObjectCommand,
38
- ListObjectsV2Command,
39
- };
40
- });
41
- const s3_cache_backend_1 = require("./s3-cache-backend");
42
- const fs_1 = require("fs");
43
- const os_1 = require("os");
44
- const path_1 = require("path");
45
- const environment_mock_1 = require("../../executer/environment-mock");
46
- describe('s3 cache backend', () => {
47
- let scratch;
48
- beforeEach(() => {
49
- sendMock.mockReset();
50
- scratch = (0, fs_1.mkdtempSync)((0, path_1.join)((0, os_1.tmpdir)(), 'hammerkit-s3-spec-'));
51
- });
52
- afterEach(() => {
53
- (0, fs_1.rmSync)(scratch, { recursive: true, force: true });
54
- });
55
- it('returns false from has() when HeadObject rejects', async () => {
56
- sendMock.mockRejectedValueOnce(new Error('not found'));
57
- const backend = (0, s3_cache_backend_1.createS3CacheBackend)({ type: 's3', bucket: 'b', region: 'us-east-1' });
58
- const env = (0, environment_mock_1.environmentMock)(scratch);
59
- expect(await backend.has('task', 'state', env)).toBe(false);
60
- });
61
- it('returns true when HeadObject resolves', async () => {
62
- sendMock.mockResolvedValueOnce({});
63
- const backend = (0, s3_cache_backend_1.createS3CacheBackend)({ type: 's3', bucket: 'b', region: 'us-east-1' });
64
- const env = (0, environment_mock_1.environmentMock)(scratch);
65
- expect(await backend.has('task', 'state', env)).toBe(true);
66
- });
67
- it('uploads files via PutObject with prefix + task + stateKey path', async () => {
68
- sendMock.mockResolvedValue({});
69
- const backend = (0, s3_cache_backend_1.createS3CacheBackend)({ type: 's3', bucket: 'b', prefix: 'p/', region: 'us-east-1' });
70
- const env = (0, environment_mock_1.environmentMock)(scratch);
71
- await env.file.writeFile((0, path_1.join)(scratch, 'stats.json'), '{}');
72
- await env.file.writeFile((0, path_1.join)(scratch, 'description.json'), '{}');
73
- await env.file.writeFile((0, path_1.join)(scratch, 'a-generates.tgz'), 'bytes');
74
- await backend.push('task1', 'key1', scratch, env);
75
- const keys = sendMock.mock.calls.map(([cmd]) => cmd.input.Key);
76
- expect(keys).toContain('p/task1/key1/a-generates.tgz');
77
- expect(keys).toContain('p/task1/key1/description.json');
78
- expect(keys).toContain('p/task1/key1/stats.json');
79
- // stats.json must be uploaded last so a partial push is invisible to has()/pull()
80
- expect(keys[keys.length - 1]).toBe('p/task1/key1/stats.json');
81
- });
82
- it('downloads files into the local cache dir on pull', async () => {
83
- const backend = (0, s3_cache_backend_1.createS3CacheBackend)({ type: 's3', bucket: 'b', region: 'us-east-1' });
84
- const env = (0, environment_mock_1.environmentMock)(scratch);
85
- // Order: Head, then List, then a Get per object
86
- sendMock.mockResolvedValueOnce({}); // head
87
- sendMock.mockResolvedValueOnce({
88
- Contents: [{ Key: 'task/state/stats.json' }, { Key: 'task/state/dist.tgz' }],
89
- });
90
- sendMock.mockResolvedValueOnce({ Body: stream_1.Readable.from(['{}']) });
91
- sendMock.mockResolvedValueOnce({ Body: stream_1.Readable.from(['tarball']) });
92
- const into = (0, path_1.join)(scratch, 'pulled');
93
- const ok = await backend.pull('task', 'state', into, env);
94
- expect(ok).toBe(true);
95
- expect(await env.file.exists((0, path_1.join)(into, 'stats.json'))).toBe(true);
96
- expect(await env.file.exists((0, path_1.join)(into, 'dist.tgz'))).toBe(true);
97
- });
98
- it('does not write objects whose key escapes the restore dir on pull', async () => {
99
- const backend = (0, s3_cache_backend_1.createS3CacheBackend)({ type: 's3', bucket: 'b', region: 'us-east-1' });
100
- const env = (0, environment_mock_1.environmentMock)(scratch);
101
- sendMock.mockResolvedValueOnce({}); // head
102
- sendMock.mockResolvedValueOnce({
103
- // a poisoned bucket can list a key whose suffix traverses out of the dir
104
- Contents: [{ Key: 'task/state/../escape.txt' }, { Key: 'task/state/safe.txt' }],
105
- });
106
- // only the safe object should be fetched; the traversal entry is skipped before Get
107
- sendMock.mockResolvedValueOnce({ Body: stream_1.Readable.from(['ok']) });
108
- const into = (0, path_1.join)(scratch, 'pulled');
109
- const ok = await backend.pull('task', 'state', into, env);
110
- expect(ok).toBe(true);
111
- expect(await env.file.exists((0, path_1.join)(into, 'safe.txt'))).toBe(true);
112
- // ../escape.txt would resolve to scratch/escape.txt, outside `into`
113
- expect(await env.file.exists((0, path_1.join)(scratch, 'escape.txt'))).toBe(false);
114
- // the traversal entry must not trigger a GetObject (head + list + 1 get = 3)
115
- expect(sendMock).toHaveBeenCalledTimes(3);
116
- });
117
- });
118
- //# sourceMappingURL=s3-cache-backend.spec.js.map
@@ -1,22 +0,0 @@
1
- "use strict";
2
- // The factory registry registers `local` and `s3` at import time. We can
3
- // exercise createCacheBackend for both registered types and the unknown-type
4
- // failure without needing the AWS SDK at runtime (`local` is real; for `s3`
5
- // we register a fake under a fresh type name).
6
- Object.defineProperty(exports, "__esModule", { value: true });
7
- const cache_backend_registry_1 = require("./cache-backend-registry");
8
- describe('createCacheBackend', () => {
9
- it('builds a local backend from a {type:"local"} spec', () => {
10
- const backend = (0, cache_backend_registry_1.createCacheBackend)({ type: 'local' });
11
- expect(backend.type).toBe('local');
12
- });
13
- it('throws on an unknown backend type', () => {
14
- expect(() => (0, cache_backend_registry_1.createCacheBackend)({ type: 'unknown-backend-xyz' })).toThrow(/unknown cache backend type: unknown-backend-xyz/);
15
- });
16
- it('dispatches to a registered custom factory', () => {
17
- const fakeBackend = { type: 'fake' };
18
- (0, cache_backend_registry_1.registerCacheBackend)('fake-backend', () => fakeBackend);
19
- expect((0, cache_backend_registry_1.createCacheBackend)({ type: 'fake-backend' })).toBe(fakeBackend);
20
- });
21
- });
22
- //# sourceMappingURL=cache-backend-registry.spec.js.map
@@ -1,52 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const resolve_cache_1 = require("./resolve-cache");
4
- describe('resolveCache', () => {
5
- const catalog = (0, resolve_cache_1.withBuiltinCaches)({
6
- s3: { method: 'modify-date', backend: { type: 'local', path: '/tmp/fake' } },
7
- });
8
- it('uses the default cache when no ref is given', () => {
9
- const r = (0, resolve_cache_1.resolveCache)(undefined, catalog, 'taskA');
10
- expect(r.name).toBe('default');
11
- expect(r.method).toBe('checksum');
12
- expect(r.backend.type).toBe('local');
13
- expect(r.implicit).toBe(true);
14
- });
15
- it('treats a string ref as a method override on default', () => {
16
- const r = (0, resolve_cache_1.resolveCache)('modify-date', catalog, 'taskA');
17
- expect(r.name).toBe('default');
18
- expect(r.method).toBe('modify-date');
19
- expect(r.backend.type).toBe('local');
20
- expect(r.implicit).toBe(false);
21
- });
22
- it('uses none method when "none" string shorthand is used', () => {
23
- const r = (0, resolve_cache_1.resolveCache)('none', catalog, 'taskA');
24
- expect(r.method).toBe('none');
25
- });
26
- it('resolves a named reference and keeps its declared method', () => {
27
- const r = (0, resolve_cache_1.resolveCache)({ name: 's3' }, catalog, 'taskA');
28
- expect(r.name).toBe('s3');
29
- expect(r.method).toBe('modify-date');
30
- });
31
- it('overrides the named cache method when the task sets one', () => {
32
- const r = (0, resolve_cache_1.resolveCache)({ name: 's3', method: 'checksum' }, catalog, 'taskA');
33
- expect(r.name).toBe('s3');
34
- expect(r.method).toBe('checksum');
35
- });
36
- it('throws when an unknown name is referenced', () => {
37
- expect(() => (0, resolve_cache_1.resolveCache)({ name: 'missing' }, catalog, 'taskA')).toThrow(/cache "missing" referenced by task "taskA"/);
38
- });
39
- it('seeds default and none even when nothing is declared', () => {
40
- const empty = (0, resolve_cache_1.withBuiltinCaches)(undefined);
41
- expect(empty.default.backend.type).toBe('local');
42
- expect(empty.none.backend.type).toBe('local');
43
- });
44
- it('lets the user override the built-in default', () => {
45
- const cat = (0, resolve_cache_1.withBuiltinCaches)({
46
- default: { method: 'modify-date', backend: { type: 'local', path: '/tmp/x' } },
47
- });
48
- const r = (0, resolve_cache_1.resolveCache)(undefined, cat, 'taskA');
49
- expect(r.method).toBe('modify-date');
50
- });
51
- });
52
- //# sourceMappingURL=resolve-cache.spec.js.map
package/dist/cli.spec.js DELETED
@@ -1,23 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const path_1 = require("path");
4
- const test_case_1 = require("./testing/test-case");
5
- const program_1 = require("./program");
6
- // Drives the real parse pipeline (createCli) over an in-memory build file and
7
- // then exercises Cli lookups against the resulting work tree — a real "not
8
- // found" situation, no mocking.
9
- describe('Cli', () => {
10
- it('service() throws for an unknown service name', async () => {
11
- const testCase = (0, test_case_1.createTestCase)('cli-service', {
12
- '.hammerkit.yaml': {
13
- services: { api: { image: 'nginx', ports: [] } },
14
- tasks: { build: { cmds: ['tsc'], needs: ['api'] } },
15
- },
16
- });
17
- await testCase.setup(async (cwd, environment) => {
18
- const cli = await (0, program_1.createCli)((0, path_1.join)(cwd, '.hammerkit.yaml'), environment, { taskName: 'build' });
19
- expect(() => cli.service('missing')).toThrow('unable to find service missing');
20
- });
21
- });
22
- });
23
- //# sourceMappingURL=cli.spec.js.map
@@ -1,23 +0,0 @@
1
- "use strict";
2
- // Verifies getLogger dispatches to the right logger by name and rejects an
3
- // unknown mode via failNever — the actual loggers are tested elsewhere.
4
- Object.defineProperty(exports, "__esModule", { value: true });
5
- vi.mock('../logging/grouped-logger', () => ({ groupedLogger: vi.fn(() => 'grouped-instance') }));
6
- vi.mock('../logging/live-logger', () => ({ liveLogger: vi.fn(() => 'live-instance') }));
7
- vi.mock('../logging/interactive-logger', () => ({ interactiveLogger: vi.fn(() => 'interactive-instance') }));
8
- const get_logger_1 = require("./get-logger");
9
- describe('getLogger', () => {
10
- it('returns the grouped logger for mode "grouped"', () => {
11
- expect((0, get_logger_1.getLogger)('grouped', {}, {})).toBe('grouped-instance');
12
- });
13
- it('returns the live logger for mode "live"', () => {
14
- expect((0, get_logger_1.getLogger)('live', {}, {})).toBe('live-instance');
15
- });
16
- it('returns the interactive logger for mode "interactive"', () => {
17
- expect((0, get_logger_1.getLogger)('interactive', {}, {})).toBe('interactive-instance');
18
- });
19
- it('throws on an unknown mode (failNever)', () => {
20
- expect(() => (0, get_logger_1.getLogger)('bogus', {}, {})).toThrow();
21
- });
22
- });
23
- //# sourceMappingURL=get-logger.spec.js.map
@@ -1,29 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const package_1 = require("./package");
4
- describe('docker/getDefaultPlatform', () => {
5
- const realArch = process.arch;
6
- function setArch(arch) {
7
- Object.defineProperty(process, 'arch', { value: arch, configurable: true });
8
- }
9
- afterEach(() => {
10
- Object.defineProperty(process, 'arch', { value: realArch, configurable: true });
11
- });
12
- it('maps x64 to linux/amd64', () => {
13
- setArch('x64');
14
- expect((0, package_1.getDefaultPlatform)()).toBe('linux/amd64');
15
- });
16
- it('maps arm64 to linux/arm64', () => {
17
- setArch('arm64');
18
- expect((0, package_1.getDefaultPlatform)()).toBe('linux/arm64');
19
- });
20
- it('maps arm to linux/arm', () => {
21
- setArch('arm');
22
- expect((0, package_1.getDefaultPlatform)()).toBe('linux/arm');
23
- });
24
- it('falls back to linux/amd64 for unknown architectures', () => {
25
- setArch('mips');
26
- expect((0, package_1.getDefaultPlatform)()).toBe('linux/amd64');
27
- });
28
- });
29
- //# sourceMappingURL=get-default-platform.spec.js.map
@@ -1,26 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const path_1 = require("path");
4
- const package_1 = require("./package");
5
- // helper to build OS-correct absolute-ish paths
6
- const p = (...segs) => path_1.sep + segs.join(path_1.sep);
7
- describe('longestCommonPrefix (package build context root)', () => {
8
- it('returns the directory of a single source, not the file itself', () => {
9
- // regression: a single source used to yield the full file path, so the
10
- // build-context copy targeted a directory and failed with EISDIR.
11
- expect((0, package_1.longestCommonPrefix)([p('a', 'b', 'hello.txt')])).toBe(p('a', 'b'));
12
- });
13
- it('returns the common parent of files in the same directory', () => {
14
- expect((0, package_1.longestCommonPrefix)([p('a', 'x.txt'), p('a', 'y.txt')])).toBe(p('a'));
15
- });
16
- it('returns the common ancestor across nested directories', () => {
17
- expect((0, package_1.longestCommonPrefix)([p('a', 'x.txt'), p('a', 'b', 'y.txt')])).toBe(p('a'));
18
- });
19
- it('compares per path segment, not per character', () => {
20
- expect((0, package_1.longestCommonPrefix)([p('a', 'foo'), p('a', 'foobar')])).toBe(p('a'));
21
- });
22
- it('returns empty for no sources', () => {
23
- expect((0, package_1.longestCommonPrefix)([])).toBe('');
24
- });
25
- });
26
- //# sourceMappingURL=longest-common-prefix.spec.js.map