hammerkit 1.5.1 → 1.6.0-alpha.2
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/.agents/FEEDBACK.md +74 -0
- package/.agents/skills/before-commit/SKILL.md +101 -0
- package/.agents/skills/do-the-work/SKILL.md +64 -0
- package/.agents/skills/fix-ci/SKILL.md +98 -0
- package/.agents/skills/speckit-git-commit/SKILL.md +53 -0
- package/.agents/skills/speckit-git-feature/SKILL.md +72 -0
- package/.agents/skills/speckit-git-initialize/SKILL.md +54 -0
- package/.agents/skills/speckit-git-remote/SKILL.md +50 -0
- package/.agents/skills/speckit-git-validate/SKILL.md +54 -0
- package/.gitbook.yaml +5 -0
- package/.specify/extensions/.registry +37 -0
- package/.specify/extensions/git/README.md +100 -0
- package/.specify/extensions/git/commands/speckit.git.commit.md +48 -0
- package/.specify/extensions/git/commands/speckit.git.feature.md +67 -0
- package/.specify/extensions/git/commands/speckit.git.initialize.md +49 -0
- package/.specify/extensions/git/commands/speckit.git.remote.md +45 -0
- package/.specify/extensions/git/commands/speckit.git.validate.md +49 -0
- package/.specify/extensions/git/config-template.yml +62 -0
- package/.specify/extensions/git/extension.yml +140 -0
- package/.specify/extensions/git/git-config.yml +62 -0
- package/.specify/extensions.yml +149 -0
- package/.specify/feature.json +3 -0
- package/.specify/init-options.json +10 -0
- package/.specify/integration.json +15 -0
- package/.specify/integrations/claude.manifest.json +16 -0
- package/.specify/integrations/speckit.manifest.json +17 -0
- package/.specify/memory/constitution.md +162 -0
- package/.specify/templates/checklist-template.md +40 -0
- package/.specify/templates/constitution-template.md +50 -0
- package/.specify/templates/plan-template.md +113 -0
- package/.specify/templates/spec-template.md +131 -0
- package/.specify/templates/tasks-template.md +252 -0
- package/.specify/workflows/speckit/workflow.yml +77 -0
- package/.specify/workflows/workflow-registry.json +13 -0
- package/AGENTS.md +94 -0
- package/Dockerfile +3 -3
- package/FOLLOWUPS.md +34 -0
- package/README.md +9 -7
- package/SUMMARY.md +30 -5
- package/VALUES.md +53 -0
- package/build.schema.json +542 -408
- package/dist/cache/auto-pull.spec.js +123 -0
- package/dist/cache/backends/local-cache-backend.js +56 -0
- package/dist/cache/backends/local-cache-backend.spec.js +72 -0
- package/dist/cache/backends/s3-cache-backend.js +124 -0
- package/dist/cache/backends/s3-cache-backend.spec.js +118 -0
- package/dist/cache/cache-backend-registry.js +21 -0
- package/dist/cache/cache-backend-registry.spec.js +22 -0
- package/dist/cache/cache-backend.js +3 -0
- package/dist/cache/resolve-cache.js +60 -0
- package/dist/cache/resolve-cache.spec.js +52 -0
- package/dist/cli.js +112 -82
- package/dist/cli.spec.js +20 -2
- package/dist/console/get-logger.spec.js +23 -0
- package/dist/docker/get-default-platform.spec.js +29 -0
- package/dist/docker/longest-common-prefix.spec.js +26 -0
- package/dist/docker/package.js +283 -0
- package/dist/docker/package.spec.js +145 -0
- package/dist/docker/pull.js +4 -4
- package/dist/docker/remove-container.spec.js +32 -0
- package/dist/docker/stream.js +3 -3
- package/dist/docker/using-container.js +29 -11
- package/dist/docker/using-container.spec.js +90 -0
- package/dist/environment/merge-environment-variables.js +21 -0
- package/dist/environment/merge-environment-variables.spec.js +24 -0
- package/dist/environment/replace-env-variables.js +43 -10
- package/dist/environment/replace-env-variables.spec.js +47 -0
- package/dist/executer/abort.spec.js +18 -0
- package/dist/executer/await-completed-dependencies.js +56 -0
- package/dist/executer/cache-metadata.js +11 -0
- package/dist/executer/check-readiness.js +1 -1
- package/dist/{planner/work-node-port.js → executer/container-bind.js} +1 -1
- package/dist/executer/docker-service.js +110 -86
- package/dist/executer/docker-task.js +132 -0
- package/dist/executer/environment-mock.js +3 -4
- package/dist/executer/environment-mock.spec.js +22 -0
- package/dist/executer/event-cache.js +60 -127
- package/dist/executer/execute-command.js +4 -3
- package/dist/executer/execute-docker.js +32 -14
- package/dist/executer/execute-work-service.js +61 -0
- package/dist/executer/execute-work-task.js +107 -0
- package/dist/executer/execute-work-tree.js +27 -0
- package/dist/executer/execute.spec.js +14 -10
- package/dist/executer/execution-steps.js +47 -31
- package/dist/executer/get-container-binds.js +19 -0
- package/dist/executer/get-docker-executor.js +12 -12
- package/dist/executer/get-scheduler-down-result.js +12 -0
- package/dist/executer/get-scheduler-execute-result.js +28 -0
- package/dist/executer/get-scheduler-up-result.js +12 -0
- package/dist/executer/get-service-containers.js +14 -0
- package/dist/executer/get-service-containers.spec.js +59 -0
- package/dist/executer/get-service-env-hints.js +35 -0
- package/dist/executer/get-service-env-hints.spec.js +65 -0
- package/dist/executer/kubernetes-service.js +116 -48
- package/dist/executer/label-values.js +29 -46
- package/dist/executer/label-values.spec.js +61 -0
- package/dist/executer/lazy-resolver.js +16 -0
- package/dist/executer/lazy-resolver.spec.js +20 -0
- package/dist/executer/local-task.js +44 -0
- package/dist/executer/process-manager.js +16 -70
- package/dist/executer/process-manager.spec.js +39 -76
- package/dist/executer/reset-work-tree.js +30 -0
- package/dist/executer/scheduler/check-for-loop.js +17 -7
- package/dist/executer/scheduler/{scheduler-state.js → dependency-state.js} +1 -1
- package/dist/executer/scheduler/enqueue-next.js +83 -19
- package/dist/executer/scheduler/enqueue-next.spec.js +136 -0
- package/dist/executer/scheduler/{node-state.js → need-state.js} +1 -1
- package/dist/{parser/build-file.js → executer/scheduler/task-state.js} +1 -1
- package/dist/executer/set-user-permission.js +1 -1
- package/dist/executer/state-resolver.js +61 -0
- package/dist/executer/state-resolver.spec.js +37 -0
- package/dist/executer/state.js +37 -31
- package/dist/executer/state.spec.js +66 -0
- package/dist/executer/states.spec.js +14 -0
- package/dist/executer/watch-loop.js +68 -0
- package/dist/executer/watch-state-key.js +44 -0
- package/dist/executer/work-scope.js +1 -5
- package/dist/file/get-file-context.js +24 -3
- package/dist/file/get-file-context.spec.js +178 -0
- package/dist/file/move-files.js +2 -2
- package/dist/file/move-files.spec.js +48 -0
- package/dist/index.js +7 -8
- package/dist/json-schema.js +12 -0
- package/dist/kubernetes/apply.js +34 -0
- package/dist/kubernetes/await-running-state.js +71 -0
- package/dist/kubernetes/ensure-http-route.js +53 -0
- package/dist/kubernetes/ensure-http-route.spec.js +70 -0
- package/dist/kubernetes/ensure-ingress.js +54 -0
- package/dist/kubernetes/ensure-kubernetes-deployment-exists.js +105 -0
- package/dist/kubernetes/ensure-kubernetes-persistent-volume-claim-exists.js +40 -0
- package/dist/kubernetes/ensure-kubernetes-service-exists.js +36 -0
- package/dist/kubernetes/ensure-namespace.js +43 -0
- package/dist/kubernetes/ensure-namespace.spec.js +39 -0
- package/dist/kubernetes/ensure-persistent-data.js +158 -0
- package/dist/kubernetes/get-service-ip.js +11 -0
- package/dist/kubernetes/kubernetes-instance.js +48 -0
- package/dist/kubernetes/remove-persistent-data.js +19 -0
- package/dist/kubernetes/resolve-pod-name.js +42 -0
- package/dist/kubernetes/resolve-pod-name.spec.js +81 -0
- package/dist/kubernetes/resources.js +12 -0
- package/dist/kubernetes/restore-kubernetes-data.js +42 -0
- package/dist/kubernetes/store-kubernetes-data.js +39 -0
- package/dist/kubernetes/volumes.js +64 -0
- package/dist/log.js +69 -69
- package/dist/log.spec.js +131 -0
- package/dist/logging/grouped-logger.js +17 -15
- package/dist/logging/grouped-logger.spec.js +84 -0
- package/dist/logging/interactive-logger.js +1 -1
- package/dist/logging/interactive-logger.spec.js +57 -0
- package/dist/logging/live-logger.js +2 -2
- package/dist/logging/live-logger.spec.js +37 -0
- package/dist/logging/loggers.spec.js +154 -0
- package/dist/optimizer/calculate-checksum.spec.js +34 -0
- package/dist/optimizer/get-cache-directory.js +6 -6
- package/dist/optimizer/get-cache-directory.spec.js +59 -0
- package/dist/optimizer/get-work-cache-stats.js +53 -0
- package/dist/optimizer/get-work-cache-stats.spec.js +79 -0
- package/dist/{parser/build-file-task.js → optimizer/work-cache-stats.js} +1 -1
- package/dist/optimizer/work-task-cache-description.js +25 -0
- package/dist/optimizer/work-task-cache-description.spec.js +45 -0
- package/dist/parser/default-build-file.js +14 -7
- package/dist/parser/default-build-file.spec.js +48 -0
- package/dist/parser/parse-label-arguments.spec.js +27 -0
- package/dist/parser/read-build-file.js +2 -13
- package/dist/parser/read-build-file.spec.js +89 -0
- package/dist/parser/read-env-file.js +4 -5
- package/dist/parser/read-env-file.spec.js +42 -0
- package/dist/planner/utils/append-work-dependencies.js +55 -0
- package/dist/planner/utils/append-work-environment.js +22 -0
- package/dist/planner/utils/append-work-needs.js +18 -0
- package/dist/planner/utils/append-work-service.js +107 -0
- package/dist/planner/utils/append-work-service.spec.js +71 -0
- package/dist/planner/utils/append-work-task.js +81 -0
- package/dist/planner/utils/get-container-user.js +14 -0
- package/dist/planner/utils/get-container-user.spec.js +30 -0
- package/dist/planner/utils/get-default-kube-config.js +10 -0
- package/dist/planner/utils/get-default-kube-config.spec.js +11 -0
- package/dist/planner/utils/get-work-runtime.js +44 -0
- package/dist/planner/utils/normalize-path.js +1 -1
- package/dist/planner/utils/normalize-path.spec.js +24 -0
- package/dist/planner/utils/parse-work-command.js +80 -0
- package/dist/planner/utils/parse-work-command.spec.js +21 -0
- package/dist/planner/utils/parse-work-generate.js +38 -0
- package/dist/planner/utils/parse-work-generate.spec.js +45 -0
- package/dist/planner/utils/{parse-work-node-mount.js → parse-work-mount.js} +10 -7
- package/dist/planner/utils/{parse-work-node-mount.spec.js → parse-work-mount.spec.js} +14 -11
- package/dist/planner/utils/parse-work-mounts.js +13 -0
- package/dist/planner/utils/parse-work-mounts.spec.js +19 -0
- package/dist/planner/utils/{parse-work-node-port.js → parse-work-port.js} +11 -8
- package/dist/planner/utils/parse-work-port.spec.js +24 -0
- package/dist/planner/utils/parse-work-ports.js +10 -0
- package/dist/planner/utils/parse-work-ports.spec.js +24 -0
- package/dist/planner/utils/parse-work-source.js +72 -0
- package/dist/planner/utils/parse-work-source.spec.js +58 -0
- package/dist/planner/utils/parse-work-volume.js +45 -0
- package/dist/planner/utils/parse-work-volume.spec.js +18 -0
- package/dist/planner/utils/plan-work-tasks.js +41 -0
- package/dist/planner/utils/plan-work-tasks.spec.js +30 -0
- package/dist/planner/utils/plan-work-volume.js +1 -0
- package/dist/planner/utils/plan-work-volume.spec.js +33 -0
- package/dist/planner/utils/split-name.js +2 -2
- package/dist/planner/utils/split-name.spec.js +16 -0
- package/dist/planner/utils/template-value.js +1 -2
- package/dist/planner/utils/template-value.spec.js +14 -0
- package/dist/planner/validate.js +71 -38
- package/dist/planner/validate.spec.js +251 -0
- package/dist/planner/work-cache-id.js +12 -0
- package/dist/planner/work-cache-id.spec.js +173 -0
- package/dist/planner/work-command.js +3 -0
- package/dist/planner/work-environment.js +3 -0
- package/dist/planner/work-healthcheck.js +3 -0
- package/dist/planner/{work-node-status.js → work-item-status.js} +16 -62
- package/dist/{parser/build-file-service.js → planner/work-item-validation.js} +1 -1
- package/dist/planner/work-item.js +16 -0
- package/dist/{optimizer/work-node-cache-stats.js → planner/work-kubernetes-selector.js} +1 -1
- package/dist/planner/{work-nodes.js → work-port.js} +1 -1
- package/dist/planner/work-runtime-docker.js +224 -0
- package/dist/planner/work-runtime-kubernetes.js +264 -0
- package/dist/planner/work-runtime-local.js +109 -0
- package/dist/planner/work-runtime-local.spec.js +193 -0
- package/dist/planner/work-service-id.js +15 -5
- package/dist/planner/work-source.js +3 -0
- package/dist/planner/work-task.js +8 -0
- package/dist/program-commands.spec.js +84 -0
- package/dist/program.js +219 -73
- package/dist/program.spec.js +34 -11
- package/dist/run-program.js +28 -0
- package/dist/{executer/process.js → runtime/runtime.js} +1 -1
- package/dist/schema/build-file-base-task-schema.js +27 -0
- package/dist/schema/build-file-container-service-schema.js +28 -0
- package/dist/schema/build-file-container-task-schema.js +13 -0
- package/dist/schema/build-file-environment-schema-ingress.js +14 -0
- package/dist/schema/build-file-environment-schema.js +23 -0
- package/dist/schema/build-file-kubernetes-service-schema.js +17 -0
- package/dist/schema/build-file-kubernetes-service-selector-schema.js +9 -0
- package/dist/schema/build-file-local-platform-schema.js +9 -0
- package/dist/schema/build-file-local-task-schema.js +12 -0
- package/dist/schema/build-file-need-schema.js +12 -0
- package/dist/schema/build-file-schema.js +23 -0
- package/dist/schema/build-file-schema.spec.js +61 -0
- package/dist/schema/build-file-service-container-healthcheck.js +8 -0
- package/dist/schema/build-file-service-schema.js +15 -0
- package/dist/schema/build-file-task-command-schema.js +12 -0
- package/dist/schema/build-file-task-schema.js +12 -0
- package/dist/schema/build-file-volume-schema.js +14 -0
- package/dist/schema/cache-schema.js +31 -0
- package/dist/schema/doc-build-files.spec.js +223 -0
- package/dist/schema/envs-schema.js +6 -0
- package/dist/schema/labels-schema.js +6 -0
- package/dist/schema/parse-context.js +3 -0
- package/dist/schema/parse-error.js +12 -0
- package/dist/schema/parse-error.spec.js +18 -0
- package/dist/schema/reference-parser.js +209 -0
- package/dist/schema/reference-parser.spec.js +83 -0
- package/dist/schema/schema-parser.js +66 -0
- package/dist/schema/schema-parser.spec.js +80 -0
- package/dist/schema/type-guards.spec.js +23 -0
- package/dist/schema/work-scope-parser.js +77 -0
- package/dist/schema/work-scope-parser.spec.js +66 -0
- package/dist/service/update-service-status.js +11 -0
- package/dist/service/update-service-status.spec.js +27 -0
- package/dist/testing/ensure-local-registry.js +134 -0
- package/dist/testing/example-test-suite.js +16 -18
- package/dist/testing/expect.js +46 -22
- package/dist/testing/http-get.js +21 -0
- package/dist/testing/integration/cache.spec.js +13 -14
- package/dist/testing/integration/cancellation.spec.js +14 -9
- package/dist/testing/integration/clean.spec.js +14 -11
- package/dist/testing/integration/cmd.spec.js +1 -1
- package/dist/testing/integration/concurrency.spec.js +3 -3
- package/dist/testing/integration/docker-package-registry.spec.js +44 -0
- package/dist/testing/integration/docker-service-crash.spec.js +36 -0
- package/dist/testing/integration/docker.spec.js +4 -3
- package/dist/testing/integration/env.spec.js +9 -8
- package/dist/testing/integration/environment.spec.js +97 -0
- package/dist/testing/integration/error.spec.js +5 -4
- package/dist/testing/integration/export.spec.js +22 -0
- package/dist/testing/integration/extend.spec.js +9 -12
- package/dist/testing/integration/glob.spec.js +12 -20
- package/dist/testing/integration/include.spec.js +3 -3
- package/dist/testing/integration/invalid.spec.js +2 -3
- package/dist/testing/integration/invalid_loop.spec.js +49 -6
- package/dist/testing/integration/k8s-state-cache.spec.js +41 -0
- package/dist/testing/integration/kubernetes-service.spec.js +148 -0
- package/dist/testing/integration/kubernetes.spec.js +93 -9
- package/dist/testing/integration/labels.spec.js +6 -11
- package/dist/testing/integration/local.spec.js +1 -1
- package/dist/testing/integration/monorepo.spec.js +4 -4
- package/dist/testing/integration/multi-line.spec.js +3 -3
- package/dist/testing/integration/reference.spec.js +4 -5
- package/dist/testing/integration/services.spec.js +22 -4
- package/dist/testing/integration/store.spec.js +15 -16
- package/dist/testing/integration/unknown.spec.js +20 -18
- package/dist/testing/integration/validate.spec.js +143 -26
- package/dist/testing/integration/watch-dependency.spec.js +11 -8
- package/dist/testing/integration/watch.spec.js +15 -16
- package/dist/testing/requires-kubernetes.js +18 -0
- package/dist/testing/requires-linux-containers.js +18 -0
- package/dist/testing/requires-windows-containers.js +18 -0
- package/dist/testing/test-case.js +75 -0
- package/dist/testing/test-streams.js +28 -0
- package/dist/testing/testing-timeout.js +32 -0
- package/dist/utils/abort-event.js +10 -4
- package/dist/utils/abort-event.spec.js +46 -0
- package/dist/utils/buffer-context.js +44 -0
- package/dist/utils/buffer-context.spec.js +38 -0
- package/dist/utils/ci.spec.js +53 -0
- package/dist/utils/emitter.spec.js +34 -0
- package/dist/utils/empty-writable.spec.js +18 -0
- package/dist/utils/environment-config.js +4 -1
- package/dist/utils/environment-config.spec.js +26 -0
- package/dist/utils/fail-never.spec.js +9 -0
- package/dist/version.js +9 -0
- package/dist/version.spec.js +12 -0
- package/package.json +41 -23
- package/specs/build-file/caches/spec.md +114 -0
- package/specs/build-file/environment-variables/spec.md +82 -0
- package/specs/build-file/includes/spec.md +60 -0
- package/specs/build-file/references/spec.md +56 -0
- package/specs/build-file/spec.md +62 -0
- package/specs/cli/clean/spec.md +85 -0
- package/specs/cli/down/spec.md +85 -0
- package/specs/cli/execute/spec.md +101 -0
- package/specs/cli/init/spec.md +64 -0
- package/specs/cli/ls/spec.md +66 -0
- package/specs/cli/package/spec.md +110 -0
- package/specs/cli/spec.md +73 -0
- package/specs/cli/store-restore/spec.md +74 -0
- package/specs/cli/up/spec.md +80 -0
- package/specs/cli/validate/spec.md +70 -0
- package/specs/labels/spec.md +91 -0
- package/specs/service/container/spec.md +120 -0
- package/specs/service/kubernetes/spec.md +97 -0
- package/specs/service/spec.md +105 -0
- package/specs/task/caching/spec.md +107 -0
- package/specs/task/container/spec.md +118 -0
- package/specs/task/dependencies/spec.md +70 -0
- package/specs/task/extending/spec.md +74 -0
- package/specs/task/kubernetes/spec.md +125 -0
- package/specs/task/needs/spec.md +59 -0
- package/specs/task/spec.md +87 -0
- package/specs/task/watching/spec.md +69 -0
- package/dist/console/console-context-mock.js +0 -14
- package/dist/environment/get-process-env.js +0 -15
- package/dist/executer/create-scheduler-state.js +0 -32
- package/dist/executer/docker-node.js +0 -123
- package/dist/executer/hierarchy-scheduler.js +0 -94
- package/dist/executer/local-node.js +0 -57
- package/dist/executer/schedule.js +0 -137
- package/dist/executer/start-node.js +0 -67
- package/dist/executer/watch-node.js +0 -56
- package/dist/executer/watch-service.js +0 -54
- package/dist/file/get-file-context-mock.js +0 -187
- package/dist/file/get-file-context-mock.spec.js +0 -56
- package/dist/get-work-context.js +0 -29
- package/dist/optimizer/get-work-node-cache-stats.js +0 -104
- package/dist/optimizer/read-work-node-cache.js +0 -19
- package/dist/optimizer/work-node-cache-description.js +0 -32
- package/dist/optimizer/write-work-node-cache.js +0 -21
- package/dist/parser/build-file-task-command.js +0 -3
- package/dist/parser/build-file-task-platform.js +0 -3
- package/dist/parser/build-file-task-source.js +0 -3
- package/dist/parser/get-build-file.js +0 -9
- package/dist/parser/parse-boolean.js +0 -29
- package/dist/parser/parse-boolean.spec.js +0 -48
- package/dist/parser/parse-build-file-references.js +0 -35
- package/dist/parser/parse-build-file-services.js +0 -58
- package/dist/parser/parse-build-file-task-command.js +0 -36
- package/dist/parser/parse-build-file-task-source.js +0 -63
- package/dist/parser/parse-build-file.js +0 -125
- package/dist/parser/parse-context.js +0 -13
- package/dist/parser/parse-envs.js +0 -29
- package/dist/parser/parse-healthcheck.js +0 -28
- package/dist/parser/parse-service-selector.js +0 -39
- package/dist/parser/parse-string-array.js +0 -20
- package/dist/parser/parse-string-map.js +0 -30
- package/dist/parser/parse-string.js +0 -20
- package/dist/planner/utils/get-container-mounts.js +0 -24
- package/dist/planner/utils/parse-work-service-volume.js +0 -23
- package/dist/planner/utils/plan-work-command.js +0 -21
- package/dist/planner/utils/plan-work-dependency.js +0 -36
- package/dist/planner/utils/plan-work-node.js +0 -293
- package/dist/planner/utils/plan-work-nodes.js +0 -78
- package/dist/planner/utils/plan-work-tree.js +0 -15
- package/dist/planner/work-context.js +0 -23
- package/dist/planner/work-node-command.js +0 -3
- package/dist/planner/work-node-id.js +0 -12
- package/dist/planner/work-node-id.spec.js +0 -106
- package/dist/planner/work-node-path.js +0 -2
- package/dist/planner/work-node-source.js +0 -3
- package/dist/planner/work-node-validation.js +0 -3
- package/dist/planner/work-node.js +0 -8
- package/dist/planner/work-service-volume.js +0 -3
- package/dist/planner/work-services.js +0 -3
- package/dist/start-watch-processes.js +0 -28
- package/dist/testing/cli.js +0 -73
- package/dist/testing/create-build-file.js +0 -9
- package/dist/testing/get-logger.js +0 -23
- package/dist/testing/get-node.js +0 -12
- package/dist/testing/get-test-context.js +0 -19
- package/dist/testing/integration/program.spec.js +0 -25
- package/dist/testing/lazy.js +0 -17
- package/dist/testing/start-watch-processes.js +0 -29
- package/dist/testing/test-suite-options.js +0 -3
- package/dist/utils/abortable-function.js +0 -71
- package/logs/cache-console.log +0 -0
- package/logs/cache-status.log +0 -6
- package/logs/cache-stout.log +0 -0
- package/logs/cancellation-console.log +0 -0
- package/logs/cancellation-status.log +0 -3
- package/logs/cancellation-stout.log +0 -3
- package/logs/clean-console.log +0 -0
- package/logs/clean-status.log +0 -37
- package/logs/clean-stout.log +0 -95
- package/logs/cmd-console.log +0 -0
- package/logs/cmd-status.log +0 -8
- package/logs/cmd-stout.log +0 -9
- package/logs/concurrency-console.log +0 -0
- package/logs/concurrency-status.log +0 -14
- package/logs/concurrency-stout.log +0 -88
- package/logs/docker-console.log +0 -0
- package/logs/docker-status.log +0 -14
- package/logs/docker-stout.log +0 -39
- package/logs/env-console.log +0 -1
- package/logs/env-status.log +0 -1
- package/logs/env-stout.log +0 -0
- package/logs/error-console.log +0 -0
- package/logs/error-status.log +0 -10
- package/logs/error-stout.log +0 -36
- package/logs/extend-console.log +0 -0
- package/logs/extend-status.log +0 -1
- package/logs/extend-stout.log +0 -0
- package/logs/glob-console.log +0 -28
- package/logs/glob-status.log +0 -23
- package/logs/glob-stout.log +0 -52
- package/logs/hello-world-node-console.log +0 -0
- package/logs/hello-world-node-status.log +0 -37
- package/logs/hello-world-node-stout.log +0 -41
- package/logs/include-console.log +0 -0
- package/logs/include-status.log +0 -3
- package/logs/include-stout.log +0 -0
- package/logs/invalid-console.log +0 -0
- package/logs/invalid-status.log +0 -1
- package/logs/invalid-stout.log +0 -0
- package/logs/invalid_loop-console.log +0 -0
- package/logs/invalid_loop-status.log +0 -1
- package/logs/invalid_loop-stout.log +0 -11
- package/logs/labels-console.log +0 -0
- package/logs/labels-status.log +0 -1
- package/logs/labels-stout.log +0 -0
- package/logs/local-console.log +0 -0
- package/logs/local-status.log +0 -7
- package/logs/local-stout.log +0 -8
- package/logs/monorepo-console.log +0 -0
- package/logs/monorepo-status.log +0 -152
- package/logs/monorepo-stout.log +0 -2354
- package/logs/multi-line-console.log +0 -0
- package/logs/multi-line-status.log +0 -1
- package/logs/multi-line-stout.log +0 -0
- package/logs/reference-console.log +0 -0
- package/logs/reference-status.log +0 -5
- package/logs/reference-stout.log +0 -0
- package/logs/services-console.log +0 -0
- package/logs/services-status.log +0 -129
- package/logs/services-stout.log +0 -639
- package/logs/store-restore-console.log +0 -0
- package/logs/store-restore-status.log +0 -40
- package/logs/store-restore-stout.log +0 -97
- package/logs/unknown-console.log +0 -3
- package/logs/unknown-status.log +0 -1
- package/logs/unknown-stout.log +0 -0
- package/logs/validate-console.log +0 -0
- package/logs/validate-status.log +0 -2
- package/logs/validate-stout.log +0 -0
- package/logs/watch-console.log +0 -0
- package/logs/watch-dependency-console.log +0 -0
- package/logs/watch-dependency-status.log +0 -176
- package/logs/watch-dependency-stout.log +0 -779
- package/logs/watch-status.log +0 -100
- package/logs/watch-stout.log +0 -349
package/dist/log.spec.js
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const stream_1 = require("stream");
|
|
4
|
+
const log_1 = require("./log");
|
|
5
|
+
const environment_mock_1 = require("./executer/environment-mock");
|
|
6
|
+
function capture() {
|
|
7
|
+
const chunks = [];
|
|
8
|
+
const stream = new stream_1.Writable({
|
|
9
|
+
write(chunk, _enc, cb) {
|
|
10
|
+
chunks.push(chunk.toString());
|
|
11
|
+
cb();
|
|
12
|
+
},
|
|
13
|
+
});
|
|
14
|
+
return { stream, read: () => chunks.join('') };
|
|
15
|
+
}
|
|
16
|
+
describe('log helpers', () => {
|
|
17
|
+
describe('getLogs', () => {
|
|
18
|
+
it('splits on \\r?\\n and drops empty lines', () => {
|
|
19
|
+
expect((0, log_1.getLogs)('a\r\nb\nc\n\n')).toEqual(['a', 'b', 'c']);
|
|
20
|
+
});
|
|
21
|
+
it('accepts a Buffer', () => {
|
|
22
|
+
expect((0, log_1.getLogs)(Buffer.from('x\ny'))).toEqual(['x', 'y']);
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
describe('getLogLevel', () => {
|
|
26
|
+
it.each(['debug', 'info', 'warn', 'error'])('returns a string containing %s', (level) => {
|
|
27
|
+
expect((0, log_1.getLogLevel)(level)).toContain(level);
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
describe('getErrorMessage', () => {
|
|
31
|
+
it('uses Error.message for Error instances', () => {
|
|
32
|
+
expect((0, log_1.getErrorMessage)(new Error('boom'))).toBe('boom');
|
|
33
|
+
});
|
|
34
|
+
it('returns a string value unchanged', () => {
|
|
35
|
+
expect((0, log_1.getErrorMessage)('plain')).toBe('plain');
|
|
36
|
+
});
|
|
37
|
+
it('falls back to "unknown error <value>" for other values', () => {
|
|
38
|
+
expect((0, log_1.getErrorMessage)({ x: 1 })).toMatch(/^unknown error/);
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
describe('consoleContext', () => {
|
|
42
|
+
it('writes formatted lines for each level', () => {
|
|
43
|
+
const cap = capture();
|
|
44
|
+
const ctx = (0, log_1.consoleContext)(cap.stream);
|
|
45
|
+
ctx.debug('d-msg');
|
|
46
|
+
ctx.info('i-msg');
|
|
47
|
+
ctx.warn('w-msg');
|
|
48
|
+
ctx.error('e-msg');
|
|
49
|
+
const out = cap.read();
|
|
50
|
+
expect(out).toContain('d-msg');
|
|
51
|
+
expect(out).toContain('i-msg');
|
|
52
|
+
expect(out).toContain('w-msg');
|
|
53
|
+
expect(out).toContain('e-msg');
|
|
54
|
+
// 4 newlines, one per call
|
|
55
|
+
expect(out.split('\n').filter((l) => l.length > 0)).toHaveLength(4);
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
describe('getType / getNodeName / getMaxNameLength', () => {
|
|
59
|
+
it('formats the type with the configured padding', () => {
|
|
60
|
+
expect((0, log_1.getType)('cli')).toContain('cli');
|
|
61
|
+
});
|
|
62
|
+
it('pads name to a given length', () => {
|
|
63
|
+
expect((0, log_1.getNodeName)('a', 4)).toContain('a');
|
|
64
|
+
// pads with spaces to reach the requested length, then color codes
|
|
65
|
+
// surround it — assert by inspecting the visible characters
|
|
66
|
+
// ANSI color wrappers can only add chars, so the rendered string is at
|
|
67
|
+
// least the requested width even when colors are enabled.
|
|
68
|
+
expect((0, log_1.getNodeName)('a', 4).length).toBeGreaterThanOrEqual(4);
|
|
69
|
+
});
|
|
70
|
+
it('getMaxNameLength returns the longest name yielded', () => {
|
|
71
|
+
const names = function* () {
|
|
72
|
+
yield 'aa';
|
|
73
|
+
yield 'aaaaa';
|
|
74
|
+
yield 'a';
|
|
75
|
+
};
|
|
76
|
+
expect((0, log_1.getMaxNameLength)(names)).toBe(5);
|
|
77
|
+
});
|
|
78
|
+
it('getMaxNameLength returns 0 for an empty source', () => {
|
|
79
|
+
expect((0, log_1.getMaxNameLength)(() => [])).toBe(0);
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
describe('print helpers', () => {
|
|
83
|
+
it('printItem with and without description', () => {
|
|
84
|
+
const env = (0, environment_mock_1.environmentMock)('/tmp');
|
|
85
|
+
const cap = capture();
|
|
86
|
+
env.stdout = cap.stream;
|
|
87
|
+
(0, log_1.printItem)(env, { name: 'a', description: null });
|
|
88
|
+
(0, log_1.printItem)(env, { name: 'b', description: 'desc' });
|
|
89
|
+
const out = cap.read();
|
|
90
|
+
expect(out).toContain('• a\n');
|
|
91
|
+
expect(out).toContain('• b');
|
|
92
|
+
expect(out).toContain('desc');
|
|
93
|
+
});
|
|
94
|
+
it('printProperty writes "<name>: <value>"', () => {
|
|
95
|
+
const env = (0, environment_mock_1.environmentMock)('/tmp');
|
|
96
|
+
const cap = capture();
|
|
97
|
+
env.stdout = cap.stream;
|
|
98
|
+
(0, log_1.printProperty)(env, 'image', 'alpine');
|
|
99
|
+
expect(cap.read()).toContain('image:');
|
|
100
|
+
expect(cap.read()).toContain('alpine');
|
|
101
|
+
});
|
|
102
|
+
it('printTitle writes the title with a colon', () => {
|
|
103
|
+
const env = (0, environment_mock_1.environmentMock)('/tmp');
|
|
104
|
+
const cap = capture();
|
|
105
|
+
env.stdout = cap.stream;
|
|
106
|
+
(0, log_1.printTitle)(env, 'Tasks');
|
|
107
|
+
expect(cap.read()).toContain('Tasks:');
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
describe('formatDate', () => {
|
|
111
|
+
it('produces a [yyyy-MM-DD HH:mm:ss.SSS] formatted timestamp', () => {
|
|
112
|
+
// Use a fixed Date in UTC; the function uses local-time getters, so only
|
|
113
|
+
// pattern-check the output rather than the literal value.
|
|
114
|
+
const out = (0, log_1.formatDate)(new Date(2024, 0, 2, 3, 4, 5, 6));
|
|
115
|
+
expect(out).toMatch(/\[\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3}\]/);
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
describe('cursor', () => {
|
|
119
|
+
it('hideCursor / showCursor write escape sequences', () => {
|
|
120
|
+
const env = (0, environment_mock_1.environmentMock)('/tmp');
|
|
121
|
+
const cap = capture();
|
|
122
|
+
env.stdout = cap.stream;
|
|
123
|
+
(0, log_1.hideCursor)(env);
|
|
124
|
+
(0, log_1.showCursor)(env);
|
|
125
|
+
const out = cap.read();
|
|
126
|
+
expect(out).toContain('\x1B[?25l');
|
|
127
|
+
expect(out).toContain('\x1B[?25h');
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
//# sourceMappingURL=log.spec.js.map
|
|
@@ -2,31 +2,33 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.groupedLogger = void 0;
|
|
4
4
|
const log_1 = require("../log");
|
|
5
|
-
const
|
|
5
|
+
const plan_work_tasks_1 = require("../planner/utils/plan-work-tasks");
|
|
6
6
|
function groupedLogger(state, env) {
|
|
7
|
-
const maxNodeNameLength = (0, log_1.
|
|
7
|
+
const maxNodeNameLength = (0, log_1.getWorkItemMaxLength)(state.current);
|
|
8
8
|
const completedNodes = [];
|
|
9
9
|
const completedServices = [];
|
|
10
|
-
state.on((currentState) => {
|
|
11
|
-
for (const
|
|
12
|
-
if (completedNodes.indexOf(
|
|
10
|
+
state.on('log-status', (currentState) => {
|
|
11
|
+
for (const task of (0, plan_work_tasks_1.iterateWorkTasks)(currentState)) {
|
|
12
|
+
if (completedNodes.indexOf(task.name) >= 0) {
|
|
13
13
|
continue;
|
|
14
14
|
}
|
|
15
|
-
if (
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
if (task.state.current.type === 'crash' ||
|
|
16
|
+
task.state.current.type === 'error' ||
|
|
17
|
+
task.state.current.type === 'completed') {
|
|
18
|
+
completedNodes.push(task.name);
|
|
19
|
+
for (const log of task.status.read()) {
|
|
20
|
+
(0, log_1.writeWorkItemLogToConsole)(env, log, maxNodeNameLength);
|
|
19
21
|
}
|
|
20
22
|
}
|
|
21
23
|
}
|
|
22
|
-
for (const service of (0,
|
|
23
|
-
if (completedServices.indexOf(service.
|
|
24
|
+
for (const service of (0, plan_work_tasks_1.iterateWorkServices)(currentState)) {
|
|
25
|
+
if (completedServices.indexOf(service.name) >= 0) {
|
|
24
26
|
continue;
|
|
25
27
|
}
|
|
26
|
-
if (service.type === 'end') {
|
|
27
|
-
completedServices.push(service.
|
|
28
|
-
for (const log of
|
|
29
|
-
(0, log_1.
|
|
28
|
+
if (service.state.current.type === 'end') {
|
|
29
|
+
completedServices.push(service.name);
|
|
30
|
+
for (const log of service.status.read()) {
|
|
31
|
+
(0, log_1.writeWorkItemLogToConsole)(env, log, maxNodeNameLength);
|
|
30
32
|
}
|
|
31
33
|
}
|
|
32
34
|
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Verifies that the grouped logger only flushes a task/service once it reaches
|
|
3
|
+
// a terminal state, by mocking the underlying log helpers and iterators.
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
vi.mock('../log', () => ({
|
|
6
|
+
writeWorkItemLogToConsole: vi.fn(),
|
|
7
|
+
printWorkTreeResult: vi.fn().mockResolvedValue(undefined),
|
|
8
|
+
getWorkItemMaxLength: vi.fn(() => 10),
|
|
9
|
+
}));
|
|
10
|
+
vi.mock('../planner/utils/plan-work-tasks', () => ({
|
|
11
|
+
iterateWorkTasks: vi.fn(),
|
|
12
|
+
iterateWorkServices: vi.fn(),
|
|
13
|
+
}));
|
|
14
|
+
const grouped_logger_1 = require("./grouped-logger");
|
|
15
|
+
const state_1 = require("../executer/state");
|
|
16
|
+
const plan_work_tasks_1 = require("../planner/utils/plan-work-tasks");
|
|
17
|
+
const log_1 = require("../log");
|
|
18
|
+
const mockedIterTasks = plan_work_tasks_1.iterateWorkTasks;
|
|
19
|
+
const mockedIterServices = plan_work_tasks_1.iterateWorkServices;
|
|
20
|
+
const mockedWrite = log_1.writeWorkItemLogToConsole;
|
|
21
|
+
const mockedPrint = log_1.printWorkTreeResult;
|
|
22
|
+
function fakeTask(name, type, logs = []) {
|
|
23
|
+
return { name, state: { current: { type } }, status: { read: () => logs } };
|
|
24
|
+
}
|
|
25
|
+
describe('groupedLogger', () => {
|
|
26
|
+
beforeEach(() => {
|
|
27
|
+
mockedWrite.mockClear();
|
|
28
|
+
mockedPrint.mockClear();
|
|
29
|
+
mockedIterTasks.mockReset();
|
|
30
|
+
mockedIterServices.mockReset();
|
|
31
|
+
});
|
|
32
|
+
it('flushes a task only once it reaches a terminal state and not again on re-emit', () => {
|
|
33
|
+
const t = fakeTask('t1', 'pending', [{ id: 1 }]);
|
|
34
|
+
mockedIterTasks.mockImplementation(function* () {
|
|
35
|
+
yield t;
|
|
36
|
+
});
|
|
37
|
+
mockedIterServices.mockImplementation(function* () { });
|
|
38
|
+
const state = new state_1.State({ services: {}, tasks: { t1: t } });
|
|
39
|
+
(0, grouped_logger_1.groupedLogger)(state, {});
|
|
40
|
+
state.set(state.current); // pending — should NOT flush
|
|
41
|
+
expect(mockedWrite).not.toHaveBeenCalled();
|
|
42
|
+
t.state.current.type = 'completed';
|
|
43
|
+
state.set(state.current); // completed — flush once
|
|
44
|
+
expect(mockedWrite).toHaveBeenCalledTimes(1);
|
|
45
|
+
state.set(state.current); // already-flushed task — must not flush again
|
|
46
|
+
expect(mockedWrite).toHaveBeenCalledTimes(1);
|
|
47
|
+
});
|
|
48
|
+
it.each(['crash', 'error', 'completed'])('flushes on terminal state %s', (terminal) => {
|
|
49
|
+
const t = fakeTask('t1', terminal, [{ id: 1 }]);
|
|
50
|
+
mockedIterTasks.mockImplementation(function* () {
|
|
51
|
+
yield t;
|
|
52
|
+
});
|
|
53
|
+
mockedIterServices.mockImplementation(function* () { });
|
|
54
|
+
const state = new state_1.State({ services: {}, tasks: { t1: t } });
|
|
55
|
+
(0, grouped_logger_1.groupedLogger)(state, {});
|
|
56
|
+
state.set(state.current);
|
|
57
|
+
expect(mockedWrite).toHaveBeenCalledTimes(1);
|
|
58
|
+
});
|
|
59
|
+
it('flushes a service on type "end" and not again', () => {
|
|
60
|
+
const s = fakeTask('svc1', 'starting', [{ id: 1 }]);
|
|
61
|
+
mockedIterTasks.mockImplementation(function* () { });
|
|
62
|
+
mockedIterServices.mockImplementation(function* () {
|
|
63
|
+
yield s;
|
|
64
|
+
});
|
|
65
|
+
const state = new state_1.State({ services: { svc1: s }, tasks: {} });
|
|
66
|
+
(0, grouped_logger_1.groupedLogger)(state, {});
|
|
67
|
+
state.set(state.current); // not yet ended
|
|
68
|
+
expect(mockedWrite).not.toHaveBeenCalled();
|
|
69
|
+
s.state.current.type = 'end';
|
|
70
|
+
state.set(state.current); // ended — flush
|
|
71
|
+
expect(mockedWrite).toHaveBeenCalledTimes(1);
|
|
72
|
+
state.set(state.current); // already flushed — no second call
|
|
73
|
+
expect(mockedWrite).toHaveBeenCalledTimes(1);
|
|
74
|
+
});
|
|
75
|
+
it('complete() delegates to printWorkTreeResult', async () => {
|
|
76
|
+
mockedIterTasks.mockImplementation(function* () { });
|
|
77
|
+
mockedIterServices.mockImplementation(function* () { });
|
|
78
|
+
const state = new state_1.State({ services: {}, tasks: {} });
|
|
79
|
+
const logger = (0, grouped_logger_1.groupedLogger)(state, {});
|
|
80
|
+
await logger.complete({ state: state.current, success: true }, {});
|
|
81
|
+
expect(mockedPrint).toHaveBeenCalledTimes(1);
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
//# sourceMappingURL=grouped-logger.spec.js.map
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// interactiveLogger hides the cursor, polls writeWorkTreeStatus on a 100ms
|
|
3
|
+
// ticker, and writes on every state change until complete() stops it.
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
vi.mock('../log', () => ({
|
|
6
|
+
writeWorkTreeStatus: vi.fn(),
|
|
7
|
+
printWorkTreeResult: vi.fn().mockResolvedValue(undefined),
|
|
8
|
+
hideCursor: vi.fn(),
|
|
9
|
+
showCursor: vi.fn(),
|
|
10
|
+
}));
|
|
11
|
+
vi.mock('readline', () => ({ clearScreenDown: vi.fn() }));
|
|
12
|
+
const interactive_logger_1 = require("./interactive-logger");
|
|
13
|
+
const state_1 = require("../executer/state");
|
|
14
|
+
const log_1 = require("../log");
|
|
15
|
+
const readline_1 = require("readline");
|
|
16
|
+
const mockedWriteStatus = log_1.writeWorkTreeStatus;
|
|
17
|
+
const mockedHide = log_1.hideCursor;
|
|
18
|
+
const mockedShow = log_1.showCursor;
|
|
19
|
+
const mockedClear = readline_1.clearScreenDown;
|
|
20
|
+
const mockedPrint = log_1.printWorkTreeResult;
|
|
21
|
+
describe('interactiveLogger', () => {
|
|
22
|
+
beforeEach(() => {
|
|
23
|
+
vi.useFakeTimers();
|
|
24
|
+
mockedWriteStatus.mockClear();
|
|
25
|
+
mockedHide.mockClear();
|
|
26
|
+
mockedShow.mockClear();
|
|
27
|
+
mockedClear.mockClear();
|
|
28
|
+
mockedPrint.mockClear();
|
|
29
|
+
});
|
|
30
|
+
afterEach(() => {
|
|
31
|
+
vi.useRealTimers();
|
|
32
|
+
});
|
|
33
|
+
it('hides the cursor, ticks every 100ms, and writes on state changes', async () => {
|
|
34
|
+
const state = new state_1.State({ services: {}, tasks: {} });
|
|
35
|
+
const env = { stdout: { write: vi.fn() } };
|
|
36
|
+
const logger = (0, interactive_logger_1.interactiveLogger)(state, env);
|
|
37
|
+
// hideCursor on construction; initial tick fired immediately
|
|
38
|
+
expect(mockedHide).toHaveBeenCalledTimes(1);
|
|
39
|
+
expect(mockedWriteStatus).toHaveBeenCalledTimes(1);
|
|
40
|
+
// advance one tick window → one additional writeWorkTreeStatus
|
|
41
|
+
vi.advanceTimersByTime(100);
|
|
42
|
+
expect(mockedWriteStatus).toHaveBeenCalledTimes(2);
|
|
43
|
+
// a state change writes status synchronously
|
|
44
|
+
state.set({ services: {}, tasks: {} });
|
|
45
|
+
expect(mockedWriteStatus).toHaveBeenCalledTimes(3);
|
|
46
|
+
// complete() stops the ticker and restores the cursor
|
|
47
|
+
await logger.complete({ state: state.current, success: true }, env);
|
|
48
|
+
expect(mockedClear).toHaveBeenCalledWith(env.stdout);
|
|
49
|
+
expect(mockedPrint).toHaveBeenCalledTimes(1);
|
|
50
|
+
expect(mockedShow).toHaveBeenCalledTimes(1);
|
|
51
|
+
// after complete, advancing time must NOT schedule further writes
|
|
52
|
+
const after = mockedWriteStatus.mock.calls.length;
|
|
53
|
+
vi.advanceTimersByTime(1000);
|
|
54
|
+
expect(mockedWriteStatus.mock.calls.length).toBe(after);
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
//# sourceMappingURL=interactive-logger.spec.js.map
|
|
@@ -3,9 +3,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.liveLogger = void 0;
|
|
4
4
|
const log_1 = require("../log");
|
|
5
5
|
function liveLogger(state, env) {
|
|
6
|
-
const maxNodeNameLength = (0, log_1.
|
|
6
|
+
const maxNodeNameLength = (0, log_1.getWorkItemMaxLength)(state.current);
|
|
7
7
|
env.status.on((log) => {
|
|
8
|
-
(0, log_1.
|
|
8
|
+
(0, log_1.writeWorkItemLogToConsole)(env, log, maxNodeNameLength);
|
|
9
9
|
});
|
|
10
10
|
return {
|
|
11
11
|
async complete(evt, env) {
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// liveLogger forwards every status message verbatim to writeWorkItemLogToConsole.
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
vi.mock('../log', () => ({
|
|
5
|
+
writeWorkItemLogToConsole: vi.fn(),
|
|
6
|
+
printWorkTreeResult: vi.fn().mockResolvedValue(undefined),
|
|
7
|
+
getWorkItemMaxLength: vi.fn(() => 10),
|
|
8
|
+
}));
|
|
9
|
+
const live_logger_1 = require("./live-logger");
|
|
10
|
+
const state_1 = require("../executer/state");
|
|
11
|
+
const log_1 = require("../log");
|
|
12
|
+
const emitter_1 = require("../utils/emitter");
|
|
13
|
+
const mockedWrite = log_1.writeWorkItemLogToConsole;
|
|
14
|
+
const mockedPrint = log_1.printWorkTreeResult;
|
|
15
|
+
describe('liveLogger', () => {
|
|
16
|
+
beforeEach(() => {
|
|
17
|
+
mockedWrite.mockClear();
|
|
18
|
+
mockedPrint.mockClear();
|
|
19
|
+
});
|
|
20
|
+
it('forwards each status message to writeWorkItemLogToConsole', () => {
|
|
21
|
+
const status = (0, emitter_1.emitter)();
|
|
22
|
+
const env = { status };
|
|
23
|
+
const state = new state_1.State({ services: {}, tasks: {} });
|
|
24
|
+
(0, live_logger_1.liveLogger)(state, env);
|
|
25
|
+
const msg = { type: 'status', message: 'hi' };
|
|
26
|
+
status.emit(msg);
|
|
27
|
+
expect(mockedWrite).toHaveBeenCalledWith(env, msg, 10);
|
|
28
|
+
});
|
|
29
|
+
it('complete() delegates to printWorkTreeResult', async () => {
|
|
30
|
+
const status = (0, emitter_1.emitter)();
|
|
31
|
+
const state = new state_1.State({ services: {}, tasks: {} });
|
|
32
|
+
const logger = (0, live_logger_1.liveLogger)(state, { status });
|
|
33
|
+
await logger.complete({ state: state.current, success: true }, {});
|
|
34
|
+
expect(mockedPrint).toHaveBeenCalledTimes(1);
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
//# sourceMappingURL=live-logger.spec.js.map
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const stream_1 = require("stream");
|
|
4
|
+
const state_1 = require("../executer/state");
|
|
5
|
+
const work_item_status_1 = require("../planner/work-item-status");
|
|
6
|
+
const live_logger_1 = require("./live-logger");
|
|
7
|
+
const grouped_logger_1 = require("./grouped-logger");
|
|
8
|
+
const interactive_logger_1 = require("./interactive-logger");
|
|
9
|
+
function memStream() {
|
|
10
|
+
const chunks = [];
|
|
11
|
+
const stream = new stream_1.Writable({
|
|
12
|
+
write(c, _e, cb) {
|
|
13
|
+
chunks.push(c.toString());
|
|
14
|
+
cb();
|
|
15
|
+
},
|
|
16
|
+
});
|
|
17
|
+
return Object.assign(stream, { read: () => chunks.join('') });
|
|
18
|
+
}
|
|
19
|
+
function noopStream() {
|
|
20
|
+
return new stream_1.Writable({ write: (_c, _e, cb) => cb() });
|
|
21
|
+
}
|
|
22
|
+
function makeEnv(stdout = noopStream()) {
|
|
23
|
+
return {
|
|
24
|
+
cwd: '/tmp',
|
|
25
|
+
file: {},
|
|
26
|
+
processEnvs: {},
|
|
27
|
+
abortCtrl: new AbortController(),
|
|
28
|
+
console: { info: vi.fn(), error: vi.fn(), warn: vi.fn(), debug: vi.fn(), log: vi.fn() },
|
|
29
|
+
status: (0, work_item_status_1.statusConsole)(noopStream()),
|
|
30
|
+
stdout,
|
|
31
|
+
stderr: noopStream(),
|
|
32
|
+
stdoutColumns: 80,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
function taskItem(env, name, state) {
|
|
36
|
+
const data = { type: 'local-task', name, description: null, scope: { fileName: 'x' } };
|
|
37
|
+
return {
|
|
38
|
+
id: () => name,
|
|
39
|
+
name,
|
|
40
|
+
data,
|
|
41
|
+
status: env.status.from(data),
|
|
42
|
+
state: new state_1.State(state),
|
|
43
|
+
needs: [],
|
|
44
|
+
deps: [],
|
|
45
|
+
requiredBy: [],
|
|
46
|
+
runtime: {},
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
function serviceItem(env, name, state) {
|
|
50
|
+
const data = { type: 'container-service', name, ports: [] };
|
|
51
|
+
return {
|
|
52
|
+
id: () => name,
|
|
53
|
+
name,
|
|
54
|
+
data,
|
|
55
|
+
status: env.status.from(data),
|
|
56
|
+
state: new state_1.State(state),
|
|
57
|
+
needs: [],
|
|
58
|
+
deps: [],
|
|
59
|
+
requiredBy: [],
|
|
60
|
+
runtime: {},
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
function workTree(items) {
|
|
64
|
+
return {
|
|
65
|
+
tasks: Object.fromEntries((items.tasks ?? []).map((t) => [t.name, t])),
|
|
66
|
+
services: Object.fromEntries((items.services ?? []).map((s) => [s.name, s])),
|
|
67
|
+
environment: { type: 'docker' },
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
describe('loggers', () => {
|
|
71
|
+
describe('liveLogger', () => {
|
|
72
|
+
it('writes each status message and renders results on complete', async () => {
|
|
73
|
+
const out = memStream();
|
|
74
|
+
const env = makeEnv(out);
|
|
75
|
+
const task = taskItem(env, 'build', { type: 'pending', stateKey: null });
|
|
76
|
+
const tree = workTree({ tasks: [task] });
|
|
77
|
+
const state = new state_1.State(tree);
|
|
78
|
+
const logger = (0, live_logger_1.liveLogger)(state, env);
|
|
79
|
+
// a write on the scoped console emits a message on env.status, which the
|
|
80
|
+
// logger's listener formats into env.stdout
|
|
81
|
+
task.status.write('info', 'compiling');
|
|
82
|
+
const result = { state: tree, success: true };
|
|
83
|
+
await logger.complete(result, env);
|
|
84
|
+
expect(out.read()).toContain('compiling');
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
describe('groupedLogger', () => {
|
|
88
|
+
it("writes a completed task's logs on the next status update", async () => {
|
|
89
|
+
const out = memStream();
|
|
90
|
+
const env = makeEnv(out);
|
|
91
|
+
const task = taskItem(env, 'build', { type: 'pending', stateKey: null });
|
|
92
|
+
const tree = workTree({ tasks: [task] });
|
|
93
|
+
const state = new state_1.State(tree);
|
|
94
|
+
const logger = (0, grouped_logger_1.groupedLogger)(state, env);
|
|
95
|
+
// buffer one info log, mark the task completed, then notify the parent state
|
|
96
|
+
task.status.write('info', 'compiling');
|
|
97
|
+
task.state.set({ type: 'completed', stateKey: 'k', duration: 5, cached: false });
|
|
98
|
+
state.set(tree);
|
|
99
|
+
await logger.complete({ state: tree, success: true }, env);
|
|
100
|
+
expect(out.read()).toContain('compiling');
|
|
101
|
+
});
|
|
102
|
+
it("writes an ended service's logs on the next status update", async () => {
|
|
103
|
+
const out = memStream();
|
|
104
|
+
const env = makeEnv(out);
|
|
105
|
+
const svc = serviceItem(env, 'db', { type: 'pending', stateKey: null });
|
|
106
|
+
const tree = workTree({ services: [svc] });
|
|
107
|
+
const state = new state_1.State(tree);
|
|
108
|
+
const logger = (0, grouped_logger_1.groupedLogger)(state, env);
|
|
109
|
+
svc.status.write('info', 'ready');
|
|
110
|
+
svc.state.set({ type: 'end', reason: 'terminated', stateKey: 'k' });
|
|
111
|
+
state.set(tree);
|
|
112
|
+
await logger.complete({ state: tree, success: true }, env);
|
|
113
|
+
expect(out.read()).toContain('ready');
|
|
114
|
+
});
|
|
115
|
+
it('does not duplicate a task that is already in the completed set', async () => {
|
|
116
|
+
const out = memStream();
|
|
117
|
+
const env = makeEnv(out);
|
|
118
|
+
const task = taskItem(env, 'build', { type: 'pending', stateKey: null });
|
|
119
|
+
const tree = workTree({ tasks: [task] });
|
|
120
|
+
const state = new state_1.State(tree);
|
|
121
|
+
const logger = (0, grouped_logger_1.groupedLogger)(state, env);
|
|
122
|
+
task.status.write('info', 'once');
|
|
123
|
+
task.state.set({ type: 'completed', stateKey: 'k', duration: 1, cached: false });
|
|
124
|
+
state.set(tree);
|
|
125
|
+
const after = out.read();
|
|
126
|
+
// a second notification with no further log message should not append another copy
|
|
127
|
+
state.set(tree);
|
|
128
|
+
expect(out.read()).toEqual(after);
|
|
129
|
+
await logger.complete({ state: tree, success: true }, env);
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
describe('interactiveLogger', () => {
|
|
133
|
+
beforeEach(() => vi.useFakeTimers());
|
|
134
|
+
afterEach(() => vi.useRealTimers());
|
|
135
|
+
it('hides the cursor, ticks, redraws on log-status, then cleans up on complete', async () => {
|
|
136
|
+
const out = memStream();
|
|
137
|
+
const env = makeEnv(out);
|
|
138
|
+
const task = taskItem(env, 'build', { type: 'pending', stateKey: null });
|
|
139
|
+
const tree = workTree({ tasks: [task] });
|
|
140
|
+
const state = new state_1.State(tree);
|
|
141
|
+
const logger = (0, interactive_logger_1.interactiveLogger)(state, env); // hideCursor + first tick + setTimeout
|
|
142
|
+
// drive a state change to re-render
|
|
143
|
+
task.state.set({ type: 'running', stateKey: 'k' });
|
|
144
|
+
state.set(tree);
|
|
145
|
+
// let one fake tick fire while running===true (covers the recursive branch)
|
|
146
|
+
vi.advanceTimersByTime(150);
|
|
147
|
+
await logger.complete({ state: tree, success: true }, env);
|
|
148
|
+
// and one more after complete — covers the `if (!running) return` early exit
|
|
149
|
+
vi.advanceTimersByTime(150);
|
|
150
|
+
expect(out.read().length).toBeGreaterThan(0);
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
//# sourceMappingURL=loggers.spec.js.map
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const path_1 = require("path");
|
|
4
|
+
const os_1 = require("os");
|
|
5
|
+
const fs_1 = require("fs");
|
|
6
|
+
const crypto_1 = require("crypto");
|
|
7
|
+
const environment_mock_1 = require("../executer/environment-mock");
|
|
8
|
+
const calculate_checksum_1 = require("./calculate-checksum");
|
|
9
|
+
describe('calculateChecksum', () => {
|
|
10
|
+
let scratch;
|
|
11
|
+
beforeEach(() => {
|
|
12
|
+
scratch = (0, fs_1.mkdtempSync)((0, path_1.join)((0, os_1.tmpdir)(), 'hammerkit-checksum-'));
|
|
13
|
+
});
|
|
14
|
+
afterEach(() => {
|
|
15
|
+
(0, fs_1.rmSync)(scratch, { recursive: true, force: true });
|
|
16
|
+
});
|
|
17
|
+
it('returns the sha1 of the file contents', async () => {
|
|
18
|
+
const env = (0, environment_mock_1.environmentMock)(scratch);
|
|
19
|
+
const file = (0, path_1.join)(scratch, 'a.txt');
|
|
20
|
+
(0, fs_1.writeFileSync)(file, 'hello world');
|
|
21
|
+
const expected = (0, crypto_1.createHash)('sha1').update('hello world').digest('hex');
|
|
22
|
+
expect(await (0, calculate_checksum_1.calculateChecksum)(env, file)).toBe(expected);
|
|
23
|
+
});
|
|
24
|
+
it('is deterministic and content-sensitive', async () => {
|
|
25
|
+
const env = (0, environment_mock_1.environmentMock)(scratch);
|
|
26
|
+
const file = (0, path_1.join)(scratch, 'a.txt');
|
|
27
|
+
(0, fs_1.writeFileSync)(file, 'one');
|
|
28
|
+
const first = await (0, calculate_checksum_1.calculateChecksum)(env, file);
|
|
29
|
+
expect(await (0, calculate_checksum_1.calculateChecksum)(env, file)).toBe(first);
|
|
30
|
+
(0, fs_1.writeFileSync)(file, 'two');
|
|
31
|
+
expect(await (0, calculate_checksum_1.calculateChecksum)(env, file)).not.toBe(first);
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
//# sourceMappingURL=calculate-checksum.spec.js.map
|
|
@@ -2,16 +2,16 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.getHammerkitDirectory = exports.getCacheStatsFile = exports.getCacheDescriptionFile = exports.getCacheDirectory = void 0;
|
|
4
4
|
const path_1 = require("path");
|
|
5
|
-
function getCacheDirectory(
|
|
6
|
-
return (0, path_1.join)(getHammerkitDirectory(), 'cache',
|
|
5
|
+
function getCacheDirectory(cacheId) {
|
|
6
|
+
return (0, path_1.join)(getHammerkitDirectory(), 'cache', cacheId);
|
|
7
7
|
}
|
|
8
8
|
exports.getCacheDirectory = getCacheDirectory;
|
|
9
|
-
function getCacheDescriptionFile(
|
|
10
|
-
return (0, path_1.join)(getCacheDirectory(
|
|
9
|
+
function getCacheDescriptionFile(cacheId) {
|
|
10
|
+
return (0, path_1.join)(getCacheDirectory(cacheId), 'description.json');
|
|
11
11
|
}
|
|
12
12
|
exports.getCacheDescriptionFile = getCacheDescriptionFile;
|
|
13
|
-
function getCacheStatsFile(
|
|
14
|
-
return (0, path_1.join)(getCacheDirectory(
|
|
13
|
+
function getCacheStatsFile(cacheId) {
|
|
14
|
+
return (0, path_1.join)(getCacheDirectory(cacheId), 'stats.json');
|
|
15
15
|
}
|
|
16
16
|
exports.getCacheStatsFile = getCacheStatsFile;
|
|
17
17
|
function getHammerkitDirectory() {
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const path_1 = require("path");
|
|
4
|
+
const get_cache_directory_1 = require("./get-cache-directory");
|
|
5
|
+
function withPlatform(value, env, fn) {
|
|
6
|
+
const originalPlatform = process.platform;
|
|
7
|
+
const originalEnv = {};
|
|
8
|
+
for (const k of Object.keys(env))
|
|
9
|
+
originalEnv[k] = process.env[k];
|
|
10
|
+
Object.defineProperty(process, 'platform', { value, configurable: true });
|
|
11
|
+
for (const [k, v] of Object.entries(env)) {
|
|
12
|
+
if (v === undefined)
|
|
13
|
+
delete process.env[k];
|
|
14
|
+
else
|
|
15
|
+
process.env[k] = v;
|
|
16
|
+
}
|
|
17
|
+
try {
|
|
18
|
+
return fn();
|
|
19
|
+
}
|
|
20
|
+
finally {
|
|
21
|
+
Object.defineProperty(process, 'platform', { value: originalPlatform, configurable: true });
|
|
22
|
+
for (const [k, v] of Object.entries(originalEnv)) {
|
|
23
|
+
if (v === undefined)
|
|
24
|
+
delete process.env[k];
|
|
25
|
+
else
|
|
26
|
+
process.env[k] = v;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
describe('cache directory helpers', () => {
|
|
31
|
+
it('builds the per-cache directory under the hammerkit directory', () => {
|
|
32
|
+
const dir = (0, get_cache_directory_1.getCacheDirectory)('abc123');
|
|
33
|
+
expect(dir.endsWith((0, path_1.join)('cache', 'abc123'))).toBe(true);
|
|
34
|
+
});
|
|
35
|
+
it('builds the description file path under the cache directory', () => {
|
|
36
|
+
expect((0, get_cache_directory_1.getCacheDescriptionFile)('id').endsWith((0, path_1.join)('cache', 'id', 'description.json'))).toBe(true);
|
|
37
|
+
});
|
|
38
|
+
it('builds the stats file path under the cache directory', () => {
|
|
39
|
+
expect((0, get_cache_directory_1.getCacheStatsFile)('id').endsWith((0, path_1.join)('cache', 'id', 'stats.json'))).toBe(true);
|
|
40
|
+
});
|
|
41
|
+
describe('getHammerkitDirectory', () => {
|
|
42
|
+
it('uses %APPDATA% on Windows', () => {
|
|
43
|
+
withPlatform('win32', { APPDATA: 'C:\\Users\\X\\AppData\\Roaming' }, () => {
|
|
44
|
+
expect((0, get_cache_directory_1.getHammerkitDirectory)()).toBe((0, path_1.join)('C:\\Users\\X\\AppData\\Roaming', 'hammerkit'));
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
it('uses ~/Library/Application Support on macOS', () => {
|
|
48
|
+
withPlatform('darwin', { HOME: '/Users/x' }, () => {
|
|
49
|
+
expect((0, get_cache_directory_1.getHammerkitDirectory)()).toBe((0, path_1.join)('/Users/x', 'Library', 'Application Support', 'hammerkit'));
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
it('uses ~/.hammerkit on Linux', () => {
|
|
53
|
+
withPlatform('linux', { HOME: '/home/x' }, () => {
|
|
54
|
+
expect((0, get_cache_directory_1.getHammerkitDirectory)()).toBe((0, path_1.join)('/home/x', '.hammerkit'));
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
//# sourceMappingURL=get-cache-directory.spec.js.map
|