hammerkit 1.6.0 → 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.
- package/README.md +4 -4
- package/build.schema.json +49 -2
- package/dist/cache/backends/local-cache-backend.js +58 -1
- package/dist/cache/backends/registry/registry-client.js +264 -0
- package/dist/cache/backends/registry/registry-credentials.js +81 -0
- package/dist/cache/backends/registry/registry-reference.js +36 -0
- package/dist/cache/backends/registry-cache-backend.js +145 -0
- package/dist/cache/backends/s3-cache-backend.js +99 -46
- package/dist/cache/cache-backend-registry.js +2 -0
- package/dist/cache/cache-inventory.js +84 -0
- package/dist/cache/cache-sync.js +77 -0
- package/dist/cache/explain.js +214 -0
- package/dist/cache/last-resolved.js +48 -0
- package/dist/cache/read-only.js +15 -0
- package/dist/cache/retention.js +61 -0
- package/dist/cli.js +43 -0
- package/dist/docker/using-container.js +5 -1
- package/dist/executer/archive-task-entry.js +21 -0
- package/dist/executer/await-completed-dependencies.js +32 -14
- package/dist/executer/cache-metadata.js +9 -2
- package/dist/executer/docker-service.js +2 -1
- package/dist/executer/docker-task.js +2 -1
- package/dist/executer/dry-run.js +73 -0
- package/dist/executer/event-cache.js +6 -5
- package/dist/executer/execute-command.js +21 -3
- package/dist/executer/execute-work-service.js +10 -1
- package/dist/executer/execute-work-task.js +95 -25
- package/dist/executer/execute-work-tree.js +26 -1
- package/dist/executer/get-scheduler-execute-result.js +4 -1
- package/dist/executer/lazy-resolver.js +15 -3
- package/dist/executer/reset-work-tree.js +2 -0
- package/dist/executer/run-summary.js +102 -0
- package/dist/executer/scheduler/enqueue-next.js +15 -60
- package/dist/executer/scheduler/state-key.js +78 -0
- package/dist/kubernetes/await-running-state.js +73 -19
- package/dist/log.js +16 -6
- package/dist/logging/live-logger.js +2 -1
- package/dist/optimizer/calculate-checksum.js +8 -2
- package/dist/optimizer/get-work-cache-stats.js +66 -16
- package/dist/optimizer/work-task-cache-description.js +34 -5
- package/dist/planner/project-root.js +26 -0
- package/dist/planner/utils/append-work-service.js +2 -1
- package/dist/planner/utils/append-work-task.js +7 -1
- package/dist/planner/utils/parse-work-source.js +35 -30
- package/dist/planner/utils/portable-path.js +30 -0
- package/dist/planner/work-cache-id.js +2 -2
- package/dist/planner/work-graph-serializer.js +103 -0
- package/dist/planner/work-instance-id.js +15 -0
- package/dist/planner/work-runtime-docker.js +53 -15
- package/dist/planner/work-runtime-kubernetes.js +35 -3
- package/dist/planner/work-runtime-local.js +9 -3
- package/dist/planner/work-service-id.js +16 -4
- package/dist/program.js +235 -1
- package/dist/schema/build-file-base-task-schema.js +4 -0
- package/dist/schema/cache-schema.js +21 -2
- package/dist/schema/duration-schema.js +18 -0
- package/dist/schema/reference-parser.js +3 -0
- package/dist/schema/size-schema.js +18 -0
- package/dist/schema/work-scope-parser.js +15 -2
- package/dist/utils/units.js +87 -0
- package/package.json +12 -1
- package/.agents/FEEDBACK.md +0 -74
- package/.agents/skills/before-commit/SKILL.md +0 -101
- package/.agents/skills/do-the-work/SKILL.md +0 -64
- package/.agents/skills/fix-ci/SKILL.md +0 -98
- package/.agents/skills/speckit-git-commit/SKILL.md +0 -53
- package/.agents/skills/speckit-git-feature/SKILL.md +0 -72
- package/.agents/skills/speckit-git-initialize/SKILL.md +0 -54
- package/.agents/skills/speckit-git-remote/SKILL.md +0 -50
- package/.agents/skills/speckit-git-validate/SKILL.md +0 -54
- package/.devcontainer/Dockerfile +0 -5
- package/.devcontainer/devcontainer.json +0 -40
- package/.gitbook.yaml +0 -5
- package/.specify/extensions/.registry +0 -37
- package/.specify/extensions/git/README.md +0 -100
- package/.specify/extensions/git/commands/speckit.git.commit.md +0 -48
- package/.specify/extensions/git/commands/speckit.git.feature.md +0 -67
- package/.specify/extensions/git/commands/speckit.git.initialize.md +0 -49
- package/.specify/extensions/git/commands/speckit.git.remote.md +0 -45
- package/.specify/extensions/git/commands/speckit.git.validate.md +0 -49
- package/.specify/extensions/git/config-template.yml +0 -62
- package/.specify/extensions/git/extension.yml +0 -140
- package/.specify/extensions/git/git-config.yml +0 -62
- package/.specify/extensions.yml +0 -149
- package/.specify/feature.json +0 -3
- package/.specify/init-options.json +0 -10
- package/.specify/integration.json +0 -15
- package/.specify/integrations/claude.manifest.json +0 -16
- package/.specify/integrations/speckit.manifest.json +0 -17
- package/.specify/memory/constitution.md +0 -162
- package/.specify/templates/checklist-template.md +0 -40
- package/.specify/templates/constitution-template.md +0 -50
- package/.specify/templates/plan-template.md +0 -113
- package/.specify/templates/spec-template.md +0 -131
- package/.specify/templates/tasks-template.md +0 -252
- package/.specify/workflows/speckit/workflow.yml +0 -77
- package/.specify/workflows/workflow-registry.json +0 -13
- package/AGENTS.md +0 -94
- package/Dockerfile +0 -32
- package/FOLLOWUPS.md +0 -34
- package/SUMMARY.md +0 -68
- package/VALUES.md +0 -53
- package/dist/cache/auto-pull.spec.js +0 -123
- package/dist/cache/backends/local-cache-backend.spec.js +0 -72
- package/dist/cache/backends/s3-cache-backend.spec.js +0 -118
- package/dist/cache/cache-backend-registry.spec.js +0 -22
- package/dist/cache/resolve-cache.spec.js +0 -52
- package/dist/cli.spec.js +0 -23
- package/dist/console/get-logger.spec.js +0 -23
- package/dist/docker/get-default-platform.spec.js +0 -29
- package/dist/docker/longest-common-prefix.spec.js +0 -26
- package/dist/docker/package.spec.js +0 -145
- package/dist/docker/remove-container.spec.js +0 -32
- package/dist/docker/using-container.spec.js +0 -90
- package/dist/environment/merge-environment-variables.spec.js +0 -24
- package/dist/environment/replace-env-variables.spec.js +0 -47
- package/dist/executer/abort.spec.js +0 -18
- package/dist/executer/environment-mock.spec.js +0 -22
- package/dist/executer/execute.spec.js +0 -51
- package/dist/executer/get-service-containers.spec.js +0 -59
- package/dist/executer/get-service-env-hints.spec.js +0 -65
- package/dist/executer/label-values.spec.js +0 -61
- package/dist/executer/lazy-resolver.spec.js +0 -20
- package/dist/executer/process-manager.spec.js +0 -54
- package/dist/executer/scheduler/enqueue-next.spec.js +0 -136
- package/dist/executer/state-resolver.spec.js +0 -37
- package/dist/executer/state.spec.js +0 -66
- package/dist/executer/states.spec.js +0 -14
- package/dist/file/get-file-context.spec.js +0 -178
- package/dist/file/move-files.spec.js +0 -48
- package/dist/kubernetes/ensure-http-route.spec.js +0 -70
- package/dist/kubernetes/ensure-namespace.spec.js +0 -39
- package/dist/kubernetes/resolve-pod-name.spec.js +0 -81
- package/dist/log.spec.js +0 -131
- package/dist/logging/grouped-logger.spec.js +0 -84
- package/dist/logging/interactive-logger.spec.js +0 -57
- package/dist/logging/live-logger.spec.js +0 -37
- package/dist/logging/loggers.spec.js +0 -154
- package/dist/optimizer/calculate-checksum.spec.js +0 -34
- package/dist/optimizer/get-cache-directory.spec.js +0 -59
- package/dist/optimizer/get-work-cache-stats.spec.js +0 -79
- package/dist/optimizer/work-task-cache-description.spec.js +0 -45
- package/dist/parser/default-build-file.spec.js +0 -48
- package/dist/parser/parse-label-arguments.spec.js +0 -27
- package/dist/parser/read-build-file.spec.js +0 -89
- package/dist/parser/read-env-file.spec.js +0 -42
- package/dist/planner/utils/append-work-service.spec.js +0 -71
- package/dist/planner/utils/get-container-user.spec.js +0 -30
- package/dist/planner/utils/get-default-kube-config.spec.js +0 -11
- package/dist/planner/utils/normalize-path.spec.js +0 -24
- package/dist/planner/utils/parse-work-command.spec.js +0 -21
- package/dist/planner/utils/parse-work-generate.spec.js +0 -45
- package/dist/planner/utils/parse-work-mount.spec.js +0 -62
- package/dist/planner/utils/parse-work-mounts.spec.js +0 -19
- package/dist/planner/utils/parse-work-port.spec.js +0 -24
- package/dist/planner/utils/parse-work-ports.spec.js +0 -24
- package/dist/planner/utils/parse-work-source.spec.js +0 -58
- package/dist/planner/utils/parse-work-volume.spec.js +0 -18
- package/dist/planner/utils/plan-work-tasks.spec.js +0 -30
- package/dist/planner/utils/plan-work-volume.spec.js +0 -33
- package/dist/planner/utils/split-name.spec.js +0 -16
- package/dist/planner/utils/template-value.spec.js +0 -14
- package/dist/planner/validate.spec.js +0 -251
- package/dist/planner/work-cache-id.spec.js +0 -173
- package/dist/planner/work-runtime-local.spec.js +0 -193
- package/dist/program-commands.spec.js +0 -84
- package/dist/program.spec.js +0 -43
- package/dist/schema/build-file-schema.spec.js +0 -61
- package/dist/schema/doc-build-files.spec.js +0 -223
- package/dist/schema/parse-error.spec.js +0 -18
- package/dist/schema/reference-parser.spec.js +0 -83
- package/dist/schema/schema-parser.spec.js +0 -80
- package/dist/schema/type-guards.spec.js +0 -23
- package/dist/schema/work-scope-parser.spec.js +0 -66
- package/dist/service/update-service-status.spec.js +0 -27
- package/dist/testing/ensure-local-registry.js +0 -134
- package/dist/testing/example-test-suite.js +0 -63
- package/dist/testing/expect.js +0 -82
- package/dist/testing/get-test-suite.js +0 -9
- package/dist/testing/http-get.js +0 -21
- package/dist/testing/integration/cache.spec.js +0 -49
- package/dist/testing/integration/cancellation.spec.js +0 -45
- package/dist/testing/integration/clean.spec.js +0 -43
- package/dist/testing/integration/cmd.spec.js +0 -15
- package/dist/testing/integration/concurrency.spec.js +0 -19
- package/dist/testing/integration/docker-package-registry.spec.js +0 -44
- package/dist/testing/integration/docker-service-crash.spec.js +0 -36
- package/dist/testing/integration/docker.spec.js +0 -18
- package/dist/testing/integration/env.spec.js +0 -48
- package/dist/testing/integration/environment.spec.js +0 -97
- package/dist/testing/integration/error.spec.js +0 -19
- package/dist/testing/integration/export.spec.js +0 -22
- package/dist/testing/integration/extend.spec.js +0 -29
- package/dist/testing/integration/glob.spec.js +0 -40
- package/dist/testing/integration/include.spec.js +0 -31
- package/dist/testing/integration/invalid.spec.js +0 -17
- package/dist/testing/integration/invalid_loop.spec.js +0 -56
- package/dist/testing/integration/k8s-state-cache.spec.js +0 -41
- package/dist/testing/integration/kubernetes-service.spec.js +0 -148
- package/dist/testing/integration/kubernetes.spec.js +0 -99
- package/dist/testing/integration/labels.spec.js +0 -42
- package/dist/testing/integration/local.spec.js +0 -14
- package/dist/testing/integration/monorepo.spec.js +0 -15
- package/dist/testing/integration/multi-line.spec.js +0 -17
- package/dist/testing/integration/reference.spec.js +0 -21
- package/dist/testing/integration/services.spec.js +0 -38
- package/dist/testing/integration/store.spec.js +0 -52
- package/dist/testing/integration/unknown.spec.js +0 -28
- package/dist/testing/integration/validate.spec.js +0 -160
- package/dist/testing/integration/watch-dependency.spec.js +0 -33
- package/dist/testing/integration/watch.spec.js +0 -45
- package/dist/testing/requires-kubernetes.js +0 -18
- package/dist/testing/requires-linux-containers.js +0 -18
- package/dist/testing/requires-windows-containers.js +0 -18
- package/dist/testing/test-case.js +0 -75
- package/dist/testing/test-streams.js +0 -28
- package/dist/testing/test-suite-setup.js +0 -3
- package/dist/testing/test-suite.js +0 -3
- package/dist/testing/testing-timeout.js +0 -32
- package/dist/utils/abort-event.spec.js +0 -46
- package/dist/utils/buffer-context.spec.js +0 -38
- package/dist/utils/ci.spec.js +0 -53
- package/dist/utils/debouncer.spec.js +0 -58
- package/dist/utils/emitter.spec.js +0 -34
- package/dist/utils/empty-writable.spec.js +0 -18
- package/dist/utils/environment-config.spec.js +0 -26
- package/dist/utils/fail-never.spec.js +0 -9
- package/dist/utils/sleep.spec.js +0 -22
- package/dist/version.spec.js +0 -12
- package/specs/build-file/caches/spec.md +0 -114
- package/specs/build-file/environment-variables/spec.md +0 -82
- package/specs/build-file/includes/spec.md +0 -60
- package/specs/build-file/references/spec.md +0 -56
- package/specs/build-file/spec.md +0 -62
- package/specs/cli/clean/spec.md +0 -85
- package/specs/cli/down/spec.md +0 -85
- package/specs/cli/execute/spec.md +0 -101
- package/specs/cli/init/spec.md +0 -64
- package/specs/cli/ls/spec.md +0 -66
- package/specs/cli/package/spec.md +0 -110
- package/specs/cli/spec.md +0 -73
- package/specs/cli/store-restore/spec.md +0 -74
- package/specs/cli/up/spec.md +0 -80
- package/specs/cli/validate/spec.md +0 -70
- package/specs/labels/spec.md +0 -91
- package/specs/service/container/spec.md +0 -120
- package/specs/service/kubernetes/spec.md +0 -97
- package/specs/service/spec.md +0 -105
- package/specs/task/caching/spec.md +0 -107
- package/specs/task/container/spec.md +0 -118
- package/specs/task/dependencies/spec.md +0 -70
- package/specs/task/extending/spec.md +0 -74
- package/specs/task/kubernetes/spec.md +0 -125
- package/specs/task/needs/spec.md +0 -59
- package/specs/task/spec.md +0 -87
- package/specs/task/watching/spec.md +0 -69
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
# Feature Specification: CLI: validate
|
|
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/validate.md`
|
|
10
|
-
|
|
11
|
-
## User Scenarios & Testing *(mandatory)*
|
|
12
|
-
|
|
13
|
-
### User Story 1 - Catch build-file errors before running tasks (Priority: P1)
|
|
14
|
-
|
|
15
|
-
A developer edits `.hammerkit.yaml` and wants to verify the configuration is correct before triggering a potentially long-running build. They run `hammerkit validate`, which checks the build file for invalid configurations or missing information and reports any problems immediately.
|
|
16
|
-
|
|
17
|
-
**Why this priority**: Early error detection prevents wasted time on builds that would fail partway through due to a misconfigured build file.
|
|
18
|
-
|
|
19
|
-
**Independent Test**: Introduce a deliberate misconfiguration in a build file (e.g., a task referencing a non-existent dependency). Run `hammerkit validate`. Confirm the command exits with a non-zero code and reports the error. Then fix the file and confirm `hammerkit validate` exits successfully.
|
|
20
|
-
|
|
21
|
-
**Acceptance Scenarios**:
|
|
22
|
-
|
|
23
|
-
1. **Given** a build file with a valid configuration, **When** `hammerkit validate` is run, **Then** the command exits with a success code and reports no errors.
|
|
24
|
-
|
|
25
|
-
2. **Given** a build file with an invalid configuration (e.g., missing required fields, unknown references), **When** `hammerkit validate` is run, **Then** the command exits with a non-zero code and reports a clear description of each problem found.
|
|
26
|
-
|
|
27
|
-
3. **Given** a build file with missing required information, **When** `hammerkit validate` is run, **Then** the specific missing information is identified in the output.
|
|
28
|
-
|
|
29
|
-
### User Story 2 - Use validate as a CI linting step (Priority: P2)
|
|
30
|
-
|
|
31
|
-
A CI pipeline operator adds `hammerkit validate` as an early linting job alongside other linters so that build-file errors are caught on every pull request before any build or test jobs run.
|
|
32
|
-
|
|
33
|
-
**Why this priority**: The docs explicitly recommend this usage pattern; catching errors in CI prevents broken build configurations from reaching the main branch.
|
|
34
|
-
|
|
35
|
-
**Independent Test**: Add `hammerkit validate` to a CI job. Introduce a build-file error on a branch. Confirm the CI job fails. Fix the error and confirm the CI job passes.
|
|
36
|
-
|
|
37
|
-
**Acceptance Scenarios**:
|
|
38
|
-
|
|
39
|
-
1. **Given** `hammerkit validate` is included as a CI job step, **When** a pull request introduces an invalid build file, **Then** the CI job fails and the error is visible in the CI output before any build or test jobs run.
|
|
40
|
-
|
|
41
|
-
2. **Given** a valid build file, **When** `hammerkit validate` runs in CI, **Then** the job passes and downstream jobs are not blocked.
|
|
42
|
-
|
|
43
|
-
### Edge Cases
|
|
44
|
-
|
|
45
|
-
- Running `hammerkit validate` when no build file exists should exit with a non-zero code and report a clear error about the missing file.
|
|
46
|
-
- A build file that is syntactically valid YAML but contains semantically invalid hammerkit configuration (e.g., referencing an undefined task as a dependency) should be caught by validate.
|
|
47
|
-
|
|
48
|
-
## Requirements *(mandatory)*
|
|
49
|
-
|
|
50
|
-
### Functional Requirements
|
|
51
|
-
|
|
52
|
-
- **FR-001**: System MUST parse and validate the build file when `hammerkit validate` is run.
|
|
53
|
-
- **FR-002**: System MUST exit with a success code when the build file contains no invalid configurations or missing required information.
|
|
54
|
-
- **FR-003**: System MUST exit with a non-zero code and report all detected errors when the build file contains invalid configurations or missing required information.
|
|
55
|
-
- **FR-004**: Error output MUST describe each problem clearly enough for a developer to identify and fix it without additional tooling.
|
|
56
|
-
|
|
57
|
-
## Success Criteria *(mandatory)*
|
|
58
|
-
|
|
59
|
-
### Measurable Outcomes
|
|
60
|
-
|
|
61
|
-
- **SC-001**: `hammerkit validate` exits with code 0 for every valid build file accepted by `hammerkit run`.
|
|
62
|
-
- **SC-002**: `hammerkit validate` exits with a non-zero code for every build file that would cause `hammerkit run` to fail due to a configuration error.
|
|
63
|
-
- **SC-003**: Each error message produced by `hammerkit validate` identifies the location or nature of the problem sufficiently for a developer to correct it.
|
|
64
|
-
|
|
65
|
-
## Assumptions
|
|
66
|
-
|
|
67
|
-
- `hammerkit validate` validates the build file in the current working directory (defaulting to `.hammerkit.yaml`); alternate file paths are not documented and are assumed unsupported by this command.
|
|
68
|
-
- The command performs static analysis only — it does not execute tasks, pull images, or make network calls to verify external resources.
|
|
69
|
-
- No `--filter`/`--exclude` options are documented for `validate`; the command always validates the entire build file.
|
|
70
|
-
- "Invalid configurations" includes but may not be limited to: undefined task/service references, missing required fields, and type mismatches in the schema.
|
package/specs/labels/spec.md
DELETED
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
# Feature Specification: Labels: 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/labels/README.md`
|
|
10
|
-
|
|
11
|
-
## User Scenarios & Testing *(mandatory)*
|
|
12
|
-
|
|
13
|
-
### User Story 1 - Filtering tasks to run only a matching subset (Priority: P1)
|
|
14
|
-
|
|
15
|
-
A developer works in a monorepo with tasks for multiple projects. They tag each task with a `project` label and run `hammerkit task -f project=a` to execute only the tasks (and their dependencies) that carry that label.
|
|
16
|
-
|
|
17
|
-
**Why this priority**: Label-based task filtering is the primary value proposition of labels — enabling targeted runs in complex build files without manually listing task names.
|
|
18
|
-
|
|
19
|
-
**Independent Test**: Declare tasks `build-a` and `build-b` with labels `project: a` and `project: b` respectively. Run `hammerkit -f project=a` and confirm only tasks with `project=a` (and their deps) are executed, not `build-b`.
|
|
20
|
-
|
|
21
|
-
**Acceptance Scenarios**:
|
|
22
|
-
|
|
23
|
-
1. **Given** tasks with different `project` label values, **When** `hammerkit -f project=a` is executed, **Then** only tasks whose `project` label equals `a` are included in the run.
|
|
24
|
-
|
|
25
|
-
2. **Given** a task with label `project: a` has a dependency task without any labels, **When** `hammerkit -f project=a` is run, **Then** the unlabelled dependency is still executed as part of the filtered run.
|
|
26
|
-
|
|
27
|
-
### User Story 2 - Excluding tasks by label (Priority: P1)
|
|
28
|
-
|
|
29
|
-
A CI pipeline runs on two separate runners — one macOS, one Linux. The macOS runner should skip all non-iOS tasks. The operator uses `hammerkit -e platform=ios` on the Linux runner to exclude all tasks labelled `platform: ios`.
|
|
30
|
-
|
|
31
|
-
**Why this priority**: Exclusion (`-e`) is equally important to inclusion (`-f`) for CI matrix setups and is documented side-by-side.
|
|
32
|
-
|
|
33
|
-
**Independent Test**: Declare tasks with and without `platform: ios`. Run `hammerkit -e platform=ios` and confirm tasks labelled `platform: ios` are skipped while unlabelled tasks run.
|
|
34
|
-
|
|
35
|
-
**Acceptance Scenarios**:
|
|
36
|
-
|
|
37
|
-
1. **Given** a mix of tasks with and without `platform: ios`, **When** `hammerkit -e platform=ios` is run, **Then** tasks with `platform: ios` are excluded and all other tasks proceed normally.
|
|
38
|
-
|
|
39
|
-
2. **Given** the macOS runner uses `hammerkit -f platform=ios`, **When** executed, **Then** only tasks labelled `platform: ios` run on that host.
|
|
40
|
-
|
|
41
|
-
### User Story 3 - Grouping unrelated tasks under a common label for parallel or sequential execution (Priority: P2)
|
|
42
|
-
|
|
43
|
-
A developer wants a single command to start both an API server and a frontend watcher that have no dependency between them. They assign both a `task: dev` label and run `hammerkit -f task=dev` to start them together.
|
|
44
|
-
|
|
45
|
-
**Why this priority**: Grouping by purpose simplifies developer workflows and reduces the need to remember multiple task names.
|
|
46
|
-
|
|
47
|
-
**Independent Test**: Declare two independent tasks with `task: dev`. Run `hammerkit -f task=dev` and verify both tasks are executed in the same run.
|
|
48
|
-
|
|
49
|
-
**Acceptance Scenarios**:
|
|
50
|
-
|
|
51
|
-
1. **Given** two independent tasks share `task: dev`, **When** `hammerkit -f task=dev` is executed, **Then** both tasks run in the same invocation.
|
|
52
|
-
|
|
53
|
-
2. **Given** tasks labelled `task: release` are also present, **When** `hammerkit -f task=dev` is run, **Then** release tasks are not executed.
|
|
54
|
-
|
|
55
|
-
### Edge Cases
|
|
56
|
-
|
|
57
|
-
- A label is a key-value pair; both key and value must be specified in filter/exclusion flags (e.g. `-f project=a`, not `-f project`).
|
|
58
|
-
- Labels can be defined at the task level or at the build-file level (build-file-level labels apply to all tasks in that file).
|
|
59
|
-
- When using store/restore across CI hosts with label-filtered runs, the `store` and `restore` commands accept the same `-f`/`-e` label arguments (e.g. `hammerkit store -e platform=ios`).
|
|
60
|
-
- A task with no labels is included in runs that use `-f` only if it is a dependency of a matching task; it is never excluded by `-e` (since it has no matching label).
|
|
61
|
-
|
|
62
|
-
## Requirements *(mandatory)*
|
|
63
|
-
|
|
64
|
-
### Functional Requirements
|
|
65
|
-
|
|
66
|
-
- **FR-001**: System MUST support defining labels as key-value pairs on individual tasks.
|
|
67
|
-
- **FR-002**: System MUST support defining labels at the build-file level, applying them to all tasks in that file.
|
|
68
|
-
- **FR-003**: System MUST provide a `-f <key>=<value>` CLI flag that restricts the run to tasks whose labels include the specified key-value pair (and their dependencies).
|
|
69
|
-
- **FR-004**: System MUST provide a `-e <key>=<value>` CLI flag that excludes tasks whose labels include the specified key-value pair from the run.
|
|
70
|
-
- **FR-005**: System MUST apply `-f` and `-e` label arguments to `store` and `restore` CLI commands in addition to task execution commands.
|
|
71
|
-
- **FR-006**: System MUST still execute unlabelled dependency tasks required by label-matched tasks when `-f` filtering is active.
|
|
72
|
-
|
|
73
|
-
### Key Entities
|
|
74
|
-
|
|
75
|
-
- **Label**: A key-value pair attached to a task or build file. Both key and value are strings.
|
|
76
|
-
|
|
77
|
-
## Success Criteria *(mandatory)*
|
|
78
|
-
|
|
79
|
-
### Measurable Outcomes
|
|
80
|
-
|
|
81
|
-
- **SC-001**: Running with `-f <key>=<value>` results in only tasks carrying that label (and their transitive dependencies) being executed, confirmed by execution logs.
|
|
82
|
-
- **SC-002**: Running with `-e <key>=<value>` results in no task carrying that label being executed, confirmed by execution logs.
|
|
83
|
-
- **SC-003**: Build files with overlapping task graphs (monorepo scenario) can be filtered to isolated subsets using a single label flag without editing the build file.
|
|
84
|
-
- **SC-004**: `hammerkit store -e platform=ios` and `hammerkit store -f platform=ios` honour the same label filtering logic as task execution.
|
|
85
|
-
|
|
86
|
-
## Assumptions
|
|
87
|
-
|
|
88
|
-
- Labels are case-sensitive for both key and value comparisons.
|
|
89
|
-
- Multiple `-f` or `-e` flags in a single invocation are assumed to be AND-combined (all specified labels must match), though the docs do not explicitly state this; noted as an assumption.
|
|
90
|
-
- Build-file-level labels are inherited by all tasks in the file but can be overridden at the task level; the docs mention both scopes without describing conflict resolution, so this is assumed additive (file labels + task labels merge).
|
|
91
|
-
- Labels are metadata only and have no effect on task execution order beyond determining which tasks are included or excluded.
|
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
# Feature Specification: Service: Container
|
|
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/service/container.md`
|
|
10
|
-
|
|
11
|
-
## User Scenarios & Testing *(mandatory)*
|
|
12
|
-
|
|
13
|
-
### User Story 1 - Declaring a container service with a healthcheck (Priority: P1)
|
|
14
|
-
|
|
15
|
-
A developer needs a postgres database that is fully ready before any dependent task starts. They declare a container service with an `image`, optional environment variables, and a `healthcheck` command. Hammerkit starts the container and repeatedly executes the healthcheck inside it; when the command exits 0, dependent tasks are unblocked.
|
|
16
|
-
|
|
17
|
-
**Why this priority**: The healthcheck is the central correctness guarantee for container services — without it tasks may fail due to a not-yet-ready service.
|
|
18
|
-
|
|
19
|
-
**Independent Test**: Declare a postgres service with `healthcheck.cmd: "pg_isready -U postgres"`. Run a task that needs it and confirm the task only begins after `pg_isready` returns 0.
|
|
20
|
-
|
|
21
|
-
**Acceptance Scenarios**:
|
|
22
|
-
|
|
23
|
-
1. **Given** a container service with a `healthcheck` configured, **When** a task that needs the service is triggered, **Then** hammerkit does not start the task until the healthcheck command exits with code 0 inside the service container.
|
|
24
|
-
|
|
25
|
-
2. **Given** a container service with no `healthcheck`, **When** a task that needs it is triggered, **Then** hammerkit starts the task as soon as the container is running (without waiting for application readiness).
|
|
26
|
-
|
|
27
|
-
### User Story 2 - Exposing service ports to the host for debugging (Priority: P2)
|
|
28
|
-
|
|
29
|
-
A developer wants to connect a local SQL client to a running postgres service for inspection. They declare a `ports` mapping on the service so the container port is published to the host machine.
|
|
30
|
-
|
|
31
|
-
**Why this priority**: Port exposure is optional for task connectivity but commonly needed during development for direct access.
|
|
32
|
-
|
|
33
|
-
**Independent Test**: Declare a postgres service with `ports: [5432:5432]`. Start the service and verify that a connection to `localhost:5432` from the host succeeds.
|
|
34
|
-
|
|
35
|
-
**Acceptance Scenarios**:
|
|
36
|
-
|
|
37
|
-
1. **Given** a service declares `ports: - 5432:5432`, **When** the service is running, **Then** port 5432 on the host machine is forwarded to the service container.
|
|
38
|
-
|
|
39
|
-
2. **Given** no `ports` are declared on a service, **When** a container task that needs it runs, **Then** the task can still reach the service on all its container ports via the container network without any published ports.
|
|
40
|
-
|
|
41
|
-
### User Story 3 - Persisting service data across restarts with volumes (Priority: P2)
|
|
42
|
-
|
|
43
|
-
A developer wants database data to survive service restarts. They add a `volumes` entry mapping a named volume to the data directory inside the container.
|
|
44
|
-
|
|
45
|
-
**Why this priority**: Without volumes, container services lose all state on every stop/start cycle, which breaks local development workflows.
|
|
46
|
-
|
|
47
|
-
**Independent Test**: Declare a postgres service with a volume, seed it with data, stop and restart the service, and verify the data is still present.
|
|
48
|
-
|
|
49
|
-
**Acceptance Scenarios**:
|
|
50
|
-
|
|
51
|
-
1. **Given** a service declares a named volume (e.g. `postgres-db:/var/lib/postgresql/data`), **When** the service is stopped and restarted, **Then** data written during the first run is still available after restart.
|
|
52
|
-
|
|
53
|
-
### User Story 4 - Mounting local config files into a service container (Priority: P3)
|
|
54
|
-
|
|
55
|
-
A developer needs to supply a custom postgres configuration file. They declare a `mounts` entry to bind-mount the local file into the container at the expected path.
|
|
56
|
-
|
|
57
|
-
**Why this priority**: Mounts are an advanced configuration option; most services do not require them.
|
|
58
|
-
|
|
59
|
-
**Independent Test**: Declare a service with a mount pointing to a local config file. Start the service and verify the file is accessible at the mounted path inside the container.
|
|
60
|
-
|
|
61
|
-
**Acceptance Scenarios**:
|
|
62
|
-
|
|
63
|
-
1. **Given** a service declares `mounts: - "./postgres.conf:/etc/postgresql/postgresql.conf"`, **When** the service container starts, **Then** the file at `./postgres.conf` on the host is readable at `/etc/postgresql/postgresql.conf` inside the container.
|
|
64
|
-
|
|
65
|
-
### User Story 5 - Exporting and importing service volume data (Priority: P3)
|
|
66
|
-
|
|
67
|
-
A developer seeds a local database from a known-good snapshot using `hammerkit store` / `hammerkit restore` to export and import the service's volume data.
|
|
68
|
-
|
|
69
|
-
**Why this priority**: Store/restore for services is a power feature enabling reproducible environments and data sharing between teammates.
|
|
70
|
-
|
|
71
|
-
**Independent Test**: Run a service, write data, run `hammerkit store`, wipe the volume, run `hammerkit restore`, verify data is back.
|
|
72
|
-
|
|
73
|
-
**Acceptance Scenarios**:
|
|
74
|
-
|
|
75
|
-
1. **Given** a running service with volume data, **When** `hammerkit store` is executed for that service, **Then** the volume data is exported to a transferable archive.
|
|
76
|
-
|
|
77
|
-
2. **Given** an exported archive, **When** `hammerkit restore` is executed, **Then** the service volume is populated with the archived data.
|
|
78
|
-
|
|
79
|
-
### Edge Cases
|
|
80
|
-
|
|
81
|
-
- A service without a healthcheck may start a dependent task before the service application is ready to accept connections.
|
|
82
|
-
- Mounts of large or frequently changing directories on macOS and Windows incur significant CPU overhead due to filesystem sync; volumes are preferred for data.
|
|
83
|
-
- A service is stopped automatically when no further tasks in the current run require it (unless kept alive via `hammerkit up`).
|
|
84
|
-
|
|
85
|
-
## Requirements *(mandatory)*
|
|
86
|
-
|
|
87
|
-
### Functional Requirements
|
|
88
|
-
|
|
89
|
-
- **FR-001**: System MUST start container services using the specified `image`.
|
|
90
|
-
- **FR-002**: System MUST pass declared `envs` as environment variables into the service container.
|
|
91
|
-
- **FR-003**: System MUST publish declared `ports` mappings from the service container to the host machine.
|
|
92
|
-
- **FR-004**: System MUST execute the `healthcheck.cmd` inside the service container and only unblock dependent tasks once it exits with code 0.
|
|
93
|
-
- **FR-005**: System MUST mount declared `volumes` (named volumes and bind paths) into the service container to persist data across restarts.
|
|
94
|
-
- **FR-006**: System MUST bind-mount declared `mounts` (host path to container path) into the service container.
|
|
95
|
-
- **FR-007**: System MUST make all container ports of a service reachable from other container tasks in the same network, regardless of whether `ports` are published to the host.
|
|
96
|
-
- **FR-008**: System MUST support store/restore operations on service volumes via the `hammerkit store` and `hammerkit restore` CLI commands.
|
|
97
|
-
|
|
98
|
-
### Key Entities
|
|
99
|
-
|
|
100
|
-
- **Container Service**: A service with an `image`, optional `envs`, `ports`, `healthcheck`, `volumes`, and `mounts`.
|
|
101
|
-
- **Healthcheck**: Contains a `cmd` string executed inside the service container. A zero exit code signals readiness.
|
|
102
|
-
- **Volume**: A named or path-based Docker volume entry mapping a volume identifier to a container path.
|
|
103
|
-
- **Mount**: A bind-mount entry mapping a host file/directory path to a container path.
|
|
104
|
-
|
|
105
|
-
## Success Criteria *(mandatory)*
|
|
106
|
-
|
|
107
|
-
### Measurable Outcomes
|
|
108
|
-
|
|
109
|
-
- **SC-001**: A task dependent on a service with a healthcheck does not start until the healthcheck exits 0, verified by log timestamps.
|
|
110
|
-
- **SC-002**: A declared port mapping results in a reachable port on the host machine while the service is running.
|
|
111
|
-
- **SC-003**: Data written to a volume-backed service directory is present after the service container is stopped and restarted.
|
|
112
|
-
- **SC-004**: A bind-mounted file is readable at its declared container path when the service container is inspected.
|
|
113
|
-
- **SC-005**: `hammerkit store` produces a restorable artifact and `hammerkit restore` repopulates the volume to a matching state.
|
|
114
|
-
|
|
115
|
-
## Assumptions
|
|
116
|
-
|
|
117
|
-
- Container services run within the same Docker network as container tasks; no additional network configuration is needed for inter-container communication.
|
|
118
|
-
- The `healthcheck.cmd` is a shell command string executed inside the service container (not on the host).
|
|
119
|
-
- Services sharing volumes across multiple hammerkit projects run as a single container instance, not duplicated per project.
|
|
120
|
-
- `ports` are not required for container tasks to reach the service — they are solely for host-side access.
|
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
# Feature Specification: Service: Kubernetes
|
|
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/service/kubernetes.md`
|
|
10
|
-
|
|
11
|
-
## User Scenarios & Testing *(mandatory)*
|
|
12
|
-
|
|
13
|
-
### User Story 1 - Forwarding a Kubernetes deployment port to a local task (Priority: P1)
|
|
14
|
-
|
|
15
|
-
A developer wants to run a local task against a staging database that lives in a Kubernetes cluster. They declare an environment with the cluster context, declare a Kubernetes service with a `selector` pointing to the deployment and the ports to forward, then run their task with `--env staging`. Hammerkit establishes the port-forward and the task reaches the database on localhost.
|
|
16
|
-
|
|
17
|
-
**Why this priority**: This is the primary use case of Kubernetes services — accessing remote cluster resources locally without leaving the hammerkit workflow.
|
|
18
|
-
|
|
19
|
-
**Independent Test**: Declare a `prod` environment with a valid kubeconfig context and a service selecting a known deployment. Run `hammerkit <task> --env prod` and confirm the task can connect on the forwarded port.
|
|
20
|
-
|
|
21
|
-
**Acceptance Scenarios**:
|
|
22
|
-
|
|
23
|
-
1. **Given** an environment declares a Kubernetes `context` and a service declares a `selector` with `type: deployment` and a `name`, **When** a task needing the service is run with `--env <env>`, **Then** hammerkit forwards the specified ports from the matching deployment to localhost.
|
|
24
|
-
|
|
25
|
-
2. **Given** an environment named `default` exists in the build file, **When** a task is run without `--env`, **Then** hammerkit uses the `default` environment automatically.
|
|
26
|
-
|
|
27
|
-
3. **Given** the same service definition, **When** the task is run with `--env local` versus `--env prod`, **Then** the port-forward targets the cluster configured in the respective environment.
|
|
28
|
-
|
|
29
|
-
### User Story 2 - Overriding the namespace or context per service (Priority: P2)
|
|
30
|
-
|
|
31
|
-
A developer has a `prod` environment pointing to a GKE cluster with a default namespace of `databases`, but one service lives in the `orders` namespace. They add `namespace: orders` on that service to override only the namespace while inheriting everything else from the environment.
|
|
32
|
-
|
|
33
|
-
**Why this priority**: Per-service overrides allow a single build file to target resources spread across multiple namespaces without duplicating environment definitions.
|
|
34
|
-
|
|
35
|
-
**Independent Test**: Declare an environment with `namespace: databases` and a service that overrides `namespace: orders`. Verify the port-forward targets the `orders` namespace.
|
|
36
|
-
|
|
37
|
-
**Acceptance Scenarios**:
|
|
38
|
-
|
|
39
|
-
1. **Given** an environment declares `namespace: databases` and a service declares `namespace: orders`, **When** the service is activated, **Then** the port-forward targets the `orders` namespace, not `databases`.
|
|
40
|
-
|
|
41
|
-
2. **Given** a service declares neither `namespace`, `context`, nor `kubeconfig`, **When** it is activated, **Then** it inherits all three from the selected environment.
|
|
42
|
-
|
|
43
|
-
### User Story 3 - Connecting to different cloud providers (Priority: P3)
|
|
44
|
-
|
|
45
|
-
A developer's team uses GKE for prod and EKS for staging. They configure two environments with the appropriate context names written by the cloud CLI tools. Services are declared once and are provider-agnostic.
|
|
46
|
-
|
|
47
|
-
**Why this priority**: Multi-provider support broadens the tool's applicability but is a configuration concern, not a behavioral distinction.
|
|
48
|
-
|
|
49
|
-
**Independent Test**: Declare two environments with provider-specific context names and run the same service-needing task against each; verify the correct cluster is targeted each time.
|
|
50
|
-
|
|
51
|
-
**Acceptance Scenarios**:
|
|
52
|
-
|
|
53
|
-
1. **Given** a GKE environment with context `gke_<project>_<location>_<cluster>`, **When** a task is run with that environment, **Then** hammerkit connects to the GKE cluster via the matching kubeconfig entry.
|
|
54
|
-
|
|
55
|
-
2. **Given** an AKS environment with a dedicated `kubeconfig` file, **When** a task is run with that environment, **Then** hammerkit uses the specified kubeconfig file rather than the default `~/.kube/config`.
|
|
56
|
-
|
|
57
|
-
### Edge Cases
|
|
58
|
-
|
|
59
|
-
- If no matching deployment, service, or pod exists in the target cluster namespace, the port-forward fails and the task does not start.
|
|
60
|
-
- If neither the service nor the environment specifies a `namespace`, hammerkit defaults to the `default` Kubernetes namespace.
|
|
61
|
-
- The `kubectl` binary is not required; hammerkit performs port-forwarding directly through the Kubernetes client library.
|
|
62
|
-
- A service can override `context` or `kubeconfig` individually while still inheriting the other fields from the environment.
|
|
63
|
-
|
|
64
|
-
## Requirements *(mandatory)*
|
|
65
|
-
|
|
66
|
-
### Functional Requirements
|
|
67
|
-
|
|
68
|
-
- **FR-001**: System MUST forward ports from a Kubernetes resource to localhost using the `selector` (type and name) and `ports` declared on the service.
|
|
69
|
-
- **FR-002**: System MUST resolve the cluster connection (`context`, `kubeconfig`, `namespace`) from the environment selected via `--env`.
|
|
70
|
-
- **FR-003**: System MUST use the environment named `default` when no `--env` flag is provided and such an environment exists.
|
|
71
|
-
- **FR-004**: System MUST allow a service to override `namespace`, `context`, and/or `kubeconfig` independently, inheriting unoverridden fields from the environment.
|
|
72
|
-
- **FR-005**: System MUST support `selector.type` values of `deployment`, `service`, and `pod`.
|
|
73
|
-
- **FR-006**: System MUST establish port-forwarding without requiring the `kubectl` binary on the host.
|
|
74
|
-
- **FR-007**: System MUST default to the environment's `kubeconfig`, falling back to `$HOME/.kube/config` when neither the service nor the environment specifies one.
|
|
75
|
-
- **FR-008**: System MUST default to the environment's `namespace`, falling back to `default` when neither specifies one.
|
|
76
|
-
|
|
77
|
-
### Key Entities
|
|
78
|
-
|
|
79
|
-
- **Kubernetes Service**: A service entry with `selector` (resource `type` and `name`), `ports` (host:container mappings), and optional `namespace`, `context`, `kubeconfig` overrides.
|
|
80
|
-
- **Environment**: A named block under `environments` containing a `kubernetes` sub-key with `context`, optional `kubeconfig`, and optional `namespace`.
|
|
81
|
-
- **Selector**: Specifies the Kubernetes resource to forward from. Fields: `type` (deployment | service | pod) and `name`.
|
|
82
|
-
|
|
83
|
-
## Success Criteria *(mandatory)*
|
|
84
|
-
|
|
85
|
-
### Measurable Outcomes
|
|
86
|
-
|
|
87
|
-
- **SC-001**: A task that needs a Kubernetes service can connect to the forwarded port on localhost while the task runs, verified by a successful network call.
|
|
88
|
-
- **SC-002**: Switching `--env` between two environments with different cluster contexts results in the port-forward targeting the correct cluster, confirmed by which database responds.
|
|
89
|
-
- **SC-003**: A per-service `namespace` override causes the forward to target the overriding namespace, not the environment's default namespace.
|
|
90
|
-
- **SC-004**: Port-forwarding succeeds on a machine that does not have `kubectl` installed.
|
|
91
|
-
|
|
92
|
-
## Assumptions
|
|
93
|
-
|
|
94
|
-
- Each port mapping in `ports` follows `hostPort:containerPort` notation; hammerkit binds `hostPort` on localhost and forwards to `containerPort` on the selected Kubernetes resource.
|
|
95
|
-
- The `--env` flag is the only mechanism for selecting a Kubernetes environment at runtime; there is no interactive prompt.
|
|
96
|
-
- Kubernetes services in hammerkit are for port-forwarding only — they do not schedule workloads on the cluster (see task/kubernetes.md for running tasks on a cluster).
|
|
97
|
-
- A Kubernetes service without an explicit `image` field is distinguished from a container service by the presence of a `selector` field.
|
package/specs/service/spec.md
DELETED
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
# Feature Specification: Service: 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/service/README.md`
|
|
10
|
-
|
|
11
|
-
## User Scenarios & Testing *(mandatory)*
|
|
12
|
-
|
|
13
|
-
### User Story 1 - Running a dependency service alongside a container task (Priority: P1)
|
|
14
|
-
|
|
15
|
-
A developer needs a database available before their application task starts. They declare a `postgres` service in the build file and add it to the task's `needs` list. When they run the task, hammerkit starts the service first (waiting for its healthcheck to pass if one is configured), then starts the task container — both share a container network so the task can reach the service by its name (e.g. `postgres:5432`).
|
|
16
|
-
|
|
17
|
-
**Why this priority**: Services are the primary mechanism for wiring runtime dependencies to tasks; this is the core use case documented first.
|
|
18
|
-
|
|
19
|
-
**Independent Test**: Create a build file with a postgres service and a container task that `needs` it. Run the task and confirm it connects to postgres using the service name as the hostname.
|
|
20
|
-
|
|
21
|
-
**Acceptance Scenarios**:
|
|
22
|
-
|
|
23
|
-
1. **Given** a build file declares a `postgres` container service and a container task with `needs: [postgres]`, **When** the task is executed, **Then** hammerkit starts the postgres container before the task and the task can reach it at hostname `postgres`.
|
|
24
|
-
|
|
25
|
-
2. **Given** the postgres service has a `healthcheck` configured, **When** the task is about to start, **Then** hammerkit waits until the healthcheck exits with code 0 before launching the task container.
|
|
26
|
-
|
|
27
|
-
### User Story 2 - Using a service from a local (non-container) task (Priority: P2)
|
|
28
|
-
|
|
29
|
-
A developer runs a local task (no `image`) that needs a running service. Because the local task is not part of the container network, hammerkit injects connection details as environment variables so the process can still reach the service.
|
|
30
|
-
|
|
31
|
-
**Why this priority**: Local tasks are common for development workflows and the env-variable injection is the only way they can reach services.
|
|
32
|
-
|
|
33
|
-
**Independent Test**: Declare a container service and a local task that `needs` it. Run the local task and verify it receives `HAMMERKIT_<NAME>_HOST`, `HAMMERKIT_<NAME>_PORT`, and `HAMMERKIT_<NAME>_PORT_<containerPort>` environment variables.
|
|
34
|
-
|
|
35
|
-
**Acceptance Scenarios**:
|
|
36
|
-
|
|
37
|
-
1. **Given** a local task (no `image`) declares `needs: [postgres]` where postgres exposes port `5432`, **When** the task is executed, **Then** the process environment contains `HAMMERKIT_POSTGRES_HOST`, `HAMMERKIT_POSTGRES_PORT`, and `HAMMERKIT_POSTGRES_PORT_5432`.
|
|
38
|
-
|
|
39
|
-
2. **Given** a service exposes multiple ports, **When** a local task needing it runs, **Then** a `HAMMERKIT_<NAME>_PORT_<containerPort>` variable is injected for each exposed container port.
|
|
40
|
-
|
|
41
|
-
### User Story 3 - Manually controlling service lifetime with `up` / `down` (Priority: P2)
|
|
42
|
-
|
|
43
|
-
A developer wants to keep a shared database running across several task runs without restarting it each time. They start the service with `hammerkit up` and stop it later with `hammerkit down`.
|
|
44
|
-
|
|
45
|
-
**Why this priority**: Manual lifecycle control is important for developer-experience workflows where repeated task runs reuse a warm service.
|
|
46
|
-
|
|
47
|
-
**Independent Test**: Run `hammerkit up`, verify the service container is running, then execute a task that needs it (it should skip starting the service), then run `hammerkit down` and verify the container stops.
|
|
48
|
-
|
|
49
|
-
**Acceptance Scenarios**:
|
|
50
|
-
|
|
51
|
-
1. **Given** a service is defined, **When** `hammerkit up` is executed, **Then** the service container starts and remains running.
|
|
52
|
-
|
|
53
|
-
2. **Given** a running service started via `hammerkit up`, **When** `hammerkit down` is executed, **Then** the service container stops.
|
|
54
|
-
|
|
55
|
-
### User Story 4 - Service-to-service and service-to-task dependencies (Priority: P3)
|
|
56
|
-
|
|
57
|
-
A developer has a service that depends on another service (via `needs`) and/or requires a task to run first (via `deps`). Hammerkit resolves the dependency chain before starting the dependent service.
|
|
58
|
-
|
|
59
|
-
**Why this priority**: Dependency chaining for services mirrors task dependencies and is a secondary but documented capability.
|
|
60
|
-
|
|
61
|
-
**Independent Test**: Declare two services where one lists the other in `needs`, and the first also lists a task in `deps`. Verify hammerkit runs the task first, then starts the dependency service, then the dependent service.
|
|
62
|
-
|
|
63
|
-
**Acceptance Scenarios**:
|
|
64
|
-
|
|
65
|
-
1. **Given** service `api` declares `deps: [install]` and `needs: [postgres]`, **When** a task needing `api` is run, **Then** hammerkit runs `install` first, then starts `postgres`, then starts `api`.
|
|
66
|
-
|
|
67
|
-
### Edge Cases
|
|
68
|
-
|
|
69
|
-
- A local task that `needs` a Kubernetes-forwarded service receives the same `HAMMERKIT_<NAME>_*` environment variables, but hammerkit does not provide DNS resolution for it.
|
|
70
|
-
- If no healthcheck is defined, hammerkit may start a dependent task before the service is fully ready.
|
|
71
|
-
- A service name is uppercased when constructing environment variable names (e.g. service `my-db` becomes `HAMMERKIT_MY_DB_HOST`).
|
|
72
|
-
|
|
73
|
-
## Requirements *(mandatory)*
|
|
74
|
-
|
|
75
|
-
### Functional Requirements
|
|
76
|
-
|
|
77
|
-
- **FR-001**: System MUST start all services listed in a task's `needs` before executing that task.
|
|
78
|
-
- **FR-002**: System MUST expose services to container tasks via the container network using the service name as the DNS hostname.
|
|
79
|
-
- **FR-003**: System MUST inject `HAMMERKIT_<NAME>_HOST`, `HAMMERKIT_<NAME>_PORT`, and `HAMMERKIT_<NAME>_PORT_<containerPort>` environment variables into local tasks for each needed service, where `<NAME>` is the uppercased service name.
|
|
80
|
-
- **FR-004**: System MUST wait for a service's healthcheck command to exit with code 0 before launching any task that needs it, when a healthcheck is configured.
|
|
81
|
-
- **FR-005**: System MUST support `hammerkit up` to start services independently of task execution.
|
|
82
|
-
- **FR-006**: System MUST support `hammerkit down` to stop running services.
|
|
83
|
-
- **FR-007**: System MUST resolve service `deps` (tasks) and `needs` (other services) before starting a service, applying the same dependency-ordering rules as for tasks.
|
|
84
|
-
- **FR-008**: System MUST support both container services (image-based) and Kubernetes port-forwarding services within the same build file.
|
|
85
|
-
|
|
86
|
-
### Key Entities
|
|
87
|
-
|
|
88
|
-
- **Service**: A continuous, long-running process declared under the `services` key in a build file. Has a `name`, optional `needs` (other services), optional `deps` (tasks), and is either container-based or Kubernetes-based.
|
|
89
|
-
- **Healthcheck**: An optional check attached to a container service. Contains a `cmd` executed inside the service container; exit code 0 means ready.
|
|
90
|
-
|
|
91
|
-
## Success Criteria *(mandatory)*
|
|
92
|
-
|
|
93
|
-
### Measurable Outcomes
|
|
94
|
-
|
|
95
|
-
- **SC-001**: A container task that `needs` a service starts only after that service is running (and its healthcheck passes, if present), verified across at least one postgres and one redis-style service.
|
|
96
|
-
- **SC-002**: A local task that `needs` a service receives the correct host/port environment variables, confirmed by the task process reading them and connecting successfully.
|
|
97
|
-
- **SC-003**: `hammerkit up` / `hammerkit down` start and stop services within a reasonable time, with no task execution triggered.
|
|
98
|
-
- **SC-004**: Service dependency chains (service needing another service, service needing a task) resolve in the correct order with zero manual intervention.
|
|
99
|
-
|
|
100
|
-
## Assumptions
|
|
101
|
-
|
|
102
|
-
- The service name used as the DNS hostname is exactly as declared in the build file (case-sensitive for the network alias; uppercased only for the environment variable injection).
|
|
103
|
-
- `HAMMERKIT_<NAME>_PORT` refers to the first/primary published port when a service exposes multiple ports.
|
|
104
|
-
- Services are automatically stopped when they are no longer needed by any remaining task in the current run, unless started with `hammerkit up`.
|
|
105
|
-
- The `--env` flag for selecting Kubernetes environments applies equally to Kubernetes-type services; container services are not environment-scoped.
|
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
# Feature Specification: Task: Caching
|
|
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/task/caching.md`
|
|
10
|
-
|
|
11
|
-
## User Scenarios & Testing *(mandatory)*
|
|
12
|
-
|
|
13
|
-
### User Story 1 - Skip unchanged tasks automatically (Priority: P1)
|
|
14
|
-
|
|
15
|
-
A developer runs their build repeatedly. Tasks whose source files have not changed since the last successful run are detected as up-to-date and skipped, saving time.
|
|
16
|
-
|
|
17
|
-
**Why this priority**: Caching is the primary mechanism that makes hammerkit practical for incremental development workflows.
|
|
18
|
-
|
|
19
|
-
**Independent Test**: Run a task with `src` defined twice with no file changes; confirm the second run reports the task as skipped and no commands execute.
|
|
20
|
-
|
|
21
|
-
**Acceptance Scenarios**:
|
|
22
|
-
|
|
23
|
-
1. **Given** a task with `src` files defined, **When** those files have not changed since the last successful run, **Then** the task is skipped.
|
|
24
|
-
2. **Given** a task with `src` files defined, **When** at least one source file has changed, **Then** the task re-executes.
|
|
25
|
-
3. **Given** a task whose dependency has no `src` defined (always runs), **When** the downstream task has `src` defined, **Then** the downstream task is also executed (cannot be skipped when an upstream task was not skipped).
|
|
26
|
-
|
|
27
|
-
### User Story 2 - Choose a cache method per task (Priority: P2)
|
|
28
|
-
|
|
29
|
-
A developer wants finer control over how staleness is detected. They set `cache: modify-date` on a task to use file modification timestamps instead of content checksums, or `cache: none` to disable caching for a specific task.
|
|
30
|
-
|
|
31
|
-
**Why this priority**: Different tasks have different performance/correctness trade-offs for cache detection.
|
|
32
|
-
|
|
33
|
-
**Independent Test**: Set `cache: none` on a task with `src` defined and verify it always re-executes regardless of source changes.
|
|
34
|
-
|
|
35
|
-
**Acceptance Scenarios**:
|
|
36
|
-
|
|
37
|
-
1. **Given** `cache: checksum` on a task, **When** source file content is identical but the modification timestamp has changed, **Then** the task is skipped.
|
|
38
|
-
2. **Given** `cache: modify-date` on a task, **When** a source file's modification timestamp has changed, **Then** the task re-executes even if content is the same.
|
|
39
|
-
3. **Given** `cache: none` on a task, **When** the task is invoked, **Then** it always re-executes regardless of source state.
|
|
40
|
-
|
|
41
|
-
### User Story 3 - Override the default cache method globally at runtime (Priority: P2)
|
|
42
|
-
|
|
43
|
-
A CI engineer wants to run all tasks with checksum-based caching regardless of the per-task defaults. They pass `--cache checksum` on the CLI to override the default for all tasks that did not set an explicit cache method.
|
|
44
|
-
|
|
45
|
-
**Why this priority**: CI environments benefit from a reliable, content-based cache independent of local developer machine defaults.
|
|
46
|
-
|
|
47
|
-
**Independent Test**: Run with `--cache none` and verify all tasks without an explicit `cache` field re-execute.
|
|
48
|
-
|
|
49
|
-
**Acceptance Scenarios**:
|
|
50
|
-
|
|
51
|
-
1. **Given** tasks with no explicit `cache` field, **When** `hammerkit build --cache modify-date` is run, **Then** all those tasks use modify-date caching.
|
|
52
|
-
2. **Given** a task with an explicit `cache: checksum`, **When** `hammerkit build --cache modify-date` is run, **Then** that task continues to use checksum caching (explicit value wins).
|
|
53
|
-
|
|
54
|
-
### User Story 4 - Use a remote cache backend to share results across machines (Priority: P3)
|
|
55
|
-
|
|
56
|
-
A team wants CI and developer machines to share build results. They declare a named cache in the build file with an S3 backend and reference it from their tasks. Hammerkit pulls a cached result before running the task and pushes a new result after a successful run.
|
|
57
|
-
|
|
58
|
-
**Why this priority**: Remote caching eliminates redundant work in distributed teams and CI/CD pipelines.
|
|
59
|
-
|
|
60
|
-
**Independent Test**: Configure an S3-backed cache, run the task on one machine, then run on a second machine that has not built locally and confirm the task is skipped via the remote cache.
|
|
61
|
-
|
|
62
|
-
**Acceptance Scenarios**:
|
|
63
|
-
|
|
64
|
-
1. **Given** a task with a named cache pointing to a remote backend, **When** a matching cache entry exists remotely, **Then** hammerkit pulls the result and skips task execution.
|
|
65
|
-
2. **Given** a task with a named cache pointing to a remote backend, **When** no matching entry exists remotely and the task runs successfully, **Then** hammerkit pushes the result to the remote backend.
|
|
66
|
-
|
|
67
|
-
### Edge Cases
|
|
68
|
-
|
|
69
|
-
- Source glob patterns using `**` do not traverse symlinked directories.
|
|
70
|
-
- Using a large directory such as `node_modules` as a source is explicitly discouraged because recursive traversal is expensive.
|
|
71
|
-
- A task can only be skipped if all of its dependencies before it were also skipped; a single non-skipped ancestor forces the task to re-execute.
|
|
72
|
-
- The default cache method is `modify-date` locally and `checksum` in CI (environment-dependent).
|
|
73
|
-
|
|
74
|
-
## Requirements *(mandatory)*
|
|
75
|
-
|
|
76
|
-
### Functional Requirements
|
|
77
|
-
|
|
78
|
-
- **FR-001**: The system MUST accept a `src` list of files, directories, and glob patterns on a task and use them to determine skip eligibility.
|
|
79
|
-
- **FR-002**: The system MUST skip a task when all its sources are unchanged and all its dependency tasks were also skipped.
|
|
80
|
-
- **FR-003**: The system MUST support three cache methods: `checksum` (content hash), `modify-date` (file mtime), and `none` (always execute).
|
|
81
|
-
- **FR-004**: The system MUST support a `cache` field on a task accepting either a shorthand method string or an object with `name` and optional `method` fields.
|
|
82
|
-
- **FR-005**: The system MUST support a `--cache <method>` CLI flag that sets the default method for all tasks that did not declare an explicit `cache` field.
|
|
83
|
-
- **FR-006**: An explicit `cache` value on a task MUST take precedence over the `--cache` flag.
|
|
84
|
-
- **FR-007**: The system MUST support named caches with remote backends (at minimum S3 and S3-compatible stores).
|
|
85
|
-
- **FR-008**: When a remote cache backend is configured and a cache hit exists remotely, the system MUST pull and restore the result without executing the task.
|
|
86
|
-
- **FR-009**: After a successful task run with a remote backend, the system MUST push the result to the remote backend.
|
|
87
|
-
- **FR-010**: Glob patterns in `src` MUST support the node-glob pattern set including `*`, `?`, `[...]`, `!(…)`, `?(…)`, `+(…)`, `*(…)`, `@(…)`, and `**`.
|
|
88
|
-
|
|
89
|
-
### Key Entities
|
|
90
|
-
|
|
91
|
-
- **Cache entry**: A record of a successful task run keyed by source fingerprint, associated with a cache method and optionally a named backend.
|
|
92
|
-
- **Named cache**: A top-level `caches` declaration with an optional remote backend (`local` or `s3`).
|
|
93
|
-
|
|
94
|
-
## Success Criteria *(mandatory)*
|
|
95
|
-
|
|
96
|
-
### Measurable Outcomes
|
|
97
|
-
|
|
98
|
-
- **SC-001**: A task with unchanged sources is skipped on the second consecutive run with zero commands executed.
|
|
99
|
-
- **SC-002**: Changing a single source file causes exactly the affected task (and its dependents) to re-execute on the next run.
|
|
100
|
-
- **SC-003**: `--cache none` causes all tasks without an explicit `cache` field to execute unconditionally.
|
|
101
|
-
- **SC-004**: With an S3 remote backend, a task result produced on machine A is reused on machine B without re-executing the task commands.
|
|
102
|
-
|
|
103
|
-
## Assumptions
|
|
104
|
-
|
|
105
|
-
- The environment-dependent default (modify-date locally, checksum in CI) is determined by whether hammerkit detects a CI environment variable (e.g., `CI=true`).
|
|
106
|
-
- Redeclaring the built-in `default` cache to use a remote backend affects all tasks that reference the default cache, as described in the build-file caches documentation.
|
|
107
|
-
- The `s3` backend covers S3-compatible stores (MinIO, Cloudflare R2, Google Cloud Storage); no additional backend types are documented for 1.6.0.
|