@atolis-hq/wake 0.2.12 → 0.2.14
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/src/adapters/docker/docker-cli.js +3 -0
- package/dist/src/cli/sandbox-command.js +6 -0
- package/dist/src/cli/self-update-command.js +25 -7
- package/dist/src/cli/stop-command.js +4 -0
- package/dist/src/core/active-run-recovery.js +39 -0
- package/dist/src/core/stale-run-reconciler.js +17 -7
- package/dist/src/core/tick-runner.js +11 -1
- package/dist/src/lib/lock.js +67 -4
- package/dist/src/main.js +31 -0
- package/dist/src/version.js +1 -1
- package/package.json +1 -1
|
@@ -57,6 +57,9 @@ function buildRunArgs(input) {
|
|
|
57
57
|
}
|
|
58
58
|
export function createDockerCli(deps) {
|
|
59
59
|
return {
|
|
60
|
+
async inspectContainerImage(containerName) {
|
|
61
|
+
return (await deps.inspectContainerImage?.(containerName)) ?? null;
|
|
62
|
+
},
|
|
60
63
|
async build(input) {
|
|
61
64
|
const buildArgFlags = Object.entries(input.buildArgs ?? {}).flatMap(([key, value]) => [
|
|
62
65
|
'--build-arg',
|
|
@@ -148,6 +148,9 @@ export async function runSandboxCommand(input) {
|
|
|
148
148
|
await runStopCommand({
|
|
149
149
|
args: input.args.slice(1),
|
|
150
150
|
stateStore: input.stateStore,
|
|
151
|
+
...(input.recoverActiveRuns === undefined
|
|
152
|
+
? {}
|
|
153
|
+
: { recoverActiveRuns: input.recoverActiveRuns }),
|
|
151
154
|
docker: input.docker,
|
|
152
155
|
containerName: input.config.sandbox.containerName,
|
|
153
156
|
sleep: input.sleep,
|
|
@@ -174,6 +177,9 @@ export async function runSandboxCommand(input) {
|
|
|
174
177
|
imageRepository: input.config.sandbox.imageRepository,
|
|
175
178
|
containerName: input.config.sandbox.containerName,
|
|
176
179
|
stateStore: input.stateStore,
|
|
180
|
+
...(input.recoverActiveRuns === undefined
|
|
181
|
+
? {}
|
|
182
|
+
: { recoverActiveRuns: input.recoverActiveRuns }),
|
|
177
183
|
docker: input.docker,
|
|
178
184
|
git: input.selfUpdate.git,
|
|
179
185
|
issueReporter: input.selfUpdate.issueReporter,
|
|
@@ -12,6 +12,13 @@ const START_PROCESS_CHECK = [
|
|
|
12
12
|
'tr "\\0" " " < "/proc/$pid/cmdline" | grep -F "node /app/dist/src/main.js start --wake-root /wake" >/dev/null',
|
|
13
13
|
].join(' && '),
|
|
14
14
|
];
|
|
15
|
+
function tagFromImage(imageRepository, image) {
|
|
16
|
+
if (image === null) {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
const prefix = `${imageRepository}:`;
|
|
20
|
+
return image.startsWith(prefix) ? image.slice(prefix.length) : null;
|
|
21
|
+
}
|
|
15
22
|
function readFlag(name, args) {
|
|
16
23
|
const index = args.indexOf(name);
|
|
17
24
|
return index === -1 ? undefined : args[index + 1];
|
|
@@ -65,10 +72,20 @@ export async function runSelfUpdateCommand(input) {
|
|
|
65
72
|
}
|
|
66
73
|
await waitForActiveRuns({
|
|
67
74
|
listRunRecords: input.stateStore.listRunRecords,
|
|
75
|
+
...(input.recoverActiveRuns === undefined
|
|
76
|
+
? {}
|
|
77
|
+
: { recoverActiveRuns: input.recoverActiveRuns }),
|
|
68
78
|
sleep: input.sleep,
|
|
69
79
|
logger: input.logger,
|
|
70
80
|
});
|
|
71
81
|
const newImage = `${input.imageRepository}:${tag}`;
|
|
82
|
+
const previousImage = (await input.docker.inspectContainerImage?.(input.containerName)) ?? null;
|
|
83
|
+
const previousImageTag = tagFromImage(input.imageRepository, previousImage);
|
|
84
|
+
const rollbackImage = previousImage ??
|
|
85
|
+
(ledger.lastKnownGoodTag !== null
|
|
86
|
+
? `${input.imageRepository}:${ledger.lastKnownGoodTag}`
|
|
87
|
+
: null);
|
|
88
|
+
const rollbackTag = previousImageTag ?? ledger.lastKnownGoodTag;
|
|
72
89
|
const updateInput = {
|
|
73
90
|
containerName: input.containerName,
|
|
74
91
|
wakeRoot: input.wakeRoot,
|
|
@@ -106,9 +123,10 @@ export async function runSelfUpdateCommand(input) {
|
|
|
106
123
|
catch (error) {
|
|
107
124
|
const reason = error instanceof Error ? error.message : String(error);
|
|
108
125
|
input.logger.error(`[self-update] rollout of ${tag} failed: ${reason}`);
|
|
109
|
-
if (
|
|
110
|
-
|
|
111
|
-
|
|
126
|
+
if (rollbackImage !== null) {
|
|
127
|
+
if (rollbackTag !== null) {
|
|
128
|
+
await input.git.checkoutTag(rollbackTag);
|
|
129
|
+
}
|
|
112
130
|
await input.docker.update({ ...updateInput, image: rollbackImage });
|
|
113
131
|
input.logger.info('[self-update] recreated rollback container; entrypoint will keep wake start running');
|
|
114
132
|
await verifyResidentStart({
|
|
@@ -119,21 +137,21 @@ export async function runSelfUpdateCommand(input) {
|
|
|
119
137
|
sleep: input.sleep,
|
|
120
138
|
context: 'rollback',
|
|
121
139
|
});
|
|
122
|
-
input.logger.info(`[self-update] rolled back to ${
|
|
140
|
+
input.logger.info(`[self-update] rolled back to ${rollbackTag ?? rollbackImage}`);
|
|
123
141
|
}
|
|
124
142
|
else {
|
|
125
143
|
input.logger.error('[self-update] no previous known-good tag to roll back to');
|
|
126
144
|
}
|
|
127
145
|
await input.writeLedger({
|
|
128
|
-
lastAppliedTag:
|
|
129
|
-
lastKnownGoodTag:
|
|
146
|
+
lastAppliedTag: rollbackTag,
|
|
147
|
+
lastKnownGoodTag: rollbackTag,
|
|
130
148
|
badTags: [...ledger.badTags, { tag, reason, recordedAt: new Date().toISOString() }],
|
|
131
149
|
});
|
|
132
150
|
try {
|
|
133
151
|
await input.issueReporter.createIssue({
|
|
134
152
|
title: `Self-update to ${tag} failed and was rolled back`,
|
|
135
153
|
body: [
|
|
136
|
-
`Automated update to \`${tag}\` failed during rollout and was rolled back to \`${
|
|
154
|
+
`Automated update to \`${tag}\` failed during rollout and was rolled back to \`${rollbackTag ?? rollbackImage ?? 'unknown'}\`.`,
|
|
137
155
|
'',
|
|
138
156
|
'```',
|
|
139
157
|
reason,
|
|
@@ -4,6 +4,7 @@ export async function waitForActiveRuns(input) {
|
|
|
4
4
|
const pollIntervalMs = input.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
|
|
5
5
|
const startedAt = Date.now();
|
|
6
6
|
for (;;) {
|
|
7
|
+
await input.recoverActiveRuns?.();
|
|
7
8
|
const records = await input.listRunRecords();
|
|
8
9
|
const activeRuns = records.filter((record) => record.status === 'running');
|
|
9
10
|
if (activeRuns.length === 0) {
|
|
@@ -32,6 +33,9 @@ function readNumberFlag(name, args) {
|
|
|
32
33
|
export async function runStopCommand(input) {
|
|
33
34
|
await waitForActiveRuns({
|
|
34
35
|
listRunRecords: input.stateStore.listRunRecords,
|
|
36
|
+
...(input.recoverActiveRuns === undefined
|
|
37
|
+
? {}
|
|
38
|
+
: { recoverActiveRuns: input.recoverActiveRuns }),
|
|
35
39
|
sleep: input.sleep,
|
|
36
40
|
pollIntervalMs: readNumberFlag('--poll-interval-ms', input.args),
|
|
37
41
|
timeoutMs: readNumberFlag('--timeout-ms', input.args),
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { readFileLockStatus } from '../lib/lock.js';
|
|
2
|
+
import { maxConfiguredRunnerTimeoutMs } from '../domain/runner-routing.js';
|
|
3
|
+
import { createOutbox } from './outbox.js';
|
|
4
|
+
import { createProjectionUpdater } from './projection-updater.js';
|
|
5
|
+
import { createStaleRunReconciler } from './stale-run-reconciler.js';
|
|
6
|
+
export function createActiveRunRecovery(deps) {
|
|
7
|
+
const projectionUpdater = createProjectionUpdater({
|
|
8
|
+
stateStore: deps.stateStore,
|
|
9
|
+
resourceIndex: deps.resourceIndex,
|
|
10
|
+
config: deps.config,
|
|
11
|
+
});
|
|
12
|
+
const { deliverOutboundEvent } = createOutbox({
|
|
13
|
+
clock: deps.clock,
|
|
14
|
+
stateStore: deps.stateStore,
|
|
15
|
+
projectionUpdater,
|
|
16
|
+
});
|
|
17
|
+
async function isRunningRecordActive(record) {
|
|
18
|
+
const lock = await readFileLockStatus(deps.stateStore.paths.runnerLockFile, {
|
|
19
|
+
expectedCommandIncludes: process.argv[1] === undefined ? [] : [process.argv[1]],
|
|
20
|
+
});
|
|
21
|
+
if (!lock.active || lock.metadata === undefined) {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
return Date.parse(lock.metadata.acquiredAt) <= Date.parse(record.startedAt);
|
|
25
|
+
}
|
|
26
|
+
const { reconcileStaleRunningRecords } = createStaleRunReconciler({
|
|
27
|
+
config: deps.config,
|
|
28
|
+
stateStore: deps.stateStore,
|
|
29
|
+
projectionUpdater,
|
|
30
|
+
runnerTimeoutMs: () => maxConfiguredRunnerTimeoutMs(deps.config),
|
|
31
|
+
isRunningRecordActive,
|
|
32
|
+
deliverOutboundEvent,
|
|
33
|
+
});
|
|
34
|
+
return {
|
|
35
|
+
recoverActiveRuns() {
|
|
36
|
+
return reconcileStaleRunningRecords(deps.clock.now());
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
}
|
|
@@ -8,21 +8,30 @@ import { createEventEnvelope } from '../lib/event-log.js';
|
|
|
8
8
|
// `deliverOutboundEvent` is injected rather than imported so this module does
|
|
9
9
|
// not depend on the outbox module directly.
|
|
10
10
|
export function createStaleRunReconciler(deps) {
|
|
11
|
-
function
|
|
11
|
+
async function staleReason(record, now) {
|
|
12
12
|
if (record.status !== 'running') {
|
|
13
|
-
return
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
if (deps.isRunningRecordActive !== undefined && !(await deps.isRunningRecordActive(record))) {
|
|
16
|
+
return 'runner-lock-not-active';
|
|
14
17
|
}
|
|
15
18
|
const startedAtMs = Date.parse(record.startedAt);
|
|
16
19
|
if (!Number.isFinite(startedAtMs)) {
|
|
17
|
-
return
|
|
20
|
+
return 'timeout';
|
|
18
21
|
}
|
|
19
|
-
return now.getTime() - startedAtMs >= deps.runnerTimeoutMs();
|
|
22
|
+
return now.getTime() - startedAtMs >= deps.runnerTimeoutMs() ? 'timeout' : null;
|
|
20
23
|
}
|
|
21
24
|
async function reconcileStaleRunningRecords(now) {
|
|
22
25
|
const finishedAt = now.toISOString();
|
|
23
26
|
const runRecords = await deps.stateStore.listRunRecords();
|
|
24
|
-
const staleRecords =
|
|
25
|
-
for (const record of
|
|
27
|
+
const staleRecords = [];
|
|
28
|
+
for (const record of runRecords) {
|
|
29
|
+
const reason = await staleReason(record, now);
|
|
30
|
+
if (reason !== null) {
|
|
31
|
+
staleRecords.push({ record, reason });
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
for (const { record, reason } of staleRecords) {
|
|
26
35
|
// Run records carry the work item they belong to, so this is a direct
|
|
27
36
|
// O(1) read — no scan, no index, no source ambiguity. The record's
|
|
28
37
|
// repo/issueNumber are representation content and take no part in it.
|
|
@@ -57,6 +66,7 @@ export function createStaleRunReconciler(deps) {
|
|
|
57
66
|
metadata: {
|
|
58
67
|
...record.metadata,
|
|
59
68
|
reconciledBy: 'stale-running-record',
|
|
69
|
+
staleReason: reason,
|
|
60
70
|
timeoutMs: deps.runnerTimeoutMs(),
|
|
61
71
|
},
|
|
62
72
|
});
|
|
@@ -79,7 +89,7 @@ export function createStaleRunReconciler(deps) {
|
|
|
79
89
|
action: record.action,
|
|
80
90
|
sentinel: 'FAILED',
|
|
81
91
|
runId: record.runId,
|
|
82
|
-
reason: 'runner:stale-timeout',
|
|
92
|
+
reason: reason === 'timeout' ? 'runner:stale-timeout' : 'runner:orphaned-process',
|
|
83
93
|
...(record.routing === undefined ? {} : { routing: record.routing }),
|
|
84
94
|
},
|
|
85
95
|
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createLifecycleService } from './lifecycle-service.js';
|
|
2
2
|
import { createPolicyEngine } from './policy-engine.js';
|
|
3
3
|
import { createProjectionUpdater } from './projection-updater.js';
|
|
4
|
-
import { acquireFileLock } from '../lib/lock.js';
|
|
4
|
+
import { acquireFileLock, readFileLockStatus } from '../lib/lock.js';
|
|
5
5
|
import { CORRELATION_REGISTERED_EVENT, parseRunnerArtifacts, parseRunnerResult, } from '../domain/schema.js';
|
|
6
6
|
import { maxConfiguredRunnerTimeoutMs, resolveRunnerRouting } from '../domain/runner-routing.js';
|
|
7
7
|
import { awaitingApprovalRunnerSentinel, stageLabelForStage } from '../domain/stages.js';
|
|
@@ -58,6 +58,7 @@ export function createTickRunner(deps) {
|
|
|
58
58
|
stateStore: deps.stateStore,
|
|
59
59
|
projectionUpdater,
|
|
60
60
|
runnerTimeoutMs,
|
|
61
|
+
isRunningRecordActive,
|
|
61
62
|
deliverOutboundEvent,
|
|
62
63
|
});
|
|
63
64
|
const { cleanupClosedIssueWorkspaces } = createWorkspaceCleanup({
|
|
@@ -203,6 +204,15 @@ export function createTickRunner(deps) {
|
|
|
203
204
|
function runnerTimeoutMs() {
|
|
204
205
|
return maxConfiguredRunnerTimeoutMs(deps.config);
|
|
205
206
|
}
|
|
207
|
+
async function isRunningRecordActive(record) {
|
|
208
|
+
const lock = await readFileLockStatus(deps.stateStore.paths.runnerLockFile, {
|
|
209
|
+
expectedCommandIncludes: process.argv[1] === undefined ? [] : [process.argv[1]],
|
|
210
|
+
});
|
|
211
|
+
if (!lock.active || lock.metadata === undefined) {
|
|
212
|
+
return false;
|
|
213
|
+
}
|
|
214
|
+
return Date.parse(lock.metadata.acquiredAt) <= Date.parse(record.startedAt);
|
|
215
|
+
}
|
|
206
216
|
async function parkConfigDriftedProjections(projections) {
|
|
207
217
|
let parked = false;
|
|
208
218
|
for (const projection of projections) {
|
package/dist/src/lib/lock.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
1
2
|
import { link, mkdir, readFile, rm, writeFile } from 'node:fs/promises';
|
|
2
3
|
import { dirname, join } from 'node:path';
|
|
3
4
|
import { randomUUID } from 'node:crypto';
|
|
@@ -21,6 +22,7 @@ function parseLockMetadata(raw) {
|
|
|
21
22
|
pid: record.pid,
|
|
22
23
|
acquiredAt: record.acquiredAt,
|
|
23
24
|
...(typeof record.lockId === 'string' ? { lockId: record.lockId } : {}),
|
|
25
|
+
...(typeof record.commandLine === 'string' ? { commandLine: record.commandLine } : {}),
|
|
24
26
|
};
|
|
25
27
|
}
|
|
26
28
|
catch {
|
|
@@ -43,6 +45,28 @@ function isPidAlive(pid) {
|
|
|
43
45
|
return true;
|
|
44
46
|
}
|
|
45
47
|
}
|
|
48
|
+
function readProcessCommandLine(pid) {
|
|
49
|
+
if (pid === process.pid) {
|
|
50
|
+
return process.argv.join(' ');
|
|
51
|
+
}
|
|
52
|
+
try {
|
|
53
|
+
return readFileSyncProcessCommandLine(pid);
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
function readFileSyncProcessCommandLine(pid) {
|
|
60
|
+
const raw = readFileSync(`/proc/${pid}/cmdline`, 'utf8');
|
|
61
|
+
const commandLine = raw.replace(/\0/g, ' ').trim();
|
|
62
|
+
return commandLine.length === 0 ? undefined : commandLine;
|
|
63
|
+
}
|
|
64
|
+
function defaultProcessInspector() {
|
|
65
|
+
return {
|
|
66
|
+
isPidAlive,
|
|
67
|
+
readCommandLine: readProcessCommandLine,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
46
70
|
async function readLockMetadata(path) {
|
|
47
71
|
try {
|
|
48
72
|
return parseLockMetadata(await readFile(path, 'utf8'));
|
|
@@ -51,13 +75,48 @@ async function readLockMetadata(path) {
|
|
|
51
75
|
return null;
|
|
52
76
|
}
|
|
53
77
|
}
|
|
54
|
-
async function
|
|
78
|
+
export async function readFileLockStatus(path, options) {
|
|
55
79
|
const metadata = await readLockMetadata(path);
|
|
56
80
|
if (metadata === null) {
|
|
57
|
-
|
|
81
|
+
try {
|
|
82
|
+
await readFile(path, 'utf8');
|
|
83
|
+
return { present: true, active: false, staleReason: 'invalid-metadata' };
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
return { present: false, active: false, staleReason: 'missing' };
|
|
87
|
+
}
|
|
58
88
|
}
|
|
89
|
+
const now = options?.now ?? new Date();
|
|
59
90
|
const ageMs = now.getTime() - Date.parse(metadata.acquiredAt);
|
|
60
|
-
|
|
91
|
+
if (options?.staleAfterMs !== undefined && ageMs >= options.staleAfterMs) {
|
|
92
|
+
return { present: true, active: false, metadata, ageMs, staleReason: 'expired' };
|
|
93
|
+
}
|
|
94
|
+
const inspector = options?.processInspector ?? defaultProcessInspector();
|
|
95
|
+
if (!inspector.isPidAlive(metadata.pid)) {
|
|
96
|
+
return { present: true, active: false, metadata, ageMs, staleReason: 'pid-not-alive' };
|
|
97
|
+
}
|
|
98
|
+
const commandLine = inspector.readCommandLine(metadata.pid);
|
|
99
|
+
if (commandLine !== undefined &&
|
|
100
|
+
((metadata.commandLine !== undefined && commandLine !== metadata.commandLine) ||
|
|
101
|
+
(metadata.commandLine === undefined &&
|
|
102
|
+
options?.expectedCommandIncludes !== undefined &&
|
|
103
|
+
!options.expectedCommandIncludes.every((fragment) => commandLine.includes(fragment))))) {
|
|
104
|
+
return {
|
|
105
|
+
present: true,
|
|
106
|
+
active: false,
|
|
107
|
+
metadata,
|
|
108
|
+
ageMs,
|
|
109
|
+
commandLine,
|
|
110
|
+
staleReason: 'pid-command-mismatch',
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
return {
|
|
114
|
+
present: true,
|
|
115
|
+
active: true,
|
|
116
|
+
metadata,
|
|
117
|
+
ageMs,
|
|
118
|
+
...(commandLine === undefined ? {} : { commandLine }),
|
|
119
|
+
};
|
|
61
120
|
}
|
|
62
121
|
export async function acquireFileLock(path, options) {
|
|
63
122
|
await mkdir(dirname(path), { recursive: true });
|
|
@@ -65,6 +124,7 @@ export async function acquireFileLock(path, options) {
|
|
|
65
124
|
pid: process.pid,
|
|
66
125
|
acquiredAt: (options?.now ?? new Date()).toISOString(),
|
|
67
126
|
lockId: randomUUID(),
|
|
127
|
+
commandLine: process.argv.join(' '),
|
|
68
128
|
};
|
|
69
129
|
async function tryAcquire() {
|
|
70
130
|
const tempPath = join(dirname(path), `.tick-lock-${metadata.lockId}.tmp`);
|
|
@@ -97,7 +157,10 @@ export async function acquireFileLock(path, options) {
|
|
|
97
157
|
catch (error) {
|
|
98
158
|
if (error.code === 'EEXIST') {
|
|
99
159
|
if (options?.staleAfterMs !== undefined &&
|
|
100
|
-
(await
|
|
160
|
+
!(await readFileLockStatus(path, {
|
|
161
|
+
staleAfterMs: options.staleAfterMs,
|
|
162
|
+
now: options.now ?? new Date(),
|
|
163
|
+
})).active) {
|
|
101
164
|
await rm(path, { force: true });
|
|
102
165
|
try {
|
|
103
166
|
return await tryAcquire();
|
package/dist/src/main.js
CHANGED
|
@@ -28,6 +28,7 @@ import { runSandboxSetupCommand } from './cli/sandbox-setup-command.js';
|
|
|
28
28
|
import { collectStartupPreflightFailures, runStartupPreflight } from './cli/startup-preflight.js';
|
|
29
29
|
import { runUiCommand } from './cli/ui-command.js';
|
|
30
30
|
import { loadWakeConfig } from './config/load-config.js';
|
|
31
|
+
import { createActiveRunRecovery } from './core/active-run-recovery.js';
|
|
31
32
|
import { createControlPlane } from './core/control-plane.js';
|
|
32
33
|
import { createOutboundSinkRouter, createWorkSourceFanIn } from './core/sink-router.js';
|
|
33
34
|
import { createTickRunner } from './core/tick-runner.js';
|
|
@@ -346,6 +347,28 @@ async function inspectDockerContainer(containerName) {
|
|
|
346
347
|
});
|
|
347
348
|
});
|
|
348
349
|
}
|
|
350
|
+
async function inspectDockerContainerImage(containerName) {
|
|
351
|
+
return await new Promise((resolveInspect, reject) => {
|
|
352
|
+
const child = spawn('docker', ['container', 'inspect', '-f', '{{.Config.Image}}', containerName], {
|
|
353
|
+
cwd: process.cwd(),
|
|
354
|
+
env: process.env,
|
|
355
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
356
|
+
});
|
|
357
|
+
let stdout = '';
|
|
358
|
+
child.stdout.on('data', (chunk) => {
|
|
359
|
+
stdout += chunk.toString();
|
|
360
|
+
});
|
|
361
|
+
child.on('error', reject);
|
|
362
|
+
child.on('close', (exitCode) => {
|
|
363
|
+
if (exitCode !== 0) {
|
|
364
|
+
resolveInspect(null);
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
const image = stdout.trim();
|
|
368
|
+
resolveInspect(image.length > 0 ? image : null);
|
|
369
|
+
});
|
|
370
|
+
});
|
|
371
|
+
}
|
|
349
372
|
async function dockerDaemonReachable() {
|
|
350
373
|
return await new Promise((resolveReachable) => {
|
|
351
374
|
const child = spawn('docker', ['info'], {
|
|
@@ -370,6 +393,7 @@ function createHostDockerCli() {
|
|
|
370
393
|
run: (dockerArgs) => runCommand('docker', dockerArgs, { ...process.env, DOCKER_BUILDKIT: '1' }),
|
|
371
394
|
inspectImage: inspectDockerImage,
|
|
372
395
|
inspectContainer: inspectDockerContainer,
|
|
396
|
+
inspectContainerImage: inspectDockerContainerImage,
|
|
373
397
|
spawnExec: (dockerArgs) => {
|
|
374
398
|
const child = spawn('docker', dockerArgs, {
|
|
375
399
|
cwd: process.cwd(),
|
|
@@ -819,6 +843,12 @@ async function main() {
|
|
|
819
843
|
wakeRoot,
|
|
820
844
|
});
|
|
821
845
|
const docker = createHostDockerCli();
|
|
846
|
+
const recoverActiveRuns = createActiveRunRecovery({
|
|
847
|
+
clock: systemClock,
|
|
848
|
+
config,
|
|
849
|
+
stateStore,
|
|
850
|
+
resourceIndex: createResourceIndex({ paths: stateStore.paths }),
|
|
851
|
+
}).recoverActiveRuns;
|
|
822
852
|
const repoRoot = config.dev?.repoRoot;
|
|
823
853
|
const selfUpdate = commandArgs[0] === 'self-update' &&
|
|
824
854
|
config.dev?.mode === 'source' &&
|
|
@@ -889,6 +919,7 @@ async function main() {
|
|
|
889
919
|
docker,
|
|
890
920
|
packagedTemplatesRoot: resolve(resolvePackageRoot(), 'docker'),
|
|
891
921
|
stateStore,
|
|
922
|
+
recoverActiveRuns,
|
|
892
923
|
sleep: (ms) => new Promise((resolveSleep) => setTimeout(resolveSleep, ms)),
|
|
893
924
|
logger: {
|
|
894
925
|
info(message) {
|
package/dist/src/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const wakeVersion = "0.2.
|
|
1
|
+
export const wakeVersion = "0.2.14+gb810105";
|