hammerkit 1.27.0-next.11 → 1.27.0-next.12

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/dist/cli.js CHANGED
@@ -79,6 +79,7 @@ class Cli {
79
79
  explain: options?.explain ?? false,
80
80
  cacheReadOnly: options?.cacheReadOnly ?? false,
81
81
  timeout: options?.timeout ?? null,
82
+ maxTimeout: options?.maxTimeout ?? null,
82
83
  skipDeps: options?.skipDeps ?? true,
83
84
  wait: options?.wait ?? 'ready',
84
85
  resourceCheck: options?.resourceCheck ?? true,
@@ -1,14 +1,28 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.withDeadline = void 0;
3
+ exports.withDeadline = exports.getTaskTimeout = void 0;
4
4
  const abort_event_1 = require("../utils/abort-event");
5
+ // How long a task may run: its own `timeout`, else the run's default (`--timeout`), and
6
+ // never longer than the run's cap (`--max-timeout`), which also covers tasks with a
7
+ // longer timeout of their own. A caller can shorten a task this way, never extend it.
8
+ function getTaskTimeout(own, run) {
9
+ const timeout = own ?? run.timeout;
10
+ if (run.maxTimeout === null) {
11
+ return timeout;
12
+ }
13
+ return timeout === null ? run.maxTimeout : Math.min(timeout, run.maxTimeout);
14
+ }
15
+ exports.getTaskTimeout = getTaskTimeout;
16
+ // the longest delay of setTimeout: Node fires a longer one after 1 ms
17
+ const MAX_TIMER_MS = 2 ** 31 - 1;
5
18
  // An abort signal that follows `parent` and additionally fires after `timeout`
6
- // ms, so a task deadline reuses the runtimes' existing abort/cleanup path.
19
+ // ms, so a task deadline reuses the runtimes' existing abort/cleanup path. A
20
+ // timeout beyond what a timer can wait (about 24.8 days) sets no deadline.
7
21
  function withDeadline(parent, timeout) {
8
22
  const controller = new AbortController();
9
23
  let expired = false;
10
24
  const parentListener = (0, abort_event_1.listenOnAbort)(parent, () => controller.abort());
11
- const timer = timeout === null
25
+ const timer = timeout === null || timeout > MAX_TIMER_MS
12
26
  ? null
13
27
  : setTimeout(() => {
14
28
  expired = true;
@@ -94,7 +94,7 @@ dependents = null) {
94
94
  stateKey: cacheState.stateKey,
95
95
  started,
96
96
  });
97
- const timeout = work.data.timeout ?? options.timeout;
97
+ const timeout = (0, deadline_1.getTaskTimeout)(work.data.timeout, options);
98
98
  const deadline = (0, deadline_1.withDeadline)(abort, timeout);
99
99
  try {
100
100
  await work.runtime.execute(environment, {
@@ -115,7 +115,8 @@ class SandboxClient {
115
115
  runOptions.args = options.args;
116
116
  }
117
117
  if (options.timeoutSeconds) {
118
- runOptions.timeout = `${Math.max(1, Math.ceil(options.timeoutSeconds))}s`;
118
+ // a cap: it also shortens a shell task that sets a longer timeout of its own
119
+ runOptions.maxTimeout = `${Math.max(1, Math.ceil(options.timeoutSeconds))}s`;
119
120
  }
120
121
  const submitted = await submitRun(this.connection, {
121
122
  definition: this.build.hash,
package/dist/program.js CHANGED
@@ -608,6 +608,7 @@ async function getProgram(environment, argv) {
608
608
  .choices(['checksum', 'modify-date', 'none']))
609
609
  .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))
610
610
  .addOption(new commander_1.Option('--timeout <duration>', 'fail tasks without their own timeout after this long (e.g. 10m)').argParser(units_1.parseDuration))
611
+ .addOption(new commander_1.Option('--max-timeout <duration>', 'fail any task after this long, also one with a longer timeout of its own (e.g. 2m)').argParser(units_1.parseDuration))
611
612
  .addOption(new commander_1.Option('--network <level>', 'the most any task or service reaches besides its needs: none, open or allow:<host>,<host>').argParser(parseNetworkArgument))
612
613
  .action(async (options, command) => {
613
614
  try {
@@ -626,6 +627,7 @@ async function getProgram(environment, argv) {
626
627
  wait: options.wait,
627
628
  cacheReadOnly: (0, read_only_1.isCacheReadOnly)(options.cacheReadOnly, environment.processEnvs),
628
629
  timeout: options.timeout ?? null,
630
+ maxTimeout: options.maxTimeout ?? null,
629
631
  network: options.network ?? null,
630
632
  });
631
633
  if (!result.success) {
@@ -688,6 +690,7 @@ async function getProgram(environment, argv) {
688
690
  .addOption(new commander_1.Option('--dry-run', 'print the execution plan with predicted cache hits/misses without running').default(false))
689
691
  .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))
690
692
  .addOption(new commander_1.Option('--timeout <duration>', 'fail tasks without their own timeout after this long (e.g. 10m)').argParser(units_1.parseDuration))
693
+ .addOption(new commander_1.Option('--max-timeout <duration>', 'fail any task after this long, also one with a longer timeout of its own (e.g. 2m)').argParser(units_1.parseDuration))
691
694
  .addOption(new commander_1.Option('--network <level>', 'the most any task or service reaches besides its needs: none, open or allow:<host>,<host>').argParser(parseNetworkArgument))
692
695
  .addOption(new commander_1.Option('--no-skip-deps', 'run dependencies even when nothing needing them has to run'))
693
696
  .action(async (task, options, command) => {
@@ -722,6 +725,7 @@ async function getProgram(environment, argv) {
722
725
  explain: options.explain,
723
726
  cacheReadOnly: (0, read_only_1.isCacheReadOnly)(options.cacheReadOnly, environment.processEnvs),
724
727
  timeout: options.timeout ?? null,
728
+ maxTimeout: options.maxTimeout ?? null,
725
729
  network: options.network ?? null,
726
730
  skipDeps: options.skipDeps !== false,
727
731
  });
@@ -139,7 +139,8 @@ function registerWorkCommands(remote, context) {
139
139
  .addOption(new commander_1.Option('--commit <sha>', 'the commit the ref has to be at, else the run is refused; the commit checked out here when --ref is left out'))
140
140
  .addOption(new commander_1.Option('--env <name>', 'the environment of the build file, as --env of run'))
141
141
  .addOption(new commander_1.Option('--cache <method>', 'as --cache of run').choices(['checksum', 'modify-date', 'none']))
142
- .addOption(new commander_1.Option('--timeout <duration>', 'stop the run after this long, such as 30m'))
142
+ .addOption(new commander_1.Option('--timeout <duration>', 'as --timeout of run: for tasks without their own, such as 30m'))
143
+ .addOption(new commander_1.Option('--max-timeout <duration>', 'as --max-timeout of run: for every task, also one with its own timeout'))
143
144
  .action(guarded('Remote run was not successful', async (tasks, options) => {
144
145
  const named = options.build ?? environment.processEnvs.HAMMERKIT_BUILD;
145
146
  const definition = named ?? (0, bundle_1.hashBundle)(await (0, collect_bundle_1.collectBundle)(buildFile, environment));
@@ -160,7 +161,7 @@ function registerWorkCommands(remote, context) {
160
161
  const { name: serverName, server } = await getServer(options.server);
161
162
  const connection = { url: server.url, ...(0, token_source_1.getTokenSource)(environment, serverName, server) };
162
163
  const runOptions = {};
163
- for (const key of ['env', 'cache', 'timeout']) {
164
+ for (const key of ['env', 'cache', 'timeout', 'maxTimeout']) {
164
165
  if (options[key] !== undefined) {
165
166
  runOptions[key] = options[key];
166
167
  }
@@ -38,6 +38,8 @@ exports.runOptionsSchema = zod_1.z
38
38
  cacheReadOnly: zod_1.z.boolean().optional(),
39
39
  // as --timeout
40
40
  timeout: duration.optional(),
41
+ // as --max-timeout: caps every task, also one with a longer timeout of its own
42
+ maxTimeout: duration.optional(),
41
43
  // as --no-skip-deps when false
42
44
  skipDeps: zod_1.z.boolean().optional(),
43
45
  // as the text after `--`: appended to the last command of the one task the run
@@ -120,6 +120,7 @@ cancelled, emit) {
120
120
  cacheDefault: options.cache ?? 'checksum',
121
121
  cacheReadOnly: options.cacheReadOnly ?? false,
122
122
  timeout: options.timeout ? (0, units_1.parseDuration)(options.timeout) : null,
123
+ maxTimeout: options.maxTimeout ? (0, units_1.parseDuration)(options.maxTimeout) : null,
123
124
  skipDeps: options.skipDeps !== false,
124
125
  network: getRunNetwork(input),
125
126
  // why a task was not taken from the cache goes into its result
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.27.0-next.11",
4
+ "version": "1.27.0-next.12",
5
5
  "keywords": [
6
6
  "build",
7
7
  "build-tool",