hammerkit 1.6.0-alpha.3 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (256) hide show
  1. package/README.md +4 -4
  2. package/build.schema.json +49 -2
  3. package/dist/cache/backends/local-cache-backend.js +58 -1
  4. package/dist/cache/backends/registry/registry-client.js +264 -0
  5. package/dist/cache/backends/registry/registry-credentials.js +81 -0
  6. package/dist/cache/backends/registry/registry-reference.js +36 -0
  7. package/dist/cache/backends/registry-cache-backend.js +145 -0
  8. package/dist/cache/backends/s3-cache-backend.js +99 -46
  9. package/dist/cache/cache-backend-registry.js +2 -0
  10. package/dist/cache/cache-inventory.js +84 -0
  11. package/dist/cache/cache-sync.js +77 -0
  12. package/dist/cache/explain.js +214 -0
  13. package/dist/cache/last-resolved.js +48 -0
  14. package/dist/cache/read-only.js +15 -0
  15. package/dist/cache/retention.js +61 -0
  16. package/dist/cli.js +43 -0
  17. package/dist/docker/using-container.js +5 -1
  18. package/dist/executer/archive-task-entry.js +21 -0
  19. package/dist/executer/await-completed-dependencies.js +32 -14
  20. package/dist/executer/cache-metadata.js +9 -2
  21. package/dist/executer/docker-service.js +2 -1
  22. package/dist/executer/docker-task.js +2 -1
  23. package/dist/executer/dry-run.js +73 -0
  24. package/dist/executer/event-cache.js +6 -5
  25. package/dist/executer/execute-command.js +21 -3
  26. package/dist/executer/execute-work-service.js +10 -1
  27. package/dist/executer/execute-work-task.js +95 -25
  28. package/dist/executer/execute-work-tree.js +26 -1
  29. package/dist/executer/get-scheduler-execute-result.js +4 -1
  30. package/dist/executer/lazy-resolver.js +15 -3
  31. package/dist/executer/reset-work-tree.js +2 -0
  32. package/dist/executer/run-summary.js +102 -0
  33. package/dist/executer/scheduler/enqueue-next.js +15 -60
  34. package/dist/executer/scheduler/state-key.js +78 -0
  35. package/dist/kubernetes/await-running-state.js +73 -19
  36. package/dist/log.js +16 -6
  37. package/dist/logging/live-logger.js +2 -1
  38. package/dist/optimizer/calculate-checksum.js +8 -2
  39. package/dist/optimizer/get-work-cache-stats.js +66 -16
  40. package/dist/optimizer/work-task-cache-description.js +34 -5
  41. package/dist/planner/project-root.js +26 -0
  42. package/dist/planner/utils/append-work-service.js +2 -1
  43. package/dist/planner/utils/append-work-task.js +7 -1
  44. package/dist/planner/utils/parse-work-source.js +35 -30
  45. package/dist/planner/utils/portable-path.js +30 -0
  46. package/dist/planner/work-cache-id.js +2 -2
  47. package/dist/planner/work-graph-serializer.js +103 -0
  48. package/dist/planner/work-instance-id.js +15 -0
  49. package/dist/planner/work-runtime-docker.js +53 -15
  50. package/dist/planner/work-runtime-kubernetes.js +35 -3
  51. package/dist/planner/work-runtime-local.js +9 -3
  52. package/dist/planner/work-service-id.js +16 -4
  53. package/dist/program.js +235 -1
  54. package/dist/schema/build-file-base-task-schema.js +4 -0
  55. package/dist/schema/cache-schema.js +21 -2
  56. package/dist/schema/duration-schema.js +18 -0
  57. package/dist/schema/reference-parser.js +3 -0
  58. package/dist/schema/size-schema.js +18 -0
  59. package/dist/schema/work-scope-parser.js +15 -2
  60. package/dist/utils/units.js +87 -0
  61. package/package.json +15 -4
  62. package/.agents/FEEDBACK.md +0 -74
  63. package/.agents/skills/before-commit/SKILL.md +0 -101
  64. package/.agents/skills/do-the-work/SKILL.md +0 -64
  65. package/.agents/skills/fix-ci/SKILL.md +0 -98
  66. package/.agents/skills/speckit-git-commit/SKILL.md +0 -53
  67. package/.agents/skills/speckit-git-feature/SKILL.md +0 -72
  68. package/.agents/skills/speckit-git-initialize/SKILL.md +0 -54
  69. package/.agents/skills/speckit-git-remote/SKILL.md +0 -50
  70. package/.agents/skills/speckit-git-validate/SKILL.md +0 -54
  71. package/.devcontainer/Dockerfile +0 -5
  72. package/.devcontainer/devcontainer.json +0 -40
  73. package/.gitbook.yaml +0 -5
  74. package/.specify/extensions/.registry +0 -37
  75. package/.specify/extensions/git/README.md +0 -100
  76. package/.specify/extensions/git/commands/speckit.git.commit.md +0 -48
  77. package/.specify/extensions/git/commands/speckit.git.feature.md +0 -67
  78. package/.specify/extensions/git/commands/speckit.git.initialize.md +0 -49
  79. package/.specify/extensions/git/commands/speckit.git.remote.md +0 -45
  80. package/.specify/extensions/git/commands/speckit.git.validate.md +0 -49
  81. package/.specify/extensions/git/config-template.yml +0 -62
  82. package/.specify/extensions/git/extension.yml +0 -140
  83. package/.specify/extensions/git/git-config.yml +0 -62
  84. package/.specify/extensions.yml +0 -149
  85. package/.specify/feature.json +0 -3
  86. package/.specify/init-options.json +0 -10
  87. package/.specify/integration.json +0 -15
  88. package/.specify/integrations/claude.manifest.json +0 -16
  89. package/.specify/integrations/speckit.manifest.json +0 -17
  90. package/.specify/memory/constitution.md +0 -162
  91. package/.specify/templates/checklist-template.md +0 -40
  92. package/.specify/templates/constitution-template.md +0 -50
  93. package/.specify/templates/plan-template.md +0 -113
  94. package/.specify/templates/spec-template.md +0 -131
  95. package/.specify/templates/tasks-template.md +0 -252
  96. package/.specify/workflows/speckit/workflow.yml +0 -77
  97. package/.specify/workflows/workflow-registry.json +0 -13
  98. package/AGENTS.md +0 -94
  99. package/Dockerfile +0 -25
  100. package/FOLLOWUPS.md +0 -34
  101. package/SUMMARY.md +0 -68
  102. package/VALUES.md +0 -53
  103. package/dist/cache/auto-pull.spec.js +0 -123
  104. package/dist/cache/backends/local-cache-backend.spec.js +0 -72
  105. package/dist/cache/backends/s3-cache-backend.spec.js +0 -118
  106. package/dist/cache/cache-backend-registry.spec.js +0 -22
  107. package/dist/cache/resolve-cache.spec.js +0 -52
  108. package/dist/cli.spec.js +0 -23
  109. package/dist/console/get-logger.spec.js +0 -23
  110. package/dist/docker/get-default-platform.spec.js +0 -29
  111. package/dist/docker/longest-common-prefix.spec.js +0 -26
  112. package/dist/docker/package.spec.js +0 -145
  113. package/dist/docker/remove-container.spec.js +0 -32
  114. package/dist/docker/using-container.spec.js +0 -90
  115. package/dist/environment/merge-environment-variables.spec.js +0 -24
  116. package/dist/environment/replace-env-variables.spec.js +0 -47
  117. package/dist/executer/abort.spec.js +0 -18
  118. package/dist/executer/environment-mock.spec.js +0 -22
  119. package/dist/executer/execute.spec.js +0 -51
  120. package/dist/executer/get-service-containers.spec.js +0 -59
  121. package/dist/executer/get-service-env-hints.spec.js +0 -65
  122. package/dist/executer/label-values.spec.js +0 -61
  123. package/dist/executer/lazy-resolver.spec.js +0 -20
  124. package/dist/executer/process-manager.spec.js +0 -54
  125. package/dist/executer/scheduler/enqueue-next.spec.js +0 -136
  126. package/dist/executer/state-resolver.spec.js +0 -37
  127. package/dist/executer/state.spec.js +0 -66
  128. package/dist/executer/states.spec.js +0 -14
  129. package/dist/file/get-file-context.spec.js +0 -178
  130. package/dist/file/move-files.spec.js +0 -48
  131. package/dist/kubernetes/ensure-http-route.spec.js +0 -70
  132. package/dist/kubernetes/ensure-namespace.spec.js +0 -39
  133. package/dist/kubernetes/resolve-pod-name.spec.js +0 -81
  134. package/dist/log.spec.js +0 -131
  135. package/dist/logging/grouped-logger.spec.js +0 -84
  136. package/dist/logging/interactive-logger.spec.js +0 -57
  137. package/dist/logging/live-logger.spec.js +0 -37
  138. package/dist/logging/loggers.spec.js +0 -154
  139. package/dist/optimizer/calculate-checksum.spec.js +0 -34
  140. package/dist/optimizer/get-cache-directory.spec.js +0 -59
  141. package/dist/optimizer/get-work-cache-stats.spec.js +0 -79
  142. package/dist/optimizer/work-task-cache-description.spec.js +0 -45
  143. package/dist/parser/default-build-file.spec.js +0 -48
  144. package/dist/parser/parse-label-arguments.spec.js +0 -27
  145. package/dist/parser/read-build-file.spec.js +0 -89
  146. package/dist/parser/read-env-file.spec.js +0 -42
  147. package/dist/planner/utils/append-work-service.spec.js +0 -71
  148. package/dist/planner/utils/get-container-user.spec.js +0 -30
  149. package/dist/planner/utils/get-default-kube-config.spec.js +0 -11
  150. package/dist/planner/utils/normalize-path.spec.js +0 -24
  151. package/dist/planner/utils/parse-work-command.spec.js +0 -21
  152. package/dist/planner/utils/parse-work-generate.spec.js +0 -45
  153. package/dist/planner/utils/parse-work-mount.spec.js +0 -62
  154. package/dist/planner/utils/parse-work-mounts.spec.js +0 -19
  155. package/dist/planner/utils/parse-work-port.spec.js +0 -24
  156. package/dist/planner/utils/parse-work-ports.spec.js +0 -24
  157. package/dist/planner/utils/parse-work-source.spec.js +0 -58
  158. package/dist/planner/utils/parse-work-volume.spec.js +0 -18
  159. package/dist/planner/utils/plan-work-tasks.spec.js +0 -30
  160. package/dist/planner/utils/plan-work-volume.spec.js +0 -33
  161. package/dist/planner/utils/split-name.spec.js +0 -16
  162. package/dist/planner/utils/template-value.spec.js +0 -14
  163. package/dist/planner/validate.spec.js +0 -251
  164. package/dist/planner/work-cache-id.spec.js +0 -173
  165. package/dist/planner/work-runtime-local.spec.js +0 -193
  166. package/dist/program-commands.spec.js +0 -84
  167. package/dist/program.spec.js +0 -43
  168. package/dist/schema/build-file-schema.spec.js +0 -61
  169. package/dist/schema/doc-build-files.spec.js +0 -223
  170. package/dist/schema/parse-error.spec.js +0 -18
  171. package/dist/schema/reference-parser.spec.js +0 -83
  172. package/dist/schema/schema-parser.spec.js +0 -80
  173. package/dist/schema/type-guards.spec.js +0 -23
  174. package/dist/schema/work-scope-parser.spec.js +0 -66
  175. package/dist/service/update-service-status.spec.js +0 -27
  176. package/dist/testing/ensure-local-registry.js +0 -134
  177. package/dist/testing/example-test-suite.js +0 -63
  178. package/dist/testing/expect.js +0 -82
  179. package/dist/testing/get-test-suite.js +0 -9
  180. package/dist/testing/http-get.js +0 -21
  181. package/dist/testing/integration/cache.spec.js +0 -49
  182. package/dist/testing/integration/cancellation.spec.js +0 -45
  183. package/dist/testing/integration/clean.spec.js +0 -43
  184. package/dist/testing/integration/cmd.spec.js +0 -15
  185. package/dist/testing/integration/concurrency.spec.js +0 -19
  186. package/dist/testing/integration/docker-package-registry.spec.js +0 -44
  187. package/dist/testing/integration/docker-service-crash.spec.js +0 -36
  188. package/dist/testing/integration/docker.spec.js +0 -17
  189. package/dist/testing/integration/env.spec.js +0 -48
  190. package/dist/testing/integration/environment.spec.js +0 -97
  191. package/dist/testing/integration/error.spec.js +0 -19
  192. package/dist/testing/integration/export.spec.js +0 -22
  193. package/dist/testing/integration/extend.spec.js +0 -29
  194. package/dist/testing/integration/glob.spec.js +0 -40
  195. package/dist/testing/integration/include.spec.js +0 -31
  196. package/dist/testing/integration/invalid.spec.js +0 -17
  197. package/dist/testing/integration/invalid_loop.spec.js +0 -56
  198. package/dist/testing/integration/k8s-state-cache.spec.js +0 -41
  199. package/dist/testing/integration/kubernetes-service.spec.js +0 -148
  200. package/dist/testing/integration/kubernetes.spec.js +0 -99
  201. package/dist/testing/integration/labels.spec.js +0 -42
  202. package/dist/testing/integration/local.spec.js +0 -14
  203. package/dist/testing/integration/monorepo.spec.js +0 -15
  204. package/dist/testing/integration/multi-line.spec.js +0 -17
  205. package/dist/testing/integration/reference.spec.js +0 -21
  206. package/dist/testing/integration/services.spec.js +0 -38
  207. package/dist/testing/integration/store.spec.js +0 -52
  208. package/dist/testing/integration/unknown.spec.js +0 -28
  209. package/dist/testing/integration/validate.spec.js +0 -160
  210. package/dist/testing/integration/watch-dependency.spec.js +0 -33
  211. package/dist/testing/integration/watch.spec.js +0 -45
  212. package/dist/testing/requires-kubernetes.js +0 -18
  213. package/dist/testing/requires-linux-containers.js +0 -18
  214. package/dist/testing/requires-windows-containers.js +0 -18
  215. package/dist/testing/test-case.js +0 -75
  216. package/dist/testing/test-streams.js +0 -28
  217. package/dist/testing/test-suite-setup.js +0 -3
  218. package/dist/testing/test-suite.js +0 -3
  219. package/dist/testing/testing-timeout.js +0 -32
  220. package/dist/utils/abort-event.spec.js +0 -46
  221. package/dist/utils/buffer-context.spec.js +0 -38
  222. package/dist/utils/ci.spec.js +0 -53
  223. package/dist/utils/debouncer.spec.js +0 -58
  224. package/dist/utils/emitter.spec.js +0 -34
  225. package/dist/utils/empty-writable.spec.js +0 -18
  226. package/dist/utils/environment-config.spec.js +0 -26
  227. package/dist/utils/fail-never.spec.js +0 -9
  228. package/dist/utils/sleep.spec.js +0 -22
  229. package/dist/version.spec.js +0 -12
  230. package/specs/build-file/caches/spec.md +0 -114
  231. package/specs/build-file/environment-variables/spec.md +0 -82
  232. package/specs/build-file/includes/spec.md +0 -60
  233. package/specs/build-file/references/spec.md +0 -56
  234. package/specs/build-file/spec.md +0 -62
  235. package/specs/cli/clean/spec.md +0 -85
  236. package/specs/cli/down/spec.md +0 -85
  237. package/specs/cli/execute/spec.md +0 -101
  238. package/specs/cli/init/spec.md +0 -64
  239. package/specs/cli/ls/spec.md +0 -66
  240. package/specs/cli/package/spec.md +0 -110
  241. package/specs/cli/spec.md +0 -73
  242. package/specs/cli/store-restore/spec.md +0 -74
  243. package/specs/cli/up/spec.md +0 -80
  244. package/specs/cli/validate/spec.md +0 -70
  245. package/specs/labels/spec.md +0 -91
  246. package/specs/service/container/spec.md +0 -120
  247. package/specs/service/kubernetes/spec.md +0 -97
  248. package/specs/service/spec.md +0 -105
  249. package/specs/task/caching/spec.md +0 -107
  250. package/specs/task/container/spec.md +0 -118
  251. package/specs/task/dependencies/spec.md +0 -70
  252. package/specs/task/extending/spec.md +0 -74
  253. package/specs/task/kubernetes/spec.md +0 -125
  254. package/specs/task/needs/spec.md +0 -59
  255. package/specs/task/spec.md +0 -87
  256. package/specs/task/watching/spec.md +0 -69
