hammerkit 1.9.2 → 1.11.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 (44) hide show
  1. package/build.schema.json +52 -1
  2. package/dist/cache/backends/registry/registry-client.js +19 -4
  3. package/dist/cli.js +1 -0
  4. package/dist/docker/pull.js +38 -1
  5. package/dist/environment/replace-env-variables.js +53 -22
  6. package/dist/executer/container-secrets.js +59 -0
  7. package/dist/executer/deadline.js +29 -0
  8. package/dist/executer/docker-service.js +58 -38
  9. package/dist/executer/docker-task.js +37 -23
  10. package/dist/executer/execute-init-task.js +100 -0
  11. package/dist/executer/execute-work-service.js +30 -1
  12. package/dist/executer/execute-work-task.js +4 -25
  13. package/dist/executer/execute-work-tree.js +28 -2
  14. package/dist/executer/get-container-binds.js +8 -4
  15. package/dist/executer/local-task.js +6 -1
  16. package/dist/kubernetes/ensure-kubernetes-deployment-exists.js +13 -7
  17. package/dist/kubernetes/secrets.js +83 -0
  18. package/dist/kubernetes/volumes.js +1 -1
  19. package/dist/optimizer/work-task-cache-description.js +6 -0
  20. package/dist/parse-set-arguments.js +38 -0
  21. package/dist/planner/service-definition.js +40 -0
  22. package/dist/planner/utils/append-work-dependencies.js +2 -0
  23. package/dist/planner/utils/append-work-needs.js +7 -0
  24. package/dist/planner/utils/append-work-service.js +67 -1
  25. package/dist/planner/utils/append-work-task.js +6 -0
  26. package/dist/planner/utils/parse-work-generate.js +4 -2
  27. package/dist/planner/utils/parse-work-mount.js +7 -3
  28. package/dist/planner/utils/parse-work-volume.js +8 -3
  29. package/dist/planner/utils/plan-work-volume.js +1 -0
  30. package/dist/planner/utils/service-hostname.js +15 -0
  31. package/dist/planner/utils/template-value.js +1 -1
  32. package/dist/planner/work-item-status.js +3 -1
  33. package/dist/planner/work-runtime-docker.js +8 -0
  34. package/dist/planner/work-runtime-kubernetes.js +40 -6
  35. package/dist/planner/work-secret.js +83 -0
  36. package/dist/program.js +9 -1
  37. package/dist/schema/build-file-base-task-schema.js +2 -0
  38. package/dist/schema/build-file-container-service-schema.js +5 -0
  39. package/dist/schema/build-file-secret-schema.js +22 -0
  40. package/dist/schema/build-file-volume-schema.js +3 -1
  41. package/dist/schema/reference-parser.js +14 -0
  42. package/dist/schema/work-scope-parser.js +30 -5
  43. package/dist/utils/redact.js +29 -0
  44. package/package.json +1 -1
@@ -73,6 +73,7 @@ async function parseReferences(ctx, mainScope, environment) {
73
73
  scope: file,
74
74
  needs: [],
75
75
  deps: [],
76
+ init: null,
76
77
  envs: serviceEnvs,
77
78
  };
78
79
  }
@@ -86,6 +87,7 @@ async function parseReferences(ctx, mainScope, environment) {
86
87
  for (const service of Object.values(reference.services)) {
87
88
  resolveDeps(reference, service, service.scope, service.schema);
88
89
  resolveNeeds(reference, service, service.scope, service.schema);
90
+ resolveInit(reference, service);
89
91
  }
90
92
  reference.caches = (0, resolve_cache_1.withBuiltinCaches)(declaredCaches);
91
93
  return reference;
@@ -142,6 +144,18 @@ function resolveDeps(reference, taskOrService, scope, schema) {
142
144
  });
143
145
  }
144
146
  }
