start-command 0.30.2 → 0.30.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # start-command
2
2
 
3
+ ## 0.30.3
4
+
5
+ ### Patch Changes
6
+
7
+ - 55ff85b: Treat detached Docker sessions with `oomKilled` as terminal in status output, using Docker's exit code when available and 137 as the OOM fallback.
8
+
3
9
  ## 0.30.2
4
10
 
5
11
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "start-command",
3
- "version": "0.30.2",
3
+ "version": "0.30.3",
4
4
  "description": "Gamification of coding, execute any command with ability to auto-report issues on GitHub",
5
5
  "main": "src/bin/cli.js",
6
6
  "exports": {
@@ -62,6 +62,23 @@ function inspectDockerState(sessionName) {
62
62
  };
63
63
  }
64
64
 
65
+ function isDetachedDockerRecord(record) {
66
+ const opts = record.options || {};
67
+ return (
68
+ opts.isolated === 'docker' &&
69
+ opts.isolationMode === 'detached' &&
70
+ Boolean(opts.sessionName)
71
+ );
72
+ }
73
+
74
+ function readDockerState(record) {
75
+ const opts = record.options || {};
76
+ if (opts.isolated !== 'docker' || !opts.sessionName) {
77
+ return null;
78
+ }
79
+ return inspectDockerState(opts.sessionName);
80
+ }
81
+
65
82
  /**
66
83
  * Best-effort terminal exit code reported by the isolation backend itself
67
84
  * (currently docker via `docker inspect .State.ExitCode`). Returns null when
@@ -71,21 +88,23 @@ function inspectDockerState(sessionName) {
71
88
  * @returns {number|null}
72
89
  */
73
90
  function readBackendExitCode(record) {
74
- const opts = record.options || {};
75
- if (opts.isolated !== 'docker' || !opts.sessionName) {
76
- return null;
77
- }
78
- const state = inspectDockerState(opts.sessionName);
91
+ const state = readDockerState(record);
79
92
  return state && !state.running ? state.exitCode : null;
80
93
  }
81
94
 
82
- function readDockerOomKilled(record) {
83
- const opts = record.options || {};
84
- if (opts.isolated !== 'docker' || !opts.sessionName) {
85
- return null;
95
+ function resolveOomExitCode(footerExit, dockerState) {
96
+ if (footerExit !== null && footerExit !== undefined) {
97
+ return footerExit;
98
+ }
99
+ if (
100
+ dockerState &&
101
+ dockerState.exitCode !== null &&
102
+ dockerState.exitCode !== undefined &&
103
+ (!dockerState.running || dockerState.exitCode !== 0)
104
+ ) {
105
+ return dockerState.exitCode;
86
106
  }
87
- const state = inspectDockerState(opts.sessionName);
88
- return state ? state.oomKilled : null;
107
+ return 137;
89
108
  }
90
109
 
91
110
  /**
@@ -172,8 +191,15 @@ function readExitCodeFromLog(logPath) {
172
191
  * @returns {Object} Possibly updated execution record
173
192
  */
174
193
  function enrichDetachedStatus(record) {
175
- const alive = isDetachedSessionAlive(record);
176
194
  const footerExit = readExitCodeFromLog(record.logPath);
195
+ const dockerState = isDetachedDockerRecord(record)
196
+ ? readDockerState(record)
197
+ : null;
198
+ const alive = isDetachedDockerRecord(record)
199
+ ? dockerState === null
200
+ ? null
201
+ : dockerState.running
202
+ : isDetachedSessionAlive(record);
177
203
 
178
204
  // Create a shallow copy to avoid mutating the original
179
205
  const cloneRecord = () => {
@@ -182,6 +208,19 @@ function enrichDetachedStatus(record) {
182
208
  return enriched;
183
209
  };
184
210
 
211
+ if (record.oomKilled === true || dockerState?.oomKilled === true) {
212
+ const enriched = cloneRecord();
213
+ enriched.oomKilled = true;
214
+ enriched.status = 'executed';
215
+ if (enriched.exitCode === null || enriched.exitCode === undefined) {
216
+ enriched.exitCode = resolveOomExitCode(footerExit, dockerState);
217
+ }
218
+ if (!enriched.endTime) {
219
+ enriched.endTime = new Date().toISOString();
220
+ }
221
+ return enriched;
222
+ }
223
+
185
224
  if (alive === null) {
186
225
  // Liveness is unknown: the backend could not be probed (e.g. a detached
187
226
  // docker container that is not visible yet on a slow Docker-in-Docker host,
@@ -204,9 +243,8 @@ function enrichDetachedStatus(record) {
204
243
  }
205
244
 
206
245
  const enriched = cloneRecord();
207
- const oomKilled = readDockerOomKilled(enriched);
208
- if (oomKilled !== null) {
209
- enriched.oomKilled = oomKilled;
246
+ if (dockerState?.oomKilled !== null && dockerState?.oomKilled !== undefined) {
247
+ enriched.oomKilled = dockerState.oomKilled;
210
248
  }
211
249
 
212
250
  if (alive && enriched.status === 'executed') {
@@ -677,6 +677,65 @@ describe('Issue #144: detached docker OOMKilled status signal', () => {
677
677
  });
678
678
  });
679
679
 
680
+ describe('Issue #148: detached docker OOMKilled terminal status', () => {
681
+ let store;
682
+
683
+ beforeEach(() => {
684
+ cleanupTestDir();
685
+ store = new ExecutionStore({
686
+ appFolder: TEST_APP_FOLDER,
687
+ useLinks: false,
688
+ });
689
+ });
690
+
691
+ afterEach(() => {
692
+ cleanupTestDir();
693
+ });
694
+
695
+ function saveDockerRecord() {
696
+ const record = new ExecutionRecord({
697
+ command: 'sh -c "allocate memory"',
698
+ logPath: '/tmp/issue-148.log',
699
+ options: {
700
+ sessionName: 'issue148-oom',
701
+ isolated: 'docker',
702
+ isolationMode: 'detached',
703
+ },
704
+ });
705
+ store.save(record);
706
+ return record;
707
+ }
708
+
709
+ it('treats oomKilled as terminal even while Docker still reports running', () => {
710
+ const record = saveDockerRecord();
711
+
712
+ withFakeDockerInspect('true 137 true', () => {
713
+ const result = queryStatus(store, record.uuid, 'json');
714
+ expect(result.success).toBe(true);
715
+ const parsed = JSON.parse(result.output);
716
+ expect(parsed.status).toBe('executed');
717
+ expect(parsed.exitCode).toBe(137);
718
+ expect(parsed.oomKilled).toBe(true);
719
+ expect(parsed.endTime).toBeTruthy();
720
+ expect(parsed.currentTime).toBeUndefined();
721
+ });
722
+ });
723
+
724
+ it('uses 137 when oomKilled is terminal but Docker has no terminal exit code yet', () => {
725
+ const record = saveDockerRecord();
726
+
727
+ withFakeDockerInspect('true 0 true', () => {
728
+ const result = queryStatus(store, record.uuid, 'json');
729
+ expect(result.success).toBe(true);
730
+ const parsed = JSON.parse(result.output);
731
+ expect(parsed.status).toBe('executed');
732
+ expect(parsed.exitCode).toBe(137);
733
+ expect(parsed.oomKilled).toBe(true);
734
+ expect(parsed.endTime).toBeTruthy();
735
+ });
736
+ });
737
+ });
738
+
680
739
  describe('Issue #105: attachCurrentTime for executing status', () => {
681
740
  it('should add currentTime to serialization when status is executing', () => {
682
741
  const record = new ExecutionRecord({