hammerkit 1.11.1 → 1.11.3

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.
@@ -16,12 +16,18 @@ async function awaitRequirement(svc, abort, options = { untilAllDone: false }) {
16
16
  return Promise.race([ready, allDone]);
17
17
  }
18
18
  exports.awaitRequirement = awaitRequirement;
19
- // Resolve true once any dependent is `ready` — it missed the cache and is about
20
- // to run, so it needs this task's outputs — and false once every dependent
21
- // finished without needing it (all cache hits, or skipped themselves).
19
+ // Resolve true once any dependent needs this task's outputs — a task that is
20
+ // `ready` (it missed the cache and is about to run), a service that is
21
+ // `starting` (a task needing it runs) — and false once every dependent
22
+ // finished without needing it: tasks that were cache hits or skipped, services
23
+ // that never started.
22
24
  async function awaitDependentNeed(dependents, abort) {
23
- const ready = Promise.race(dependents.map((dependent) => (0, state_resolver_1.awaitState)('await-dependent', dependent.state, (s) => s.type === 'ready', abort))).then(() => true);
24
- const allDone = Promise.all(dependents.map((dependent) => (0, state_resolver_1.awaitState)('await-dependent-done', dependent.state, (s) => s.type === 'completed' || s.type === 'error' || s.type === 'crash' || s.type === 'canceled', abort))).then(() => false);
25
+ const ready = Promise.race(dependents.map((dependent) => (0, work_item_1.isWorkTaskItem)(dependent)
26
+ ? (0, state_resolver_1.awaitState)('await-dependent', dependent.state, (s) => s.type === 'ready', abort)
27
+ : (0, state_resolver_1.awaitState)('await-dependent', dependent.state, (s) => s.type === 'starting' || s.type === 'ready' || s.type === 'running', abort))).then(() => true);
28
+ const allDone = Promise.all(dependents.map((dependent) => (0, work_item_1.isWorkTaskItem)(dependent)
29
+ ? (0, state_resolver_1.awaitState)('await-dependent-done', dependent.state, (s) => s.type === 'completed' || s.type === 'error' || s.type === 'crash' || s.type === 'canceled', abort)
30
+ : (0, state_resolver_1.awaitState)('await-dependent-done', dependent.state, (s) => s.type === 'end' || s.type === 'error' || s.type === 'canceled', abort))).then(() => false);
25
31
  return Promise.race([ready, allDone]);
26
32
  }
27
33
  exports.awaitDependentNeed = awaitDependentNeed;
@@ -38,14 +38,14 @@ function onlyGroupsCacheHits(work) {
38
38
  work.deps.every((dep) => dep.state.current.type === 'completed' && dep.state.current.cached));
39
39
  }
40
40
  async function executeWorkTask(work, environment, options,
41
- // when set, the task was not requested and only runs if one of these tasks
42
- // depending on it misses the cache (see executeWorkTree)
41
+ // when set, the task was not requested and only runs if one of these items
42
+ // depending on it needs it (see executeWorkTree)
43
43
  dependents = null) {
44
44
  try {
45
45
  if (dependents) {
46
46
  const needed = await (0, await_completed_dependencies_1.awaitDependentNeed)(dependents, environment.abortCtrl.signal);
47
47
  if (!needed) {
48
- work.status.write('debug', `${work.name} skipped, every task depending on it was a cache hit`);
48
+ work.status.write('debug', `${work.name} skipped, nothing depending on it had to run`);
49
49
  work.state.set({ type: 'completed', cached: false, skipped: true, duration: 0, stateKey: '' });
50
50
  return;
51
51
  }
@@ -51,26 +51,29 @@ function mirrorServicesToInitViews(work) {
51
51
  });
52
52
  }
53
53
  }
54
- // For every task that may be skipped when nothing needs it, the tasks in this
55
- // run that depend on it. Requested tasks always run; so does anything a service
56
- // depends on, and everything in watch mode or with skipping turned off.
54
+ // For every task that may be skipped when nothing needs it, the tasks and
55
+ // services in this run that depend on it. Requested tasks always run; a
56
+ // service's dependency runs once the service starts. Everything runs in watch
57
+ // mode, under `up` and with skipping turned off.
57
58
  function skippableDependents(work, options) {
58
59
  const result = new Map();
59
60
  if (options.type !== 'execute' || !options.skipDeps || options.watch || !work.requested) {
60
61
  return result;
61
62
  }
62
- const neededByService = new Set();
63
- for (const service of (0, plan_work_tasks_1.iterateWorkServices)(work)) {
64
- for (const dep of service.deps) {
65
- neededByService.add(dep.name);
63
+ // a dependency of nothing in the run (its service needs no one here) has no
64
+ // dependents left, and is skipped
65
+ for (const task of (0, plan_work_tasks_1.iterateWorkTasks)(work)) {
66
+ if (!work.requested.includes(task.name) && !(0, execute_init_task_1.findInitOf)(task)) {
67
+ result.set(task.name, []);
66
68
  }
67
69
  }
68
- for (const task of (0, plan_work_tasks_1.iterateWorkTasks)(work)) {
69
- for (const dep of task.deps) {
70
- if (work.requested.includes(dep.name) || neededByService.has(dep.name)) {
70
+ const items = [...(0, plan_work_tasks_1.iterateWorkTasks)(work), ...(0, plan_work_tasks_1.iterateWorkServices)(work)];
71
+ for (const item of items) {
72
+ for (const dep of item.deps) {
73
+ if (work.requested.includes(dep.name)) {
71
74
  continue;
72
75
  }
73
- result.set(dep.name, [...(result.get(dep.name) ?? []), task]);
76
+ result.set(dep.name, [...(result.get(dep.name) ?? []), item]);
74
77
  }
75
78
  }
76
79
  return result;
@@ -139,7 +139,8 @@ function appendServiceInit(workTree, service, item, environment, context) {
139
139
  task.needs.push({ name: item.name, service: view });
140
140
  }
141
141
  removeRequirer(item, task.name);
142
- task.data.caching = { ...task.data.caching, method: 'none' };
142
+ // explicit, so neither the default method nor `--cache` turns it back on
143
+ task.data.caching = { ...task.data.caching, method: 'none', implicit: false };
143
144
  if (task.data.timeout === null) {
144
145
  task.data.timeout = getInitTimeout(undefined, environment);
145
146
  }
package/dist/program.js CHANGED
@@ -508,7 +508,7 @@ async function getProgram(environment, argv) {
508
508
  .addOption(new commander_1.Option('--dry-run', 'print the execution plan with predicted cache hits/misses without running').default(false))
509
509
  .addOption(new commander_1.Option('--cache-read-only', `restore from cache backends but never push to them (or set ${read_only_1.CACHE_READ_ONLY_ENV}=1)`).default(false))
510
510
  .addOption(new commander_1.Option('--timeout <duration>', 'fail tasks without their own timeout after this long (e.g. 10m)').argParser(units_1.parseDuration))
511
- .addOption(new commander_1.Option('--no-skip-deps', 'run dependencies even when every task needing them is a cache hit'))
511
+ .addOption(new commander_1.Option('--no-skip-deps', 'run dependencies even when nothing needing them has to run'))
512
512
  .action(async (task, options) => {
513
513
  try {
514
514
  // For machine-readable JSON, suppress the human progress logger (which
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.11.1",
4
+ "version": "1.11.3",
5
5
  "keywords": [
6
6
  "build",
7
7
  "build-tool",