@atolis-hq/wake 0.4.16 → 0.4.18
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/config/root-schema.d.ts +10 -0
- package/dist/src/bootstrap/config/root-schema.js +18 -2
- package/dist/src/bootstrap/index.d.ts +1 -0
- package/dist/src/bootstrap/index.js +1 -0
- package/dist/src/bootstrap/npm-update-port.d.ts +11 -0
- package/dist/src/bootstrap/npm-update-port.js +45 -0
- package/dist/src/bootstrap/self-update-application.js +2 -3
- package/dist/src/bootstrap/surface-cli-applications.js +54 -25
- package/dist/src/bootstrap/version.js +1 -1
- package/dist/src/surfaces/cli/usage.js +1 -1
- package/package.json +1 -1
|
@@ -140,6 +140,11 @@ declare const hostConfigSchema: z.ZodDefault<z.ZodObject<{
|
|
|
140
140
|
selfUpdate: z.ZodDefault<z.ZodObject<{
|
|
141
141
|
drainTimeoutMs: z.ZodDefault<z.ZodNumber>;
|
|
142
142
|
cancellationTimeoutMs: z.ZodDefault<z.ZodNumber>;
|
|
143
|
+
npm: z.ZodDefault<z.ZodObject<{
|
|
144
|
+
package: z.ZodDefault<z.ZodString>;
|
|
145
|
+
distTag: z.ZodDefault<z.ZodString>;
|
|
146
|
+
registry: z.ZodOptional<z.ZodString>;
|
|
147
|
+
}, z.core.$strict>>;
|
|
143
148
|
}, z.core.$strict>>;
|
|
144
149
|
}, z.core.$strict>>;
|
|
145
150
|
export declare const rootConfigSchema: z.ZodObject<{
|
|
@@ -436,6 +441,11 @@ export declare const rootConfigSchema: z.ZodObject<{
|
|
|
436
441
|
selfUpdate: z.ZodDefault<z.ZodObject<{
|
|
437
442
|
drainTimeoutMs: z.ZodDefault<z.ZodNumber>;
|
|
438
443
|
cancellationTimeoutMs: z.ZodDefault<z.ZodNumber>;
|
|
444
|
+
npm: z.ZodDefault<z.ZodObject<{
|
|
445
|
+
package: z.ZodDefault<z.ZodString>;
|
|
446
|
+
distTag: z.ZodDefault<z.ZodString>;
|
|
447
|
+
registry: z.ZodOptional<z.ZodString>;
|
|
448
|
+
}, z.core.$strict>>;
|
|
439
449
|
}, z.core.$strict>>;
|
|
440
450
|
}, z.core.$strict>>;
|
|
441
451
|
}, z.core.$strict>;
|
|
@@ -92,9 +92,21 @@ const hostConfigSchema = z
|
|
|
92
92
|
.object({
|
|
93
93
|
drainTimeoutMs: z.number().int().positive().default(30_000),
|
|
94
94
|
cancellationTimeoutMs: z.number().int().positive().default(30_000),
|
|
95
|
+
npm: z
|
|
96
|
+
.object({
|
|
97
|
+
package: z.string().trim().min(1).default('@atolis-hq/wake'),
|
|
98
|
+
distTag: z.string().trim().min(1).default('latest'),
|
|
99
|
+
registry: z.string().url().optional(),
|
|
100
|
+
})
|
|
101
|
+
.strict()
|
|
102
|
+
.default({ package: '@atolis-hq/wake', distTag: 'latest' }),
|
|
95
103
|
})
|
|
96
104
|
.strict()
|
|
97
|
-
.default({
|
|
105
|
+
.default({
|
|
106
|
+
drainTimeoutMs: 30_000,
|
|
107
|
+
cancellationTimeoutMs: 30_000,
|
|
108
|
+
npm: { package: '@atolis-hq/wake', distTag: 'latest' },
|
|
109
|
+
}),
|
|
98
110
|
})
|
|
99
111
|
.strict()
|
|
100
112
|
.default({
|
|
@@ -108,7 +120,11 @@ const hostConfigSchema = z
|
|
|
108
120
|
extraMounts: [],
|
|
109
121
|
},
|
|
110
122
|
development: {},
|
|
111
|
-
selfUpdate: {
|
|
123
|
+
selfUpdate: {
|
|
124
|
+
drainTimeoutMs: 30_000,
|
|
125
|
+
cancellationTimeoutMs: 30_000,
|
|
126
|
+
npm: { package: '@atolis-hq/wake', distTag: 'latest' },
|
|
127
|
+
},
|
|
112
128
|
})
|
|
113
129
|
.superRefine((value, context) => {
|
|
114
130
|
if (value.development.mode === 'source' && value.development.repoRoot === undefined)
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { SourceUpdatePort } from './self-update-application.js';
|
|
2
|
+
export interface NpmUpdateProcess {
|
|
3
|
+
execute(command: string, args: readonly string[]): Promise<string>;
|
|
4
|
+
}
|
|
5
|
+
/** Resolves immutable published package versions. Installation is performed by the rollout. */
|
|
6
|
+
export declare function createNpmUpdatePort(input: {
|
|
7
|
+
readonly packageName: string;
|
|
8
|
+
readonly distTag?: string;
|
|
9
|
+
readonly registry?: string;
|
|
10
|
+
readonly execute?: NpmUpdateProcess['execute'];
|
|
11
|
+
}): SourceUpdatePort;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { execFile as nodeExecFile } from 'node:child_process';
|
|
2
|
+
import { promisify } from 'node:util';
|
|
3
|
+
const execFile = promisify(nodeExecFile);
|
|
4
|
+
/** Resolves immutable published package versions. Installation is performed by the rollout. */
|
|
5
|
+
export function createNpmUpdatePort(input) {
|
|
6
|
+
const execute = input.execute ?? runProcess;
|
|
7
|
+
const distTag = input.distTag ?? 'latest';
|
|
8
|
+
const registry = input.registry === undefined ? [] : [`--registry=${input.registry}`];
|
|
9
|
+
return {
|
|
10
|
+
isClean: async () => true,
|
|
11
|
+
async latestTag() {
|
|
12
|
+
const resolved = await resolveVersion();
|
|
13
|
+
if (resolved === undefined)
|
|
14
|
+
throw new Error(`No npm version is available for ${input.packageName}`);
|
|
15
|
+
return resolved;
|
|
16
|
+
},
|
|
17
|
+
async candidateTags() {
|
|
18
|
+
const resolved = await resolveVersion();
|
|
19
|
+
return resolved === undefined ? [] : [resolved];
|
|
20
|
+
},
|
|
21
|
+
// The Docker rollout installs this exact version. Keeping this side-effect-free means
|
|
22
|
+
// the updater never overwrites the CLI process which is coordinating the update.
|
|
23
|
+
checkout: async () => { },
|
|
24
|
+
healthy: async () => true,
|
|
25
|
+
};
|
|
26
|
+
async function resolveVersion() {
|
|
27
|
+
const output = await execute('npm', [
|
|
28
|
+
'view',
|
|
29
|
+
`${input.packageName}@${distTag}`,
|
|
30
|
+
'version',
|
|
31
|
+
'--json',
|
|
32
|
+
...registry,
|
|
33
|
+
]);
|
|
34
|
+
const parsed = JSON.parse(output);
|
|
35
|
+
const value = Array.isArray(parsed) ? parsed.at(0) : parsed;
|
|
36
|
+
if (typeof value !== 'string' ||
|
|
37
|
+
!/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/u.test(value))
|
|
38
|
+
throw new Error(`npm returned an invalid version for ${input.packageName}@${distTag}`);
|
|
39
|
+
return value;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
async function runProcess(command, args) {
|
|
43
|
+
const result = await execFile(command, args, { encoding: 'utf8' });
|
|
44
|
+
return result.stdout;
|
|
45
|
+
}
|
|
@@ -10,9 +10,8 @@ export function createSelfUpdateApplication(input) {
|
|
|
10
10
|
async updateLatest(force = false) {
|
|
11
11
|
const candidates = await input.source.candidateTags();
|
|
12
12
|
const [latestCandidate] = candidates;
|
|
13
|
-
if (latestCandidate === undefined)
|
|
14
|
-
throw new Error('No
|
|
15
|
-
}
|
|
13
|
+
if (latestCandidate === undefined)
|
|
14
|
+
throw new Error('No update versions are available');
|
|
16
15
|
for (const tag of candidates) {
|
|
17
16
|
if (!force && (await input.ledger.isBad(tag))) {
|
|
18
17
|
continue;
|
|
@@ -12,6 +12,7 @@ import { ResourceCorrelationRole, resourceId } from '../resources/index.js';
|
|
|
12
12
|
import { DockerProcessError, createApiDispatcher, createLoggedDockerCli, createPackagedAssetSource, createPairingGrant, createProcessLogSink, createSandboxDockerPort, createSurfaceHttpServer, drainProcessOutput, loadOrCreateCredentials, redeemPairingGrant, replaceAccessKey, runDoctor, runSandbox, runSandboxEntrypoint, runSandboxSetup, runSelfUpdateLatestLoop, runTargetSmoke, verifyResidentStart, waitForActiveRuns, waitForever, } from '../surfaces/index.js';
|
|
13
13
|
import { WorkStreamKind, workItemId } from '../work/index.js';
|
|
14
14
|
import { loadConfig } from './config/load-config.js';
|
|
15
|
+
import { createNpmUpdatePort } from './npm-update-port.js';
|
|
15
16
|
import { runtimeProjectionDefinitions } from './projection-runtime.js';
|
|
16
17
|
import { createRunnerRegistry } from './runner-registry.js';
|
|
17
18
|
import { createOneShotRunnerAdvance, createResidentRunnerAdvance, withFinalProjectionCatchUp, } from './runner-tick-adapter.js';
|
|
@@ -315,7 +316,7 @@ function sandboxDockerOptions(root, overrides) {
|
|
|
315
316
|
wakeRoot: root.paths.wakeRoot,
|
|
316
317
|
containerHomeRoot: root.paths.containerHomeRoot,
|
|
317
318
|
image: overrides?.image ?? root.config.host.sandbox.image,
|
|
318
|
-
development: root.config.host.development,
|
|
319
|
+
development: overrides?.development ?? root.config.host.development,
|
|
319
320
|
containerName: root.config.host.sandbox.containerName,
|
|
320
321
|
...(root.config.surfaces.api.enabled ? { publishedPort: root.config.surfaces.api.port } : {}),
|
|
321
322
|
wakeMountPath: root.config.host.sandbox.wakeMountPath,
|
|
@@ -324,7 +325,9 @@ function sandboxDockerOptions(root, overrides) {
|
|
|
324
325
|
startEnabled: root.config.host.sandbox.start.enabled,
|
|
325
326
|
...(process.env.WAKE_MEMORY_PROFILE === 'runner' ? { memoryProfile: 'runner' } : {}),
|
|
326
327
|
inspect: createDockerInspection(root.paths.wakeRoot),
|
|
327
|
-
resolveBuildVersion: () =>
|
|
328
|
+
resolveBuildVersion: async () => overrides?.buildVersion === undefined
|
|
329
|
+
? resolveSandboxBuildVersion(root)
|
|
330
|
+
: overrides.buildVersion,
|
|
328
331
|
};
|
|
329
332
|
}
|
|
330
333
|
function createSandboxDocker(root, overrides) {
|
|
@@ -408,23 +411,24 @@ function createSandboxRuntimeApplications(root) {
|
|
|
408
411
|
* resident `wake start` process actually came back up. Reused for both the
|
|
409
412
|
* forward deploy and a same-shaped rollback to a previously-built tag.
|
|
410
413
|
*/
|
|
411
|
-
async function deploySandboxTag(root, docker, tag, image) {
|
|
412
|
-
const port = createSandboxDockerPort(docker, sandboxDockerOptions(root, { image }));
|
|
414
|
+
async function deploySandboxTag(root, docker, tag, image, overrides) {
|
|
415
|
+
const port = createSandboxDockerPort(docker, sandboxDockerOptions(root, { image, ...overrides }));
|
|
416
|
+
const wakeInvocation = overrides?.development?.mode === 'source'
|
|
417
|
+
? ['node', '/app/dist/src/main.js']
|
|
418
|
+
: sandboxWakeInvocation(root);
|
|
419
|
+
const healthcheckRoot = `/tmp/wake-self-update-healthcheck-${tag}`;
|
|
413
420
|
await port.build();
|
|
414
421
|
await port.update();
|
|
415
|
-
const wakeInvocation = sandboxWakeInvocation(root);
|
|
416
422
|
await verifyResidentStart(docker, root.config.host.sandbox.containerName, [...wakeInvocation, 'start', '--wake-root', '/wake'].join(' '));
|
|
417
|
-
await port.exec([
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
`/tmp/wake-self-update-healthcheck-${tag}`,
|
|
422
|
-
]);
|
|
423
|
-
}
|
|
424
|
-
async function rollbackSandboxTag(root, docker, image) {
|
|
423
|
+
await port.exec([...wakeInvocation, 'init', healthcheckRoot]);
|
|
424
|
+
await port.exec([...wakeInvocation, 'tick', '--wake-root', healthcheckRoot, '--no-sandbox']);
|
|
425
|
+
}
|
|
426
|
+
async function rollbackSandboxTag(root, docker, image, development) {
|
|
425
427
|
const port = createSandboxDockerPort(docker, sandboxDockerOptions(root, { image }));
|
|
426
428
|
await port.update();
|
|
427
|
-
const wakeInvocation =
|
|
429
|
+
const wakeInvocation = development?.mode === 'source'
|
|
430
|
+
? ['node', '/app/dist/src/main.js']
|
|
431
|
+
: sandboxWakeInvocation(root);
|
|
428
432
|
await verifyResidentStart(docker, root.config.host.sandbox.containerName, [...wakeInvocation, 'start', '--wake-root', '/wake'].join(' '));
|
|
429
433
|
}
|
|
430
434
|
function createOperationalApplications(root) {
|
|
@@ -450,31 +454,56 @@ function createOperationalApplications(root) {
|
|
|
450
454
|
sandboxEntrypoint: async () => {
|
|
451
455
|
await runSandboxEntrypoint(createSandboxEntrypointDependencies(root));
|
|
452
456
|
},
|
|
457
|
+
// eslint-disable-next-line complexity -- update source and loop options are validated at this CLI boundary.
|
|
453
458
|
selfUpdate: async (arguments_) => {
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
const repoRoot = root.config.host.development.repoRoot;
|
|
457
|
-
if (
|
|
459
|
+
const sourceRoot = optionalOperationalOption(arguments_, '--source');
|
|
460
|
+
const configuredSource = root.config.host.development.mode === 'source';
|
|
461
|
+
const repoRoot = sourceRoot ?? root.config.host.development.repoRoot;
|
|
462
|
+
if (sourceRoot !== undefined && root.config.host.development.mode === 'source')
|
|
463
|
+
throw new Error('wake self-update --source cannot override source-mode configuration');
|
|
464
|
+
if (configuredSource && repoRoot === undefined)
|
|
458
465
|
throw new Error('wake self-update requires host.development.repoRoot');
|
|
459
466
|
const tag = optionalOperationalOption(arguments_, '--tag');
|
|
467
|
+
const version = optionalOperationalOption(arguments_, '--version');
|
|
468
|
+
if (tag !== undefined && version !== undefined)
|
|
469
|
+
throw new Error('wake self-update accepts only one of --tag or --version');
|
|
470
|
+
if (version !== undefined && (configuredSource || sourceRoot !== undefined))
|
|
471
|
+
throw new Error('wake self-update --version is only valid for npm updates');
|
|
472
|
+
const useSource = configuredSource || sourceRoot !== undefined;
|
|
473
|
+
const updatePort = useSource
|
|
474
|
+
? createSourceUpdatePort({ repoRoot: repoRoot })
|
|
475
|
+
: createNpmUpdatePort({
|
|
476
|
+
packageName: root.config.host.selfUpdate.npm.package,
|
|
477
|
+
distTag: root.config.host.selfUpdate.npm.distTag,
|
|
478
|
+
...(root.config.host.selfUpdate.npm.registry === undefined
|
|
479
|
+
? {}
|
|
480
|
+
: { registry: root.config.host.selfUpdate.npm.registry }),
|
|
481
|
+
});
|
|
482
|
+
const updateTag = tag ?? version;
|
|
483
|
+
const updateDevelopment = useSource
|
|
484
|
+
? { mode: 'source', repoRoot: repoRoot }
|
|
485
|
+
: { mode: 'packaged' };
|
|
460
486
|
const dockerCli = createDockerCliForRoot(root);
|
|
461
487
|
const imageRepository = root.config.host.sandbox.imageRepository;
|
|
462
488
|
const failureLog = createSelfUpdateFailureLog(root.paths.wakeRoot);
|
|
463
489
|
const application = createSelfUpdateApplication({
|
|
464
490
|
ledger: createUpdateLedger(root.paths.wakeRoot),
|
|
465
|
-
source:
|
|
491
|
+
source: updatePort,
|
|
466
492
|
rollout: {
|
|
467
493
|
async deploy(deployTag) {
|
|
468
494
|
await waitForActiveRuns({
|
|
469
495
|
activeRunIds: () => activeExecutionRunIds(root),
|
|
470
496
|
sleep: async (milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds)),
|
|
471
497
|
});
|
|
472
|
-
await deploySandboxTag(root, dockerCli, deployTag, `${imageRepository}:${deployTag}
|
|
498
|
+
await deploySandboxTag(root, dockerCli, deployTag, `${imageRepository}:${deployTag}`, {
|
|
499
|
+
development: updateDevelopment,
|
|
500
|
+
buildVersion: deployTag,
|
|
501
|
+
});
|
|
473
502
|
// A later deploy succeeding after a prior rollback clears the stale warning.
|
|
474
503
|
await failureLog.clear();
|
|
475
504
|
},
|
|
476
505
|
async rollback(rollbackTag) {
|
|
477
|
-
await rollbackSandboxTag(root, dockerCli, `${imageRepository}:${rollbackTag}
|
|
506
|
+
await rollbackSandboxTag(root, dockerCli, `${imageRepository}:${rollbackTag}`, updateDevelopment);
|
|
478
507
|
},
|
|
479
508
|
recordFailure: (failedTag, error) => failureLog.record(failedTag, error),
|
|
480
509
|
},
|
|
@@ -485,12 +514,12 @@ function createOperationalApplications(root) {
|
|
|
485
514
|
const force = arguments_.includes('--force');
|
|
486
515
|
if (arguments_.includes('--loop')) {
|
|
487
516
|
const intervalMs = selfUpdateLoopInterval(arguments_);
|
|
488
|
-
if (
|
|
489
|
-
return runSelfUpdateLatestLoop(async () => application.update(
|
|
517
|
+
if (updateTag !== undefined)
|
|
518
|
+
return runSelfUpdateLatestLoop(async () => application.update(updateTag, force), () => delay(intervalMs));
|
|
490
519
|
return runSelfUpdateLatestLoop(async () => application.updateLatest(force), () => delay(intervalMs));
|
|
491
520
|
}
|
|
492
|
-
if (
|
|
493
|
-
return { tag, updated: await application.update(
|
|
521
|
+
if (updateTag !== undefined)
|
|
522
|
+
return { tag: updateTag, updated: await application.update(updateTag, force) };
|
|
494
523
|
return application.updateLatest(force);
|
|
495
524
|
},
|
|
496
525
|
smoke: async () => runTargetSmoke(createRunnerRegistry(root.config.execution, root.fakeScenarios), root.config.execution.defaultRunnerPool, new AbortController().signal),
|
|
@@ -124,6 +124,6 @@ export function resolveWakeVersion(options = {}) {
|
|
|
124
124
|
return `g${headHash.slice(0, 7)}`;
|
|
125
125
|
return '0.1.0-dev';
|
|
126
126
|
}
|
|
127
|
-
export const wakeVersion = "v0.4.
|
|
127
|
+
export const wakeVersion = "v0.4.18";
|
|
128
128
|
/** The registry-installable version used by packaged sandbox images. */
|
|
129
129
|
export const wakePackageVersion = packageVersion(defaultRepoRoot) ?? wakeVersion;
|
|
@@ -24,7 +24,7 @@ export const usage = [
|
|
|
24
24
|
' wake run resolve <run-id> --failed --reason <message>',
|
|
25
25
|
' wake api Run the target API surface',
|
|
26
26
|
' wake sandbox-entrypoint Run the sandbox resident entrypoint',
|
|
27
|
-
' wake self-update Safely update a source
|
|
27
|
+
' wake self-update Safely update Wake from npm or a source checkout',
|
|
28
28
|
'',
|
|
29
29
|
'Getting started:',
|
|
30
30
|
' 1. wake init ./wake-home',
|