147
+ function resolveInit(reference, service) {
148
+ if ((0, build_file_service_schema_1.isBuildFileKubernetesServiceSchema)(service.schema) || !service.schema.init) {
149
+ return;
150
+ }
151
+ const initTask = findTask(reference, service.scope, service.schema.init, null);
152
+ service.init = {
153
+ task: initTask.task,
154
+ type: initTask.type,
155
+ cwd: initTask.type === 'include' ? service.cwd : initTask.task.cwd,
156
+ relativeName: service.schema.init,
157
+ };
158
+ }
145
159
  function resolveNeeds(reference, taskOrService, scope, schema) {
146
160
  if ((0, build_file_service_schema_1.isBuildFileKubernetesServiceSchema)(schema)) {
147
161
  return;
@@ -37,22 +37,42 @@ function getWorkContext(context, scope, environment) {
37
37
  workTree.environment = filteredWorkTree.environment;
38
38
  }
39
39
  }
40
- for (const task of Object.values(context.tasks)) {
41
- const item = (0, append_work_task_1.appendWorkTask)(workTree, task.cwd, task, environment, context);
40
+ // everything is planned before the run is narrowed: planning a service adds
41
+ // its init task's need on it, which narrowing follows
42
+ const tasks = Object.values(context.tasks).map((task) => (0, append_work_task_1.appendWorkTask)(workTree, task.cwd, task, environment, context));
43
+ const services = Object.values(context.services).map((service) => (0, append_work_service_1.appendWorkService)(workTree, service, environment, context));
44
+ for (const item of tasks) {
42
45
  if (matchesTaskWorkScope(item, scope)) {
43
46
  requested.push(item.name);
44
47
  applyTask(filteredWorkTree, item);
45
48
  }
46
49
  }
47
- for (const service of Object.values(context.services)) {
48
- const item = (0, append_work_service_1.appendWorkService)(workTree, service, environment, context);
50
+ for (const item of services) {
49
51
  if (matchesServiceWorkScope(item.data, scope)) {
50
52
  applyService(filteredWorkTree, item);
51
53
  }
52
54
  }
55
+ keepRequirersInScope(filteredWorkTree);
53
56
  return filteredWorkTree;
54
57
  }
55
58
  exports.getWorkContext = getWorkContext;
59
+ // The whole build file is planned first, so a shared service lists every task
60
+ // and service that needs it. Those outside this run never start or end, and a
61
+ // service waiting for them to finish would keep the run open forever.
62
+ function keepRequirersInScope(workTree) {
63
+ const inScope = new Set([
64
+ ...Object.values(workTree.tasks),
65
+ ...Object.values(workTree.services),
66
+ ]);
67
+ for (const service of Object.values(workTree.services)) {
68
+ service.requiredBy = service.requiredBy.filter((required) => inScope.has(required));
69
+ // an init task asked for alone is what its service starts for
70
+ const init = service.data.type === 'container-service' ? service.data.init : null;
71
+ if (init && workTree.requested?.length === 1 && workTree.requested[0] === init.task.name) {
72
+ service.requiredBy.push(init.task);
73
+ }
74
+ }
75
+ }
56
76
  function applyTask(workTree, item) {
57
77
  if (workTree.tasks[item.name]) {
58
78
  return;
@@ -60,12 +80,17 @@ function applyTask(workTree, item) {
60
80
  workTree.tasks[item.name] = item;
61
81
  applyNeedsAndDeps(workTree, item);
62
82
  }
63
- function applyService(workTree, item) {
83
+ function applyService(workTree, need) {
84
+ const item = (0, append_work_service_1.getPlannedService)(need);
64
85
  if (workTree.services[item.name]) {
65
86
  return;
66
87
  }
67
88
  workTree.services[item.name] = item;
68
89
  applyNeedsAndDeps(workTree, item);
90
+ // a service in the run brings its init task, which runs once it is healthy
91
+ if (item.data.type === 'container-service' && item.data.init) {
92
+ applyTask(workTree, item.data.init.task);
93
+ }
69
94
  }
70
95
  function applyNeedsAndDeps(workTree, item) {
71
96
  for (const dep of item.deps) {
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.redact = exports.registerSecretValue = void 0;
4
+ // Secret values hammerkit has read, masked in everything it logs, including the
5
+ // output it streams from a task or service. Logs are per line, so each line of
6
+ // a multi-line value (a key file) is masked on its own. Values shorter than
7
+ // MIN_LENGTH are left alone: masking them would mangle unrelated output.
8
+ const MIN_LENGTH = 4;
9
+ const MASK = '***';
10
+ const values = new Set();
11
+ function registerSecretValue(value) {
12
+ for (const candidate of [value, ...value.split(/\r?\n/)]) {
13
+ const trimmed = candidate.trim();
14
+ if (trimmed.length >= MIN_LENGTH) {
15
+ values.add(trimmed);
16
+ }
17
+ }
18
+ }
19
+ exports.registerSecretValue = registerSecretValue;
20
+ function redact(message) {
21
+ if (values.size === 0) {
22
+ return message;
23
+ }
24
+ // longest first, so a value containing another is masked as a whole
25
+ const sorted = [...values].sort((a, b) => b.length - a.length);
26
+ return sorted.reduce((result, value) => result.split(value).join(MASK), message);
27
+ }
28
+ exports.redact = redact;
29
+ //# sourceMappingURL=redact.js.map
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "hammerkit",
3
3
  "description": "Containerized build tool with incremental caching that runs the same build file locally, in Docker and on Kubernetes.",
4
- "version": "1.9.2",
4
+ "version": "1.11.0",
5
5
  "keywords": [
6
6
  "build",
7
7
  "build-tool",