@@ -1,101 +0,0 @@
1
- # Feature Specification: CLI: execute
2
-
3
- **Feature Branch**: `001-current-state-specs`
4
-
5
- **Created**: 2026-05-30
6
-
7
- **Status**: Current State (reverse-specified from docs, hammerkit 1.6.0)
8
-
9
- **Input**: Existing behavior documented in `docs/cli/execute.md`
10
-
11
- ## User Scenarios & Testing *(mandatory)*
12
-
13
- ### User Story 1 - Running a task by name (Priority: P1)
14
-
15
- A developer wants to build a specific artifact. They run `hammerkit <task-name>` (or `hammerkit run <task-name>`) and hammerkit executes that task and all its declared dependencies inside the appropriate containers.
16
-
17
- **Why this priority**: Executing tasks is the core purpose of the tool; all other features support this.
18
-
19
- **Independent Test**: Given a build file with a task named `example`, run `hammerkit example` and verify it completes successfully and produces the expected output.
20
-
21
- **Acceptance Scenarios**:
22
-
23
- 1. **Given** a build file with a task `build`, **When** the user runs `hammerkit build`, **Then** hammerkit executes the `build` task (and its dependencies) and exits with code 0 on success.
24
-
25
- 2. **Given** a build file with a task `build`, **When** the user runs `hammerkit run build`, **Then** the result is identical to running `hammerkit build`.
26
-
27
- 3. **Given** a task name that does not exist in the build file, **When** the user runs `hammerkit <nonexistent>`, **Then** hammerkit exits with a non-zero code and a meaningful error message.
28
-
29
- ### User Story 2 - Filtering tasks by labels (Priority: P2)
30
-
31
- A monorepo developer wants to run only the iOS build tasks. They use `--filter` to include only tasks with a matching label or `--exclude` to skip tasks with a given label, so unrelated tasks are not executed.
32
-
33
- **Why this priority**: Label-based filtering is a key scalability feature for large builds with many tasks.
34
-
35
- **Independent Test**: Given a build file with tasks labeled `build=ios` and `build=android`, run `hammerkit -f build=ios` and verify only the iOS tasks (and their dependencies) execute.
36
-
37
- **Acceptance Scenarios**:
38
-
39
- 1. **Given** tasks with label `type=build` and tasks without that label, **When** the user runs `hammerkit -f type=build`, **Then** only tasks matching `type=build` are executed; dependent tasks without that label may still run to satisfy dependencies.
40
-
41
- 2. **Given** tasks with label `build=ios`, **When** the user runs `hammerkit -e build=ios`, **Then** tasks with `build=ios` are excluded; if a dependent task also matches the excluded label, it is excluded too.
42
-
43
- 3. **Given** multiple label filters, **When** the user provides multiple `-f` arguments, **Then** only tasks matching all specified labels are included.
44
-
45
- ### User Story 3 - Controlling concurrency, caching, and log mode (Priority: P3)
46
-
47
- A developer running on a slow machine wants to limit parallel workers, or a CI engineer wants checksum-based caching and grouped log output. They pass the relevant flags to tune hammerkit's behavior for their environment.
48
-
49
- **Why this priority**: These are power-user options that improve CI reliability and local performance but are not needed for basic usage.
50
-
51
- **Independent Test**: Run `hammerkit run <task> --concurrency 1` and verify tasks execute sequentially (no interleaved log lines from parallel workers).
52
-
53
- **Acceptance Scenarios**:
54
-
55
- 1. **Given** a build file with multiple independent tasks, **When** the user runs `hammerkit -c 1`, **Then** tasks execute one at a time rather than in parallel.
56
-
57
- 2. **Given** a CI environment, **When** hammerkit detects CI, **Then** `--log` defaults to `live` and `--cache` defaults to `checksum` without requiring explicit flags.
58
-
59
- 3. **Given** a local (non-CI) environment, **When** hammerkit runs without explicit `--log` or `--cache` flags, **Then** `--log` defaults to `interactive` and `--cache` defaults to `modify-date`.
60
-
61
- 4. **Given** a task with `src` files, **When** `--cache none` is specified, **Then** the task runs unconditionally without checking whether inputs have changed.
62
-
63
- 5. **Given** a task with `src` files that have not changed, **When** `--watch` is specified, **Then** hammerkit watches those files and re-executes the task when they change.
64
-
65
- ### Edge Cases
66
-
67
- - Specifying both `--filter` and `--exclude` with overlapping labels must resolve deterministically (exclude takes precedence for matched tasks).
68
- - `--concurrency 0` or negative values should be rejected with a clear error.
69
- - `--env <name>` is accepted but its behavior is defined by the environment configuration, not by the execute command itself.
70
-
71
- ## Requirements *(mandatory)*
72
-
73
- ### Functional Requirements
74
-
75
- - **FR-001**: System MUST execute a task by name when the user supplies the task name as the first argument (with or without the explicit `run` subcommand).
76
- - **FR-002**: System MUST execute all declared dependency tasks before the named task.
77
- - **FR-003**: System MUST support label-based inclusion filtering via `-f / --filter <key=value>` where only tasks matching the label are directly executed (dependencies may still run).
78
- - **FR-004**: System MUST support label-based exclusion filtering via `-e / --exclude <key=value>` where tasks (and their dependents) matching the label are skipped.
79
- - **FR-005**: System MUST support a `-c / --concurrency <number>` option controlling the number of parallel worker threads (default: 4).
80
- - **FR-006**: System MUST support a `-w / --watch` flag that re-executes tasks when their source files change.
81
- - **FR-007**: System MUST support a `--cache <method>` option with choices `checksum`, `modify-date`, and `none`.
82
- - **FR-008**: System MUST support a `-l / --log <mode>` option with choices `interactive`, `live`, and `grouped`.
83
- - **FR-009**: System MUST default `--log` to `interactive` and `--cache` to `modify-date` outside CI, and to `live` and `checksum` inside CI.
84
- - **FR-010**: System MUST support an `--env <name>` option to target a configured named environment.
85
-
86
- ## Success Criteria *(mandatory)*
87
-
88
- ### Measurable Outcomes
89
-
90
- - **SC-001**: `hammerkit <task>` and `hammerkit run <task>` produce identical exit codes and output for the same task.
91
- - **SC-002**: With `-f type=build`, only tasks bearing the `type=build` label are directly invoked; the set of executed tasks is smaller than running without the filter.
92
- - **SC-003**: With `-e build=ios`, no task bearing `build=ios` appears in the execution log.
93
- - **SC-004**: With `-c 1`, task execution log lines are never interleaved across concurrent workers.
94
- - **SC-005**: With `--cache none`, a task runs even when no source files have changed since the last run.
95
-
96
- ## Assumptions
97
-
98
- - `hammerkit` with no subcommand and no task name runs all tasks (or the default task if one is defined); the docs show a task name is required for named execution.
99
- - `--filter` and `--exclude` accept `key=value` pairs; partial-key matching (e.g., filtering by key name only) is not documented and assumed unsupported.
100
- - The `--watch` flag watches the `src` files declared on the task; the exact file-watching mechanism is an implementation detail.
101
- - CI is detected automatically via standard CI environment variables (e.g., `CI=true`); this is an informed guess not explicitly stated in the docs.
@@ -1,64 +0,0 @@
1
- # Feature Specification: CLI: init
2
-
3
- **Feature Branch**: `001-current-state-specs`
4
-
5
- **Created**: 2026-05-30
6
-
7
- **Status**: Current State (reverse-specified from docs, hammerkit 1.6.0)
8
-
9
- **Input**: Existing behavior documented in `docs/cli/init.md`
10
-
11
- ## User Scenarios & Testing *(mandatory)*
12
-
13
- ### User Story 1 - Bootstrapping a new project (Priority: P1)
14
-
15
- A developer starts a brand-new project and wants to adopt hammerkit. They run `hammerkit init` in the project root, which creates a ready-to-edit `.hammerkit.yaml` with a working example task and appends the hammerkit cache directory to `.gitignore` so build artifacts are not accidentally committed.
16
-
17
- **Why this priority**: `init` is the mandatory first step for every new hammerkit user; without it the rest of the tool cannot be used.
18
-
19
- **Independent Test**: In an empty directory, run `hammerkit init` and verify that `.hammerkit.yaml` and `.gitignore` are created (or updated) with the documented content.
20
-
21
- **Acceptance Scenarios**:
22
-
23
- 1. **Given** an empty directory with no existing build file, **When** the user runs `hammerkit init`, **Then** a `.hammerkit.yaml` file is created containing an `envs: {}` block, a `tasks:` block with an `example` task using the `alpine` image and the command `echo "it's Hammer Time!"`.
24
-
25
- 2. **Given** an empty directory with no existing `.gitignore`, **When** the user runs `hammerkit init`, **Then** a `.gitignore` file is created containing the entry `.hammerkit`.
26
-
27
- 3. **Given** a directory that already has a `.gitignore`, **When** the user runs `hammerkit init`, **Then** the `.hammerkit` entry is appended to the existing `.gitignore` without overwriting other entries.
28
-
29
- 4. **Given** a directory that already has a `.hammerkit.yaml`, **When** the user runs `hammerkit init`, **Then** the command succeeds (or warns) without overwriting the existing build file.
30
-
31
- ### Edge Cases
32
-
33
- - If the user lacks write permissions in the working directory, `init` must exit with a non-zero code and a meaningful error message.
34
- - Running `init` when a `.hammerkit.yaml` already exists must not destroy existing configuration.
35
-
36
- ## Requirements *(mandatory)*
37
-
38
- ### Functional Requirements
39
-
40
- - **FR-001**: System MUST create a `.hammerkit.yaml` file in the current working directory when `hammerkit init` is run and no build file exists.
41
- - **FR-002**: The generated `.hammerkit.yaml` MUST contain an `envs: {}` key and a `tasks:` key with at least one example task using the `alpine` image.
42
- - **FR-003**: System MUST create or update a `.gitignore` file in the current working directory to include the `.hammerkit` cache directory entry.
43
- - **FR-004**: System MUST print confirmation output indicating each file that was created or updated, including the file path.
44
-
45
- ### Key Entities
46
-
47
- - **Build file (`.hammerkit.yaml`)**: The hammerkit project configuration file. Created by `init` with a minimal, runnable example task.
48
- - **Cache directory entry (`.hammerkit`)**: The directory hammerkit uses for local caching; excluded from version control via `.gitignore`.
49
-
50
- ## Success Criteria *(mandatory)*
51
-
52
- ### Measurable Outcomes
53
-
54
- - **SC-001**: After `hammerkit init`, the file `.hammerkit.yaml` exists in the working directory and is parseable as valid YAML with the documented structure.
55
- - **SC-002**: After `hammerkit init`, the file `.gitignore` in the working directory contains the line `.hammerkit`.
56
- - **SC-003**: `hammerkit init` exits with code 0 and prints at least two confirmation lines (one per created/updated file).
57
- - **SC-004**: Running `hammerkit run example` immediately after `hammerkit init` in a Docker-enabled environment succeeds without manual editing.
58
-
59
- ## Assumptions
60
-
61
- - `init` creates files relative to the current working directory (not a project-root detection heuristic).
62
- - The console confirmation output format is a checkmark prefix followed by the absolute path of each file, as shown in the docs example.
63
- - If `.hammerkit.yaml` already exists, `init` skips creation rather than overwriting; this is an informed guess based on the "non-destructive bootstrap" intent.
64
- - The `--file` global option does not affect where `init` writes the build file; it always writes `.hammerkit.yaml`.
@@ -1,66 +0,0 @@
1
- # Feature Specification: CLI: ls
2
-
3
- **Feature Branch**: `001-current-state-specs`
4
-
5
- **Created**: 2026-05-30
6
-
7
- **Status**: Current State (reverse-specified from docs, hammerkit 1.6.0)
8
-
9
- **Input**: Existing behavior documented in `docs/cli/ls.md`
10
-
11
- ## User Scenarios & Testing *(mandatory)*
12
-
13
- ### User Story 1 - Discovering available tasks and services (Priority: P1)
14
-
15
- A developer opens a project they are unfamiliar with and wants to understand what build tasks and services are defined before running anything. They run `hammerkit ls` and see a structured list of all services and tasks with their key attributes: ports, images, labels, source files, generated outputs, and inter-task dependencies.
16
-
17
- **Why this priority**: `ls` is the primary discovery and onboarding command; it must accurately reflect the build file content so users know what they can run.
18
-
19
- **Independent Test**: Given a build file containing at least one service and one task, run `hammerkit ls` and verify that both sections appear with correct attribute values.
20
-
21
- **Acceptance Scenarios**:
22
-
23
- 1. **Given** a build file with a service `postgres` bound to port `5432` using image `postgres:12`, **When** the user runs `hammerkit ls`, **Then** the output contains a `Services:` section listing `postgres` with its port mapping (`127.0.0.1:5432 -> 5432`) and image.
24
-
25
- 2. **Given** a build file with a task `install` that has an image, labels, src files, and generated outputs, **When** the user runs `hammerkit ls`, **Then** the output contains a `Tasks:` section listing `install` with all those attributes displayed.
26
-
27
- 3. **Given** a build file with a task `api` that declares `needs: postgres` and `deps: install`, **When** the user runs `hammerkit ls`, **Then** the output for `api` shows both the `needs` (service dependency) and `deps` (task dependency) fields.
28
-
29
- 4. **Given** a build file with only tasks and no services, **When** the user runs `hammerkit ls`, **Then** the output contains only a `Tasks:` section with no `Services:` section.
30
-
31
- ### Edge Cases
32
-
33
- - A build file with no tasks and no services should produce empty or minimal output without crashing.
34
- - Labels with multiple key=value pairs must all appear on the same `labels:` line.
35
- - Tasks with multiple source files or generated outputs must list them space-separated.
36
-
37
- ## Requirements *(mandatory)*
38
-
39
- ### Functional Requirements
40
-
41
- - **FR-001**: System MUST print a `Services:` section listing every service defined in the build file, each preceded by a bullet (`•`).
42
- - **FR-002**: For each service, system MUST display the port mappings and container image.
43
- - **FR-003**: System MUST print a `Tasks:` section listing every task defined in the build file, each preceded by a bullet (`•`).
44
- - **FR-004**: For each task, system MUST display the container image, labels (if any), source files (if any), generated outputs (if any), service dependencies via `needs:` (if any), and task dependencies via `deps:` (if any).
45
- - **FR-005**: System MUST omit attributes that are not set for a given task or service (no empty lines for unset fields).
46
-
47
- ### Key Entities
48
-
49
- - **Service**: A long-running container dependency (e.g., a database) with port mappings and an image.
50
- - **Task**: A build step with an image, optional labels, source globs, generated paths, and optional dependencies on other tasks or services.
51
-
52
- ## Success Criteria *(mandatory)*
53
-
54
- ### Measurable Outcomes
55
-
56
- - **SC-001**: `hammerkit ls` exits with code 0 for any valid build file.
57
- - **SC-002**: Every service and task name defined in the build file appears exactly once in the `hammerkit ls` output.
58
- - **SC-003**: Port mappings shown in the output match the bind address and container port declared in the build file.
59
- - **SC-004**: Label values shown in the output match the `key=value` pairs declared in the build file.
60
-
61
- ## Assumptions
62
-
63
- - `ls` reads the default build file (`.hammerkit.yaml`) unless `--file` is supplied globally.
64
- - No filtering options are documented for `ls`; it always prints all services and all tasks.
65
- - The output format is plain text (not JSON or YAML) with a bullet-point layout as shown in the docs example.
66
- - The `Services:` section is printed before the `Tasks:` section when both are present.
@@ -1,110 +0,0 @@
1
- # Feature Specification: CLI: package
2
-
3
- **Feature Branch**: `001-current-state-specs`
4
-
5
- **Created**: 2026-05-30
6
-
7
- **Status**: Current State (reverse-specified from docs, hammerkit 1.6.0)
8
-
9
- **Input**: Existing behavior documented in `docs/cli/package.md`
10
-
11
- ## User Scenarios & Testing *(mandatory)*
12
-
13
- ### User Story 1 - Build self-contained Docker images from container services (Priority: P1)
14
-
15
- A developer runs `hammerkit package <registry>` to turn every container service in their build file into a Docker image. Each image is named `<registry>/<service>:latest` and contains the service's base image plus the outputs generated by all of its dependency tasks, making the image runnable without hammerkit.
16
-
17
- **Why this priority**: This is the core purpose of the command — producing deployable, self-contained images from the build graph.
18
-
19
- **Independent Test**: Define a service with one dependency task that generates an output directory. Run `hammerkit package localhost:5000`. Confirm an image named `localhost:5000/<service>:latest` is created and that the generated directory is present inside the image.
20
-
21
- **Acceptance Scenarios**:
22
-
23
- 1. **Given** a build file with a container service that has dependency tasks declaring `generates` outputs, **When** `hammerkit package <registry>` is run, **Then** a Docker image named `<registry>/<service>:latest` is produced with the dependency outputs baked in.
24
-
25
- 2. **Given** a service with `envs`, `ports`, and `cmd` configured, **When** the image is built, **Then** the image encodes those values (environment variables, `EXPOSE` directives, and the default command) so the service runs correctly without hammerkit.
26
-
27
- 3. **Given** a service with no dependency tasks, **When** `hammerkit package <registry>` is run, **Then** a valid image is still produced from the service's base image and any mounted sources.
28
-
29
- ### User Story 2 - Push images to a container registry (Priority: P1)
30
-
31
- A CI pipeline operator adds `--push` to upload built images to a remote registry. Registry credentials are supplied via `-u`/`-p` flags. This enables promotion of a tested image across environments without rebuilding.
32
-
33
- **Why this priority**: Pushing to a registry is the primary delivery mechanism and is equally critical as building.
34
-
35
- **Independent Test**: Run `hammerkit package <registry> --push -u <user> -p <token>` and confirm the image is accessible in the registry under the expected name and tag.
36
-
37
- **Acceptance Scenarios**:
38
-
39
- 1. **Given** `--push` is provided with valid `-u`/`-p` credentials, **When** `hammerkit package <registry> --push -u <u> -p <p>` completes, **Then** the image `<registry>/<service>:<tag>` is available in the remote registry.
40
-
41
- 2. **Given** `--push` is omitted, **When** `hammerkit package <registry>` completes, **Then** the image is built locally only and not pushed.
42
-
43
- ### User Story 3 - Pin an explicit image tag for release promotion (Priority: P2)
44
-
45
- A release engineer specifies `-t 1.2.3` so the produced image is tagged with a version number or a CI commit SHA rather than `latest`, enabling the same artifact to be promoted across staging and production.
46
-
47
- **Why this priority**: Reproducible, version-tagged images are required for safe promotion workflows.
48
-
49
- **Independent Test**: Run `hammerkit package <registry> -t 1.2.3` and confirm the resulting image tag is `<registry>/<service>:1.2.3`.
50
-
51
- **Acceptance Scenarios**:
52
-
53
- 1. **Given** `-t 1.2.3` is supplied, **When** `hammerkit package <registry> -t 1.2.3` completes, **Then** the image is tagged `<registry>/<service>:1.2.3` instead of `latest`.
54
-
55
- ### User Story 4 - Build for a specific target platform (Priority: P2)
56
-
57
- A developer on an Apple Silicon machine builds images targeting `linux/amd64` so the images can run on x64 production servers, by passing `--platform linux/amd64`.
58
-
59
- **Why this priority**: Cross-architecture builds are a common CI/CD requirement, especially in hybrid development environments.
60
-
61
- **Independent Test**: Run `hammerkit package <registry> --platform linux/amd64` on an arm64 host and confirm the produced image manifest declares the `linux/amd64` platform.
62
-
63
- **Acceptance Scenarios**:
64
-
65
- 1. **Given** `--platform linux/amd64` is specified, **When** `hammerkit package <registry> --platform linux/amd64` is run on an arm64 host, **Then** the produced image targets the `linux/amd64` platform.
66
-
67
- 2. **Given** `--platform` is omitted, **When** `hammerkit package <registry>` is run, **Then** the image targets the host architecture.
68
-
69
- ### Edge Cases
70
-
71
- - Services without a container image (local-only services) are not packaged and are silently skipped.
72
- - Dependency tasks that run locally (without an `image` field) are not supported for packaging.
73
- - The `<registry>` argument is required; omitting it is an error.
74
- - `--push` without `-u`/`-p` relies on pre-authenticated Docker credentials (e.g., `docker login`); no error is thrown by hammerkit for missing flags if the daemon is already authenticated.
75
- - Cross-architecture builds require the Docker daemon to support the requested platform (buildkit/emulation); hammerkit forwards `--platform` to the Docker build and does not validate platform support independently.
76
-
77
- ## Requirements *(mandatory)*
78
-
79
- ### Functional Requirements
80
-
81
- - **FR-001**: System MUST accept a required `<registry>` argument and produce a Docker image named `<registry>/<service>:<tag>` for every container service in the build file.
82
- - **FR-002**: System MUST generate a multi-stage Dockerfile per service that starts FROM the service's base image and copies in the `generates` outputs of all dependency tasks.
83
- - **FR-003**: System MUST apply the service's `envs`, `ports` (as `EXPOSE` directives), and `cmd` to the produced image.
84
- - **FR-004**: System MUST label each produced image with `hammerkit.dev/name` and `hammerkit.dev/id`.
85
- - **FR-005**: System MUST default the image tag to `latest` when `-t`/`--tag` is not specified.
86
- - **FR-006**: When `--push` is supplied, system MUST push the built image to the specified registry; credentials MAY be supplied via `-u`/`--username` and `-p`/`--password`.
87
- - **FR-007**: System MUST default to the host architecture when `--platform` is not specified, and MUST forward the specified platform to the Docker build when `--platform` is provided.
88
- - **FR-008**: System MUST support `-f`/`--filter` and `-e`/`--exclude` label options to limit which services are packaged.
89
-
90
- ### Key Entities
91
-
92
- - **Container service**: A service defined in the build file with a base `image`; the unit of packaging.
93
- - **Registry**: The image-name prefix supplied by the user, used to construct the full image name `<registry>/<service>:<tag>`.
94
- - **Generated output**: Files or directories produced by a dependency task's `generates` field that are baked into the service image.
95
-
96
- ## Success Criteria *(mandatory)*
97
-
98
- ### Measurable Outcomes
99
-
100
- - **SC-001**: After `hammerkit package <registry>`, a runnable Docker image exists locally for each container service, named `<registry>/<service>:<tag>`, containing all dependency task outputs.
101
- - **SC-002**: After `hammerkit package <registry> --push`, the image is accessible from the remote registry under the expected name and tag without any additional build steps.
102
- - **SC-003**: An image built with `hammerkit package <registry> -t <version>` carries exactly the tag `<version>` (not `latest`).
103
- - **SC-004**: An image built without hammerkit present in the runtime environment starts the service correctly using its declared `cmd`.
104
-
105
- ## Assumptions
106
-
107
- - Packaging operates exclusively against the Docker runtime; non-Docker (local) services are out of scope.
108
- - The `--build-override-user` flag creates and runs a dedicated uid/gid 1000 user inside the image; its exact semantics beyond that are not specified in the doc and are not elaborated here.
109
- - The `<registry>` value is a plain string prefix with no validation by hammerkit; any registry format accepted by Docker is valid.
110
- - Services with no `deps` still produce a valid image from their base image and `mounts`; this is documented as supported behavior.
package/specs/cli/spec.md DELETED
@@ -1,73 +0,0 @@
1
- # Feature Specification: CLI: Overview
2
-
3
- **Feature Branch**: `001-current-state-specs`
4
-
5
- **Created**: 2026-05-30
6
-
7
- **Status**: Current State (reverse-specified from docs, hammerkit 1.6.0)
8
-
9
- **Input**: Existing behavior documented in `docs/cli/README.md`
10
-
11
- ## User Scenarios & Testing *(mandatory)*
12
-
13
- ### User Story 1 - Running hammerkit for the first time (Priority: P1)
14
-
15
- A developer downloads hammerkit and runs `hammerkit --help` to discover what commands are available. They see the full command list and global options, understand how to specify a custom build file, and know how to turn on verbose logging.
16
-
17
- **Why this priority**: The CLI entry point and help text are the first thing every user sees; a broken or incomplete help surface breaks all downstream workflows.
18
-
19
- **Independent Test**: Run `hammerkit --help` in a directory that contains a `.hammerkit.yaml` file and verify the output lists all commands and global options.
20
-
21
- **Acceptance Scenarios**:
22
-
23
- 1. **Given** a directory with a valid `.hammerkit.yaml`, **When** the user runs `hammerkit --help`, **Then** the output includes the version flag (`-V, --version`), `--verbose`, `--file`, and all subcommands (`ls`, `clean`, `store`, `restore`, `package`, `validate`, `up`, `down`, `run`, `help`).
24
-
25
- 2. **Given** any directory, **When** the user runs `hammerkit --version`, **Then** the current version number is printed.
26
-
27
- 3. **Given** a directory with a valid build file, **When** the user runs `hammerkit --file custom.yaml <task>`, **Then** hammerkit uses `custom.yaml` as the build file instead of `.hammerkit.yaml`.
28
-
29
- 4. **Given** a directory without a build file, **When** the user runs any command other than `init`, **Then** hammerkit reports that no build file is present and only the `init` command is available.
30
-
31
- ### User Story 2 - Enabling verbose output (Priority: P2)
32
-
33
- A developer is troubleshooting an unexpected task result and wants to see internal debugging information. They add `--verbose` before the command and observe additional log lines.
34
-
35
- **Why this priority**: Debugging is a high-frequency need but not the primary happy path.
36
-
37
- **Independent Test**: Run `hammerkit --verbose ls` and confirm that extra debug-level log lines appear beyond what the default output produces.
38
-
39
- **Acceptance Scenarios**:
40
-
41
- 1. **Given** a valid build file, **When** the user runs `hammerkit --verbose ls`, **Then** the output contains debugging information in addition to the normal task list.
42
-
43
- ### Edge Cases
44
-
45
- - When no build file is found, hammerkit must restrict available commands to `init` only and surface a helpful message.
46
- - The `--file` flag accepts any path; if the file does not exist, hammerkit must report an error.
47
- - Global options (`--verbose`, `--file`) must be accepted before the subcommand name.
48
-
49
- ## Requirements *(mandatory)*
50
-
51
- ### Functional Requirements
52
-
53
- - **FR-001**: System MUST provide a global `--verbose` flag that enables debug-level logging for any subcommand.
54
- - **FR-002**: System MUST provide a global `--file <path>` option that overrides the default build file path (`.hammerkit.yaml`).
55
- - **FR-003**: System MUST expose the following subcommands: `ls`, `clean`, `store`, `restore`, `package`, `validate`, `up`, `down`, `run`, `help`.
56
- - **FR-004**: System MUST treat `hammerkit <task>` (no explicit subcommand) as equivalent to `hammerkit run <task>` (default command).
57
- - **FR-005**: System MUST print the installed version when `--version` / `-V` is supplied.
58
- - **FR-006**: System MUST restrict available commands to `init` when no build file is present in the working directory.
59
-
60
- ## Success Criteria *(mandatory)*
61
-
62
- ### Measurable Outcomes
63
-
64
- - **SC-001**: `hammerkit --help` exits with code 0 and lists all documented commands and global options.
65
- - **SC-002**: `hammerkit --version` exits with code 0 and prints a semantic version string.
66
- - **SC-003**: `hammerkit --file <nonexistent>` exits with a non-zero code and a human-readable error message.
67
- - **SC-004**: Running `hammerkit <task>` without the `run` keyword produces identical output to `hammerkit run <task>`.
68
-
69
- ## Assumptions
70
-
71
- - The default build file name is `.hammerkit.yaml` (as shown in the CLI help output).
72
- - "Only `init` is available" when no build file is present means hammerkit exits with an error for all other commands, not that it silently ignores them.
73
- - `--verbose` applies globally to all subcommands; per-command verbosity overrides are not documented.
@@ -1,74 +0,0 @@
1
- # Feature Specification: CLI: store / restore
2
-
3
- **Feature Branch**: `001-current-state-specs`
4
-
5
- **Created**: 2026-05-30
6
-
7
- **Status**: Current State (reverse-specified from docs, hammerkit 1.6.0)
8
-
9
- **Input**: Existing behavior documented in `docs/cli/store-restore.md`
10
-
11
- ## User Scenarios & Testing *(mandatory)*
12
-
13
- ### User Story 1 - Cache CI artifacts between pipeline runs (Priority: P1)
14
-
15
- A developer configures their CI pipeline so that after each successful build, all task-generated outputs and the hammerkit cache are saved to a designated folder. On the next pipeline run, CI restores that folder first, then runs hammerkit. Tasks whose inputs have not changed are skipped because their outputs and cache state are already present, reducing total pipeline time.
16
-
17
- **Why this priority**: The primary value of store/restore is CI acceleration by avoiding redundant work across pipeline runs.
18
-
19
- **Independent Test**: Create a build file with a task that has `src` and `generates` fields. Run the task, store the cache, delete local outputs, restore the cache, and confirm hammerkit skips the task on the next run.
20
-
21
- **Acceptance Scenarios**:
22
-
23
- 1. **Given** a build file with tasks that declare `generates` outputs, **When** `hammerkit store <dir>` is run after tasks have executed, **Then** all generated files/folders and the hammerkit cache are moved into `<dir>`.
24
-
25
- 2. **Given** a previously stored cache directory `<dir>`, **When** `hammerkit restore <dir>` is run, **Then** the hammerkit cache and generated outputs are recovered so that unchanged tasks are skipped on the next run.
26
-
27
- 3. **Given** a CI pipeline that saves and restores the cache directory between runs, **When** source inputs have not changed between runs, **Then** hammerkit skips the affected tasks on the second run.
28
-
29
- ### User Story 2 - Selective state recovery after cache restoration (Priority: P2)
30
-
31
- A developer restores a previously stored cache and then modifies one source file. Only the task whose inputs changed is re-executed; all other tasks with unchanged inputs are still skipped.
32
-
33
- **Why this priority**: Demonstrates that store/restore integrates correctly with hammerkit's existing input-change detection rather than replacing it.
34
-
35
- **Independent Test**: After restoring a cache, modify one source file and run the dependent task. Confirm only that task executes and others are skipped.
36
-
37
- **Acceptance Scenarios**:
38
-
39
- 1. **Given** a restored cache where some task outputs and cache state are present, **When** a source file for one task is modified and hammerkit is run, **Then** only the task whose source changed is re-executed.
40
-
41
- ### Edge Cases
42
-
43
- - Running `hammerkit restore <dir>` when `<dir>` does not exist or is empty should not silently corrupt the local cache state.
44
- - Running `hammerkit store <dir>` when no tasks have generated any outputs should still complete without error.
45
- - The destination folder for `store` must be provided; omitting it is an error.
46
-
47
- ## Requirements *(mandatory)*
48
-
49
- ### Functional Requirements
50
-
51
- - **FR-001**: System MUST accept a required destination folder argument for `hammerkit store <dir>` and move all task-generated outputs (declared via `generates`) and the hammerkit cache into that folder.
52
- - **FR-002**: System MUST accept a required source folder argument for `hammerkit restore <dir>` and recover the hammerkit cache and generated outputs from that folder into their expected locations.
53
- - **FR-003**: After a successful restore, hammerkit MUST recognize restored task outputs as valid cached results so that tasks with unchanged inputs are skipped on the next run.
54
- - **FR-004**: The store and restore commands MUST be designed for use in CI environments where the destination folder is managed by the CI caching mechanism.
55
-
56
- ### Key Entities
57
-
58
- - **Cache directory**: A folder supplied by the user that holds the exported hammerkit cache state and all generated task outputs; it is the unit transferred between CI pipeline runs.
59
- - **Generated output**: A file or directory declared in a task's `generates` field that is produced by task execution and included in the stored cache.
60
-
61
- ## Success Criteria *(mandatory)*
62
-
63
- ### Measurable Outcomes
64
-
65
- - **SC-001**: A CI pipeline that uses store/restore completes a second run with unchanged inputs in less time than the first run, with task execution skipped for all tasks whose inputs have not changed.
66
- - **SC-002**: After `hammerkit store <dir>`, all directories and files listed in any task's `generates` field are present inside `<dir>`.
67
- - **SC-003**: After `hammerkit restore <dir>` followed by `hammerkit <task>`, the task execution log shows the task was skipped (cache hit) when inputs are unchanged.
68
-
69
- ## Assumptions
70
-
71
- - The store command moves (rather than copies) generated outputs; the exact mechanism (move vs copy) is not critical to the observable contract but the doc says "moved".
72
- - Only container-compatible and local task outputs that appear in `generates` are included; tasks without a `generates` field contribute nothing to the store.
73
- - No `--filter`/`--exclude` options are documented for store/restore; the commands operate on all tasks in the build file.
74
- - The cache directory path is relative to the working directory unless an absolute path is given; this mirrors standard shell behavior.
@@ -1,80 +0,0 @@
1
- # Feature Specification: CLI: up
2
-
3
- **Feature Branch**: `001-current-state-specs`
4
-
5
- **Created**: 2026-05-30
6
-
7
- **Status**: Current State (reverse-specified from docs, hammerkit 1.6.0)
8
-
9
- **Input**: Existing behavior documented in `docs/cli/up.md`
10
-
11
- ## User Scenarios & Testing *(mandatory)*
12
-
13
- ### User Story 1 - Starting shared services for local development (Priority: P1)
14
-
15
- A developer needs a database running continuously while they iterate on several tasks. Instead of letting each task start and stop the database automatically, they run `hammerkit up` to start all services and keep them alive. When they are done for the day, they run `hammerkit down`.
16
-
17
- **Why this priority**: Persistent service management during development is the primary use case for the `up` command; it directly reduces the startup overhead between task runs.
18
-
19
- **Independent Test**: Given a build file with a service (e.g., `postgres`), run `hammerkit up` and verify the service container is running and remains running until the process is interrupted or `hammerkit down` is called.
20
-
21
- **Acceptance Scenarios**:
22
-
23
- 1. **Given** a build file with one or more services, **When** the user runs `hammerkit up`, **Then** all services start and the process blocks until the user sends an interrupt signal (Ctrl+C or equivalent).
24
-
25
- 2. **Given** a build file with one or more services, **When** the user runs `hammerkit up --daemon`, **Then** all services start in the background and the command returns immediately with exit code 0.
26
-
27
- 3. **Given** services started with `hammerkit up --daemon`, **When** the user subsequently runs `hammerkit down`, **Then** those services are stopped.
28
-
29
- 4. **Given** a build file with multiple services and only some bearing a specific label, **When** the user runs `hammerkit up -f <label>`, **Then** only the services matching the label are started.
30
-
31
- 5. **Given** a build file with multiple services and some bearing an excluded label, **When** the user runs `hammerkit up -e <label>`, **Then** services matching the excluded label are not started.
32
-
33
- ### User Story 2 - Starting services in a named environment (Priority: P2)
34
-
35
- A developer wants to start services inside a Kubernetes cluster configured under `environments:` in their build file. They pass `--env <name>` to target that environment instead of the local Docker runtime.
36
-
37
- **Why this priority**: Multi-environment support is an important power-user scenario but not required for local Docker-based development.
38
-
39
- **Independent Test**: Given a build file with a named Kubernetes environment, run `hammerkit up --env <name>` and verify that service start commands target the configured environment.
40
-
41
- **Acceptance Scenarios**:
42
-
43
- 1. **Given** a named environment declared in the build file, **When** the user runs `hammerkit up --env <name>`, **Then** services are started within that environment's runtime.
44
-
45
- ### Edge Cases
46
-
47
- - Running `hammerkit up` when no services are defined in the build file should exit cleanly (code 0) with an informational message.
48
- - Running `hammerkit up --daemon` multiple times for already-running services should either be idempotent or report that services are already running.
49
- - Interrupt signal during foreground `up` must cleanly stop the started services before exiting.
50
-
51
- ## Requirements *(mandatory)*
52
-
53
- ### Functional Requirements
54
-
55
- - **FR-001**: System MUST start all services defined in the build file when `hammerkit up` is run without filter options.
56
- - **FR-002**: System MUST keep services running and block the terminal until interrupted, unless `--daemon` is supplied.
57
- - **FR-003**: System MUST start services in the background and return immediately when `--daemon` is supplied.
58
- - **FR-004**: System MUST support label-based inclusion filtering via `-f / --filter <key=value>` to start only matching services.
59
- - **FR-005**: System MUST support label-based exclusion filtering via `-e / --exclude <key=value>` to skip matching services.
60
- - **FR-006**: System MUST support an `--env <name>` option to start services in a configured named environment.
61
- - **FR-007**: System MUST support `-c / --concurrency <number>`, `-w / --watch`, `--cache <method>`, and `-l / --log <mode>` options (consistent with the `run` command option surface).
62
-
63
- ### Key Entities
64
-
65
- - **Service**: A long-running container (e.g., a database) declared in the build file under `services:`, started and kept running by `up`.
66
-
67
- ## Success Criteria *(mandatory)*
68
-
69
- ### Measurable Outcomes
70
-
71
- - **SC-001**: After `hammerkit up` (foreground), the service container is reachable on its declared port while the process is running.
72
- - **SC-002**: After `hammerkit up --daemon`, the command exits with code 0 and the service container is reachable on its declared port.
73
- - **SC-003**: With `-f <label>`, only services bearing that label are started; other services remain stopped.
74
- - **SC-004**: Services started with `hammerkit up` are stopped when the foreground process receives an interrupt signal.
75
-
76
- ## Assumptions
77
-
78
- - Services that are already running (e.g., started by a prior `up` invocation) are handled gracefully; exact behavior (skip, restart, error) is an implementation detail not specified in the docs.
79
- - The `--watch` and `--cache` flags carry over from the `run` command option surface and apply to any tasks that services might trigger; their exact effect on pure-service `up` runs is not specified in the docs and assumed to be a no-op for services themselves.
80
- - `--env` targets named environments declared under the `environments:` key in the build file (informed by the cross-reference to the Kubernetes doc).