hammerkit 1.6.0 → 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (277) hide show
  1. package/README.md +7 -7
  2. package/build.schema.json +59 -15
  3. package/dist/cache/backends/local-cache-backend.js +58 -1
  4. package/dist/cache/backends/registry/registry-client.js +264 -0
  5. package/dist/cache/backends/registry/registry-credentials.js +81 -0
  6. package/dist/cache/backends/registry/registry-reference.js +36 -0
  7. package/dist/cache/backends/registry-cache-backend.js +145 -0
  8. package/dist/cache/backends/s3-cache-backend.js +99 -46
  9. package/dist/cache/cache-backend-registry.js +2 -0
  10. package/dist/cache/cache-inventory.js +84 -0
  11. package/dist/cache/cache-sync.js +77 -0
  12. package/dist/cache/explain.js +214 -0
  13. package/dist/cache/last-resolved.js +48 -0
  14. package/dist/cache/read-only.js +15 -0
  15. package/dist/cache/retention.js +61 -0
  16. package/dist/cli.js +43 -0
  17. package/dist/docker/using-container.js +17 -1
  18. package/dist/executer/archive-task-entry.js +21 -0
  19. package/dist/executer/await-completed-dependencies.js +32 -14
  20. package/dist/executer/cache-metadata.js +9 -2
  21. package/dist/executer/docker-service.js +2 -1
  22. package/dist/executer/docker-task.js +18 -3
  23. package/dist/executer/dry-run.js +73 -0
  24. package/dist/executer/event-cache.js +6 -5
  25. package/dist/executer/execute-command.js +21 -3
  26. package/dist/executer/execute-docker.js +18 -2
  27. package/dist/executer/execute-work-service.js +10 -1
  28. package/dist/executer/execute-work-task.js +111 -26
  29. package/dist/executer/execute-work-tree.js +26 -1
  30. package/dist/executer/execution-steps.js +3 -8
  31. package/dist/executer/get-container-binds.js +13 -1
  32. package/dist/executer/get-scheduler-execute-result.js +4 -1
  33. package/dist/executer/lazy-resolver.js +15 -3
  34. package/dist/executer/reset-work-tree.js +2 -0
  35. package/dist/executer/run-summary.js +102 -0
  36. package/dist/executer/scheduler/enqueue-next.js +15 -60
  37. package/dist/executer/scheduler/state-key.js +78 -0
  38. package/dist/index.js +14 -1
  39. package/dist/kubernetes/apply.js +6 -7
  40. package/dist/kubernetes/await-running-state.js +100 -60
  41. package/dist/kubernetes/ensure-http-route.js +7 -10
  42. package/dist/kubernetes/ensure-namespace.js +9 -10
  43. package/dist/kubernetes/ensure-persistent-data.js +6 -5
  44. package/dist/kubernetes/get-service-ip.js +2 -2
  45. package/dist/kubernetes/kubernetes-instance.js +0 -4
  46. package/dist/kubernetes/remove-persistent-data.js +16 -6
  47. package/dist/kubernetes/resolve-pod-name.js +7 -7
  48. package/dist/log.js +16 -6
  49. package/dist/logging/live-logger.js +2 -1
  50. package/dist/optimizer/calculate-checksum.js +8 -2
  51. package/dist/optimizer/get-work-cache-stats.js +66 -16
  52. package/dist/optimizer/work-task-cache-description.js +34 -5
  53. package/dist/planner/project-root.js +26 -0
  54. package/dist/planner/utils/append-work-environment.js +1 -1
  55. package/dist/planner/utils/append-work-service.js +2 -1
  56. package/dist/planner/utils/append-work-task.js +7 -1
  57. package/dist/planner/utils/get-outputs-to-reset.js +20 -0
  58. package/dist/planner/utils/parse-work-generate.js +2 -2
  59. package/dist/planner/utils/parse-work-source.js +35 -30
  60. package/dist/planner/utils/portable-path.js +30 -0
  61. package/dist/planner/work-cache-id.js +2 -2
  62. package/dist/planner/work-graph-serializer.js +103 -0
  63. package/dist/planner/work-instance-id.js +15 -0
  64. package/dist/planner/work-runtime-docker.js +58 -15
  65. package/dist/planner/work-runtime-kubernetes.js +95 -45
  66. package/dist/planner/work-runtime-local.js +31 -8
  67. package/dist/planner/work-service-id.js +16 -4
  68. package/dist/program.js +235 -1
  69. package/dist/schema/build-file-base-task-schema.js +4 -0
  70. package/dist/schema/build-file-environment-schema-http-route.js +15 -0
  71. package/dist/schema/build-file-environment-schema.js +8 -2
  72. package/dist/schema/build-file-schema.js +1 -1
  73. package/dist/schema/cache-schema.js +21 -2
  74. package/dist/schema/duration-schema.js +18 -0
  75. package/dist/schema/envs-schema.js +1 -1
  76. package/dist/schema/reference-parser.js +3 -0
  77. package/dist/schema/size-schema.js +18 -0
  78. package/dist/schema/work-scope-parser.js +15 -2
  79. package/dist/utils/units.js +87 -0
  80. package/package.json +20 -9
  81. package/.agents/FEEDBACK.md +0 -74
  82. package/.agents/skills/before-commit/SKILL.md +0 -101
  83. package/.agents/skills/do-the-work/SKILL.md +0 -64
  84. package/.agents/skills/fix-ci/SKILL.md +0 -98
  85. package/.agents/skills/speckit-git-commit/SKILL.md +0 -53
  86. package/.agents/skills/speckit-git-feature/SKILL.md +0 -72
  87. package/.agents/skills/speckit-git-initialize/SKILL.md +0 -54
  88. package/.agents/skills/speckit-git-remote/SKILL.md +0 -50
  89. package/.agents/skills/speckit-git-validate/SKILL.md +0 -54
  90. package/.devcontainer/Dockerfile +0 -5
  91. package/.devcontainer/devcontainer.json +0 -40
  92. package/.gitbook.yaml +0 -5
  93. package/.specify/extensions/.registry +0 -37
  94. package/.specify/extensions/git/README.md +0 -100
  95. package/.specify/extensions/git/commands/speckit.git.commit.md +0 -48
  96. package/.specify/extensions/git/commands/speckit.git.feature.md +0 -67
  97. package/.specify/extensions/git/commands/speckit.git.initialize.md +0 -49
  98. package/.specify/extensions/git/commands/speckit.git.remote.md +0 -45
  99. package/.specify/extensions/git/commands/speckit.git.validate.md +0 -49
  100. package/.specify/extensions/git/config-template.yml +0 -62
  101. package/.specify/extensions/git/extension.yml +0 -140
  102. package/.specify/extensions/git/git-config.yml +0 -62
  103. package/.specify/extensions.yml +0 -149
  104. package/.specify/feature.json +0 -3
  105. package/.specify/init-options.json +0 -10
  106. package/.specify/integration.json +0 -15
  107. package/.specify/integrations/claude.manifest.json +0 -16
  108. package/.specify/integrations/speckit.manifest.json +0 -17
  109. package/.specify/memory/constitution.md +0 -162
  110. package/.specify/templates/checklist-template.md +0 -40
  111. package/.specify/templates/constitution-template.md +0 -50
  112. package/.specify/templates/plan-template.md +0 -113
  113. package/.specify/templates/spec-template.md +0 -131
  114. package/.specify/templates/tasks-template.md +0 -252
  115. package/.specify/workflows/speckit/workflow.yml +0 -77
  116. package/.specify/workflows/workflow-registry.json +0 -13
  117. package/AGENTS.md +0 -94
  118. package/Dockerfile +0 -32
  119. package/FOLLOWUPS.md +0 -34
  120. package/SUMMARY.md +0 -68
  121. package/VALUES.md +0 -53
  122. package/dist/cache/auto-pull.spec.js +0 -123
  123. package/dist/cache/backends/local-cache-backend.spec.js +0 -72
  124. package/dist/cache/backends/s3-cache-backend.spec.js +0 -118
  125. package/dist/cache/cache-backend-registry.spec.js +0 -22
  126. package/dist/cache/resolve-cache.spec.js +0 -52
  127. package/dist/cli.spec.js +0 -23
  128. package/dist/console/get-logger.spec.js +0 -23
  129. package/dist/docker/get-default-platform.spec.js +0 -29
  130. package/dist/docker/longest-common-prefix.spec.js +0 -26
  131. package/dist/docker/package.spec.js +0 -145
  132. package/dist/docker/remove-container.spec.js +0 -32
  133. package/dist/docker/using-container.spec.js +0 -90
  134. package/dist/environment/merge-environment-variables.spec.js +0 -24
  135. package/dist/environment/replace-env-variables.spec.js +0 -47
  136. package/dist/executer/abort.spec.js +0 -18
  137. package/dist/executer/environment-mock.spec.js +0 -22
  138. package/dist/executer/execute.spec.js +0 -51
  139. package/dist/executer/get-service-containers.spec.js +0 -59
  140. package/dist/executer/get-service-env-hints.spec.js +0 -65
  141. package/dist/executer/label-values.spec.js +0 -61
  142. package/dist/executer/lazy-resolver.spec.js +0 -20
  143. package/dist/executer/process-manager.spec.js +0 -54
  144. package/dist/executer/scheduler/enqueue-next.spec.js +0 -136
  145. package/dist/executer/state-resolver.spec.js +0 -37
  146. package/dist/executer/state.spec.js +0 -66
  147. package/dist/executer/states.spec.js +0 -14
  148. package/dist/file/get-file-context.spec.js +0 -178
  149. package/dist/file/move-files.spec.js +0 -48
  150. package/dist/kubernetes/ensure-http-route.spec.js +0 -70
  151. package/dist/kubernetes/ensure-ingress.js +0 -54
  152. package/dist/kubernetes/ensure-namespace.spec.js +0 -39
  153. package/dist/kubernetes/resolve-pod-name.spec.js +0 -81
  154. package/dist/log.spec.js +0 -131
  155. package/dist/logging/grouped-logger.spec.js +0 -84
  156. package/dist/logging/interactive-logger.spec.js +0 -57
  157. package/dist/logging/live-logger.spec.js +0 -37
  158. package/dist/logging/loggers.spec.js +0 -154
  159. package/dist/optimizer/calculate-checksum.spec.js +0 -34
  160. package/dist/optimizer/get-cache-directory.spec.js +0 -59
  161. package/dist/optimizer/get-work-cache-stats.spec.js +0 -79
  162. package/dist/optimizer/work-task-cache-description.spec.js +0 -45
  163. package/dist/parser/default-build-file.spec.js +0 -48
  164. package/dist/parser/parse-label-arguments.spec.js +0 -27
  165. package/dist/parser/read-build-file.spec.js +0 -89
  166. package/dist/parser/read-env-file.spec.js +0 -42
  167. package/dist/planner/utils/append-work-service.spec.js +0 -71
  168. package/dist/planner/utils/get-container-user.spec.js +0 -30
  169. package/dist/planner/utils/get-default-kube-config.spec.js +0 -11
  170. package/dist/planner/utils/normalize-path.spec.js +0 -24
  171. package/dist/planner/utils/parse-work-command.spec.js +0 -21
  172. package/dist/planner/utils/parse-work-generate.spec.js +0 -45
  173. package/dist/planner/utils/parse-work-mount.spec.js +0 -62
  174. package/dist/planner/utils/parse-work-mounts.spec.js +0 -19
  175. package/dist/planner/utils/parse-work-port.spec.js +0 -24
  176. package/dist/planner/utils/parse-work-ports.spec.js +0 -24
  177. package/dist/planner/utils/parse-work-source.spec.js +0 -58
  178. package/dist/planner/utils/parse-work-volume.spec.js +0 -18
  179. package/dist/planner/utils/plan-work-tasks.spec.js +0 -30
  180. package/dist/planner/utils/plan-work-volume.spec.js +0 -33
  181. package/dist/planner/utils/split-name.spec.js +0 -16
  182. package/dist/planner/utils/template-value.spec.js +0 -14
  183. package/dist/planner/validate.spec.js +0 -251
  184. package/dist/planner/work-cache-id.spec.js +0 -173
  185. package/dist/planner/work-runtime-local.spec.js +0 -193
  186. package/dist/program-commands.spec.js +0 -84
  187. package/dist/program.spec.js +0 -43
  188. package/dist/schema/build-file-environment-schema-ingress.js +0 -14
  189. package/dist/schema/build-file-schema.spec.js +0 -61
  190. package/dist/schema/doc-build-files.spec.js +0 -223
  191. package/dist/schema/parse-error.spec.js +0 -18
  192. package/dist/schema/reference-parser.spec.js +0 -83
  193. package/dist/schema/schema-parser.spec.js +0 -80
  194. package/dist/schema/type-guards.spec.js +0 -23
  195. package/dist/schema/work-scope-parser.spec.js +0 -66
  196. package/dist/service/update-service-status.spec.js +0 -27
  197. package/dist/testing/ensure-local-registry.js +0 -134
  198. package/dist/testing/example-test-suite.js +0 -63
  199. package/dist/testing/expect.js +0 -82
  200. package/dist/testing/get-test-suite.js +0 -9
  201. package/dist/testing/http-get.js +0 -21
  202. package/dist/testing/integration/cache.spec.js +0 -49
  203. package/dist/testing/integration/cancellation.spec.js +0 -45
  204. package/dist/testing/integration/clean.spec.js +0 -43
  205. package/dist/testing/integration/cmd.spec.js +0 -15
  206. package/dist/testing/integration/concurrency.spec.js +0 -19
  207. package/dist/testing/integration/docker-package-registry.spec.js +0 -44
  208. package/dist/testing/integration/docker-service-crash.spec.js +0 -36
  209. package/dist/testing/integration/docker.spec.js +0 -18
  210. package/dist/testing/integration/env.spec.js +0 -48
  211. package/dist/testing/integration/environment.spec.js +0 -97
  212. package/dist/testing/integration/error.spec.js +0 -19
  213. package/dist/testing/integration/export.spec.js +0 -22
  214. package/dist/testing/integration/extend.spec.js +0 -29
  215. package/dist/testing/integration/glob.spec.js +0 -40
  216. package/dist/testing/integration/include.spec.js +0 -31
  217. package/dist/testing/integration/invalid.spec.js +0 -17
  218. package/dist/testing/integration/invalid_loop.spec.js +0 -56
  219. package/dist/testing/integration/k8s-state-cache.spec.js +0 -41
  220. package/dist/testing/integration/kubernetes-service.spec.js +0 -148
  221. package/dist/testing/integration/kubernetes.spec.js +0 -99
  222. package/dist/testing/integration/labels.spec.js +0 -42
  223. package/dist/testing/integration/local.spec.js +0 -14
  224. package/dist/testing/integration/monorepo.spec.js +0 -15
  225. package/dist/testing/integration/multi-line.spec.js +0 -17
  226. package/dist/testing/integration/reference.spec.js +0 -21
  227. package/dist/testing/integration/services.spec.js +0 -38
  228. package/dist/testing/integration/store.spec.js +0 -52
  229. package/dist/testing/integration/unknown.spec.js +0 -28
  230. package/dist/testing/integration/validate.spec.js +0 -160
  231. package/dist/testing/integration/watch-dependency.spec.js +0 -33
  232. package/dist/testing/integration/watch.spec.js +0 -45
  233. package/dist/testing/requires-kubernetes.js +0 -18
  234. package/dist/testing/requires-linux-containers.js +0 -18
  235. package/dist/testing/requires-windows-containers.js +0 -18
  236. package/dist/testing/test-case.js +0 -75
  237. package/dist/testing/test-streams.js +0 -28
  238. package/dist/testing/test-suite-setup.js +0 -3
  239. package/dist/testing/test-suite.js +0 -3
  240. package/dist/testing/testing-timeout.js +0 -32
  241. package/dist/utils/abort-event.spec.js +0 -46
  242. package/dist/utils/buffer-context.spec.js +0 -38
  243. package/dist/utils/ci.spec.js +0 -53
  244. package/dist/utils/debouncer.spec.js +0 -58
  245. package/dist/utils/emitter.spec.js +0 -34
  246. package/dist/utils/empty-writable.spec.js +0 -18
  247. package/dist/utils/environment-config.spec.js +0 -26
  248. package/dist/utils/fail-never.spec.js +0 -9
  249. package/dist/utils/sleep.spec.js +0 -22
  250. package/dist/version.spec.js +0 -12
  251. package/specs/build-file/caches/spec.md +0 -114
  252. package/specs/build-file/environment-variables/spec.md +0 -82
  253. package/specs/build-file/includes/spec.md +0 -60
  254. package/specs/build-file/references/spec.md +0 -56
  255. package/specs/build-file/spec.md +0 -62
  256. package/specs/cli/clean/spec.md +0 -85
  257. package/specs/cli/down/spec.md +0 -85
  258. package/specs/cli/execute/spec.md +0 -101
  259. package/specs/cli/init/spec.md +0 -64
  260. package/specs/cli/ls/spec.md +0 -66
  261. package/specs/cli/package/spec.md +0 -110
  262. package/specs/cli/spec.md +0 -73
  263. package/specs/cli/store-restore/spec.md +0 -74
  264. package/specs/cli/up/spec.md +0 -80
  265. package/specs/cli/validate/spec.md +0 -70
  266. package/specs/labels/spec.md +0 -91
  267. package/specs/service/container/spec.md +0 -120
  268. package/specs/service/kubernetes/spec.md +0 -97
  269. package/specs/service/spec.md +0 -105
  270. package/specs/task/caching/spec.md +0 -107
  271. package/specs/task/container/spec.md +0 -118
  272. package/specs/task/dependencies/spec.md +0 -70
  273. package/specs/task/extending/spec.md +0 -74
  274. package/specs/task/kubernetes/spec.md +0 -125
  275. package/specs/task/needs/spec.md +0 -59
  276. package/specs/task/spec.md +0 -87
  277. package/specs/task/watching/spec.md +0 -69
