hammerkit 1.4.0-beta.6 → 1.5.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (182) hide show
  1. package/.devcontainer/Dockerfile +5 -0
  2. package/.devcontainer/devcontainer.json +40 -0
  3. package/Dockerfile +1 -1
  4. package/README.md +13 -86
  5. package/SUMMARY.md +43 -0
  6. package/build.schema.json +56 -2
  7. package/dist/cli.js +97 -0
  8. package/dist/cli.spec.js +5 -0
  9. package/dist/console/console-context-mock.js +11 -0
  10. package/dist/console/get-logger.js +23 -0
  11. package/dist/docker/pull.js +5 -5
  12. package/dist/docker/remove-container.js +21 -0
  13. package/dist/docker/stream.js +19 -21
  14. package/dist/docker/using-container.js +28 -0
  15. package/dist/environment/get-process-env.js +4 -4
  16. package/dist/environment/replace-env-variables.js +2 -3
  17. package/dist/executer/abort.js +13 -0
  18. package/dist/executer/check-readiness.js +25 -0
  19. package/dist/executer/create-scheduler-state.js +32 -0
  20. package/dist/executer/docker-node.js +123 -0
  21. package/dist/executer/docker-service.js +113 -0
  22. package/dist/executer/environment-mock.js +17 -0
  23. package/dist/executer/event-cache.js +165 -0
  24. package/dist/executer/execute-command.js +42 -0
  25. package/dist/executer/execute-docker.js +73 -223
  26. package/dist/executer/execute.spec.js +37 -37
  27. package/dist/executer/execution-steps.js +57 -0
  28. package/dist/executer/get-docker-executor.js +21 -20
  29. package/dist/executer/hierarchy-scheduler.js +94 -0
  30. package/dist/executer/kubernetes-service.js +64 -0
  31. package/dist/executer/label-values.js +65 -0
  32. package/dist/executer/local-node.js +57 -0
  33. package/dist/executer/print-container-options.js +17 -0
  34. package/dist/{logging/log-strategy.js → executer/process-item.js} +1 -1
  35. package/dist/executer/process-listener.js +3 -0
  36. package/dist/executer/process-manager.js +111 -0
  37. package/dist/executer/process-manager.spec.js +91 -0
  38. package/dist/executer/{executor.js → process.js} +1 -1
  39. package/dist/executer/{execute-result.js → readonly-state.js} +1 -1
  40. package/dist/executer/schedule.js +139 -0
  41. package/dist/executer/scheduler/check-for-loop.js +21 -0
  42. package/dist/executer/scheduler/enqueue-next.js +27 -0
  43. package/dist/{testing/test-context.js → executer/scheduler/node-state.js} +1 -1
  44. package/dist/executer/scheduler/scheduler-result.js +3 -0
  45. package/dist/executer/scheduler/scheduler-state.js +3 -0
  46. package/dist/executer/scheduler/service-state.js +3 -0
  47. package/dist/executer/service-dns.js +6 -0
  48. package/dist/executer/set-user-permission.js +16 -0
  49. package/dist/executer/start-node.js +59 -0
  50. package/dist/executer/state-listener.js +3 -0
  51. package/dist/executer/state.js +49 -0
  52. package/dist/executer/states.js +4 -129
  53. package/dist/executer/watch-node.js +57 -0
  54. package/dist/executer/watch-service.js +54 -0
  55. package/dist/executer/work-scope.js +10 -0
  56. package/dist/file/get-file-context-mock.js +1 -0
  57. package/dist/file/get-file-context-mock.spec.js +28 -27
  58. package/dist/file/get-file-context.js +73 -63
  59. package/dist/file/move-files.js +1 -1
  60. package/dist/get-work-context.js +29 -0
  61. package/dist/index.js +11 -10
  62. package/dist/log.js +209 -52
  63. package/dist/logging/grouped-logger.js +31 -19
  64. package/dist/logging/interactive-logger.js +20 -23
  65. package/dist/logging/live-logger.js +7 -17
  66. package/dist/optimizer/calculate-checksum.js +1 -1
  67. package/dist/optimizer/get-cache-directory.js +8 -13
  68. package/dist/optimizer/get-work-node-cache-stats.js +68 -21
  69. package/dist/optimizer/read-work-node-cache.js +2 -2
  70. package/dist/optimizer/work-node-cache-description.js +12 -11
  71. package/dist/optimizer/write-work-node-cache.js +9 -8
  72. package/dist/{executer/execution-context.js → parser/build-file-service.js} +1 -1
  73. package/dist/{optimizer → parser}/cache-method.js +0 -0
  74. package/dist/parser/get-build-file.js +1 -1
  75. package/dist/parser/parse-boolean.js +29 -0
  76. package/dist/parser/parse-boolean.spec.js +48 -0
  77. package/dist/parser/parse-build-file-references.js +2 -2
  78. package/dist/parser/parse-build-file-services.js +58 -0
  79. package/dist/parser/parse-build-file-task-command.js +6 -5
  80. package/dist/parser/parse-build-file-task-source.js +5 -5
  81. package/dist/parser/parse-build-file.js +29 -17
  82. package/dist/parser/parse-context.js +13 -0
  83. package/dist/parser/parse-envs.js +4 -3
  84. package/dist/parser/parse-healthcheck.js +28 -0
  85. package/dist/parser/parse-label-arguments.js +23 -0
  86. package/dist/parser/parse-service-selector.js +39 -0
  87. package/dist/parser/parse-string-array.js +6 -5
  88. package/dist/parser/parse-string-map.js +30 -0
  89. package/dist/parser/parse-string.js +20 -0
  90. package/dist/parser/read-build-file.js +16 -5
  91. package/dist/parser/read-env-file.js +7 -7
  92. package/dist/planner/utils/get-container-mounts.js +24 -0
  93. package/dist/planner/utils/normalize-path.js +17 -0
  94. package/dist/planner/utils/parse-work-node-mount.js +3 -14
  95. package/dist/planner/utils/parse-work-node-mount.spec.js +10 -10
  96. package/dist/planner/utils/parse-work-service-volume.js +21 -0
  97. package/dist/planner/utils/plan-work-command.js +1 -1
  98. package/dist/planner/utils/plan-work-dependency.js +29 -24
  99. package/dist/planner/utils/plan-work-node.js +203 -120
  100. package/dist/planner/utils/plan-work-nodes.js +60 -12
  101. package/dist/planner/utils/plan-work-tree.js +8 -4
  102. package/dist/planner/utils/plan-work-volume.js +25 -0
  103. package/dist/planner/validate.js +56 -8
  104. package/dist/planner/work-context.js +20 -0
  105. package/dist/planner/work-mount.js +3 -0
  106. package/dist/planner/work-node-id.js +4 -4
  107. package/dist/planner/work-node-id.spec.js +11 -8
  108. package/dist/planner/work-node-path.js +0 -1
  109. package/dist/planner/work-node-status.js +121 -18
  110. package/dist/planner/work-node.js +3 -3
  111. package/dist/planner/work-service-id.js +13 -0
  112. package/dist/planner/work-service-volume.js +3 -0
  113. package/dist/planner/work-service.js +6 -0
  114. package/dist/planner/work-services.js +3 -0
  115. package/dist/planner/work-volume.js +3 -0
  116. package/dist/program.js +155 -91
  117. package/dist/program.spec.js +20 -0
  118. package/dist/start-watch-processes.js +28 -0
  119. package/dist/testing/cli.js +73 -0
  120. package/dist/testing/create-build-file.js +1 -1
  121. package/dist/testing/example-test-suite.js +54 -0
  122. package/dist/testing/expect.js +30 -18
  123. package/dist/testing/get-logger.js +23 -0
  124. package/dist/testing/get-node.js +2 -2
  125. package/dist/testing/get-test-context.js +6 -5
  126. package/dist/testing/get-test-suite.js +2 -51
  127. package/dist/testing/integration/cache.spec.js +25 -28
  128. package/dist/testing/integration/cancellation.spec.js +19 -21
  129. package/dist/testing/integration/clean.spec.js +29 -13
  130. package/dist/testing/integration/cmd.spec.js +5 -8
  131. package/dist/testing/integration/concurrency.spec.js +8 -17
  132. package/dist/testing/integration/docker.spec.js +6 -9
  133. package/dist/testing/integration/env.spec.js +27 -34
  134. package/dist/testing/integration/error.spec.js +5 -9
  135. package/dist/testing/integration/extend.spec.js +32 -0
  136. package/dist/testing/integration/glob.spec.js +35 -28
  137. package/dist/testing/integration/include.spec.js +17 -16
  138. package/dist/testing/integration/invalid.spec.js +3 -2
  139. package/dist/testing/integration/invalid_loop.spec.js +13 -0
  140. package/dist/testing/integration/kubernetes.spec.js +15 -0
  141. package/dist/testing/integration/labels.spec.js +47 -0
  142. package/dist/testing/integration/local.spec.js +4 -7
  143. package/dist/testing/integration/monorepo.spec.js +6 -30
  144. package/dist/testing/integration/multi-line.spec.js +4 -4
  145. package/dist/testing/integration/program.spec.js +3 -10
  146. package/dist/testing/integration/reference.spec.js +10 -13
  147. package/dist/testing/integration/services.spec.js +14 -0
  148. package/dist/testing/integration/store.spec.js +42 -35
  149. package/dist/testing/integration/unknown.spec.js +4 -4
  150. package/dist/testing/integration/validate.spec.js +4 -4
  151. package/dist/testing/integration/watch.spec.js +25 -23
  152. package/dist/testing/lazy.js +17 -0
  153. package/dist/testing/start-watch-processes.js +29 -0
  154. package/dist/testing/test-suite-setup.js +3 -0
  155. package/dist/utils/abort-event.js +24 -0
  156. package/dist/utils/abortable-function.js +71 -0
  157. package/dist/utils/debouncer.spec.js +8 -8
  158. package/dist/utils/environment-config.js +13 -0
  159. package/dist/utils/fail-never.js +1 -1
  160. package/dist/utils/sleep.spec.js +3 -3
  161. package/package.json +24 -21
  162. package/dist/console/get-console-context-mock.js +0 -33
  163. package/dist/executer/clean.js +0 -22
  164. package/dist/executer/execute-local.js +0 -58
  165. package/dist/executer/execute.js +0 -95
  166. package/dist/executer/execution-context-event.js +0 -3
  167. package/dist/executer/execution-context-mock.js +0 -3
  168. package/dist/executer/executor-mock.js +0 -39
  169. package/dist/executer/get-environment-mock.js +0 -17
  170. package/dist/executer/get-execution-context-mock.js +0 -19
  171. package/dist/executer/get-local-executor.js +0 -22
  172. package/dist/executer/get-ready-work-nodes.js +0 -15
  173. package/dist/executer/restore.js +0 -22
  174. package/dist/executer/store.js +0 -23
  175. package/dist/logging/get-logger.js +0 -23
  176. package/dist/logging/message-to-console.js +0 -16
  177. package/dist/optimizer/optimize.js +0 -32
  178. package/dist/optimizer/work-node-description.js +0 -12
  179. package/dist/planner/utils/find-build-task.js +0 -18
  180. package/dist/testing/create-work-tree.js +0 -11
  181. package/dist/utils/defer.js +0 -57
  182. package/dist/utils/defer.spec.js +0 -76
