@atolis-hq/wake 0.3.92 → 0.3.94
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/bootstrap/initialise.js +14 -2
- package/dist/src/bootstrap/source-update-port.js +28 -3
- package/dist/src/bootstrap/surface-cli-applications.js +6 -1
- package/dist/src/bootstrap/version.js +1 -1
- package/dist/src/surfaces/cli/infrastructure/resident-start.js +6 -1
- package/docker/Dockerfile +6 -1
- package/docker/Dockerfile.packaged +7 -1
- package/package.json +1 -1
|
@@ -366,7 +366,13 @@ ENV WAKE_MAIN_JS=/app/dist/src/main.js
|
|
|
366
366
|
# for \`docker exec\` (i.e. \`wake sandbox setup\`/\`wake sandbox exec\`) even
|
|
367
367
|
# when WAKE_START_ENABLED's supervised \`wake start\` child keeps crashing —
|
|
368
368
|
# e.g. on first boot, before sandbox auth has been configured.
|
|
369
|
-
|
|
369
|
+
#
|
|
370
|
+
# /wake/.wake is bind-mounted from the host and self-update actively creates
|
|
371
|
+
# and deletes its own lock files there while this container is starting, so
|
|
372
|
+
# a recursive chown can race a file disappearing mid-walk. That single ENOENT
|
|
373
|
+
# must not be fatal under set -eu — it would otherwise crash the container
|
|
374
|
+
# before wake start ever runs.
|
|
375
|
+
ENTRYPOINT ["sh", "-c", "set -eu; mkdir -p /wake/.wake; chown -R wake:wake /wake/.wake || true; if [ -n \\"$WAKE_HOME_INIT_DIRS\\" ]; then printf '%s\\n' \\"$WAKE_HOME_INIT_DIRS\\" | while IFS= read -r directory; do case \\"$directory\\" in \\"$WAKE_HOME_INIT_ROOT\\"/*) mkdir -p \\"$directory\\"; chown wake:wake \\"$directory\\" ;; *) exit 1 ;; esac; done; fi; exec su wake -s /bin/sh -c 'HOME=/home/wake exec node \\"$WAKE_MAIN_JS\\" sandbox-entrypoint --wake-root /wake'"]
|
|
370
376
|
`;
|
|
371
377
|
const packagedDockerfile = `# syntax=docker/dockerfile:1
|
|
372
378
|
FROM node:24-bookworm-slim
|
|
@@ -418,7 +424,13 @@ EXPOSE 4317
|
|
|
418
424
|
# sandbox-entrypoint-command.ts) that the CLI should be invoked via the bare
|
|
419
425
|
# \`wake\` binary that \`npm install -g\` puts on PATH, rather than a hardcoded
|
|
420
426
|
# npm global lib path that varies by npm/OS setup.
|
|
421
|
-
|
|
427
|
+
#
|
|
428
|
+
# /wake/.wake is bind-mounted from the host and self-update actively creates
|
|
429
|
+
# and deletes its own lock files there while this container is starting, so
|
|
430
|
+
# a recursive chown can race a file disappearing mid-walk. That single ENOENT
|
|
431
|
+
# must not be fatal under set -eu — it would otherwise crash the container
|
|
432
|
+
# before wake start ever runs.
|
|
433
|
+
ENTRYPOINT ["sh", "-c", "set -eu; mkdir -p /wake/.wake; chown -R wake:wake /wake/.wake || true; if [ -n \\"$WAKE_HOME_INIT_DIRS\\" ]; then printf '%s\\n' \\"$WAKE_HOME_INIT_DIRS\\" | while IFS= read -r directory; do case \\"$directory\\" in \\"$WAKE_HOME_INIT_ROOT\\"/*) mkdir -p \\"$directory\\"; chown wake:wake \\"$directory\\" ;; *) exit 1 ;; esac; done; fi; exec su wake -s /bin/sh -c 'HOME=/home/wake exec wake sandbox-entrypoint'"]
|
|
422
434
|
`;
|
|
423
435
|
/** Creates an immediately-valid, human-readable target Wake root. */
|
|
424
436
|
export async function initialiseWakeRoot(wakeRoot) {
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { execFile as nodeExecFile } from 'node:child_process';
|
|
2
|
+
import { existsSync } from 'node:fs';
|
|
3
|
+
import { join } from 'node:path';
|
|
2
4
|
import { promisify } from 'node:util';
|
|
3
5
|
const execFile = promisify(nodeExecFile);
|
|
4
6
|
export function createSourceUpdatePort(input) {
|
|
@@ -18,14 +20,17 @@ export function createSourceUpdatePort(input) {
|
|
|
18
20
|
},
|
|
19
21
|
async checkout(tag) {
|
|
20
22
|
await execute('git', ['checkout', tag], input.repoRoot);
|
|
23
|
+
// Dependencies are part of what a tag means; without refreshing them,
|
|
24
|
+
// node_modules can silently drift from the checked-out
|
|
25
|
+
// package-lock.json and the health check below only ever proves the
|
|
26
|
+
// previously-installed dependencies still compile, not this tag's own.
|
|
27
|
+
await execute('npm', npmInstallArgs(input.repoRoot), input.repoRoot);
|
|
21
28
|
},
|
|
22
29
|
async healthy() {
|
|
23
30
|
try {
|
|
24
31
|
// Verify the checked-out code can be built (TypeScript compiles).
|
|
25
32
|
// This is the minimal meaningful health check: the code must at least compile.
|
|
26
|
-
|
|
27
|
-
const buildArgs = ['exec', 'tsc', '--'];
|
|
28
|
-
await execute(buildCommand, [...buildArgs], input.repoRoot);
|
|
33
|
+
await execute('npm', ['exec', 'tsc', '--'], input.repoRoot);
|
|
29
34
|
return true;
|
|
30
35
|
}
|
|
31
36
|
catch {
|
|
@@ -34,6 +39,9 @@ export function createSourceUpdatePort(input) {
|
|
|
34
39
|
},
|
|
35
40
|
};
|
|
36
41
|
}
|
|
42
|
+
function npmInstallArgs(repoRoot) {
|
|
43
|
+
return existsSync(join(repoRoot, 'package-lock.json')) ? ['ci', '--include=dev'] : ['install'];
|
|
44
|
+
}
|
|
37
45
|
async function candidateTags(execute, repoRoot) {
|
|
38
46
|
await execute('git', ['fetch', '--tags'], repoRoot);
|
|
39
47
|
const output = await execute('git', ['tag', '--list', 'v*', '--sort=-v:refname'], repoRoot);
|
|
@@ -43,6 +51,23 @@ async function candidateTags(execute, repoRoot) {
|
|
|
43
51
|
.filter((line) => line.length > 0);
|
|
44
52
|
}
|
|
45
53
|
async function runProcess(command, args, cwd) {
|
|
54
|
+
// On Windows, npm resolves to an `npm.cmd` shim: execFile can't spawn it
|
|
55
|
+
// directly (ENOENT, since it isn't a real executable) and refuses to spawn
|
|
56
|
+
// it with a separate argument array either (EINVAL, Node's own hardening
|
|
57
|
+
// after CVE-2024-27980). Routing the whole command through a shell as one
|
|
58
|
+
// string is the platform-sanctioned way to invoke it without the argument
|
|
59
|
+
// array's deprecated, unescaped shell concatenation (DEP0190). Every
|
|
60
|
+
// argument passed to npm here is a fixed literal, never external input, so
|
|
61
|
+
// that lack of escaping is not a code-injection concern. git is a real
|
|
62
|
+
// executable and needs none of this.
|
|
63
|
+
if (process.platform === 'win32' && command === 'npm') {
|
|
64
|
+
const result = await execFile([command, ...args].join(' '), [], {
|
|
65
|
+
cwd,
|
|
66
|
+
encoding: 'utf8',
|
|
67
|
+
shell: true,
|
|
68
|
+
});
|
|
69
|
+
return result.stdout;
|
|
70
|
+
}
|
|
46
71
|
const result = await execFile(command, args, { cwd, encoding: 'utf8' });
|
|
47
72
|
return result.stdout;
|
|
48
73
|
}
|
|
@@ -295,7 +295,12 @@ async function deploySandboxTag(root, docker, tag, image) {
|
|
|
295
295
|
await port.update();
|
|
296
296
|
const wakeInvocation = sandboxWakeInvocation(root);
|
|
297
297
|
await verifyResidentStart(docker, root.config.host.sandbox.containerName, [...wakeInvocation, 'start', '--wake-root', '/wake'].join(' '));
|
|
298
|
-
await port.exec([
|
|
298
|
+
await port.exec([
|
|
299
|
+
...wakeInvocation,
|
|
300
|
+
'tick',
|
|
301
|
+
'--wake-root',
|
|
302
|
+
`/tmp/wake-self-update-healthcheck-${tag}`,
|
|
303
|
+
]);
|
|
299
304
|
}
|
|
300
305
|
async function rollbackSandboxTag(root, docker, image) {
|
|
301
306
|
const port = createSandboxDockerPort(docker, sandboxDockerOptions(root, { image }));
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import { DockerProcessError, } from './docker-invocation.js';
|
|
2
2
|
/** Verifies the replacement sandbox has started its resident Wake process. */
|
|
3
3
|
export async function verifyResidentStart(docker, containerName, expectedCmdlineFragment, options) {
|
|
4
|
-
|
|
4
|
+
// A mature Wake home's bind-mounted .wake can hold weeks of accumulated
|
|
5
|
+
// event, projection, and transcript files; the entrypoint's recursive
|
|
6
|
+
// chown over all of it — especially through Docker Desktop's virtualized
|
|
7
|
+
// filesystem on Windows — can alone take longer than a short, test-sized
|
|
8
|
+
// budget before the resident process ever gets to write its PID file.
|
|
9
|
+
const attempts = options?.attempts ?? 90;
|
|
5
10
|
const intervalMs = options?.intervalMs ?? 1000;
|
|
6
11
|
const check = [
|
|
7
12
|
'pid="$(cat /wake/.wake/logs/start.pid)"',
|
package/docker/Dockerfile
CHANGED
|
@@ -64,4 +64,9 @@ EXPOSE 4317
|
|
|
64
64
|
# that's fine since it's the very first thing that runs.
|
|
65
65
|
ENV WAKE_MAIN_JS=/app/dist/src/main.js
|
|
66
66
|
|
|
67
|
-
|
|
67
|
+
# /wake/.wake is bind-mounted from the host and self-update actively creates
|
|
68
|
+
# and deletes its own lock files there while this container is starting, so
|
|
69
|
+
# a recursive chown can race a file disappearing mid-walk. That single ENOENT
|
|
70
|
+
# must not be fatal under set -eu — it would otherwise crash the container
|
|
71
|
+
# before wake start ever runs.
|
|
72
|
+
ENTRYPOINT ["sh", "-c", "set -eu; mkdir -p /wake/.wake; chown -R wake:wake /wake/.wake || true; exec su wake -s /bin/sh -c 'if [ \"$WAKE_START_ENABLED\" = \"true\" ]; then exec node /app/dist/src/main.js start --wake-root /wake --no-sandbox; else exec sleep infinity; fi'"]
|
|
@@ -50,4 +50,10 @@ EXPOSE 4317
|
|
|
50
50
|
# sandbox-entrypoint-command.ts) that the CLI should be invoked via the bare
|
|
51
51
|
# `wake` binary that `npm install -g` puts on PATH, rather than a hardcoded
|
|
52
52
|
# npm global lib path that varies by npm/OS setup.
|
|
53
|
-
|
|
53
|
+
#
|
|
54
|
+
# /wake/.wake is bind-mounted from the host and self-update actively creates
|
|
55
|
+
# and deletes its own lock files there while this container is starting, so
|
|
56
|
+
# a recursive chown can race a file disappearing mid-walk. That single ENOENT
|
|
57
|
+
# must not be fatal under set -eu — it would otherwise crash the container
|
|
58
|
+
# before wake start ever runs.
|
|
59
|
+
ENTRYPOINT ["sh", "-c", "set -eu; mkdir -p /wake/.wake; chown -R wake:wake /wake/.wake || true; exec su wake -s /bin/sh -c 'exec wake sandbox-entrypoint'"]
|