hammerkit 1.4.0 → 1.5.0-alpha.1
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/.devcontainer/Dockerfile +5 -0
- package/.devcontainer/devcontainer.json +40 -0
- package/README.md +13 -86
- package/SUMMARY.md +43 -0
- package/build.schema.json +56 -2
- package/dist/cli.js +97 -0
- package/dist/cli.spec.js +5 -0
- package/dist/console/console-context-mock.js +11 -0
- package/dist/console/get-logger.js +23 -0
- package/dist/docker/pull.js +5 -5
- package/dist/docker/remove-container.js +21 -0
- package/dist/docker/stream.js +14 -10
- package/dist/docker/using-container.js +28 -0
- package/dist/environment/get-process-env.js +4 -4
- package/dist/environment/replace-env-variables.js +2 -3
- package/dist/executer/abort.js +13 -0
- package/dist/executer/check-readiness.js +25 -0
- package/dist/executer/create-scheduler-state.js +32 -0
- package/dist/executer/docker-node.js +123 -0
- package/dist/executer/docker-service.js +113 -0
- package/dist/executer/environment-mock.js +17 -0
- package/dist/executer/event-cache.js +165 -0
- package/dist/executer/execute-command.js +42 -0
- package/dist/executer/execute-docker.js +59 -195
- package/dist/executer/execute.spec.js +37 -37
- package/dist/executer/execution-steps.js +57 -0
- package/dist/executer/get-docker-executor.js +6 -100
- package/dist/executer/hierarchy-scheduler.js +94 -0
- package/dist/executer/kubernetes-service.js +64 -0
- package/dist/executer/label-values.js +65 -0
- package/dist/executer/local-node.js +57 -0
- package/dist/executer/print-container-options.js +17 -0
- package/dist/{logging/log-strategy.js → executer/process-item.js} +1 -1
- package/dist/executer/process-listener.js +3 -0
- package/dist/executer/process-manager.js +111 -0
- package/dist/executer/process-manager.spec.js +91 -0
- package/dist/executer/{executor.js → process.js} +1 -1
- package/dist/executer/{execute-result.js → readonly-state.js} +1 -1
- package/dist/executer/schedule.js +139 -0
- package/dist/executer/scheduler/check-for-loop.js +21 -0
- package/dist/executer/scheduler/enqueue-next.js +27 -0
- package/dist/{testing/test-context.js → executer/scheduler/node-state.js} +1 -1
- package/dist/executer/scheduler/scheduler-result.js +3 -0
- package/dist/executer/scheduler/scheduler-state.js +3 -0
- package/dist/executer/scheduler/service-state.js +3 -0
- package/dist/executer/service-dns.js +6 -0
- package/dist/executer/set-user-permission.js +16 -0
- package/dist/executer/start-node.js +59 -0
- package/dist/executer/state-listener.js +3 -0
- package/dist/executer/state.js +49 -0
- package/dist/executer/states.js +4 -159
- package/dist/executer/watch-node.js +57 -0
- package/dist/executer/watch-service.js +54 -0
- package/dist/executer/work-scope.js +10 -0
- package/dist/file/get-file-context-mock.spec.js +5 -5
- package/dist/file/get-file-context.js +41 -18
- package/dist/file/move-files.js +1 -1
- package/dist/get-work-context.js +29 -0
- package/dist/index.js +8 -4
- package/dist/log.js +197 -50
- package/dist/logging/grouped-logger.js +31 -19
- package/dist/logging/interactive-logger.js +20 -23
- package/dist/logging/live-logger.js +7 -17
- package/dist/optimizer/calculate-checksum.js +1 -1
- package/dist/optimizer/get-cache-directory.js +8 -13
- package/dist/optimizer/get-work-node-cache-stats.js +68 -21
- package/dist/optimizer/read-work-node-cache.js +2 -2
- package/dist/optimizer/work-node-cache-description.js +12 -11
- package/dist/optimizer/write-work-node-cache.js +9 -8
- package/dist/{executer/execution-context.js → parser/build-file-service.js} +1 -1
- package/dist/{optimizer → parser}/cache-method.js +0 -0
- package/dist/parser/get-build-file.js +1 -1
- package/dist/parser/parse-boolean.js +29 -0
- package/dist/parser/parse-boolean.spec.js +48 -0
- package/dist/parser/parse-build-file-references.js +2 -2
- package/dist/parser/parse-build-file-services.js +58 -0
- package/dist/parser/parse-build-file-task-command.js +6 -5
- package/dist/parser/parse-build-file-task-source.js +5 -5
- package/dist/parser/parse-build-file.js +39 -15
- package/dist/parser/parse-context.js +13 -0
- package/dist/parser/parse-envs.js +4 -3
- package/dist/parser/parse-healthcheck.js +28 -0
- package/dist/parser/parse-label-arguments.js +23 -0
- package/dist/parser/parse-service-selector.js +39 -0
- package/dist/parser/parse-string-array.js +6 -5
- package/dist/parser/parse-string-map.js +30 -0
- package/dist/parser/parse-string.js +20 -0
- package/dist/parser/read-build-file.js +16 -5
- package/dist/parser/read-env-file.js +3 -3
- package/dist/planner/utils/get-container-mounts.js +24 -0
- package/dist/planner/utils/normalize-path.js +17 -0
- package/dist/planner/utils/parse-work-node-mount.js +3 -14
- package/dist/planner/utils/parse-work-node-mount.spec.js +10 -10
- package/dist/planner/utils/parse-work-service-volume.js +21 -0
- package/dist/planner/utils/plan-work-command.js +1 -1
- package/dist/planner/utils/plan-work-dependency.js +14 -8
- package/dist/planner/utils/plan-work-node.js +203 -128
- package/dist/planner/utils/plan-work-nodes.js +59 -14
- package/dist/planner/utils/plan-work-tree.js +8 -4
- package/dist/planner/utils/plan-work-volume.js +29 -0
- package/dist/planner/validate.js +56 -8
- package/dist/planner/work-context.js +20 -0
- package/dist/planner/work-mount.js +3 -0
- package/dist/planner/work-node-id.js +4 -4
- package/dist/planner/work-node-id.spec.js +11 -8
- package/dist/planner/work-node-path.js +0 -1
- package/dist/planner/work-node-status.js +121 -18
- package/dist/planner/work-node.js +3 -3
- package/dist/planner/work-service-id.js +13 -0
- package/dist/planner/work-service-volume.js +3 -0
- package/dist/planner/work-service.js +6 -0
- package/dist/planner/work-services.js +3 -0
- package/dist/planner/work-volume.js +3 -0
- package/dist/program.js +155 -90
- package/dist/program.spec.js +20 -0
- package/dist/start-watch-processes.js +28 -0
- package/dist/testing/cli.js +73 -0
- package/dist/testing/create-build-file.js +1 -1
- package/dist/testing/example-test-suite.js +54 -0
- package/dist/testing/expect.js +35 -18
- package/dist/testing/get-logger.js +23 -0
- package/dist/testing/get-node.js +2 -2
- package/dist/testing/get-test-context.js +5 -3
- package/dist/testing/get-test-suite.js +2 -50
- package/dist/testing/integration/cache.spec.js +25 -28
- package/dist/testing/integration/cancellation.spec.js +19 -21
- package/dist/testing/integration/clean.spec.js +25 -28
- package/dist/testing/integration/cmd.spec.js +5 -8
- package/dist/testing/integration/concurrency.spec.js +8 -17
- package/dist/testing/integration/docker.spec.js +6 -9
- package/dist/testing/integration/env.spec.js +27 -34
- package/dist/testing/integration/error.spec.js +5 -9
- package/dist/testing/integration/extend.spec.js +32 -0
- package/dist/testing/integration/glob.spec.js +35 -28
- package/dist/testing/integration/include.spec.js +17 -16
- package/dist/testing/integration/invalid.spec.js +3 -2
- package/dist/testing/integration/invalid_loop.spec.js +13 -0
- package/dist/testing/integration/kubernetes.spec.js +15 -0
- package/dist/testing/integration/labels.spec.js +47 -0
- package/dist/testing/integration/local.spec.js +4 -7
- package/dist/testing/integration/monorepo.spec.js +5 -9
- package/dist/testing/integration/multi-line.spec.js +4 -4
- package/dist/testing/integration/program.spec.js +3 -10
- package/dist/testing/integration/reference.spec.js +10 -13
- package/dist/testing/integration/services.spec.js +14 -0
- package/dist/testing/integration/store.spec.js +42 -38
- package/dist/testing/integration/unknown.spec.js +4 -4
- package/dist/testing/integration/validate.spec.js +4 -4
- package/dist/testing/integration/watch.spec.js +34 -33
- package/dist/testing/lazy.js +17 -0
- package/dist/testing/start-watch-processes.js +29 -0
- package/dist/testing/test-suite-setup.js +3 -0
- package/dist/utils/abort-event.js +14 -1
- package/dist/utils/abortable-function.js +71 -0
- package/dist/utils/debouncer.spec.js +8 -8
- package/dist/utils/environment-config.js +13 -0
- package/dist/utils/fail-never.js +1 -1
- package/dist/utils/sleep.spec.js +3 -3
- package/package.json +24 -21
- package/dist/console/get-console-context-mock.js +0 -33
- package/dist/executer/clean.js +0 -17
- package/dist/executer/execute-local.js +0 -59
- package/dist/executer/execute.js +0 -139
- package/dist/executer/execution-context-event.js +0 -3
- package/dist/executer/execution-context-mock.js +0 -3
- package/dist/executer/executor-mock.js +0 -61
- package/dist/executer/get-environment-mock.js +0 -16
- package/dist/executer/get-execution-context-mock.js +0 -18
- package/dist/executer/get-local-executor.js +0 -56
- package/dist/executer/restore.js +0 -19
- package/dist/executer/store.js +0 -19
- package/dist/logging/get-logger.js +0 -23
- package/dist/logging/message-to-console.js +0 -16
- package/dist/optimizer/optimize.js +0 -32
- package/dist/optimizer/work-node-description.js +0 -12
- package/dist/planner/utils/find-build-task.js +0 -18
- package/dist/testing/create-work-tree.js +0 -11
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at:
|
|
2
|
+
// https://github.com/microsoft/vscode-dev-containers/tree/v0.195.0/containers/javascript-node
|
|
3
|
+
{
|
|
4
|
+
"name": "Hammerkit",
|
|
5
|
+
"build": {
|
|
6
|
+
"dockerfile": "Dockerfile"
|
|
7
|
+
},
|
|
8
|
+
|
|
9
|
+
// Configure tool-specific properties.
|
|
10
|
+
"customizations": {
|
|
11
|
+
// Configure properties specific to VS Code.
|
|
12
|
+
"vscode": {
|
|
13
|
+
// Set *default* container specific settings.json values on container create.
|
|
14
|
+
"settings": {},
|
|
15
|
+
|
|
16
|
+
// Add the IDs of extensions you want installed when the container is created.
|
|
17
|
+
"extensions": [
|
|
18
|
+
"dbaeumer.vscode-eslint"
|
|
19
|
+
]
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
|
|
23
|
+
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
|
24
|
+
// "forwardPorts": [3000],
|
|
25
|
+
|
|
26
|
+
// Use 'portsAttributes' to set default properties for specific forwarded ports. More info: https://code.visualstudio.com/docs/remote/devcontainerjson-reference.
|
|
27
|
+
"portsAttributes": {
|
|
28
|
+
},
|
|
29
|
+
|
|
30
|
+
// Use 'otherPortsAttributes' to configure any ports that aren't configured using 'portsAttributes'.
|
|
31
|
+
// "otherPortsAttributes": {
|
|
32
|
+
// "onAutoForward": "silent"
|
|
33
|
+
// },
|
|
34
|
+
|
|
35
|
+
// Use 'postCreateCommand' to run commands after the container is created.
|
|
36
|
+
"postCreateCommand": "npm install",
|
|
37
|
+
|
|
38
|
+
// Comment out connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.
|
|
39
|
+
"remoteUser": "node"
|
|
40
|
+
}
|
package/README.md
CHANGED
|
@@ -1,97 +1,24 @@
|
|
|
1
|
-
|
|
2
|
-
<img src="docs/hammerkit.png" alt="hammerkit" width="250">
|
|
3
|
-
</h1>
|
|
4
|
-
<h2 align="center">Hammerkit</h2>
|
|
1
|
+
# About
|
|
5
2
|
|
|
6
|
-
<
|
|
7
|
-
<img alt='build' src='https://img.shields.io/github/workflow/status/no0dles/hammerkit/master'>
|
|
8
|
-
<img alt='license' src='https://img.shields.io/npm/l/hammerkit'>
|
|
9
|
-
<img alt='npm' src='https://img.shields.io/npm/v/hammerkit'>
|
|
10
|
-
</p>
|
|
3
|
+
<figure><img src="docs/hammerkit-small.png" width="144" alt=""><figcaption></figcaption></figure>
|
|
11
4
|
|
|
12
|
-
##
|
|
5
|
+
## What is hammerkit?
|
|
13
6
|
|
|
14
|
-
Hammerkit is a build tool intended to build software projects in containers locally and in a CI. The advantage of containerized builds are:
|
|
15
|
-
|
|
16
|
-
|
|
7
|
+
Hammerkit is a build tool intended to build software projects in **containers** locally and in a CI. The advantage of containerized builds are:
|
|
8
|
+
|
|
9
|
+
* the build tools used from the container image are the same across all machines.
|
|
10
|
+
* the build process is isolated since every file in the container has to be declared as a source or an output. Therefore side effects can be reduced.
|
|
17
11
|
|
|
18
12
|
There are a lot of containerized CI systems that allow writing containerized builds today, but they lack some features hammerkit tries to solve:
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
13
|
+
|
|
14
|
+
* they are often not **usable during development** on your local machine, so you have either maintain two build scripts or wrap one of them in the other.
|
|
15
|
+
* they mount the entire repository into the container and can therefore **side effect** can reduce reliability
|
|
16
|
+
* some of them do not allow **switching images** during build steps. Requiring to maintain a large build image, that contains all tools needed to build to software.
|
|
22
17
|
|
|
23
18
|
Additionally hammerkit tries to reduce complexity of build caching in CI systems. Usually you define a directory that is being cached and restored before and after the CI build. That's totally fine for smaller project where there are not that many directories to cache. The bigger the project gets, the more effort it is, to keep all directories up to date.
|
|
24
19
|
|
|
25
20
|
Hammerkit on the other hand knows the source and output files of each build step and can therefore compact the build results into a single directory. Making it easy to being cached. Once restored, hammerkit can detect what's changed since last time and only build the changes.
|
|
26
21
|
|
|
27
|
-
|
|
28
|
-
<a href="https://no0dles.gitbook.io/hammerkit">
|
|
29
|
-
<img src="docs/read_the_docs.png" alt="docs">
|
|
30
|
-
</a>
|
|
31
|
-
</p>
|
|
32
|
-
|
|
33
|
-
## Installation
|
|
34
|
-
|
|
35
|
-
### Yarn/npm
|
|
36
|
-
```npm i -g hammerkit```
|
|
37
|
-
|
|
38
|
-
### Homebrew
|
|
39
|
-
```
|
|
40
|
-
brew tap no0dles/hammerkit
|
|
41
|
-
brew install hammerkit
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
### Gitlab CI
|
|
45
|
-
```
|
|
46
|
-
variables:
|
|
47
|
-
DOCKER_DRIVER: overlay2
|
|
48
|
-
|
|
49
|
-
services:
|
|
50
|
-
- docker:19.03.0-dind
|
|
51
|
-
|
|
52
|
-
build:
|
|
53
|
-
image: no0dles/hammerkit
|
|
54
|
-
script:
|
|
55
|
-
- hammerkit build
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
### Github Action
|
|
59
|
-
```
|
|
60
|
-
jobs:
|
|
61
|
-
build:
|
|
62
|
-
runs-on: ubuntu-18.04
|
|
63
|
-
steps:
|
|
64
|
-
- name: Checkout
|
|
65
|
-
uses: actions/checkout@v2
|
|
66
|
-
- uses: actions/setup-node@v2
|
|
67
|
-
with:
|
|
68
|
-
node-version: '14'
|
|
69
|
-
- uses: no0dles/hammerkit-github-action@v1.3
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
## Roadmap
|
|
73
|
-
The following topics are planned to be addressed in the future.
|
|
74
|
-
|
|
75
|
-
### Services
|
|
76
|
-
Task sometimes require some service to run.
|
|
77
|
-
For example a database, which is required to run an api or an integration test.
|
|
78
|
-
The goal will be to define those and hammerkit spins them up for the task and shuts them down if not needed.
|
|
79
|
-
|
|
80
|
-
### Distributed caching
|
|
81
|
-
Distribute local cache, so other developers and the CI can use already build up caches.
|
|
82
|
-
The goal will be general performance improvements on CI and local development.
|
|
83
|
-
|
|
84
|
-
### Distributed computing
|
|
85
|
-
Offload complex and compute intense tasks to remote hardware.
|
|
86
|
-
The goal will be use the power of cloud computing, with very less configuration.
|
|
87
|
-
|
|
88
|
-
### Platform requirements
|
|
89
|
-
Define tasks that require a specific platform for local tasks.
|
|
90
|
-
The goal will be to either skip local tasks where platform requirements are not met or execute them on a remote machine.
|
|
91
|
-
|
|
22
|
+
## Where to go next?
|
|
92
23
|
|
|
93
|
-
|
|
94
|
-
```
|
|
95
|
-
npm version patch|minor|major|prerelease
|
|
96
|
-
git push origin --tags
|
|
97
|
-
```
|
|
24
|
+
Check out the [docs](https://no0dles.gitbook.io/hammerkit/) for more info about how hammerkit can be used.
|
package/SUMMARY.md
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Table of contents
|
|
2
|
+
|
|
3
|
+
* [About](README.md)
|
|
4
|
+
* [Installation](docs/installation.md)
|
|
5
|
+
* [Getting started](docs/getting-started.md)
|
|
6
|
+
* [Build file](docs/build-file/README.md)
|
|
7
|
+
* [Environment Variables](docs/build-file/environment-variables.md)
|
|
8
|
+
* [References](docs/build-file/references.md)
|
|
9
|
+
* [Includes](docs/build-file/includes.md)
|
|
10
|
+
* [Task](docs/task/README.md)
|
|
11
|
+
* [Dependencies](docs/task/dependencies.md)
|
|
12
|
+
* [Needs](docs/task/needs.md)
|
|
13
|
+
* [Caching](docs/task/caching.md)
|
|
14
|
+
* [Container](docs/task/container.md)
|
|
15
|
+
* [Watching](docs/task/watching.md)
|
|
16
|
+
* [Extending](docs/task/extending.md)
|
|
17
|
+
* [Service](docs/service/README.md)
|
|
18
|
+
* [Container](docs/service/container.md)
|
|
19
|
+
* [Kubernetes](docs/service/kubernetes.md)
|
|
20
|
+
* [Labels](docs/labels/README.md)
|
|
21
|
+
* [CLI](docs/cli/README.md)
|
|
22
|
+
* [Init](docs/cli/init.md)
|
|
23
|
+
* [ls](docs/cli/ls.md)
|
|
24
|
+
* [Execute](docs/cli/execute.md)
|
|
25
|
+
* [Store / Restore](docs/cli/store-restore.md)
|
|
26
|
+
* [Clean](docs/cli/clean.md)
|
|
27
|
+
* [Validate](docs/cli/validate.md)
|
|
28
|
+
* [Help](docs/cli/help.md)
|
|
29
|
+
|
|
30
|
+
## External Links
|
|
31
|
+
|
|
32
|
+
* [Docker Hub](https://hub.docker.com/r/no0dles/hammerkit)
|
|
33
|
+
* [Homebrew Repository](https://github.com/no0dles/homebrew-hammerkit)
|
|
34
|
+
|
|
35
|
+
## Release Blog
|
|
36
|
+
|
|
37
|
+
* [Release 1.4.0](docs/release-blog/release-1.4.0.md)
|
|
38
|
+
* [Release 1.5.0](docs/release-blog/release-1.5.0.md)
|
|
39
|
+
|
|
40
|
+
## Contribution
|
|
41
|
+
|
|
42
|
+
* [Roadmap](docs/contribution/roadmap.md)
|
|
43
|
+
* [Publishing](docs/contribution/publish.md)
|
package/build.schema.json
CHANGED
|
@@ -15,6 +15,13 @@
|
|
|
15
15
|
"$ref": "#/definitions/task"
|
|
16
16
|
}
|
|
17
17
|
},
|
|
18
|
+
"services": {
|
|
19
|
+
"type": "object",
|
|
20
|
+
"description": "A map of services",
|
|
21
|
+
"additionalProperties": {
|
|
22
|
+
"$ref": "#/definitions/service"
|
|
23
|
+
}
|
|
24
|
+
},
|
|
18
25
|
"includes": {
|
|
19
26
|
"$ref": "#/definitions/include"
|
|
20
27
|
},
|
|
@@ -23,6 +30,11 @@
|
|
|
23
30
|
}
|
|
24
31
|
},
|
|
25
32
|
"definitions": {
|
|
33
|
+
"labels": {
|
|
34
|
+
"description": "A map of label values",
|
|
35
|
+
"type": "object",
|
|
36
|
+
"additionalProperties": {"type": "string"}
|
|
37
|
+
},
|
|
26
38
|
"envMap": {
|
|
27
39
|
"description": "A map of environment values",
|
|
28
40
|
"type": "object",
|
|
@@ -88,7 +100,7 @@
|
|
|
88
100
|
"type": "string"
|
|
89
101
|
}
|
|
90
102
|
},
|
|
91
|
-
"
|
|
103
|
+
"mount": {
|
|
92
104
|
"type": "array",
|
|
93
105
|
"description": "A list of file paths that get mounted into the container",
|
|
94
106
|
"items": {
|
|
@@ -115,6 +127,9 @@
|
|
|
115
127
|
"type": "string",
|
|
116
128
|
"description": "Name of task that gets used a template"
|
|
117
129
|
},
|
|
130
|
+
"labels": {
|
|
131
|
+
"$ref": "#/definitions/labels"
|
|
132
|
+
},
|
|
118
133
|
"envs": {
|
|
119
134
|
"$ref": "#/definitions/envMap"
|
|
120
135
|
},
|
|
@@ -149,6 +164,9 @@
|
|
|
149
164
|
"type": "string",
|
|
150
165
|
"description": "A description of the task"
|
|
151
166
|
},
|
|
167
|
+
"labels": {
|
|
168
|
+
"$ref": "#/definitions/labels"
|
|
169
|
+
},
|
|
152
170
|
"extend": {
|
|
153
171
|
"type": "string",
|
|
154
172
|
"description": "Name of task that gets used a template"
|
|
@@ -175,7 +193,7 @@
|
|
|
175
193
|
"description": "Define the container image the commands run in"
|
|
176
194
|
},
|
|
177
195
|
"mounts": {
|
|
178
|
-
"$ref": "#/definitions/
|
|
196
|
+
"$ref": "#/definitions/mount"
|
|
179
197
|
},
|
|
180
198
|
"src": {
|
|
181
199
|
"$ref": "#/definitions/taskSource"
|
|
@@ -198,6 +216,42 @@
|
|
|
198
216
|
}, {
|
|
199
217
|
"$ref": "#/definitions/localTask"
|
|
200
218
|
}]
|
|
219
|
+
},
|
|
220
|
+
"service": {
|
|
221
|
+
"description": "A daemon in the background",
|
|
222
|
+
"required": ["image"],
|
|
223
|
+
"additionalProperties": false,
|
|
224
|
+
"properties": {
|
|
225
|
+
"image": {
|
|
226
|
+
"type": "string",
|
|
227
|
+
"description": "Define the container image the commands run in"
|
|
228
|
+
},
|
|
229
|
+
"labels": {
|
|
230
|
+
"$ref": "#/definitions/labels"
|
|
231
|
+
},
|
|
232
|
+
"envs": {
|
|
233
|
+
"$ref": "#/definitions/envMap"
|
|
234
|
+
},
|
|
235
|
+
"ports": {
|
|
236
|
+
"$ref": "#/definitions/taskPort"
|
|
237
|
+
},
|
|
238
|
+
"healthcheck": {
|
|
239
|
+
"$ref": "#/definitions/serviceHealthcheck"
|
|
240
|
+
},
|
|
241
|
+
"mounts": {
|
|
242
|
+
"$ref": "#/definitions/mount"
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
},
|
|
246
|
+
"serviceHealthcheck": {
|
|
247
|
+
"additionalItems": false,
|
|
248
|
+
"description": "A check to detect if the service is started and ready",
|
|
249
|
+
"properties": {
|
|
250
|
+
"cmd": {
|
|
251
|
+
"description": "task to check if the service is healthy",
|
|
252
|
+
"type": "string"
|
|
253
|
+
}
|
|
254
|
+
}
|
|
201
255
|
}
|
|
202
256
|
}
|
|
203
257
|
}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getCli = exports.isCliService = exports.isCliTask = void 0;
|
|
4
|
+
const plan_work_nodes_1 = require("./planner/utils/plan-work-nodes");
|
|
5
|
+
const create_scheduler_state_1 = require("./executer/create-scheduler-state");
|
|
6
|
+
const ci_1 = require("./utils/ci");
|
|
7
|
+
const get_logger_1 = require("./console/get-logger");
|
|
8
|
+
const schedule_1 = require("./executer/schedule");
|
|
9
|
+
const event_cache_1 = require("./executer/event-cache");
|
|
10
|
+
const validate_1 = require("./planner/validate");
|
|
11
|
+
const process_manager_1 = require("./executer/process-manager");
|
|
12
|
+
const remove_container_1 = require("./docker/remove-container");
|
|
13
|
+
const isCliTask = (val) => val.type === 'task';
|
|
14
|
+
exports.isCliTask = isCliTask;
|
|
15
|
+
const isCliService = (val) => val.type === 'service';
|
|
16
|
+
exports.isCliService = isCliService;
|
|
17
|
+
function getCli(workTree, environment) {
|
|
18
|
+
return {
|
|
19
|
+
execWatch(options) {
|
|
20
|
+
const processManager = new process_manager_1.ProcessManager(environment, options?.workers ?? 0);
|
|
21
|
+
const state = (0, create_scheduler_state_1.createSchedulerState)({
|
|
22
|
+
services: workTree.services,
|
|
23
|
+
nodes: workTree.nodes,
|
|
24
|
+
watch: options?.watch ?? false,
|
|
25
|
+
logMode: options?.logMode ?? ci_1.isCI ? 'live' : 'interactive',
|
|
26
|
+
cacheMethod: options?.cacheDefault ?? 'checksum',
|
|
27
|
+
});
|
|
28
|
+
const logMode = options?.logMode ?? (ci_1.isCI ? 'live' : 'interactive');
|
|
29
|
+
const logger = (0, get_logger_1.getLogger)(logMode, state, environment);
|
|
30
|
+
return {
|
|
31
|
+
state,
|
|
32
|
+
processManager,
|
|
33
|
+
start: async () => {
|
|
34
|
+
const result = await (0, schedule_1.schedule)(processManager, state, environment);
|
|
35
|
+
await logger.complete(result, environment);
|
|
36
|
+
return result;
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
},
|
|
40
|
+
async shutdown() {
|
|
41
|
+
for (const node of (0, plan_work_nodes_1.iterateWorkNodes)(workTree.nodes)) {
|
|
42
|
+
const containers = await environment.docker.listContainers({
|
|
43
|
+
all: true,
|
|
44
|
+
filters: {
|
|
45
|
+
label: [`hammerkit-id=${node.id}`],
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
for (const container of containers) {
|
|
49
|
+
await (0, remove_container_1.removeContainer)(environment.docker.getContainer(container.Id));
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
for (const service of (0, plan_work_nodes_1.iterateWorkServices)(workTree.services)) {
|
|
53
|
+
const containers = await environment.docker.listContainers({
|
|
54
|
+
all: true,
|
|
55
|
+
filters: {
|
|
56
|
+
label: [`hammerkit-id=${service.id}`],
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
for (const container of containers) {
|
|
60
|
+
await (0, remove_container_1.removeContainer)(environment.docker.getContainer(container.Id));
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
async exec(options) {
|
|
65
|
+
return this.execWatch(options).start();
|
|
66
|
+
},
|
|
67
|
+
async clean(options) {
|
|
68
|
+
await (0, event_cache_1.cleanCache)(workTree, environment, {
|
|
69
|
+
service: options?.service ?? false,
|
|
70
|
+
});
|
|
71
|
+
},
|
|
72
|
+
async restore(path) {
|
|
73
|
+
await (0, event_cache_1.restoreCache)(path, workTree, environment);
|
|
74
|
+
},
|
|
75
|
+
async store(path) {
|
|
76
|
+
await (0, event_cache_1.storeCache)(path, workTree, environment);
|
|
77
|
+
},
|
|
78
|
+
ls() {
|
|
79
|
+
return [
|
|
80
|
+
...Array.from((0, plan_work_nodes_1.iterateWorkServices)(workTree.services)).map((item) => ({ item, type: 'service' })),
|
|
81
|
+
...Array.from((0, plan_work_nodes_1.iterateWorkNodes)(workTree.nodes)).map((item) => ({ item, type: 'task' })),
|
|
82
|
+
];
|
|
83
|
+
},
|
|
84
|
+
validate() {
|
|
85
|
+
return (0, validate_1.validate)(workTree, environment);
|
|
86
|
+
},
|
|
87
|
+
node(name) {
|
|
88
|
+
const node = Object.values(workTree.nodes).find((n) => n.name == name);
|
|
89
|
+
if (!node) {
|
|
90
|
+
throw new Error(`unable to find node ${name}`);
|
|
91
|
+
}
|
|
92
|
+
return node;
|
|
93
|
+
},
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
exports.getCli = getCli;
|
|
97
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.spec.js
ADDED
|
@@ -1,3 +1,14 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.consoleContextMock = void 0;
|
|
4
|
+
/* eslint-disable @typescript-eslint/no-empty-function */
|
|
5
|
+
function consoleContextMock() {
|
|
6
|
+
return {
|
|
7
|
+
warn() { },
|
|
8
|
+
info() { },
|
|
9
|
+
error() { },
|
|
10
|
+
debug() { },
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
exports.consoleContextMock = consoleContextMock;
|
|
3
14
|
//# sourceMappingURL=console-context-mock.js.map
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getLogger = void 0;
|
|
4
|
+
const grouped_logger_1 = require("../logging/grouped-logger");
|
|
5
|
+
const live_logger_1 = require("../logging/live-logger");
|
|
6
|
+
const interactive_logger_1 = require("../logging/interactive-logger");
|
|
7
|
+
const fail_never_1 = require("../utils/fail-never");
|
|
8
|
+
function getLogger(mode, state, env) {
|
|
9
|
+
if (mode === 'grouped') {
|
|
10
|
+
return (0, grouped_logger_1.groupedLogger)(state, env);
|
|
11
|
+
}
|
|
12
|
+
else if (mode === 'live') {
|
|
13
|
+
return (0, live_logger_1.liveLogger)(state, env);
|
|
14
|
+
}
|
|
15
|
+
else if (mode === 'interactive') {
|
|
16
|
+
return (0, interactive_logger_1.interactiveLogger)(state, env);
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
(0, fail_never_1.failNever)(mode, 'unknown log mode');
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
exports.getLogger = getLogger;
|
|
23
|
+
//# sourceMappingURL=get-logger.js.map
|
package/dist/docker/pull.js
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.pull = void 0;
|
|
4
|
-
async function pull(
|
|
4
|
+
async function pull(status, environment, imageName) {
|
|
5
5
|
let searchImageName = imageName;
|
|
6
6
|
if (imageName.indexOf(':') === -1) {
|
|
7
7
|
searchImageName += ':latest';
|
|
8
8
|
}
|
|
9
|
-
const images = await docker.listImages({});
|
|
9
|
+
const images = await environment.docker.listImages({});
|
|
10
10
|
if (images.some((i) => i.RepoTags?.some((repoTag) => repoTag === searchImageName))) {
|
|
11
11
|
return;
|
|
12
12
|
}
|
|
13
|
-
|
|
14
|
-
const image = await docker.pull(imageName);
|
|
13
|
+
status.write('debug', `pull image ${imageName}`);
|
|
14
|
+
const image = await environment.docker.pull(imageName);
|
|
15
15
|
await new Promise((resolve, reject) => {
|
|
16
|
-
docker.modem.followProgress(image, (err, res) => (err ? reject(err) : resolve(res)));
|
|
16
|
+
environment.docker.modem.followProgress(image, (err, res) => (err ? reject(err) : resolve(res)));
|
|
17
17
|
});
|
|
18
18
|
}
|
|
19
19
|
exports.pull = pull;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.removeContainer = void 0;
|
|
4
|
+
const sleep_1 = require("../utils/sleep");
|
|
5
|
+
async function removeContainer(container) {
|
|
6
|
+
try {
|
|
7
|
+
await container.remove({ force: true });
|
|
8
|
+
}
|
|
9
|
+
catch (e) {
|
|
10
|
+
if (e.statusCode === 404) {
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
else if (e.statusCode === 409) {
|
|
14
|
+
await (0, sleep_1.sleep)(500);
|
|
15
|
+
return removeContainer(container);
|
|
16
|
+
}
|
|
17
|
+
throw e;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
exports.removeContainer = removeContainer;
|
|
21
|
+
//# sourceMappingURL=remove-container.js.map
|
package/dist/docker/stream.js
CHANGED
|
@@ -1,23 +1,27 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.awaitStream = void 0;
|
|
3
|
+
exports.logStream = exports.awaitStream = void 0;
|
|
4
4
|
const log_1 = require("../log");
|
|
5
|
-
async function awaitStream(
|
|
5
|
+
async function awaitStream(status, stream) {
|
|
6
6
|
return new Promise((resolve, reject) => {
|
|
7
|
-
|
|
8
|
-
return (buffer) => {
|
|
9
|
-
for (const log of log_1.getLogs(buffer)) {
|
|
10
|
-
node.status.console.write('process', leve, log.endsWith('\n') ? log.substr(0, log.length - 1) : log);
|
|
11
|
-
}
|
|
12
|
-
};
|
|
13
|
-
}
|
|
7
|
+
logStream(status, stream);
|
|
14
8
|
stream.on('error', reject);
|
|
15
9
|
stream.on('end', resolve);
|
|
16
10
|
stream.on('close', resolve);
|
|
17
|
-
demuxStream(stream, writeLog('info'), writeLog('error'));
|
|
18
11
|
});
|
|
19
12
|
}
|
|
20
13
|
exports.awaitStream = awaitStream;
|
|
14
|
+
function logStream(status, stream) {
|
|
15
|
+
demuxStream(stream, writeLog(status, 'stdout'), writeLog(status, 'stderr'));
|
|
16
|
+
}
|
|
17
|
+
exports.logStream = logStream;
|
|
18
|
+
function writeLog(status, level) {
|
|
19
|
+
return async (buffer) => {
|
|
20
|
+
for (const log of (0, log_1.getLogs)(buffer)) {
|
|
21
|
+
status.console(level, log.endsWith('\n') ? log.substr(0, log.length - 1) : log);
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
}
|
|
21
25
|
function demuxStream(stream, stdoutFn, stderrFn) {
|
|
22
26
|
let nextDataType = null;
|
|
23
27
|
let nextDataLength = null;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.usingContainer = void 0;
|
|
4
|
+
const remove_container_1 = require("./remove-container");
|
|
5
|
+
const log_1 = require("../log");
|
|
6
|
+
const execute_docker_1 = require("../executer/execute-docker");
|
|
7
|
+
async function usingContainer(environment, node, createOptions, callback) {
|
|
8
|
+
const status = environment.status.from(node);
|
|
9
|
+
let container = null;
|
|
10
|
+
try {
|
|
11
|
+
container = await environment.docker.createContainer(createOptions);
|
|
12
|
+
status.write('debug', `starting container with image ${node.image}`);
|
|
13
|
+
await (0, execute_docker_1.startContainer)(status, container);
|
|
14
|
+
return await callback(container);
|
|
15
|
+
}
|
|
16
|
+
finally {
|
|
17
|
+
if (container) {
|
|
18
|
+
try {
|
|
19
|
+
await (0, remove_container_1.removeContainer)(container);
|
|
20
|
+
}
|
|
21
|
+
catch (e) {
|
|
22
|
+
status.write('error', `remove of container failed ${(0, log_1.getErrorMessage)(e)}`);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
exports.usingContainer = usingContainer;
|
|
28
|
+
//# sourceMappingURL=using-container.js.map
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
4
|
-
function
|
|
3
|
+
exports.extendHostVariables = void 0;
|
|
4
|
+
function extendHostVariables(envs, ctx) {
|
|
5
5
|
const result = { ...envs };
|
|
6
|
-
for (const key of
|
|
6
|
+
for (const key of ['PATH']) {
|
|
7
7
|
const value = ctx.processEnvs[key];
|
|
8
8
|
if (!result[key] && value) {
|
|
9
9
|
result[key] = value;
|
|
@@ -11,5 +11,5 @@ function getProcessEnvs(envs, ctx) {
|
|
|
11
11
|
}
|
|
12
12
|
return result;
|
|
13
13
|
}
|
|
14
|
-
exports.
|
|
14
|
+
exports.extendHostVariables = extendHostVariables;
|
|
15
15
|
//# sourceMappingURL=get-process-env.js.map
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.replaceEnvVariables = void 0;
|
|
4
|
-
function replaceEnvVariables(
|
|
5
|
-
const result = { ...
|
|
4
|
+
function replaceEnvVariables(envs, processEnv) {
|
|
5
|
+
const result = { ...envs };
|
|
6
6
|
for (const key of Object.keys(result)) {
|
|
7
7
|
const value = result[key];
|
|
8
8
|
if (value.startsWith('$')) {
|
|
9
9
|
const processEnvValue = processEnv[value.substr(1)];
|
|
10
10
|
if (processEnvValue) {
|
|
11
|
-
node.status.console.write('internal', 'debug', `use process env ${value.substr(1)}`);
|
|
12
11
|
result[key] = processEnvValue;
|
|
13
12
|
}
|
|
14
13
|
else {
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AbortError = exports.checkForAbort = void 0;
|
|
4
|
+
function checkForAbort(abortSignal) {
|
|
5
|
+
if (abortSignal.aborted) {
|
|
6
|
+
throw new AbortError();
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
exports.checkForAbort = checkForAbort;
|
|
10
|
+
class AbortError extends Error {
|
|
11
|
+
}
|
|
12
|
+
exports.AbortError = AbortError;
|
|
13
|
+
//# sourceMappingURL=abort.js.map
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.checkReadiness = void 0;
|
|
4
|
+
const execute_docker_1 = require("./execute-docker");
|
|
5
|
+
async function checkReadiness(status, healthCheck, environment, container, abort) {
|
|
6
|
+
const result = await (0, execute_docker_1.execCommand)(status, environment, container, undefined, healthCheck.cmd.split(' '), null, 2000, abort);
|
|
7
|
+
if (result.type === 'timeout') {
|
|
8
|
+
return false;
|
|
9
|
+
}
|
|
10
|
+
else if (result.type === 'canceled') {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
else {
|
|
14
|
+
if (result.result.ExitCode === 0) {
|
|
15
|
+
status.write('debug', `healthcheck ${healthCheck.cmd} succeeded`);
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
status.write('debug', `healthcheck ${healthCheck.cmd} failed with ${result.result.ExitCode}`);
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
exports.checkReadiness = checkReadiness;
|
|
25
|
+
//# sourceMappingURL=check-readiness.js.map
|