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,154 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const stream_1 = require("stream");
4
- const state_1 = require("../executer/state");
5
- const work_item_status_1 = require("../planner/work-item-status");
6
- const live_logger_1 = require("./live-logger");
7
- const grouped_logger_1 = require("./grouped-logger");
8
- const interactive_logger_1 = require("./interactive-logger");
9
- function memStream() {
10
- const chunks = [];
11
- const stream = new stream_1.Writable({
12
- write(c, _e, cb) {
13
- chunks.push(c.toString());
14
- cb();
15
- },
16
- });
17
- return Object.assign(stream, { read: () => chunks.join('') });
18
- }
19
- function noopStream() {
20
- return new stream_1.Writable({ write: (_c, _e, cb) => cb() });
21
- }
22
- function makeEnv(stdout = noopStream()) {
23
- return {
24
- cwd: '/tmp',
25
- file: {},
26
- processEnvs: {},
27
- abortCtrl: new AbortController(),
28
- console: { info: vi.fn(), error: vi.fn(), warn: vi.fn(), debug: vi.fn(), log: vi.fn() },
29
- status: (0, work_item_status_1.statusConsole)(noopStream()),
30
- stdout,
31
- stderr: noopStream(),
32
- stdoutColumns: 80,
33
- };
34
- }
35
- function taskItem(env, name, state) {
36
- const data = { type: 'local-task', name, description: null, scope: { fileName: 'x' } };
37
- return {
38
- id: () => name,
39
- name,
40
- data,
41
- status: env.status.from(data),
42
- state: new state_1.State(state),
43
- needs: [],
44
- deps: [],
45
- requiredBy: [],
46
- runtime: {},
47
- };
48
- }
49
- function serviceItem(env, name, state) {
50
- const data = { type: 'container-service', name, ports: [] };
51
- return {
52
- id: () => name,
53
- name,
54
- data,
55
- status: env.status.from(data),
56
- state: new state_1.State(state),
57
- needs: [],
58
- deps: [],
59
- requiredBy: [],
60
- runtime: {},
61
- };
62
- }
63
- function workTree(items) {
64
- return {
65
- tasks: Object.fromEntries((items.tasks ?? []).map((t) => [t.name, t])),
66
- services: Object.fromEntries((items.services ?? []).map((s) => [s.name, s])),
67
- environment: { type: 'docker' },
68
- };
69
- }
70
- describe('loggers', () => {
71
- describe('liveLogger', () => {
72
- it('writes each status message and renders results on complete', async () => {
73
- const out = memStream();
74
- const env = makeEnv(out);
75
- const task = taskItem(env, 'build', { type: 'pending', stateKey: null });
76
- const tree = workTree({ tasks: [task] });
77
- const state = new state_1.State(tree);
78
- const logger = (0, live_logger_1.liveLogger)(state, env);
79
- // a write on the scoped console emits a message on env.status, which the
80
- // logger's listener formats into env.stdout
81
- task.status.write('info', 'compiling');
82
- const result = { state: tree, success: true };
83
- await logger.complete(result, env);
84
- expect(out.read()).toContain('compiling');
85
- });
86
- });
87
- describe('groupedLogger', () => {
88
- it("writes a completed task's logs on the next status update", async () => {
89
- const out = memStream();
90
- const env = makeEnv(out);
91
- const task = taskItem(env, 'build', { type: 'pending', stateKey: null });
92
- const tree = workTree({ tasks: [task] });
93
- const state = new state_1.State(tree);
94
- const logger = (0, grouped_logger_1.groupedLogger)(state, env);
95
- // buffer one info log, mark the task completed, then notify the parent state
96
- task.status.write('info', 'compiling');
97
- task.state.set({ type: 'completed', stateKey: 'k', duration: 5, cached: false });
98
- state.set(tree);
99
- await logger.complete({ state: tree, success: true }, env);
100
- expect(out.read()).toContain('compiling');
101
- });
102
- it("writes an ended service's logs on the next status update", async () => {
103
- const out = memStream();
104
- const env = makeEnv(out);
105
- const svc = serviceItem(env, 'db', { type: 'pending', stateKey: null });
106
- const tree = workTree({ services: [svc] });
107
- const state = new state_1.State(tree);
108
- const logger = (0, grouped_logger_1.groupedLogger)(state, env);
109
- svc.status.write('info', 'ready');
110
- svc.state.set({ type: 'end', reason: 'terminated', stateKey: 'k' });
111
- state.set(tree);
112
- await logger.complete({ state: tree, success: true }, env);
113
- expect(out.read()).toContain('ready');
114
- });
115
- it('does not duplicate a task that is already in the completed set', async () => {
116
- const out = memStream();
117
- const env = makeEnv(out);
118
- const task = taskItem(env, 'build', { type: 'pending', stateKey: null });
119
- const tree = workTree({ tasks: [task] });
120
- const state = new state_1.State(tree);
121
- const logger = (0, grouped_logger_1.groupedLogger)(state, env);
122
- task.status.write('info', 'once');
123
- task.state.set({ type: 'completed', stateKey: 'k', duration: 1, cached: false });
124
- state.set(tree);
125
- const after = out.read();
126
- // a second notification with no further log message should not append another copy
127
- state.set(tree);
128
- expect(out.read()).toEqual(after);
129
- await logger.complete({ state: tree, success: true }, env);
130
- });
131
- });
132
- describe('interactiveLogger', () => {
133
- beforeEach(() => vi.useFakeTimers());
134
- afterEach(() => vi.useRealTimers());
135
- it('hides the cursor, ticks, redraws on log-status, then cleans up on complete', async () => {
136
- const out = memStream();
137
- const env = makeEnv(out);
138
- const task = taskItem(env, 'build', { type: 'pending', stateKey: null });
139
- const tree = workTree({ tasks: [task] });
140
- const state = new state_1.State(tree);
141
- const logger = (0, interactive_logger_1.interactiveLogger)(state, env); // hideCursor + first tick + setTimeout
142
- // drive a state change to re-render
143
- task.state.set({ type: 'running', stateKey: 'k' });
144
- state.set(tree);
145
- // let one fake tick fire while running===true (covers the recursive branch)
146
- vi.advanceTimersByTime(150);
147
- await logger.complete({ state: tree, success: true }, env);
148
- // and one more after complete — covers the `if (!running) return` early exit
149
- vi.advanceTimersByTime(150);
150
- expect(out.read().length).toBeGreaterThan(0);
151
- });
152
- });
153
- });
154
- //# sourceMappingURL=loggers.spec.js.map
@@ -1,34 +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 crypto_1 = require("crypto");
7
- const environment_mock_1 = require("../executer/environment-mock");
8
- const calculate_checksum_1 = require("./calculate-checksum");
9
- describe('calculateChecksum', () => {
10
- let scratch;
11
- beforeEach(() => {
12
- scratch = (0, fs_1.mkdtempSync)((0, path_1.join)((0, os_1.tmpdir)(), 'hammerkit-checksum-'));
13
- });
14
- afterEach(() => {
15
- (0, fs_1.rmSync)(scratch, { recursive: true, force: true });
16
- });
17
- it('returns the sha1 of the file contents', async () => {
18
- const env = (0, environment_mock_1.environmentMock)(scratch);
19
- const file = (0, path_1.join)(scratch, 'a.txt');
20
- (0, fs_1.writeFileSync)(file, 'hello world');
21
- const expected = (0, crypto_1.createHash)('sha1').update('hello world').digest('hex');
22
- expect(await (0, calculate_checksum_1.calculateChecksum)(env, file)).toBe(expected);
23
- });
24
- it('is deterministic and content-sensitive', async () => {
25
- const env = (0, environment_mock_1.environmentMock)(scratch);
26
- const file = (0, path_1.join)(scratch, 'a.txt');
27
- (0, fs_1.writeFileSync)(file, 'one');
28
- const first = await (0, calculate_checksum_1.calculateChecksum)(env, file);
29
- expect(await (0, calculate_checksum_1.calculateChecksum)(env, file)).toBe(first);
30
- (0, fs_1.writeFileSync)(file, 'two');
31
- expect(await (0, calculate_checksum_1.calculateChecksum)(env, file)).not.toBe(first);
32
- });
33
- });
34
- //# sourceMappingURL=calculate-checksum.spec.js.map
@@ -1,59 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const path_1 = require("path");
4
- const get_cache_directory_1 = require("./get-cache-directory");
5
- function withPlatform(value, env, fn) {
6
- const originalPlatform = process.platform;
7
- const originalEnv = {};
8
- for (const k of Object.keys(env))
9
- originalEnv[k] = process.env[k];
10
- Object.defineProperty(process, 'platform', { value, configurable: true });
11
- for (const [k, v] of Object.entries(env)) {
12
- if (v === undefined)
13
- delete process.env[k];
14
- else
15
- process.env[k] = v;
16
- }
17
- try {
18
- return fn();
19
- }
20
- finally {
21
- Object.defineProperty(process, 'platform', { value: originalPlatform, configurable: true });
22
- for (const [k, v] of Object.entries(originalEnv)) {
23
- if (v === undefined)
24
- delete process.env[k];
25
- else
26
- process.env[k] = v;
27
- }
28
- }
29
- }
30
- describe('cache directory helpers', () => {
31
- it('builds the per-cache directory under the hammerkit directory', () => {
32
- const dir = (0, get_cache_directory_1.getCacheDirectory)('abc123');
33
- expect(dir.endsWith((0, path_1.join)('cache', 'abc123'))).toBe(true);
34
- });
35
- it('builds the description file path under the cache directory', () => {
36
- expect((0, get_cache_directory_1.getCacheDescriptionFile)('id').endsWith((0, path_1.join)('cache', 'id', 'description.json'))).toBe(true);
37
- });
38
- it('builds the stats file path under the cache directory', () => {
39
- expect((0, get_cache_directory_1.getCacheStatsFile)('id').endsWith((0, path_1.join)('cache', 'id', 'stats.json'))).toBe(true);
40
- });
41
- describe('getHammerkitDirectory', () => {
42
- it('uses %APPDATA% on Windows', () => {
43
- withPlatform('win32', { APPDATA: 'C:\\Users\\X\\AppData\\Roaming' }, () => {
44
- expect((0, get_cache_directory_1.getHammerkitDirectory)()).toBe((0, path_1.join)('C:\\Users\\X\\AppData\\Roaming', 'hammerkit'));
45
- });
46
- });
47
- it('uses ~/Library/Application Support on macOS', () => {
48
- withPlatform('darwin', { HOME: '/Users/x' }, () => {
49
- expect((0, get_cache_directory_1.getHammerkitDirectory)()).toBe((0, path_1.join)('/Users/x', 'Library', 'Application Support', 'hammerkit'));
50
- });
51
- });
52
- it('uses ~/.hammerkit on Linux', () => {
53
- withPlatform('linux', { HOME: '/home/x' }, () => {
54
- expect((0, get_cache_directory_1.getHammerkitDirectory)()).toBe((0, path_1.join)('/home/x', '.hammerkit'));
55
- });
56
- });
57
- });
58
- });
59
- //# sourceMappingURL=get-cache-directory.spec.js.map
@@ -1,79 +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 get_work_cache_stats_1 = require("./get-work-cache-stats");
7
- const environment_mock_1 = require("../executer/environment-mock");
8
- function stats(files) {
9
- return { created: new Date(0), files };
10
- }
11
- describe('getStateKey', () => {
12
- const sample = stats({
13
- 'a.txt': { lastModified: 1000, checksum: 'aaa' },
14
- 'b.txt': { lastModified: 2000, checksum: 'bbb' },
15
- });
16
- it('is deterministic for identical stats', () => {
17
- expect((0, get_work_cache_stats_1.getStateKey)(sample, 'checksum')).toBe((0, get_work_cache_stats_1.getStateKey)(sample, 'checksum'));
18
- });
19
- it('produces different keys for checksum vs modify-date', () => {
20
- expect((0, get_work_cache_stats_1.getStateKey)(sample, 'checksum')).not.toBe((0, get_work_cache_stats_1.getStateKey)(sample, 'modify-date'));
21
- });
22
- it('changes when a checksum changes (checksum method)', () => {
23
- const changed = stats({
24
- 'a.txt': { lastModified: 1000, checksum: 'aaa' },
25
- 'b.txt': { lastModified: 2000, checksum: 'CHANGED' },
26
- });
27
- expect((0, get_work_cache_stats_1.getStateKey)(changed, 'checksum')).not.toBe((0, get_work_cache_stats_1.getStateKey)(sample, 'checksum'));
28
- });
29
- it('ignores checksum changes when using modify-date', () => {
30
- const changed = stats({
31
- 'a.txt': { lastModified: 1000, checksum: 'aaa' },
32
- 'b.txt': { lastModified: 2000, checksum: 'CHANGED' },
33
- });
34
- expect((0, get_work_cache_stats_1.getStateKey)(changed, 'modify-date')).toBe((0, get_work_cache_stats_1.getStateKey)(sample, 'modify-date'));
35
- });
36
- it('returns a constant key for method "none" regardless of files', () => {
37
- expect((0, get_work_cache_stats_1.getStateKey)(sample, 'none')).toBe((0, get_work_cache_stats_1.getStateKey)(stats({}), 'none'));
38
- });
39
- });
40
- describe('getWorkCacheStats', () => {
41
- let dir;
42
- beforeEach(() => {
43
- dir = (0, fs_1.mkdtempSync)((0, path_1.join)((0, os_1.tmpdir)(), 'hammerkit-stats-'));
44
- });
45
- afterEach(() => {
46
- (0, fs_1.rmSync)(dir, { recursive: true, force: true });
47
- });
48
- function src(absolutePath, matcher = () => true) {
49
- return { absolutePath, source: absolutePath, matcher };
50
- }
51
- it('returns empty files when src has no entries', async () => {
52
- const env = (0, environment_mock_1.environmentMock)(dir);
53
- const stats = await (0, get_work_cache_stats_1.getWorkCacheStats)({ cwd: dir, src: [] }, env);
54
- expect(stats.files).toEqual({});
55
- });
56
- it('skips a src path that does not exist (early return)', async () => {
57
- const env = (0, environment_mock_1.environmentMock)(dir);
58
- const stats = await (0, get_work_cache_stats_1.getWorkCacheStats)({ cwd: dir, src: [src((0, path_1.join)(dir, 'missing'))] }, env);
59
- expect(stats.files).toEqual({});
60
- });
61
- it('records a single file with checksum and lastModified', async () => {
62
- (0, fs_1.writeFileSync)((0, path_1.join)(dir, 'a.txt'), 'hello');
63
- const env = (0, environment_mock_1.environmentMock)(dir);
64
- const stats = await (0, get_work_cache_stats_1.getWorkCacheStats)({ cwd: dir, src: [src((0, path_1.join)(dir, 'a.txt'))] }, env);
65
- expect(stats.files['a.txt']).toBeDefined();
66
- expect(stats.files['a.txt'].checksum).toMatch(/^[0-9a-f]+$/);
67
- expect(stats.files['a.txt'].lastModified).toBeGreaterThan(0);
68
- });
69
- it('walks a directory recursively and respects the matcher', async () => {
70
- (0, fs_1.mkdirSync)((0, path_1.join)(dir, 'sub'));
71
- (0, fs_1.writeFileSync)((0, path_1.join)(dir, 'sub', 'keep.txt'), 'k');
72
- (0, fs_1.writeFileSync)((0, path_1.join)(dir, 'sub', 'skip.md'), 's');
73
- const env = (0, environment_mock_1.environmentMock)(dir);
74
- const stats = await (0, get_work_cache_stats_1.getWorkCacheStats)({ cwd: dir, src: [src((0, path_1.join)(dir, 'sub'), (file) => file.endsWith('.txt'))] }, env);
75
- expect(Object.keys(stats.files)).toContain((0, path_1.join)('sub', 'keep.txt'));
76
- expect(Object.keys(stats.files)).not.toContain((0, path_1.join)('sub', 'skip.md'));
77
- });
78
- });
79
- //# sourceMappingURL=get-work-cache-stats.spec.js.map
@@ -1,45 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const os_1 = require("os");
4
- const work_task_cache_description_1 = require("./work-task-cache-description");
5
- const baseTask = {
6
- name: 'build',
7
- cwd: '/work',
8
- description: null,
9
- src: [{ source: 'b' }, { source: 'a' }],
10
- generates: [{ path: 'out/b' }, { path: 'out/a' }],
11
- envs: { variables: { B: '2', A: '1' }, replacements: [] },
12
- cmds: [{ cwd: '/work', cmd: 'tsc' }],
13
- scope: {},
14
- labels: {},
15
- shell: '/bin/sh',
16
- caching: {},
17
- continuous: false,
18
- };
19
- describe('getWorkTaskCacheDescription', () => {
20
- it('describes a container task with image as platform and sorted mounts', () => {
21
- const task = {
22
- ...baseTask,
23
- type: 'container-task',
24
- image: 'node:alpine',
25
- user: null,
26
- mounts: [{ mount: 'b:b' }, { mount: 'a:a' }],
27
- };
28
- const d = (0, work_task_cache_description_1.getWorkTaskCacheDescription)(task);
29
- expect(d.platform).toBe('node:alpine');
30
- expect(d.mounts).toEqual(['a:a', 'b:b']);
31
- expect(d.src).toEqual(['a', 'b']);
32
- expect(d.generates).toEqual(['out/a', 'out/b']);
33
- expect(Object.keys(d.envs)).toEqual(['A', 'B']); // sorted
34
- expect(d.cmds).toEqual([{ cwd: '/work', cmd: 'tsc' }]);
35
- expect(d.cwd).toBe('/work');
36
- expect(d.shell).toBe('/bin/sh');
37
- });
38
- it('describes a local task with the host platform and no mounts', () => {
39
- const task = { ...baseTask, type: 'local-task' };
40
- const d = (0, work_task_cache_description_1.getWorkTaskCacheDescription)(task);
41
- expect(d.platform).toBe((0, os_1.platform)());
42
- expect(d.mounts).toBeUndefined();
43
- });
44
- });
45
- //# sourceMappingURL=work-task-cache-description.spec.js.map
@@ -1,48 +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 default_build_file_1 = require("./default-build-file");
8
- describe('getBuildFilename', () => {
9
- let dir;
10
- beforeEach(() => {
11
- dir = (0, fs_1.mkdtempSync)((0, path_1.join)((0, os_1.tmpdir)(), 'hammerkit-getbuildfilename-'));
12
- });
13
- afterEach(() => {
14
- (0, fs_1.rmSync)(dir, { recursive: true, force: true });
15
- });
16
- it('returns the path itself when it points at an existing file', async () => {
17
- const file = (0, path_1.join)(dir, '.hammerkit.yaml');
18
- (0, fs_1.writeFileSync)(file, 'tasks: {}\n');
19
- expect(await (0, default_build_file_1.getBuildFilename)(file, (0, environment_mock_1.environmentMock)(dir))).toBe(file);
20
- });
21
- it('falls back to <dir>/.hammerkit.yaml when the directory has no build file', async () => {
22
- expect(await (0, default_build_file_1.getBuildFilename)(dir, (0, environment_mock_1.environmentMock)(dir))).toBe((0, path_1.join)(dir, '.hammerkit.yaml'));
23
- });
24
- it('picks an existing build file in the directory', async () => {
25
- (0, fs_1.writeFileSync)((0, path_1.join)(dir, 'build.yaml'), 'tasks: {}\n');
26
- expect(await (0, default_build_file_1.getBuildFilename)(dir, (0, environment_mock_1.environmentMock)(dir))).toBe((0, path_1.join)(dir, 'build.yaml'));
27
- });
28
- it('prefers .hammerkit.yaml when multiple build files coexist and warns', async () => {
29
- (0, fs_1.writeFileSync)((0, path_1.join)(dir, '.hammerkit.yaml'), 'tasks: {}\n');
30
- (0, fs_1.writeFileSync)((0, path_1.join)(dir, 'build.yaml'), 'tasks: {}\n');
31
- const env = (0, environment_mock_1.environmentMock)(dir);
32
- const warn = vi.spyOn(env.console, 'warn').mockImplementation(() => undefined);
33
- const result = await (0, default_build_file_1.getBuildFilename)(dir, env);
34
- expect(result).toBe((0, path_1.join)(dir, '.hammerkit.yaml'));
35
- expect(warn).toHaveBeenCalledWith(expect.stringContaining('multiple hammerkit files'));
36
- });
37
- it('handles a missing path by returning <path>/.hammerkit.yaml', async () => {
38
- const missing = (0, path_1.join)(dir, 'does-not-exist');
39
- expect(await (0, default_build_file_1.getBuildFilename)(missing, (0, environment_mock_1.environmentMock)(dir))).toBe((0, path_1.join)(missing, '.hammerkit.yaml'));
40
- });
41
- it('ignores a path that exists but is neither a file nor a build dir match', async () => {
42
- const sub = (0, path_1.join)(dir, 'sub');
43
- (0, fs_1.mkdirSync)(sub);
44
- // empty directory exists but has no build files; falls back to default
45
- expect(await (0, default_build_file_1.getBuildFilename)(sub, (0, environment_mock_1.environmentMock)(dir))).toBe((0, path_1.join)(sub, '.hammerkit.yaml'));
46
- });
47
- });
48
- //# sourceMappingURL=default-build-file.spec.js.map
@@ -1,27 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const parse_label_arguments_1 = require("./parse-label-arguments");
4
- describe('parse-label-arguments', () => {
5
- it('parses a single key=value argument', () => {
6
- expect((0, parse_label_arguments_1.parseLabelArguments)(['app=web'])).toEqual({ app: ['web'] });
7
- });
8
- it('collects multiple values for the same key', () => {
9
- expect((0, parse_label_arguments_1.parseLabelArguments)(['app=web', 'app=api'])).toEqual({ app: ['web', 'api'] });
10
- });
11
- it('dedupes repeated values', () => {
12
- expect((0, parse_label_arguments_1.parseLabelArguments)(['app=web', 'app=web'])).toEqual({ app: ['web'] });
13
- });
14
- it('keeps distinct keys separate', () => {
15
- expect((0, parse_label_arguments_1.parseLabelArguments)(['app=web', 'tier=frontend'])).toEqual({ app: ['web'], tier: ['frontend'] });
16
- });
17
- it('returns an empty object for no arguments', () => {
18
- expect((0, parse_label_arguments_1.parseLabelArguments)([])).toEqual({});
19
- });
20
- it('throws when an argument has no value', () => {
21
- expect(() => (0, parse_label_arguments_1.parseLabelArguments)(['app'])).toThrow('invalid label app');
22
- });
23
- it('throws when an argument has multiple equals signs', () => {
24
- expect(() => (0, parse_label_arguments_1.parseLabelArguments)(['app=web=extra'])).toThrow('invalid label app=web=extra');
25
- });
26
- });
27
- //# sourceMappingURL=parse-label-arguments.spec.js.map
@@ -1,89 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || function (mod) {
19
- if (mod && mod.__esModule) return mod;
20
- var result = {};
21
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
- __setModuleDefault(result, mod);
23
- return result;
24
- };
25
- Object.defineProperty(exports, "__esModule", { value: true });
26
- const path_1 = require("path");
27
- const os_1 = require("os");
28
- const fs_1 = require("fs");
29
- const yaml = __importStar(require("yaml"));
30
- const environment_mock_1 = require("../executer/environment-mock");
31
- const read_build_file_1 = require("./read-build-file");
32
- // Re-export the real module through a plain (mutable) object so a single test can
33
- // spyOn parse. A true ESM namespace has non-configurable exports that vi.spyOn
34
- // cannot redefine; the spread keeps the real parse/stringify for every other test.
35
- vi.mock('yaml', async () => ({ ...(await vi.importActual('yaml')) }));
36
- describe('read-build-file', () => {
37
- let scratch;
38
- beforeEach(() => {
39
- scratch = (0, fs_1.mkdtempSync)((0, path_1.join)((0, os_1.tmpdir)(), 'hammerkit-read-'));
40
- });
41
- afterEach(() => {
42
- (0, fs_1.rmSync)(scratch, { recursive: true, force: true });
43
- });
44
- describe('read', () => {
45
- it('parses a yaml build file into an object', async () => {
46
- const env = (0, environment_mock_1.environmentMock)(scratch);
47
- const file = (0, path_1.join)(scratch, '.hammerkit.yaml');
48
- (0, fs_1.writeFileSync)(file, 'tasks:\n build:\n cmds:\n - echo hi\n');
49
- const result = await (0, read_build_file_1.read)(file, env);
50
- expect(result.tasks.build.cmds).toEqual(['echo hi']);
51
- });
52
- it('throws a readable error for a missing file', async () => {
53
- const env = (0, environment_mock_1.environmentMock)(scratch);
54
- await expect((0, read_build_file_1.read)((0, path_1.join)(scratch, 'does-not-exist.yaml'), env)).rejects.toThrow(/unable to read/);
55
- });
56
- it('throws a readable error for malformed yaml', async () => {
57
- const env = (0, environment_mock_1.environmentMock)(scratch);
58
- const file = (0, path_1.join)(scratch, '.hammerkit.yaml');
59
- // tabs are not valid yaml indentation
60
- (0, fs_1.writeFileSync)(file, 'tasks:\n\t- build\n');
61
- await expect((0, read_build_file_1.read)(file, env)).rejects.toThrow(/unable to parse/);
62
- });
63
- it('rethrows non-Error throws from the yaml parser unchanged', async () => {
64
- // Stub yaml.parse to throw a non-Error value so the `else throw e` branch
65
- // in read() is exercised (the wrap-with-message branch only handles Error).
66
- const spy = vi.spyOn(yaml, 'parse').mockImplementation(() => {
67
- // eslint-disable-next-line @typescript-eslint/no-throw-literal
68
- throw 'a plain string';
69
- });
70
- try {
71
- const env = (0, environment_mock_1.environmentMock)(scratch);
72
- const file = (0, path_1.join)(scratch, '.hammerkit.yaml');
73
- (0, fs_1.writeFileSync)(file, 'x: 1\n');
74
- await expect((0, read_build_file_1.read)(file, env)).rejects.toBe('a plain string');
75
- }
76
- finally {
77
- spy.mockRestore();
78
- }
79
- });
80
- });
81
- describe('write', () => {
82
- it('serializes the value as yaml via the environment file context', async () => {
83
- const env = (0, environment_mock_1.environmentMock)(scratch);
84
- await (0, read_build_file_1.write)((0, path_1.join)(scratch, 'out.yaml'), { tasks: { hi: { cmds: ['echo hi'] } } }, env);
85
- expect((0, fs_1.readFileSync)((0, path_1.join)(scratch, 'out.yaml'), 'utf8')).toMatch(/tasks:\n\s+hi:\n\s+cmds:\n\s+- echo hi/);
86
- });
87
- });
88
- });
89
- //# sourceMappingURL=read-build-file.spec.js.map
@@ -1,42 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const path_1 = require("path");
4
- const read_env_file_1 = require("./read-env-file");
5
- // Build an Environment whose file context serves .env files from an in-memory map
6
- // keyed by directory. readEnvFile asks for `join(<dir>, '.env')`, so build the
7
- // keys the same way (native separators) — keeps the test correct on Windows too.
8
- function envWithFiles(dirs) {
9
- const files = {};
10
- for (const [dir, content] of Object.entries(dirs)) {
11
- files[(0, path_1.join)(dir, '.env')] = content;
12
- }
13
- return {
14
- file: {
15
- exists: vi.fn(async (p) => p in files),
16
- read: vi.fn(async (p) => files[p]),
17
- },
18
- };
19
- }
20
- describe('read-env-file', () => {
21
- it('parses KEY=VALUE pairs from a .env file', async () => {
22
- const env = envWithFiles({ proj: 'A=1\nB=2' });
23
- expect(await (0, read_env_file_1.readEnvFile)('proj', env)).toEqual({ A: '1', B: '2' });
24
- });
25
- it('keeps "=" characters in the value and skips lines without "="', async () => {
26
- const env = envWithFiles({ proj: 'URL=http://x?y=z\nNOEQ\n\nC=3' });
27
- expect(await (0, read_env_file_1.readEnvFile)('proj', env)).toEqual({ URL: 'http://x?y=z', C: '3' });
28
- });
29
- it('does not overwrite values already present in baseEnv', async () => {
30
- const env = envWithFiles({ proj: 'A=fromfile\nB=2' });
31
- expect(await (0, read_env_file_1.readEnvFile)('proj', env, { A: 'base' })).toEqual({ A: 'base', B: '2' });
32
- });
33
- it('returns the base env when no .env file exists', async () => {
34
- const env = envWithFiles({});
35
- expect(await (0, read_env_file_1.readEnvFile)('proj', env, { X: 'y' })).toEqual({ X: 'y' });
36
- });
37
- it('reads the basename .env first, then the full path', async () => {
38
- const env = envWithFiles({ b: 'FROM_B=1', 'a/b': 'FROM_AB=2' });
39
- expect(await (0, read_env_file_1.readEnvFile)('a/b', env)).toEqual({ FROM_B: '1', FROM_AB: '2' });
40
- });
41
- });
42
- //# sourceMappingURL=read-env-file.spec.js.map
@@ -1,71 +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 schema_parser_1 = require("../../schema/schema-parser");
8
- const reference_parser_1 = require("../../schema/reference-parser");
9
- const work_scope_parser_1 = require("../../schema/work-scope-parser");
10
- describe('appendWorkService (kubernetes service env inheritance)', () => {
11
- let scratch;
12
- beforeEach(() => {
13
- scratch = (0, fs_1.mkdtempSync)((0, path_1.join)((0, os_1.tmpdir)(), 'hammerkit-k8s-svc-'));
14
- });
15
- afterEach(() => {
16
- (0, fs_1.rmSync)(scratch, { recursive: true, force: true });
17
- });
18
- async function parse(content, environmentName) {
19
- const env = (0, environment_mock_1.environmentMock)(scratch);
20
- const file = (0, path_1.join)(scratch, '.hammerkit.yaml');
21
- (0, fs_1.writeFileSync)(file, content);
22
- const { ctx, scope } = await (0, schema_parser_1.createParseContext)(file, env);
23
- const referenced = await (0, reference_parser_1.parseReferences)(ctx, scope, env);
24
- const workScope = { environmentName, filterLabels: {}, excludeLabels: {} };
25
- return { referenced, env, workScope };
26
- }
27
- const envBlock = `environments:
28
- prod:
29
- kubernetes:
30
- context: gke_prod
31
- kubeconfig: ./prod-kube.yaml
32
- namespace: databases
33
- `;
34
- function postgres(extra = '') {
35
- return `services:
36
- postgres:
37
- ${extra} selector:
38
- type: deployment
39
- name: postgres
40
- ports:
41
- - 5432:5432
42
- `;
43
- }
44
- it('inherits context, kubeconfig and namespace from the selected environment', async () => {
45
- const { referenced, env, workScope } = await parse(envBlock + postgres(), 'prod');
46
- const work = (0, work_scope_parser_1.getWorkContext)(referenced, workScope, env);
47
- const service = work.services['postgres'].data;
48
- expect(service.type).toEqual('kubernetes-service');
49
- expect(service.context).toEqual('gke_prod');
50
- expect(service.kubeconfig).toEqual('./prod-kube.yaml');
51
- expect(service.namespace).toEqual('databases');
52
- });
53
- it('lets a service override the namespace while inheriting the context', async () => {
54
- const { referenced, env, workScope } = await parse(envBlock + postgres(' namespace: orders\n'), 'prod');
55
- const work = (0, work_scope_parser_1.getWorkContext)(referenced, workScope, env);
56
- const service = work.services['postgres'].data;
57
- expect(service.namespace).toEqual('orders');
58
- expect(service.context).toEqual('gke_prod');
59
- });
60
- it('keeps an explicit service context over the environment (backward compatible)', async () => {
61
- const { referenced, env, workScope } = await parse(envBlock + postgres(' context: own-ctx\n'), 'prod');
62
- const work = (0, work_scope_parser_1.getWorkContext)(referenced, workScope, env);
63
- const service = work.services['postgres'].data;
64
- expect(service.context).toEqual('own-ctx');
65
- });
66
- it('throws a descriptive error when no context can be resolved', async () => {
67
- const { referenced, env, workScope } = await parse(postgres(), null);
68
- expect(() => (0, work_scope_parser_1.getWorkContext)(referenced, workScope, env)).toThrow(/postgres has no context/);
69
- });
70
- });
71
- //# sourceMappingURL=append-work-service.spec.js.map