start-command 0.37.7 → 0.37.9

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.
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Links-notation and text rendering of nested `--status` values.
3
+ *
4
+ * `--status` stores structured option values such as `launchOwner`,
5
+ * `exitEvidence`, `resolvedLimits` and `resourceLimitSpecs`. They used to be
6
+ * passed through `escapeForLinksNotation` (and a template string in the text
7
+ * format), which printed `"[object Object]"` and lost the values (issue #226).
8
+ * These helpers render nested objects and arrays the same way the Rust
9
+ * formatter's `append_links_value` does, so both implementations print the
10
+ * same layout.
11
+ */
12
+
13
+ const { escapeForLinksNotation } = require('./output-blocks');
14
+
15
+ function isPresent(value) {
16
+ return value !== null && value !== undefined;
17
+ }
18
+
19
+ /**
20
+ * Append an array as a `(` … `)` block.
21
+ * @param {string[]} lines - Output lines
22
+ * @param {Array} values - Array to render
23
+ * @param {number} indent - Indentation of the parentheses
24
+ */
25
+ function appendLinksArray(lines, values, indent) {
26
+ const prefix = ' '.repeat(indent);
27
+ if (values.length === 0) {
28
+ lines.push(`${prefix}()`);
29
+ return;
30
+ }
31
+ lines.push(`${prefix}(`);
32
+ for (const value of values) {
33
+ if (Array.isArray(value)) {
34
+ appendLinksArray(lines, value, indent + 2);
35
+ } else if (isPresent(value) && typeof value === 'object') {
36
+ for (const [childKey, childValue] of Object.entries(value)) {
37
+ if (isPresent(childValue)) {
38
+ appendLinksValue(lines, childKey, childValue, indent + 2);
39
+ }
40
+ }
41
+ } else {
42
+ lines.push(`${' '.repeat(indent + 2)}${escapeForLinksNotation(value)}`);
43
+ }
44
+ }
45
+ lines.push(`${prefix})`);
46
+ }
47
+
48
+ /**
49
+ * Append `key value`, or `key` followed by its nested children.
50
+ * Children of an object are indented by four more spaces, matching the
51
+ * top-level ` key` / ` child` layout `--status` has always used.
52
+ * @param {string[]} lines - Output lines
53
+ * @param {string} key - Field name
54
+ * @param {*} value - Field value
55
+ * @param {number} indent - Indentation of the key
56
+ */
57
+ function appendLinksValue(lines, key, value, indent) {
58
+ const prefix = ' '.repeat(indent);
59
+ if (Array.isArray(value)) {
60
+ lines.push(`${prefix}${key}`);
61
+ appendLinksArray(lines, value, indent + 2);
62
+ return;
63
+ }
64
+ if (isPresent(value) && typeof value === 'object') {
65
+ const entries = Object.entries(value);
66
+ if (entries.length === 0) {
67
+ return;
68
+ }
69
+ lines.push(`${prefix}${key}`);
70
+ for (const [childKey, childValue] of entries) {
71
+ if (isPresent(childValue)) {
72
+ appendLinksValue(lines, childKey, childValue, indent + 4);
73
+ }
74
+ }
75
+ return;
76
+ }
77
+ lines.push(`${prefix}${key} ${escapeForLinksNotation(value)}`);
78
+ }
79
+
80
+ /**
81
+ * Render an option value for the text format: scalars as-is, structured
82
+ * values as compact JSON (the Rust formatter uses `serde_json::to_string`).
83
+ * @param {*} value - Option value
84
+ * @returns {string} Rendered value
85
+ */
86
+ function formatTextOptionValue(value) {
87
+ if (isPresent(value) && typeof value === 'object') {
88
+ return JSON.stringify(value);
89
+ }
90
+ return String(value);
91
+ }
92
+
93
+ module.exports = {
94
+ appendLinksArray,
95
+ appendLinksValue,
96
+ formatTextOptionValue,
97
+ };
@@ -14,6 +14,8 @@ class LockManager {
14
14
  this.identity = null;
15
15
  this.token = null;
16
16
  this.monitorOwner = monitorOwner;
17
+ // Extra fields published in the lock file, for example who holds it.
18
+ this.metadata = {};
17
19
  }
18
20
 
19
21
  acquire(timeout = LOCK_TIMEOUT_MS) {
@@ -44,6 +46,7 @@ class LockManager {
44
46
  this.fs.writeFileSync(
45
47
  temp,
46
48
  JSON.stringify({
49
+ ...this.metadata,
47
50
  pid: process.pid,
48
51
  timestamp: Date.now(),
49
52
  hostname: os.hostname(),
package/src/lib/usage.js CHANGED
@@ -54,9 +54,16 @@ Options:
54
54
  --resume <id> Restart a stopped detached execution in the same environment
55
55
  (append -- <command> to run a different command there)
56
56
  --remove-original With legacy snapshot resume, remove the stopped original after launch
57
+ --remove-original-after <s> Remove it only if the replacement still runs after <s> seconds
58
+ --snapshot-wait <s> Wait up to <s> seconds for another snapshot on this host (exit 75 after)
59
+ --snapshot-disk-wait <s> Wait up to <s> seconds for free disk instead of refusing (exit 75 after)
60
+ --snapshot-size-hint <bytes> Use this writable-layer size instead of docker inspect --size
61
+ --snapshot-prepare <cmd> docker exec <cmd> in the started original before it is snapshotted
57
62
  --resume-all Re-attach or reconcile every execution still marked running
58
63
  --cleanup Clean up stale "executing" records (crashed/killed processes)
59
64
  --cleanup-dry-run Show stale records that would be cleaned up (without cleaning)
65
+ --cleanup <id> Remove a Docker execution's containers and snapshot images (never forced)
66
+ --cleanup-dry-run <id> Show the containers and images --cleanup <id> would remove
60
67
  --version, -v Show version information
61
68
 
62
69
  Examples:
@@ -0,0 +1,61 @@
1
+ const { describe, it, expect } = require('bun:test');
2
+ const { spawn } = require('child_process');
3
+ const fs = require('fs');
4
+ const os = require('os');
5
+ const path = require('path');
6
+
7
+ const CLI = path.join(__dirname, '../src/bin/cli.js');
8
+
9
+ // Run the CLI with the read ends of the given output pipes closed before it
10
+ // starts writing, like `$ --version | head -1` once `head` has exited.
11
+ function runWithClosedPipes(args, closed) {
12
+ return new Promise((resolve) => {
13
+ const child = spawn(process.execPath, [CLI, ...args], {
14
+ stdio: ['ignore', 'pipe', 'pipe'],
15
+ env: {
16
+ ...process.env,
17
+ START_APP_FOLDER: fs.mkdtempSync(path.join(os.tmpdir(), 'start-222-')),
18
+ START_DISABLE_AUTO_ISSUE: '1',
19
+ START_DISABLE_LOG_UPLOAD: '1',
20
+ },
21
+ });
22
+ let stderr = '';
23
+ for (const name of ['stdout', 'stderr']) {
24
+ if (closed.includes(name)) {
25
+ child[name].destroy();
26
+ } else {
27
+ child[name].on('data', (chunk) => (stderr += chunk));
28
+ }
29
+ }
30
+ child.on('close', (code) => resolve({ code, stderr }));
31
+ });
32
+ }
33
+
34
+ describe('closed output pipes (#222)', () => {
35
+ if (process.platform === 'win32') {
36
+ return;
37
+ }
38
+ const big = 'seq 1 200000';
39
+ for (const args of [['--version'], ['-v'], ['--help']]) {
40
+ it(`${args[0]} exits 0 when stdout is closed`, async () => {
41
+ const result = await runWithClosedPipes(args, ['stdout']);
42
+ expect(result.stderr).not.toContain('EPIPE');
43
+ expect(result.code).toBe(0);
44
+ }, 15000);
45
+ }
46
+ it('a command keeps its exit code when stdout is closed', async () => {
47
+ const result = await runWithClosedPipes(
48
+ ['--', 'sh', '-c', `${big}; exit 3`],
49
+ ['stdout']
50
+ );
51
+ expect(result.stderr).not.toContain('EPIPE');
52
+ expect(result.code).toBe(3);
53
+ }, 15000);
54
+ it('a command keeps its exit code when stdout and stderr are closed', async () => {
55
+ const result = await runWithClosedPipes(
56
+ ['--', 'sh', '-c', `${big}; ${big} >&2; exit 3`],
57
+ ['stdout', 'stderr']
58
+ );
59
+ expect(result.code).toBe(3);
60
+ }, 15000);
61
+ });