@@ -2,24 +2,27 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const work_node_id_1 = require("./work-node-id");
4
4
  const create_build_file_1 = require("../testing/create-build-file");
5
- const get_environment_mock_1 = require("../executer/get-environment-mock");
6
5
  const plan_work_node_1 = require("./utils/plan-work-node");
6
+ const work_context_1 = require("./work-context");
7
+ const environment_mock_1 = require("../executer/environment-mock");
7
8
  async function compareTasks(firstTask, secondTask, expectEqual) {
8
- const environmentMock = get_environment_mock_1.getEnvironmentMock();
9
- const firstBuildFile = await create_build_file_1.createBuildFile(environmentMock, {
9
+ const environment = (0, environment_mock_1.environmentMock)(process.cwd());
10
+ const firstBuildFile = await (0, create_build_file_1.createBuildFile)(environment, {
10
11
  tasks: {
11
12
  test: firstTask,
12
13
  },
13
14
  });
14
- const secondBuildFile = await create_build_file_1.createBuildFile(environmentMock, {
15
+ const secondBuildFile = await (0, create_build_file_1.createBuildFile)(environment, {
15
16
  tasks: {
16
17
  test: secondTask,
17
18
  },
18
19
  });
19
- const firstMerged = plan_work_node_1.getMergedBuildTask(firstBuildFile, firstBuildFile.tasks['test']);
20
- const secondMerged = plan_work_node_1.getMergedBuildTask(secondBuildFile, secondBuildFile.tasks['test']);
21
- const firstNodeId = work_node_id_1.getWorkNodeId(firstBuildFile.path, firstMerged.task, firstMerged.deps);
22
- const secondNodeId = work_node_id_1.getWorkNodeId(secondBuildFile.path, secondMerged.task, secondMerged.deps);
20
+ const firstContext = (0, work_context_1.createWorkContext)(firstBuildFile);
21
+ const firstMerged = (0, plan_work_node_1.planTask)(firstContext, (0, plan_work_node_1.findBuildTask)(firstContext, { taskName: 'test' }));
22
+ const secondContext = (0, work_context_1.createWorkContext)(secondBuildFile);
23
+ const secondMerged = (0, plan_work_node_1.planTask)(secondContext, (0, plan_work_node_1.findBuildTask)(secondContext, { taskName: 'test' }));
24
+ const firstNodeId = (0, work_node_id_1.getWorkNodeId)(firstMerged);
25
+ const secondNodeId = (0, work_node_id_1.getWorkNodeId)(secondMerged);
23
26
  if (expectEqual) {
24
27
  expect(firstNodeId).toEqual(secondNodeId);
25
28
  }
@@ -1,3 +1,2 @@
1
1
  "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
2
  //# sourceMappingURL=work-node-path.js.map
@@ -1,31 +1,134 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.nodeConsole = void 0;
4
- const log_1 = require("../log");
3
+ exports.statusConsole = exports.logContext = void 0;
5
4
  const emitter_1 = require("../utils/emitter");
6
- function nodeConsole() {
7
- let current = null;
8
- const logs = [];
9
- const emit = emitter_1.emitter();
5
+ const work_node_1 = require("./work-node");
6
+ const environment_config_1 = require("../utils/environment-config");
7
+ const log_1 = require("../log");
8
+ const StatusBufferMax = (0, environment_config_1.getEnvironmentConfig)('STATUS_BUFFER_LIMIT', 300);
9
+ const ConsoleBufferMax = (0, environment_config_1.getEnvironmentConfig)('CONSOLE_BUFFER_LIMIT', 30);
10
+ function logContext(type, node) {
11
+ return {
12
+ type,
13
+ name: node.name,
14
+ id: node.id,
15
+ };
16
+ }
17
+ exports.logContext = logContext;
18
+ class BufferContext {
19
+ constructor(limit) {
20
+ this.limit = limit;
21
+ this.scopedBuffer = {};
22
+ this.buffer = [];
23
+ }
24
+ add(id, data) {
25
+ this.buffer.push(data);
26
+ if (id in this.scopedBuffer) {
27
+ const scopedBuffer = this.scopedBuffer[id];
28
+ scopedBuffer.push(data);
29
+ if (scopedBuffer.length > this.limit) {
30
+ const removedBuffer = this.buffer.splice(0, 1)[0];
31
+ const bufferIndex = this.buffer.indexOf(removedBuffer);
32
+ if (bufferIndex >= 0) {
33
+ this.buffer.splice(bufferIndex, 1);
34
+ }
35
+ }
36
+ }
37
+ else {
38
+ this.scopedBuffer[id] = [data];
39
+ }
40
+ }
41
+ current(id) {
42
+ if (!(id in this.scopedBuffer)) {
43
+ return null;
44
+ }
45
+ const value = this.scopedBuffer[id];
46
+ return value[value.length - 1];
47
+ }
48
+ get(id) {
49
+ if (id) {
50
+ return this.scopedBuffer[id] ?? [];
51
+ }
52
+ else {
53
+ return this.buffer;
54
+ }
55
+ }
56
+ }
57
+ function statusConsole() {
58
+ const statusBuffer = new BufferContext(StatusBufferMax);
59
+ const consoleBuffer = new BufferContext(ConsoleBufferMax);
60
+ const emit = (0, emitter_1.emitter)();
61
+ function addMessage(context, message) {
62
+ emit.emit(message);
63
+ if (message.type === 'console') {
64
+ consoleBuffer.add(context.id, message);
65
+ }
66
+ else if (log_1.isVerbose || message.level !== 'debug') {
67
+ statusBuffer.add(context.id, message);
68
+ }
69
+ }
10
70
  return {
11
- read() {
12
- return Promise.resolve(logs);
71
+ task(task) {
72
+ return this.context(logContext('task', task));
73
+ },
74
+ service(service) {
75
+ return this.context(logContext('service', service));
76
+ },
77
+ from(node) {
78
+ if ((0, work_node_1.isWorkNode)(node)) {
79
+ return this.task(node);
80
+ }
81
+ else {
82
+ return this.service(node);
83
+ }
84
+ },
85
+ context(context) {
86
+ return {
87
+ currentLog() {
88
+ return consoleBuffer.current(context.id);
89
+ },
90
+ current() {
91
+ return statusBuffer.current(context.id);
92
+ },
93
+ *logs() {
94
+ for (const log of consoleBuffer.get(context.id)) {
95
+ yield log;
96
+ }
97
+ },
98
+ *read() {
99
+ for (const log of statusBuffer.get(context.id)) {
100
+ yield log;
101
+ }
102
+ },
103
+ console(type, message) {
104
+ addMessage(context, {
105
+ type: 'console',
106
+ console: type,
107
+ message,
108
+ date: new Date(),
109
+ context,
110
+ });
111
+ },
112
+ write(level, message) {
113
+ addMessage(context, {
114
+ type: 'status',
115
+ message,
116
+ context,
117
+ level,
118
+ date: new Date(),
119
+ });
120
+ },
121
+ };
13
122
  },
14
123
  on(listener) {
15
124
  return emit.on(listener);
16
125
  },
17
- write(type, level, message) {
18
- const log = { level, type, message, date: new Date() };
19
- if (log_1.isVerbose || level !== 'debug') {
20
- current = log;
126
+ *read() {
127
+ for (const message of statusBuffer.get()) {
128
+ yield message;
21
129
  }
22
- emit.emit(log);
23
- logs.push(log);
24
- },
25
- get current() {
26
- return current;
27
130
  },
28
131
  };
29
132
  }
30
- exports.nodeConsole = nodeConsole;
133
+ exports.statusConsole = statusConsole;
31
134
  //# sourceMappingURL=work-node-status.js.map
@@ -1,8 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.isLocalWorkNode = exports.isContainerWorkNode = void 0;
3
+ exports.isWorkNode = exports.isContainerWorkNode = void 0;
4
4
  const isContainerWorkNode = (val) => val.type === 'container';
5
5
  exports.isContainerWorkNode = isContainerWorkNode;
6
- const isLocalWorkNode = (val) => val.type === 'local';
7
- exports.isLocalWorkNode = isLocalWorkNode;
6
+ const isWorkNode = (val) => val.type === 'container' || val.type === 'local';
7
+ exports.isWorkNode = isWorkNode;
8
8
  //# sourceMappingURL=work-node.js.map
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getWorkServiceId = void 0;
4
+ const crypto_1 = require("crypto");
5
+ function getWorkServiceId(buildFile, service) {
6
+ const jsonData = JSON.stringify({
7
+ path: buildFile.path,
8
+ image: service.image,
9
+ });
10
+ return (0, crypto_1.createHash)('sha1').update(jsonData).digest('hex');
11
+ }
12
+ exports.getWorkServiceId = getWorkServiceId;
13
+ //# sourceMappingURL=work-service-id.js.map
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=work-service-volume.js.map
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isContainerWorkService = void 0;
4
+ const isContainerWorkService = (svc) => 'image' in svc && svc.type === 'container-service';
5
+ exports.isContainerWorkService = isContainerWorkService;
6
+ //# sourceMappingURL=work-service.js.map
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=work-services.js.map
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=work-volume.js.map
package/dist/program.js CHANGED
@@ -1,75 +1,146 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getProgram = void 0;
3
+ exports.getProgram = exports.createCli = void 0;
4
4
  const commander_1 = require("commander");
5
5
  const path_1 = require("path");
6
- const get_build_file_1 = require("./parser/get-build-file");
7
- const plan_work_nodes_1 = require("./planner/utils/plan-work-nodes");
8
- const execute_1 = require("./executer/execute");
9
- const plan_work_tree_1 = require("./planner/utils/plan-work-tree");
10
- const restore_1 = require("./executer/restore");
11
- const store_1 = require("./executer/store");
12
- const clean_1 = require("./executer/clean");
13
- const validate_1 = require("./planner/validate");
14
- const get_local_executor_1 = require("./executer/get-local-executor");
15
- const get_docker_executor_1 = require("./executer/get-docker-executor");
16
- const get_logger_1 = require("./logging/get-logger");
17
6
  const ci_1 = require("./utils/ci");
18
- const emitter_1 = require("./utils/emitter");
7
+ const parse_label_arguments_1 = require("./parser/parse-label-arguments");
8
+ const cli_1 = require("./cli");
9
+ const get_build_file_1 = require("./parser/get-build-file");
10
+ const work_scope_1 = require("./executer/work-scope");
11
+ const get_work_context_1 = require("./get-work-context");
12
+ const log_1 = require("./log");
13
+ const label_values_1 = require("./executer/label-values");
14
+ async function createCli(fileName, environment, workScope) {
15
+ const buildFile = await (0, get_build_file_1.getBuildFile)(fileName, environment);
16
+ const workTree = (0, get_work_context_1.getWorkScope)(buildFile, workScope, environment);
17
+ return (0, cli_1.getCli)(workTree, environment);
18
+ }
19
+ exports.createCli = createCli;
20
+ function parseWorkScope(options) {
21
+ const scope = (0, work_scope_1.emptyWorkLabelScope)();
22
+ if (typeof options !== 'object') {
23
+ return scope;
24
+ }
25
+ if (options === undefined || options === null) {
26
+ return scope;
27
+ }
28
+ for (const [key, value] of Object.entries(options)) {
29
+ if (key === 'filter' && value instanceof Array) {
30
+ scope.filterLabels = (0, parse_label_arguments_1.parseLabelArguments)(value);
31
+ }
32
+ else if (key === 'exclude' && value instanceof Array) {
33
+ scope.excludeLabels = (0, parse_label_arguments_1.parseLabelArguments)(value);
34
+ }
35
+ }
36
+ return scope;
37
+ }
19
38
  async function getProgram(environment, argv) {
20
39
  const program = new commander_1.Command();
21
40
  const args = [...argv];
22
41
  const fileIndex = args.indexOf('--file');
23
- const fileName = path_1.join(environment.cwd, fileIndex >= 0 ? args[fileIndex + 1] : 'build.yaml');
42
+ const fileName = (0, path_1.join)(environment.cwd, fileIndex >= 0 ? args[fileIndex + 1] : 'build.yaml');
24
43
  if (fileIndex >= 0) {
25
44
  args.splice(fileIndex, 2);
26
45
  }
27
46
  if (await environment.file.exists(fileName)) {
28
- const buildFile = await get_build_file_1.getBuildFile(fileName, environment);
29
- const workNodes = plan_work_nodes_1.planWorkNodes(buildFile);
30
- const reservedCommands = ['clean', 'store', 'restore', 'validate'];
31
47
  program
32
- .command('clean')
33
- .description('clear task cache')
34
- .action(async () => {
35
- try {
36
- await clean_1.clean(workNodes, environment);
48
+ .command('ls')
49
+ .description('list all tasks')
50
+ .addOption(new commander_1.Option('-f, --filter <labels...>', 'filter task and services with labels'))
51
+ .addOption(new commander_1.Option('-e, --exclude <labels...>', 'exclude task and services with labels'))
52
+ .action(async (options) => {
53
+ const cli = await createCli(fileName, environment, parseWorkScope(options));
54
+ const items = cli.ls();
55
+ const tasks = items.filter(cli_1.isCliTask);
56
+ const services = items.filter(cli_1.isCliService);
57
+ if (services.length > 0) {
58
+ (0, log_1.printTitle)('Services');
59
+ for (const node of services) {
60
+ (0, log_1.printItem)(node.item);
61
+ (0, log_1.printProperty)('ports', node.item.ports.map((p) => `127.0.0.1:${p.hostPort} -> ${p.containerPort}`).join(', '));
62
+ if (node.item.type === 'kubernetes-service') {
63
+ (0, log_1.printProperty)('context', node.item.context);
64
+ (0, log_1.printProperty)('selector', `${node.item.selector.type}/${node.item.selector.name}`);
65
+ }
66
+ else {
67
+ (0, log_1.printProperty)('image', node.item.image);
68
+ }
69
+ }
37
70
  }
38
- catch (e) {
39
- environment.console.error(e);
40
- process.exit(1);
71
+ if (tasks.length > 0 && services.length > 0) {
72
+ process.stdout.write('\n');
41
73
  }
74
+ if (tasks.length > 0) {
75
+ (0, log_1.printTitle)('Tasks');
76
+ for (const node of tasks) {
77
+ (0, log_1.printItem)(node.item);
78
+ if (node.item.needs.length > 0) {
79
+ (0, log_1.printProperty)('needs', node.item.needs.map((d) => d.name).join(', '));
80
+ }
81
+ if (node.item.deps.length > 0) {
82
+ (0, log_1.printProperty)('deps', node.item.deps.map((d) => d.name).join(', '));
83
+ }
84
+ if (node.item.type === 'container') {
85
+ (0, log_1.printProperty)('image', node.item.image);
86
+ }
87
+ if (node.item.caching) {
88
+ (0, log_1.printProperty)('caching', node.item.caching);
89
+ }
90
+ if ((0, label_values_1.hasLabels)(node.item.labels)) {
91
+ (0, log_1.printProperty)('labels', `${Object.keys(node.item.labels)
92
+ .map((key) => `${key}=${node.item.labels[key].join(',')}`)
93
+ .join(' ')}`);
94
+ }
95
+ if (node.item.src.length > 0) {
96
+ (0, log_1.printProperty)('src', node.item.src.map((d) => (0, path_1.relative)(node.item.cwd, d.absolutePath)).join(' '));
97
+ }
98
+ if (node.item.generates.length > 0) {
99
+ (0, log_1.printProperty)('generates', node.item.generates.map((d) => (0, path_1.relative)(node.item.cwd, d.path)).join(' '));
100
+ }
101
+ }
102
+ }
103
+ process.stdout.write('\n');
104
+ });
105
+ program
106
+ .command('clean')
107
+ .description('clear task cache')
108
+ .addOption(new commander_1.Option('--service', 'clean data volumes of services'))
109
+ .addOption(new commander_1.Option('-f, --filter <labels...>', 'filter task and services with labels'))
110
+ .addOption(new commander_1.Option('-e, --exclude <labels...>', 'exclude task and services with labels'))
111
+ .action(async (options) => {
112
+ const cli = await createCli(fileName, environment, parseWorkScope(options));
113
+ await cli.clean({
114
+ service: options.service,
115
+ });
42
116
  });
43
117
  program
44
118
  .command('store <path>')
45
119
  .description('save task outputs into <path>')
46
- .action(async (path) => {
47
- try {
48
- await store_1.store(workNodes, path, environment);
49
- }
50
- catch (e) {
51
- environment.console.error(e);
52
- process.exit(1);
53
- }
120
+ .addOption(new commander_1.Option('-f, --filter <labels...>', 'filter task and services with labels'))
121
+ .addOption(new commander_1.Option('-e, --exclude <labels...>', 'exclude task and services with labels'))
122
+ .action(async (path, options) => {
123
+ const cli = await createCli(fileName, environment, parseWorkScope(options));
124
+ await cli.store((0, path_1.resolve)(path));
54
125
  });
55
126
  program
56
127
  .command('restore <path>')
57
128
  .description('restore task outputs from <path>')
58
- .action(async (path) => {
59
- try {
60
- await restore_1.restore(workNodes, path, environment);
61
- }
62
- catch (e) {
63
- environment.console.error(e);
64
- process.exit(1);
65
- }
129
+ .addOption(new commander_1.Option('-f, --filter <labels...>', 'filter task and services with labels'))
130
+ .addOption(new commander_1.Option('-e, --exclude <labels...>', 'exclude task and services with labels'))
131
+ .action(async (path, options) => {
132
+ const cli = await createCli(fileName, environment, parseWorkScope(options));
133
+ await cli.restore((0, path_1.resolve)(path));
66
134
  });
67
135
  program
68
136
  .command('validate')
69
137
  .description('validate build.yaml')
70
- .action(async () => {
138
+ .addOption(new commander_1.Option('-f, --filter <labels...>', 'filter task and services with labels'))
139
+ .addOption(new commander_1.Option('-e, --exclude <labels...>', 'exclude task and services with labels'))
140
+ .action(async (options) => {
71
141
  let errors = 0;
72
- for await (const validation of validate_1.validate(buildFile, environment)) {
142
+ const cli = await createCli(fileName, environment, parseWorkScope(options));
143
+ for await (const validation of cli.validate()) {
73
144
  if (validation.type === 'error') {
74
145
  errors++;
75
146
  environment.console.error(validation.message);
@@ -78,56 +149,46 @@ async function getProgram(environment, argv) {
78
149
  environment.console.warn(validation.message);
79
150
  }
80
151
  }
81
- if (errors === 0) {
82
- process.exit(0);
83
- }
84
- else {
85
- process.exit(1);
152
+ if (errors !== 0) {
153
+ program.error('Detected errors in your build.yaml', { exitCode: 1 });
86
154
  }
87
155
  });
88
- for (const node of plan_work_nodes_1.iterateWorkNodes(workNodes)) {
89
- if (reservedCommands.indexOf(node.name) >= 0) {
90
- environment.console.warn(`${node.name} is reserved, please use another name`);
91
- continue;
92
- }
93
- program
94
- .command(node.name)
95
- .description(node.description || '')
96
- .option('-c, --concurrency <number>', 'parallel worker count', parseInt, 4)
97
- .addOption(new commander_1.Option('-w, --watch', 'watch tasks').default(false))
98
- .addOption(new commander_1.Option('-l, --log <mode>', 'log mode')
99
- .default(ci_1.isCI ? 'live' : 'interactive')
100
- .choices(['interactive', 'live', 'grouped']))
101
- .addOption(new commander_1.Option('--cache <method>', 'caching method to compare')
102
- .default(ci_1.isCI ? 'checksum' : 'modify-date')
103
- .choices(['checksum', 'modify-date', 'none']))
104
- .addOption(new commander_1.Option('--no-container', 'run every task locally without containers').default(false))
105
- .action(async (options) => {
106
- const executionContext = {
107
- workers: options.concurrency,
108
- cacheMethod: options.cache,
109
- watch: options.watch,
110
- events: emitter_1.emitter(),
111
- executor: options.container ? get_docker_executor_1.getDockerExecutor() : get_local_executor_1.getLocalExecutor(),
112
- environment: environment,
113
- runningNodes: {},
114
- };
115
- const logger = get_logger_1.getLogger(options.log);
116
- try {
117
- const workTree = plan_work_tree_1.planWorkTree(buildFile, node.name);
118
- logger.start(executionContext, workTree);
119
- const result = await execute_1.execute(workTree, executionContext);
120
- await logger.finish(workTree, result);
121
- if (!result.success) {
122
- process.exit(1);
123
- }
124
- }
125
- catch (e) {
126
- logger.abort(e);
127
- process.exit(1);
128
- }
156
+ program
157
+ .command('shutdown')
158
+ .description('end all container tasks/services')
159
+ .arguments('[task]')
160
+ .addOption(new commander_1.Option('-f, --filter <labels...>', 'filter task and services with labels'))
161
+ .addOption(new commander_1.Option('-e, --exclude <labels...>', 'exclude task and services with labels'))
162
+ .action(async (task, options) => {
163
+ const cli = await createCli(fileName, environment, task ? { taskName: task } : parseWorkScope(options));
164
+ cli.shutdown();
165
+ });
166
+ program
167
+ .command('exec', { isDefault: true })
168
+ .description('execute task(s)')
169
+ .arguments('[task]')
170
+ .addOption(new commander_1.Option('-f, --filter <labels...>', 'filter task and services with labels'))
171
+ .addOption(new commander_1.Option('-e, --exclude <labels...>', 'exclude task and services with labels'))
172
+ .addOption(new commander_1.Option('-c, --concurrency <number>', 'parallel worker count').argParser(parseInt).default(4))
173
+ .addOption(new commander_1.Option('-w, --watch', 'watch tasks').default(false))
174
+ .addOption(new commander_1.Option('-l, --log <mode>', 'log mode')
175
+ .default(ci_1.isCI ? 'live' : 'interactive')
176
+ .choices(['interactive', 'live', 'grouped']))
177
+ .addOption(new commander_1.Option('--cache <method>', 'caching method to compare')
178
+ .default(ci_1.isCI ? 'checksum' : 'modify-date')
179
+ .choices(['checksum', 'modify-date', 'none']))
180
+ .action(async (task, options) => {
181
+ const cli = await createCli(fileName, environment, task ? { taskName: task } : parseWorkScope(options));
182
+ const result = await cli.exec({
183
+ cacheDefault: options.cache,
184
+ watch: options.watch,
185
+ workers: options.concurrency,
186
+ logMode: options.log,
129
187
  });
130
- }
188
+ if (!result.success) {
189
+ program.error('Execution was not successful', { exitCode: 1 });
190
+ }
191
+ });
131
192
  }
132
193
  else {
133
194
  if (fileIndex >= 0) {
@@ -153,7 +214,10 @@ tasks:
153
214
  program.version(require('../package.json').version);
154
215
  program.option('--verbose', 'log debugging information', false);
155
216
  program.option('--file', 'set build file', 'build.yaml');
156
- program.name('hammerkit');
217
+ program.configureOutput({
218
+ writeOut: (str) => environment.console.info(str),
219
+ writeErr: (str) => environment.console.error(str),
220
+ });
157
221
  return { program, args };
158
222
  }
159
223
  exports.getProgram = getProgram;
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const path_1 = require("path");
4
+ const program_1 = require("./program");
5
+ const environment_mock_1 = require("./executer/environment-mock");
6
+ describe('program', () => {
7
+ async function testCommand(commandArgs) {
8
+ const fileName = (0, path_1.join)(__dirname, '../examples/program');
9
+ const context = (0, environment_mock_1.environmentMock)(fileName);
10
+ const { program, args } = await (0, program_1.getProgram)(context, [process.argv[0], fileName, ...commandArgs]);
11
+ return program.exitOverride().parseAsync(args);
12
+ }
13
+ it('should run when cache is up to date and --no-cache arg is passed', async () => {
14
+ await testCommand(['example', '--cache', 'none']);
15
+ }, 120000);
16
+ it('should warn about invalid task name', async () => {
17
+ await expect(testCommand(['example2'])).rejects.toThrow();
18
+ });
19
+ });
20
+ //# sourceMappingURL=program.spec.js.map
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.startWatchProcesses = void 0;
4
+ const plan_work_nodes_1 = require("./planner/utils/plan-work-nodes");
5
+ const watch_node_1 = require("./executer/watch-node");
6
+ const work_service_1 = require("./planner/work-service");
7
+ const watch_service_1 = require("./executer/watch-service");
8
+ function startWatchProcesses(state, processManager, environment) {
9
+ for (const node of (0, plan_work_nodes_1.iterateWorkNodes)(state.current.node)) {
10
+ if (node.node.src.length > 0) {
11
+ if (node.node.continuous) {
12
+ environment.status.task(node.node).write('debug', 'do not start files watches for continuous task');
13
+ continue;
14
+ }
15
+ processManager.background({ type: 'task', id: node.node.id + '-watch', name: node.node.name }, (0, watch_node_1.watchNode)(node.node, state, processManager, environment));
16
+ }
17
+ }
18
+ for (const service of (0, plan_work_nodes_1.iterateWorkServices)(state.current.service)) {
19
+ if (!(0, work_service_1.isContainerWorkService)(service.service)) {
20
+ continue;
21
+ }
22
+ if (service.service.mounts.length > 0) {
23
+ processManager.background({ type: 'service', id: service.service.id + '-watch', name: service.service.name }, (0, watch_service_1.watchService)(service.service, state, processManager, environment));
24
+ }
25
+ }
26
+ }
27
+ exports.startWatchProcesses = startWatchProcesses;
28
+ //# sourceMappingURL=start-watch-processes.js.map