@@ -1,36 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const test_case_1 = require("../test-case");
4
- const requires_linux_containers_1 = require("../requires-linux-containers");
5
- /**
6
- * Regression for the crash detection in docker-service.ts: a container that
7
- * exits while the runtime is waiting on abort must surface as
8
- * `{ type: 'end', reason: 'crash' }` instead of being silently 'terminated'.
9
- */
10
- describe('docker service crash detection', () => {
11
- const testCase = (0, test_case_1.createTestCase)('docker-service-crash', {
12
- '.hammerkit.yaml': {
13
- services: {
14
- flaky: {
15
- image: 'alpine:3.19',
16
- // command exits with non-zero after a short delay
17
- cmd: 'sh -c "sleep 1 && exit 17"',
18
- ports: [':9000'],
19
- },
20
- },
21
- },
22
- });
23
- it('reports crash when the container exits while running', (0, requires_linux_containers_1.requiresLinuxContainers)(async () => {
24
- await testCase.cli({}, async (cli) => {
25
- // daemon=false so docker-service.ts awaits container.wait() instead of
26
- // returning as soon as the service is 'running'
27
- await cli.runUp({ daemon: false });
28
- const service = cli.service('flaky');
29
- expect(service.state.current.type).toBe('end');
30
- if (service.state.current.type === 'end') {
31
- expect(service.state.current.reason).toBe('crash');
32
- }
33
- });
34
- }), 90000);
35
- });
36
- //# sourceMappingURL=docker-service-crash.spec.js.map
@@ -1,18 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const expect_1 = require("../expect");
4
- const get_test_suite_1 = require("../get-test-suite");
5
- const requires_linux_containers_1 = require("../requires-linux-containers");
6
- describe('docker', () => {
7
- const suite = (0, get_test_suite_1.getTestSuite)('docker', ['.hammerkit.yaml']);
8
- afterAll(() => suite.close());
9
- it('should pull docker image', (0, requires_linux_containers_1.requiresLinuxContainers)(async () => {
10
- const { cli, environment } = await suite.setup({ taskName: 'example' });
11
- const result = await cli.runExec();
12
- await (0, expect_1.expectSuccessfulResult)(result, environment);
13
- // versions printed by the pinned node:24.16.0-alpine fixture image
14
- await (0, expect_1.expectLog)(result, environment, `example`, '11.13.0');
15
- await (0, expect_1.expectLog)(result, environment, `example`, 'v24.16.0');
16
- }));
17
- });
18
- //# sourceMappingURL=docker.spec.js.map
@@ -1,48 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const expect_1 = require("../expect");
4
- const get_test_suite_1 = require("../get-test-suite");
5
- const requires_linux_containers_1 = require("../requires-linux-containers");
6
- describe('env', () => {
7
- const suite = (0, get_test_suite_1.getTestSuite)('env', ['.hammerkit.yaml', '.env']);
8
- afterAll(() => suite.close());
9
- it('should use env from build file', async () => {
10
- const { cli, environment } = await suite.setup({ taskName: 'example' });
11
- const result = await cli.runExec();
12
- await (0, expect_1.expectSuccessfulResult)(result, environment);
13
- await (0, expect_1.expectLog)(result, environment, `example`, '16.6.0');
14
- });
15
- it('should pass env to docker', (0, requires_linux_containers_1.requiresLinuxContainers)(async () => {
16
- const { cli, environment } = await suite.setup({ taskName: 'example_docker' });
17
- const result = await cli.runExec();
18
- await (0, expect_1.expectSuccessfulResult)(result, environment);
19
- await (0, expect_1.expectLog)(result, environment, `example_docker`, '16.6.0');
20
- }));
21
- it('should use env from task', async () => {
22
- const { cli, environment } = await suite.setup({ taskName: 'example_override' });
23
- const result = await cli.runExec();
24
- await (0, expect_1.expectSuccessfulResult)(result, environment);
25
- await (0, expect_1.expectLog)(result, environment, `example_override`, '15.0.0');
26
- });
27
- it('should use env from .env', async () => {
28
- const { cli, environment } = await suite.setup({ taskName: 'example_with_dotenv' });
29
- const result = await cli.runExec();
30
- await (0, expect_1.expectSuccessfulResult)(result, environment);
31
- await (0, expect_1.expectLog)(result, environment, `example_with_dotenv`, '123456');
32
- });
33
- it('should use process env', async () => {
34
- const { cli, environment } = await suite.setup({
35
- taskName: 'example_with_shell_env',
36
- envs: {
37
- VERSION: '1.0.0',
38
- },
39
- });
40
- const result = await cli.runExec();
41
- await (0, expect_1.expectSuccessfulResult)(result, environment);
42
- await (0, expect_1.expectLog)(result, environment, `example_with_shell_env`, '1.0.0');
43
- });
44
- it('should throw if process env is missing', async () => {
45
- await expect(suite.setup({ taskName: 'example_with_shell_env' })).rejects.toThrow('missing environment variable VERSION');
46
- });
47
- });
48
- //# sourceMappingURL=env.spec.js.map
@@ -1,97 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const get_test_suite_1 = require("../get-test-suite");
4
- const test_case_1 = require("../test-case");
5
- const requires_kubernetes_1 = require("../requires-kubernetes");
6
- const testing_timeout_1 = require("../testing-timeout");
7
- describe('environment', () => {
8
- const suite = (0, get_test_suite_1.getTestSuite)('error', ['.hammerkit.yaml']);
9
- afterAll(() => suite.close());
10
- it('should run on environment (kubernetes)', (0, requires_kubernetes_1.requiresKubernetes)(async () => {
11
- const testCase = (0, test_case_1.createTestCase)('environment', {
12
- '.hammerkit.yaml': {
13
- tasks: {
14
- install: {
15
- image: 'node:18-alpine',
16
- cmds: ['npm install'],
17
- src: ['package.json'],
18
- generates: ['node_modules'],
19
- },
20
- },
21
- services: {
22
- postgres: {
23
- image: 'postgres:16-alpine',
24
- envs: {
25
- POSTGRES_USER: 'postgres',
26
- POSTGRES_DB: 'demo',
27
- POSTGRES_PASSWORD: '123456',
28
- },
29
- ports: [':5432'],
30
- volumes: ['postgres-db:/var/lib/postgresql/data'],
31
- },
32
- api: {
33
- deps: ['install'],
34
- needs: ['postgres'],
35
- image: 'node:16.6.0-alpine',
36
- envs: {
37
- DATABASE_URL: 'postgres://postgres:123456@postgres/demo',
38
- },
39
- cmd: 'node index.js',
40
- src: ['index.js'],
41
- ports: [':3000'],
42
- },
43
- },
44
- environments: {
45
- staging: {
46
- kubernetes: {
47
- namespace: `hammerkit-env-${(process.env.HAMMERKIT_TEST_RUN_ID ?? 'local').slice(0, 40)}`,
48
- context: process.env.CLUSTER_NAME || 'docker-desktop',
49
- ingresses: [
50
- {
51
- host: 'demo.bertschi.io',
52
- service: 'api',
53
- },
54
- ],
55
- },
56
- },
57
- },
58
- },
59
- 'package.json': '{ "dependencies": { "pg": "^8.7.1" } }',
60
- 'index.js': `const { Client } = require('pg')
61
- const { createServer } = require('http')
62
-
63
- const client = new Client(process.env.DATABASE_URL)
64
-
65
- const server = createServer(async (req, res) => {
66
- const result = await client.query('SELECT $1::text as message', ['Hello world!'])
67
- res.writeHead(200)
68
- res.end(result.rows[0].message)
69
- })
70
-
71
- async function main() {
72
- await client.connect()
73
- server.listen(3000)
74
- }
75
-
76
- process.on('SIGINT', async function () {
77
- server.close()
78
- await client.end()
79
- })
80
-
81
- main().catch((err) => {
82
- console.error(err)
83
- process.exit(1)
84
- })
85
- `,
86
- });
87
- await testCase.cli({ environmentName: 'staging' }, async (cli) => {
88
- try {
89
- await (0, testing_timeout_1.testingTimeout)(cli.up({ daemon: true }));
90
- }
91
- finally {
92
- await cli.runDown();
93
- }
94
- });
95
- }));
96
- });
97
- //# sourceMappingURL=environment.spec.js.map
@@ -1,19 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const get_test_suite_1 = require("../get-test-suite");
4
- const requires_linux_containers_1 = require("../requires-linux-containers");
5
- describe('error', () => {
6
- const suite = (0, get_test_suite_1.getTestSuite)('error', ['.hammerkit.yaml']);
7
- afterAll(() => suite.close());
8
- it('should return error when local task failed', async () => {
9
- const { cli } = await suite.setup({ taskName: 'local_error' });
10
- const result = await cli.runExec();
11
- expect(result.success).toBeFalsy();
12
- });
13
- it('should return error when docker task failed', (0, requires_linux_containers_1.requiresLinuxContainers)(async () => {
14
- const { cli } = await suite.setup({ taskName: 'docker_error' });
15
- const result = await cli.runExec();
16
- expect(result.success).toBeFalsy();
17
- }));
18
- });
19
- //# sourceMappingURL=error.spec.js.map
@@ -1,22 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const get_test_suite_1 = require("../get-test-suite");
4
- const path_1 = require("path");
5
- const requires_linux_containers_1 = require("../requires-linux-containers");
6
- describe('export', () => {
7
- const suite = (0, get_test_suite_1.getTestSuite)('export', ['.hammerkit.yaml']);
8
- afterAll(() => suite.close());
9
- it('should export created file', (0, requires_linux_containers_1.requiresLinuxContainers)(async () => {
10
- const { cli, environment } = await suite.setup({ taskName: 'example_file' });
11
- const result = await cli.runExec();
12
- expect(result.success).toBeTruthy();
13
- expect(await environment.file.read((0, path_1.join)(environment.cwd, 'test.txt'))).toEqual('hello\n');
14
- }));
15
- it('should export created directory', (0, requires_linux_containers_1.requiresLinuxContainers)(async () => {
16
- const { cli, environment } = await suite.setup({ taskName: 'example_dir' });
17
- const result = await cli.runExec();
18
- expect(result.success).toBeTruthy();
19
- expect(await environment.file.read((0, path_1.join)(environment.cwd, 'dist/test.txt'))).toEqual('hello\n');
20
- }));
21
- });
22
- //# sourceMappingURL=export.spec.js.map
@@ -1,29 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const get_test_suite_1 = require("../get-test-suite");
4
- describe('extend', () => {
5
- const suite = (0, get_test_suite_1.getTestSuite)('extend', ['.hammerkit.yaml']);
6
- afterAll(() => suite.close());
7
- it('should merge env variables', async () => {
8
- const { cli } = await suite.setup({ taskName: 'extend_env' });
9
- const task = await cli.task('extend_env');
10
- expect(task.data.envs.variables).toMatchObject({ NAME: 'base', KEEP: 'value' });
11
- });
12
- it('should extend and override', async () => {
13
- const { cli } = await suite.setup({ taskName: 'extend_container_task' });
14
- const task = await cli.task('extend_container_task');
15
- expect(task.data.envs.variables).toEqual({
16
- NAME: 'override',
17
- });
18
- expect(task.data.type).toBe('container-task');
19
- if (task.data.type === 'container-task') {
20
- expect(task.data.image).toBe('alpine:latest');
21
- }
22
- });
23
- it('should merge deps', async () => {
24
- const { cli } = await suite.setup({ taskName: 'extend_dep' });
25
- const task = await cli.task('extend_dep');
26
- expect(task.deps.map((d) => d.name).sort()).toEqual(['base_env', 'extend_env'].sort());
27
- });
28
- });
29
- //# sourceMappingURL=extend.spec.js.map
@@ -1,40 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const get_test_suite_1 = require("../get-test-suite");
4
- const enqueue_next_1 = require("../../executer/scheduler/enqueue-next");
5
- describe('glob', () => {
6
- const suite = (0, get_test_suite_1.getTestSuite)('glob', ['.hammerkit.yaml', 'test.md', 'test.txt']);
7
- afterAll(() => suite.close());
8
- async function testCache(expectInvalidate, action) {
9
- const { cli, environment } = await suite.setup({ taskName: 'example' });
10
- const task = cli.task('example');
11
- const cacheBefore = await (0, enqueue_next_1.checkCacheState)(task, 'checksum', environment);
12
- expect(cacheBefore.stateKey).not.toBeNull();
13
- if (action) {
14
- await action(environment);
15
- }
16
- const exampleAfter = cli.task('example');
17
- const cacheAfter = await (0, enqueue_next_1.checkCacheState)(exampleAfter, 'checksum', environment);
18
- expect(cacheAfter.stateKey).not.toBeNull();
19
- if (expectInvalidate) {
20
- expect(cacheAfter.stateKey).not.toEqual(cacheBefore.stateKey);
21
- }
22
- else {
23
- expect(cacheAfter.stateKey).toEqual(cacheBefore.stateKey);
24
- }
25
- }
26
- it('should remove task after written cache', async () => {
27
- await testCache(false);
28
- });
29
- it('should keep being cached after ignored file changed', async () => {
30
- await testCache(false, async (env) => {
31
- await env.file.appendFile('test.txt', '\n');
32
- });
33
- });
34
- it('should invalid cache after file has changed', async () => {
35
- await testCache(true, async (env) => {
36
- await env.file.appendFile('test.md', '\n');
37
- });
38
- });
39
- });
40
- //# sourceMappingURL=glob.spec.js.map
@@ -1,31 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const expect_1 = require("../expect");
4
- const get_test_suite_1 = require("../get-test-suite");
5
- describe('include', () => {
6
- const suite = (0, get_test_suite_1.getTestSuite)('include', ['.hammerkit.yaml', 'foo', 'foobar.txt']);
7
- afterAll(() => suite.close());
8
- it('should run included task', async () => {
9
- const { cli, environment } = await suite.setup({ taskName: 'example' });
10
- const result = await cli.runExec();
11
- await (0, expect_1.expectSuccessfulResult)(result, environment);
12
- await (0, expect_1.expectLog)(result, environment, `foo:bar`, 'foobar');
13
- });
14
- it('should get name:example', async () => {
15
- const { cli, environment } = await suite.setup({ taskName: 'name:example' });
16
- const result = await cli.runExec();
17
- await (0, expect_1.expectSuccessfulResult)(result, environment);
18
- });
19
- it('should get included task', async () => {
20
- const { cli } = await suite.setup({ taskName: 'foo:bar' });
21
- expect(cli.ls().map((n) => n.item.name)).toEqual(['foo:bar']);
22
- const item = cli.ls()[0];
23
- if (item.type === 'task') {
24
- expect(item.item.data.cwd).toBe(suite.path);
25
- }
26
- else {
27
- expect(item.type).toBe('task');
28
- }
29
- });
30
- });
31
- //# sourceMappingURL=include.spec.js.map
@@ -1,17 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const get_test_suite_1 = require("../get-test-suite");
4
- describe('invalid', () => {
5
- const suite = (0, get_test_suite_1.getTestSuite)('invalid', ['.hammerkit.yaml']);
6
- afterAll(() => suite.close());
7
- it('should throw on invalid yaml', async () => {
8
- try {
9
- await suite.setup({});
10
- expect.fail('should not be called');
11
- }
12
- catch (e) {
13
- expect(e.message.startsWith('unable to parse')).toBe(true);
14
- }
15
- });
16
- });
17
- //# sourceMappingURL=invalid.spec.js.map
@@ -1,56 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const test_case_1 = require("../test-case");
4
- describe('invalid', () => {
5
- it('should detect loop in execution', async () => {
6
- const testCase = (0, test_case_1.createTestCase)('invalid_loop', {
7
- '.hammerkit.yaml': {
8
- tasks: {
9
- bar: {
10
- deps: ['foo'],
11
- cmds: ['echo bar'],
12
- },
13
- foo: {
14
- deps: ['bar'],
15
- cmds: ['echo foo'],
16
- },
17
- },
18
- },
19
- });
20
- await testCase.cli({ taskName: 'foo' }, async (cli) => {
21
- const result = await cli.runExec();
22
- expect(result.success).toBeFalsy();
23
- });
24
- });
25
- it('should detect loop in services', async () => {
26
- const testCase = (0, test_case_1.createTestCase)('invalid_loop', {
27
- '.hammerkit.yaml': {
28
- services: {
29
- foodb: {
30
- image: 'postgres:12-alpine',
31
- volumes: ['foo:/var/lib/postgresql/data'],
32
- needs: ['bardb'],
33
- ports: [':5432'],
34
- },
35
- bardb: {
36
- image: 'postgres:12-alpine',
37
- volumes: ['bar:/var/lib/postgresql/data'],
38
- needs: ['foodb'],
39
- ports: [':5432'],
40
- },
41
- },
42
- tasks: {
43
- example: {
44
- needs: ['foodb'],
45
- cmds: ['echo bar'],
46
- },
47
- },
48
- },
49
- });
50
- await testCase.cli({ taskName: 'example' }, async (cli) => {
51
- const result = await cli.runExec();
52
- expect(result.success).toBeFalsy();
53
- });
54
- });
55
- });
56
- //# sourceMappingURL=invalid_loop.spec.js.map
@@ -1,41 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const test_case_1 = require("../test-case");
4
- const requires_kubernetes_1 = require("../requires-kubernetes");
5
- /**
6
- * Regression test for the k8s currentStateKey caching wired up in
7
- * feature/shared-services. Running `up` twice with no input change should
8
- * produce a cached second run, not redeploy.
9
- */
10
- describe('k8s deployment caches via hammerkit.dev/state label', () => {
11
- const namespace = `hammerkit-cache-${(process.env.HAMMERKIT_TEST_RUN_ID ?? 'local').slice(0, 30)}`;
12
- const suite = (0, test_case_1.createTestCase)('k8s-state-cache', {
13
- '.hammerkit.yaml': {
14
- services: {
15
- redis: {
16
- image: 'redis:7-alpine',
17
- ports: [':6379'],
18
- },
19
- },
20
- environments: {
21
- default: {
22
- kubernetes: {
23
- namespace,
24
- context: process.env.CLUSTER_NAME || 'docker-desktop',
25
- },
26
- },
27
- },
28
- },
29
- });
30
- it('second up() is a cache hit on the same state key', (0, requires_kubernetes_1.requiresKubernetes)(async () => {
31
- await suite.cli({}, async (cli, env) => {
32
- await cli.runUp({ daemon: true });
33
- const firstKey = await cli.service('redis').runtime.currentStateKey(env);
34
- expect(firstKey).not.toBeNull();
35
- await cli.runUp({ daemon: true });
36
- const secondKey = await cli.service('redis').runtime.currentStateKey(env);
37
- expect(secondKey).toBe(firstKey);
38
- });
39
- }), 240000);
40
- });
41
- //# sourceMappingURL=k8s-state-cache.spec.js.map
@@ -1,148 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const requires_kubernetes_1 = require("../requires-kubernetes");
4
- const test_case_1 = require("../test-case");
5
- const client_node_1 = require("@kubernetes/client-node");
6
- const http_1 = require("http");
7
- // Exercises src/executer/kubernetes-service.ts end-to-end: hammerkit's
8
- // `kubernetes-service` type opens a local TCP server (one per published port)
9
- // that tunnels via the k8s API to a pod. We deploy nginx directly with the k8s
10
- // client, then point a hammerkit kubernetes-service at it, drive the lifecycle
11
- // (load kubeconfig -> resolve pod -> listen -> state `running`) and actually
12
- // HTTP-GET the forwarded port to prove data flows through to the pod.
13
- const RUN_ID = (process.env.HAMMERKIT_TEST_RUN_ID ?? 'local').slice(0, 30);
14
- const NS = `hammerkit-k8s-svc-${RUN_ID}`;
15
- const CTX = process.env.CLUSTER_NAME || 'docker-desktop';
16
- const HOST_PORT = 28080;
17
- const DEPLOY_NAME = 'nginx';
18
- function makeClients() {
19
- const kc = new client_node_1.KubeConfig();
20
- kc.loadFromDefault();
21
- kc.setCurrentContext(CTX);
22
- return {
23
- coreApi: kc.makeApiClient(client_node_1.CoreV1Api),
24
- appsApi: kc.makeApiClient(client_node_1.AppsV1Api),
25
- };
26
- }
27
- async function ensureNamespace(coreApi) {
28
- try {
29
- await coreApi.readNamespace(NS);
30
- return;
31
- }
32
- catch {
33
- /* missing */
34
- }
35
- try {
36
- await coreApi.createNamespace({ metadata: { name: NS } });
37
- }
38
- catch {
39
- /* racing creator */
40
- }
41
- }
42
- async function applyNginx(coreApi, appsApi) {
43
- await appsApi.deleteNamespacedDeployment(DEPLOY_NAME, NS).catch(() => undefined);
44
- await appsApi.createNamespacedDeployment(NS, {
45
- metadata: { name: DEPLOY_NAME, labels: { app: DEPLOY_NAME } },
46
- spec: {
47
- replicas: 1,
48
- selector: { matchLabels: { app: DEPLOY_NAME } },
49
- template: {
50
- metadata: { labels: { app: DEPLOY_NAME } },
51
- spec: {
52
- containers: [{ name: DEPLOY_NAME, image: 'nginx:1.25-alpine', ports: [{ containerPort: 80 }] }],
53
- },
54
- },
55
- },
56
- });
57
- const deadline = Date.now() + 90000;
58
- while (Date.now() < deadline) {
59
- const pods = await coreApi.listNamespacedPod(NS, undefined, undefined, undefined, undefined, `app=${DEPLOY_NAME}`);
60
- const ready = pods.body.items.some((p) => p.status?.conditions?.some((c) => c.type === 'Ready' && c.status === 'True'));
61
- if (ready)
62
- return;
63
- await new Promise((r) => setTimeout(r, 1000));
64
- }
65
- throw new Error(`nginx pod in ${NS} did not become ready in time`);
66
- }
67
- async function waitFor(predicate, timeoutMs, intervalMs = 200) {
68
- const deadline = Date.now() + timeoutMs;
69
- while (Date.now() < deadline) {
70
- if (predicate())
71
- return;
72
- await new Promise((r) => setTimeout(r, intervalMs));
73
- }
74
- throw new Error('waitFor: predicate never became true');
75
- }
76
- function httpGet(host, port, path) {
77
- return new Promise((resolve, reject) => {
78
- const req = (0, http_1.request)({ host, port, path, method: 'GET', timeout: 5000 }, (res) => {
79
- let body = '';
80
- res.on('data', (c) => (body += c));
81
- res.on('end', () => resolve({ status: res.statusCode ?? 0, body }));
82
- });
83
- req.on('error', reject);
84
- req.on('timeout', () => {
85
- req.destroy();
86
- reject(new Error('http timeout'));
87
- });
88
- req.end();
89
- });
90
- }
91
- describe('kubernetes-service (port-forward)', () => {
92
- const suite = (0, test_case_1.createTestCase)('k8s-service-forward', {
93
- '.hammerkit.yaml': {
94
- services: {
95
- nginx: {
96
- context: CTX,
97
- namespace: NS,
98
- selector: { type: 'deployment', name: DEPLOY_NAME },
99
- ports: [`${HOST_PORT}:80`],
100
- },
101
- },
102
- environments: {
103
- default: {
104
- kubernetes: { context: CTX, namespace: NS },
105
- },
106
- },
107
- },
108
- });
109
- beforeAll((0, requires_kubernetes_1.requiresKubernetes)(async () => {
110
- const { coreApi, appsApi } = makeClients();
111
- await ensureNamespace(coreApi);
112
- await applyNginx(coreApi, appsApi);
113
- }), 120000);
114
- afterAll(async () => {
115
- try {
116
- const { coreApi } = makeClients();
117
- await coreApi.deleteNamespace(NS).catch(() => undefined);
118
- }
119
- catch {
120
- /* no cluster available */
121
- }
122
- });
123
- it('forwards a kubernetes-service to a pod and tears down on abort', (0, requires_kubernetes_1.requiresKubernetes)(async () => {
124
- await suite.cli({}, async (cli, env) => {
125
- const exec = cli.up({ logMode: 'live' });
126
- const done = exec.start();
127
- try {
128
- // state transitions to `running` only after the forward server's
129
- // listen() resolves — proves loadKubeConfig + resolvePodName + the
130
- // TCP listener setup all completed against a live cluster.
131
- await waitFor(() => exec.state.current.services['nginx']?.state.current.type === 'running', 30000);
132
- // and an actual byte round-trip through the forward confirms the
133
- // per-connection portForward path (createServer callback) works too.
134
- const res = await httpGet('127.0.0.1', HOST_PORT, '/');
135
- expect(res.status).toBe(200);
136
- expect(res.body).toMatch(/nginx/i);
137
- }
138
- finally {
139
- env.abortCtrl.abort();
140
- await done;
141
- }
142
- // abort triggers the finally block in kubernetesService: forwards close,
143
- // state advances to `end`.
144
- expect(exec.state.current.services['nginx']?.state.current.type).toBe('end');
145
- });
146
- }), 120000);
147
- });
148
- //# sourceMappingURL=kubernetes-service.spec.js.map