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,251 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const validate_1 = require("./validate");
4
- function makeTask(name) {
5
- const item = {
6
- id: () => name,
7
- name,
8
- status: {},
9
- data: { type: 'local-task', name },
10
- needs: [],
11
- deps: [],
12
- requiredBy: [],
13
- state: {},
14
- runtime: {},
15
- };
16
- return item;
17
- }
18
- function makeService(name) {
19
- const item = {
20
- id: () => name,
21
- name,
22
- status: {},
23
- data: { type: 'container-service', name },
24
- needs: [],
25
- deps: [],
26
- requiredBy: [],
27
- state: {},
28
- runtime: {},
29
- };
30
- return item;
31
- }
32
- describe('validate', () => {
33
- describe('hasDependencyCycle', () => {
34
- it('returns null for an item with no deps', () => {
35
- const t = makeTask('a');
36
- expect((0, validate_1.hasDependencyCycle)(t, [])).toBeNull();
37
- });
38
- it('returns null for a linear chain', () => {
39
- const a = makeTask('a');
40
- const b = makeTask('b');
41
- const c = makeTask('c');
42
- a.deps.push(b);
43
- b.deps.push(c);
44
- expect((0, validate_1.hasDependencyCycle)(a, [])).toBeNull();
45
- });
46
- it('detects a direct self-cycle', () => {
47
- const a = makeTask('a');
48
- a.deps.push(a);
49
- const cycle = (0, validate_1.hasDependencyCycle)(a, []);
50
- expect(cycle).not.toBeNull();
51
- expect(cycle.map((i) => i.name)).toEqual(['a', 'a']);
52
- });
53
- it('detects an indirect cycle', () => {
54
- const a = makeTask('a');
55
- const b = makeTask('b');
56
- const c = makeTask('c');
57
- a.deps.push(b);
58
- b.deps.push(c);
59
- c.deps.push(a);
60
- const cycle = (0, validate_1.hasDependencyCycle)(a, []);
61
- expect(cycle).not.toBeNull();
62
- expect(cycle.map((i) => i.name)).toEqual(['a', 'b', 'c', 'a']);
63
- });
64
- it('does not flag a diamond as a cycle', () => {
65
- const root = makeTask('root');
66
- const left = makeTask('left');
67
- const right = makeTask('right');
68
- const leaf = makeTask('leaf');
69
- root.deps.push(left, right);
70
- left.deps.push(leaf);
71
- right.deps.push(leaf);
72
- expect((0, validate_1.hasDependencyCycle)(root, [])).toBeNull();
73
- });
74
- });
75
- describe('hasNeedCycle', () => {
76
- it('returns null when no needs', () => {
77
- const s = makeService('db');
78
- expect((0, validate_1.hasNeedCycle)(s, [])).toBeNull();
79
- });
80
- it('detects a need cycle across services', () => {
81
- const a = makeService('a');
82
- const b = makeService('b');
83
- a.needs.push({ name: 'b', service: b });
84
- b.needs.push({ name: 'a', service: a });
85
- const cycle = (0, validate_1.hasNeedCycle)(a, []);
86
- expect(cycle).not.toBeNull();
87
- expect(cycle.map((i) => i.name)).toEqual(['a', 'b', 'a']);
88
- });
89
- it('detects a self-need cycle', () => {
90
- const a = makeService('a');
91
- a.needs.push({ name: 'a', service: a });
92
- const cycle = (0, validate_1.hasNeedCycle)(a, []);
93
- expect(cycle).not.toBeNull();
94
- expect(cycle.map((i) => i.name)).toEqual(['a', 'a']);
95
- });
96
- });
97
- describe('hasMixedCycle', () => {
98
- it('returns null for an acyclic task → service → task chain', () => {
99
- const t1 = makeTask('build');
100
- const s = makeService('db');
101
- const t2 = makeTask('migrate');
102
- t1.needs.push({ name: 'db', service: s });
103
- s.deps.push(t2);
104
- expect((0, validate_1.hasMixedCycle)(t1, [])).toBeNull();
105
- });
106
- it('detects a cycle that alternates deps and needs', () => {
107
- const t = makeTask('api-build');
108
- const s = makeService('api');
109
- t.needs.push({ name: 'api', service: s });
110
- s.deps.push(t);
111
- const cycle = (0, validate_1.hasMixedCycle)(t, []);
112
- expect(cycle).not.toBeNull();
113
- expect(cycle.map((i) => i.name)).toEqual(['api-build', 'api', 'api-build']);
114
- });
115
- it('detects a long mixed cycle', () => {
116
- const a = makeTask('a');
117
- const b = makeService('b');
118
- const c = makeTask('c');
119
- const d = makeService('d');
120
- a.needs.push({ name: 'b', service: b });
121
- b.deps.push(c);
122
- c.needs.push({ name: 'd', service: d });
123
- d.deps.push(a);
124
- const cycle = (0, validate_1.hasMixedCycle)(a, []);
125
- expect(cycle).not.toBeNull();
126
- expect(cycle.map((i) => i.name)).toEqual(['a', 'b', 'c', 'd', 'a']);
127
- });
128
- });
129
- });
130
- function taskItem(name, data) {
131
- const item = {
132
- id: () => name,
133
- name,
134
- status: {},
135
- data: { type: 'local-task', name, description: 'a task', cmds: [{}], src: [], ...data },
136
- needs: [],
137
- deps: [],
138
- requiredBy: [],
139
- state: {},
140
- runtime: {},
141
- };
142
- return item;
143
- }
144
- function serviceItem(name, data) {
145
- const item = {
146
- id: () => name,
147
- name,
148
- status: {},
149
- data: { type: 'container-service', name, description: 'a service', healthcheck: {}, mounts: [], ...data },
150
- needs: [],
151
- deps: [],
152
- requiredBy: [],
153
- state: {},
154
- runtime: {},
155
- };
156
- return item;
157
- }
158
- function workTree(items) {
159
- return {
160
- tasks: Object.fromEntries((items.tasks ?? []).map((t) => [t.name, t])),
161
- services: Object.fromEntries((items.services ?? []).map((s) => [s.name, s])),
162
- environment: {},
163
- };
164
- }
165
- // Environment whose file.exists only returns true for the listed paths.
166
- function fakeEnv(existing = []) {
167
- return { file: { exists: vi.fn(async (p) => existing.includes(p)) } };
168
- }
169
- async function collect(gen) {
170
- const out = [];
171
- for await (const v of gen) {
172
- out.push(v);
173
- }
174
- return out;
175
- }
176
- describe('validate (generator)', () => {
177
- it('warns about a task without a description', async () => {
178
- const tree = workTree({ tasks: [taskItem('build', { description: null })] });
179
- const result = await collect((0, validate_1.validate)(tree, fakeEnv()));
180
- expect(result).toContainEqual(expect.objectContaining({ type: 'warn', message: 'missing description' }));
181
- });
182
- it('warns about an empty task (no cmds and no deps)', async () => {
183
- const tree = workTree({ tasks: [taskItem('noop', { cmds: [] })] });
184
- const result = await collect((0, validate_1.validate)(tree, fakeEnv()));
185
- expect(result).toContainEqual(expect.objectContaining({ type: 'warn', message: 'task is empty' }));
186
- });
187
- it('warns about a missing src path', async () => {
188
- const tree = workTree({ tasks: [taskItem('build', { src: [{ absolutePath: '/nope' }] })] });
189
- const result = await collect((0, validate_1.validate)(tree, fakeEnv([])));
190
- expect(result).toContainEqual(expect.objectContaining({ type: 'warn', message: 'src /nope does not exist' }));
191
- });
192
- it('does not warn when the src path exists', async () => {
193
- const tree = workTree({ tasks: [taskItem('build', { src: [{ absolutePath: '/yes' }] })] });
194
- const result = await collect((0, validate_1.validate)(tree, fakeEnv(['/yes'])));
195
- expect(result.map((r) => r.message)).not.toContain('src /yes does not exist');
196
- });
197
- it('emits a cycle error through the generator', async () => {
198
- const a = taskItem('a', {});
199
- a.deps.push(a);
200
- const tree = workTree({ tasks: [a] });
201
- const result = await collect((0, validate_1.validate)(tree, fakeEnv()));
202
- expect(result).toContainEqual(expect.objectContaining({ type: 'error', message: 'task cycle detected a -> a' }));
203
- });
204
- it('warns about a container service missing a healthcheck', async () => {
205
- const tree = workTree({ services: [serviceItem('db', { healthcheck: null })] });
206
- const result = await collect((0, validate_1.validate)(tree, fakeEnv()));
207
- expect(result).toContainEqual(expect.objectContaining({ type: 'warn', message: 'missing healthcheck' }));
208
- });
209
- it('warns about a container service mount that does not exist', async () => {
210
- const tree = workTree({
211
- services: [serviceItem('db', { mounts: [{ localPath: '/data' }] })],
212
- });
213
- const result = await collect((0, validate_1.validate)(tree, fakeEnv([])));
214
- expect(result).toContainEqual(expect.objectContaining({ type: 'warn', message: 'mount /data does not exist' }));
215
- });
216
- it('warns when a kubernetes service kubeconfig is missing', async () => {
217
- const tree = workTree({
218
- services: [
219
- serviceItem('cluster', {
220
- type: 'kubernetes-service',
221
- kubeconfig: '/missing/kubeconfig',
222
- context: 'prod',
223
- }),
224
- ],
225
- });
226
- const result = await collect((0, validate_1.validate)(tree, fakeEnv([])));
227
- expect(result).toContainEqual(expect.objectContaining({ type: 'warn', message: 'kubeconfig /missing/kubeconfig does not exist' }));
228
- });
229
- });
230
- describe('checkIfContextExists', () => {
231
- function kubeconfigEnv(yaml) {
232
- return {
233
- file: { read: vi.fn(async () => yaml) },
234
- status: { context: () => ({ write: vi.fn() }) },
235
- };
236
- }
237
- const service = { kubeconfig: '/kube/config', context: 'prod' };
238
- it('is true when the context is present', async () => {
239
- const env = kubeconfigEnv('contexts:\n - name: prod\n - name: dev\n');
240
- expect(await (0, validate_1.checkIfContextExists)(service, env)).toBe(true);
241
- });
242
- it('is false when the context is absent', async () => {
243
- const env = kubeconfigEnv('contexts:\n - name: dev\n');
244
- expect(await (0, validate_1.checkIfContextExists)(service, env)).toBe(false);
245
- });
246
- it('is false when there are no contexts', async () => {
247
- const env = kubeconfigEnv('clusters:\n - name: c1\n');
248
- expect(await (0, validate_1.checkIfContextExists)(service, env)).toBe(false);
249
- });
250
- });
251
- //# sourceMappingURL=validate.spec.js.map
@@ -1,173 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const test_case_1 = require("../testing/test-case");
4
- const program_1 = require("../program");
5
- const path_1 = require("path");
6
- async function compareTasks(first, second, taskName, expectEqual) {
7
- const testCase = (0, test_case_1.createTestCase)('work-task-id', {
8
- 'first.yaml': first,
9
- 'second.yaml': second,
10
- });
11
- await testCase.setup(async (cwd, environment) => {
12
- const firstCli = await (0, program_1.createCli)((0, path_1.join)(cwd, 'first.yaml'), environment, { taskName });
13
- const secondCli = await (0, program_1.createCli)((0, path_1.join)(cwd, 'second.yaml'), environment, { taskName });
14
- const firstTask = firstCli.task(taskName);
15
- const secondTask = secondCli.task(taskName);
16
- if (expectEqual) {
17
- expect(firstTask.id()).toEqual(secondTask.id());
18
- }
19
- else {
20
- expect(firstTask.id()).not.toEqual(secondTask.id());
21
- }
22
- });
23
- }
24
- describe('work-cache-id', () => {
25
- it('should be the same for different mount order', async () => {
26
- await compareTasks({
27
- tasks: {
28
- example: {
29
- image: 'node',
30
- mounts: ['$PWD/.npm:/.npm', '$PWD/.config:/.config'],
31
- },
32
- },
33
- }, {
34
- tasks: {
35
- example: {
36
- image: 'node',
37
- mounts: ['$PWD/.config:/.config', '$PWD/.npm:/.npm'],
38
- },
39
- },
40
- }, 'example', true);
41
- });
42
- it('should be the same for different object key order', async () => {
43
- await compareTasks({
44
- tasks: {
45
- example: {
46
- image: 'node:alpine',
47
- cmds: ['npm ci'],
48
- },
49
- },
50
- }, {
51
- tasks: {
52
- example: {
53
- cmds: ['npm ci'],
54
- image: 'node:alpine',
55
- },
56
- },
57
- }, 'example', true);
58
- });
59
- it('should be the same with additional description', async () => {
60
- await compareTasks({
61
- tasks: {
62
- example: {
63
- image: 'node:alpine',
64
- cmds: ['npm ci'],
65
- },
66
- },
67
- }, {
68
- tasks: {
69
- example: {
70
- image: 'node:alpine',
71
- cmds: ['npm ci'],
72
- description: 'should not matter',
73
- },
74
- },
75
- }, 'example', true);
76
- });
77
- it('should be not be the same for different images', async () => {
78
- await compareTasks({
79
- tasks: {
80
- example: {
81
- image: 'node:alpine',
82
- },
83
- },
84
- }, {
85
- tasks: {
86
- example: {
87
- image: 'node',
88
- },
89
- },
90
- }, 'example', false);
91
- });
92
- it('should be not be the same for different cmd order', async () => {
93
- await compareTasks({
94
- tasks: {
95
- example: {
96
- cmds: ['npm ci', 'npm run build'],
97
- },
98
- },
99
- }, {
100
- tasks: {
101
- example: {
102
- cmds: ['npm run build', 'npm ci'],
103
- },
104
- },
105
- }, 'example', false);
106
- });
107
- it('should be be the same for different env key order', async () => {
108
- await compareTasks({
109
- tasks: {
110
- example: {
111
- envs: {
112
- NODE_VERSION: '16.6.0',
113
- NPM_VERSION: '6.0.0',
114
- },
115
- cmds: ['npm ci', 'npm run build'],
116
- },
117
- },
118
- }, {
119
- tasks: {
120
- example: {
121
- envs: {
122
- NPM_VERSION: '6.0.0',
123
- NODE_VERSION: '16.6.0',
124
- },
125
- cmds: ['npm ci', 'npm run build'],
126
- },
127
- },
128
- }, 'example', true);
129
- });
130
- it('should be be the same for deps in different order', async () => {
131
- await compareTasks({
132
- tasks: {
133
- install: {
134
- cmds: ['npm install'],
135
- },
136
- build: {
137
- cmds: ['tsc'],
138
- },
139
- example: {
140
- deps: ['install', 'build'],
141
- },
142
- },
143
- }, {
144
- tasks: {
145
- install: {
146
- cmds: ['npm install'],
147
- },
148
- build: {
149
- cmds: ['tsc'],
150
- },
151
- example: {
152
- deps: ['build', 'install'],
153
- },
154
- },
155
- }, 'example', true);
156
- });
157
- it('should be be the same for sources in different order', async () => {
158
- await compareTasks({
159
- tasks: {
160
- example: {
161
- src: ['package.json', 'package-lock.json'],
162
- },
163
- },
164
- }, {
165
- tasks: {
166
- example: {
167
- src: ['package-lock.json', 'package.json'],
168
- },
169
- },
170
- }, 'example', true);
171
- });
172
- });
173
- //# sourceMappingURL=work-cache-id.spec.js.map
@@ -1,193 +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 work_runtime_local_1 = require("./work-runtime-local");
7
- const environment_mock_1 = require("../executer/environment-mock");
8
- const state_1 = require("../executer/state");
9
- const mockFindProcess = vi.hoisted(() => vi.fn());
10
- vi.mock('find-process', () => ({
11
- __esModule: true,
12
- default: (...args) => mockFindProcess(...args),
13
- }));
14
- vi.mock('../executer/local-task', () => ({
15
- localTask: vi.fn().mockResolvedValue(undefined),
16
- }));
17
- function makeTask(cwd) {
18
- return {
19
- id: () => 'task-1',
20
- name: 'task-1',
21
- status: { write: vi.fn() },
22
- data: {
23
- type: 'local-task',
24
- name: 'task-1',
25
- cwd,
26
- cmds: [],
27
- generates: [],
28
- src: [],
29
- envs: { variables: {}, processEnvs: {} },
30
- labels: {},
31
- shell: '/bin/sh',
32
- continuous: false,
33
- caching: {
34
- name: 'none',
35
- method: 'none',
36
- backend: {
37
- type: 'local',
38
- has: async () => false,
39
- pull: async () => false,
40
- push: async () => { },
41
- clear: async () => { },
42
- },
43
- implicit: true,
44
- },
45
- description: null,
46
- scope: {},
47
- },
48
- needs: [],
49
- deps: [],
50
- requiredBy: [],
51
- };
52
- }
53
- describe('local task pidfile', () => {
54
- let cwd;
55
- beforeEach(() => {
56
- cwd = (0, fs_1.mkdtempSync)((0, path_1.join)((0, os_1.tmpdir)(), 'hammerkit-pidfile-'));
57
- mockFindProcess.mockReset();
58
- });
59
- afterEach(() => {
60
- (0, fs_1.rmSync)(cwd, { recursive: true, force: true });
61
- });
62
- it('writes pidfile during execute and cleans up after', async () => {
63
- const task = makeTask(cwd);
64
- const runtime = (0, work_runtime_local_1.getLocalWorkRuntime)(task);
65
- const env = (0, environment_mock_1.environmentMock)(cwd);
66
- const state = new state_1.State({ type: 'pending', stateKey: null });
67
- await runtime.execute(env, {
68
- stateKey: 'state-1',
69
- abort: new AbortController().signal,
70
- state,
71
- cache: {
72
- cached: false,
73
- stateKey: 'state-x',
74
- resolved: {
75
- name: 'none',
76
- method: 'none',
77
- backend: {
78
- type: 'local',
79
- has: async () => false,
80
- pull: async () => false,
81
- push: async () => { },
82
- clear: async () => { },
83
- },
84
- implicit: true,
85
- },
86
- },
87
- daemon: false,
88
- });
89
- expect(await env.file.exists((0, path_1.join)(cwd, '.hammerkit', 'task-1.pid'))).toBe(false);
90
- expect(await env.file.exists((0, path_1.join)(cwd, '.hammerkit', 'task-1'))).toBe(true);
91
- });
92
- it('removes a stale pidfile when the process is not alive', async () => {
93
- const task = makeTask(cwd);
94
- const runtime = (0, work_runtime_local_1.getLocalWorkRuntime)(task);
95
- const env = (0, environment_mock_1.environmentMock)(cwd);
96
- const state = new state_1.State({ type: 'pending', stateKey: null });
97
- await env.file.createDirectory((0, path_1.join)(cwd, '.hammerkit'));
98
- (0, fs_1.writeFileSync)((0, path_1.join)(cwd, '.hammerkit', 'task-1.pid'), '999999');
99
- mockFindProcess.mockResolvedValueOnce([]);
100
- await runtime.execute(env, {
101
- stateKey: 'state-2',
102
- abort: new AbortController().signal,
103
- state,
104
- cache: {
105
- cached: false,
106
- stateKey: 'state-x',
107
- resolved: {
108
- name: 'none',
109
- method: 'none',
110
- backend: {
111
- type: 'local',
112
- has: async () => false,
113
- pull: async () => false,
114
- push: async () => { },
115
- clear: async () => { },
116
- },
117
- implicit: true,
118
- },
119
- },
120
- daemon: false,
121
- });
122
- expect(state.current.type).not.toBe('error');
123
- expect(mockFindProcess).toHaveBeenCalledWith('pid', 999999);
124
- });
125
- it('refuses to run when the pidfile points at a live process', async () => {
126
- const task = makeTask(cwd);
127
- const runtime = (0, work_runtime_local_1.getLocalWorkRuntime)(task);
128
- const env = (0, environment_mock_1.environmentMock)(cwd);
129
- const state = new state_1.State({ type: 'pending', stateKey: null });
130
- await env.file.createDirectory((0, path_1.join)(cwd, '.hammerkit'));
131
- (0, fs_1.writeFileSync)((0, path_1.join)(cwd, '.hammerkit', 'task-1.pid'), '12345');
132
- mockFindProcess.mockResolvedValueOnce([{ pid: 12345, name: 'node' }]);
133
- await runtime.execute(env, {
134
- stateKey: 'state-3',
135
- abort: new AbortController().signal,
136
- state,
137
- cache: {
138
- cached: false,
139
- stateKey: 'state-x',
140
- resolved: {
141
- name: 'none',
142
- method: 'none',
143
- backend: {
144
- type: 'local',
145
- has: async () => false,
146
- pull: async () => false,
147
- push: async () => { },
148
- clear: async () => { },
149
- },
150
- implicit: true,
151
- },
152
- },
153
- daemon: false,
154
- });
155
- expect(state.current.type).toBe('error');
156
- if (state.current.type === 'error') {
157
- expect(state.current.errorMessage).toMatch(/already running.*12345/);
158
- }
159
- });
160
- it('does not block when the pidfile contains our own pid', async () => {
161
- const task = makeTask(cwd);
162
- const runtime = (0, work_runtime_local_1.getLocalWorkRuntime)(task);
163
- const env = (0, environment_mock_1.environmentMock)(cwd);
164
- const state = new state_1.State({ type: 'pending', stateKey: null });
165
- await env.file.createDirectory((0, path_1.join)(cwd, '.hammerkit'));
166
- (0, fs_1.writeFileSync)((0, path_1.join)(cwd, '.hammerkit', 'task-1.pid'), `${process.pid}`);
167
- await runtime.execute(env, {
168
- stateKey: 'state-4',
169
- abort: new AbortController().signal,
170
- state,
171
- cache: {
172
- cached: false,
173
- stateKey: 'state-x',
174
- resolved: {
175
- name: 'none',
176
- method: 'none',
177
- backend: {
178
- type: 'local',
179
- has: async () => false,
180
- pull: async () => false,
181
- push: async () => { },
182
- clear: async () => { },
183
- },
184
- implicit: true,
185
- },
186
- },
187
- daemon: false,
188
- });
189
- expect(state.current.type).not.toBe('error');
190
- expect(mockFindProcess).not.toHaveBeenCalled();
191
- });
192
- });
193
- //# sourceMappingURL=work-runtime-local.spec.js.map