@pleri/olam-cli 0.1.14 → 0.1.21
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/__tests__/auth-upgrade.test.js +236 -1
- package/dist/__tests__/auth-upgrade.test.js.map +1 -1
- package/dist/__tests__/install-root.test.d.ts +2 -0
- package/dist/__tests__/install-root.test.d.ts.map +1 -0
- package/dist/__tests__/install-root.test.js +119 -0
- package/dist/__tests__/install-root.test.js.map +1 -0
- package/dist/__tests__/upgrade.test.js +292 -2
- package/dist/__tests__/upgrade.test.js.map +1 -1
- package/dist/commands/__tests__/bootstrap.test.d.ts +2 -0
- package/dist/commands/__tests__/bootstrap.test.d.ts.map +1 -0
- package/dist/commands/__tests__/bootstrap.test.js +288 -0
- package/dist/commands/__tests__/bootstrap.test.js.map +1 -0
- package/dist/commands/__tests__/upgrade.rollback.test.js +1 -1
- package/dist/commands/__tests__/upgrade.swap.test.js +1 -1
- package/dist/commands/auth-upgrade.d.ts +41 -0
- package/dist/commands/auth-upgrade.d.ts.map +1 -1
- package/dist/commands/auth-upgrade.js +158 -6
- package/dist/commands/auth-upgrade.js.map +1 -1
- package/dist/commands/bootstrap.d.ts +95 -0
- package/dist/commands/bootstrap.d.ts.map +1 -0
- package/dist/commands/bootstrap.js +329 -0
- package/dist/commands/bootstrap.js.map +1 -0
- package/dist/commands/create.d.ts.map +1 -1
- package/dist/commands/create.js +19 -1
- package/dist/commands/create.js.map +1 -1
- package/dist/commands/upgrade.d.ts +76 -1
- package/dist/commands/upgrade.d.ts.map +1 -1
- package/dist/commands/upgrade.js +215 -8
- package/dist/commands/upgrade.js.map +1 -1
- package/dist/image-digests.json +8 -0
- package/dist/index.js +951 -201
- package/dist/index.js.map +1 -1
- package/dist/install-root.d.ts +74 -0
- package/dist/install-root.d.ts.map +1 -0
- package/dist/install-root.js +98 -0
- package/dist/install-root.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
// C1 + C3 (Phase C — fresh-install self-bootstrap): `olam bootstrap`
|
|
2
|
+
// orchestrator. Closes the user goal end-to-end:
|
|
3
|
+
// `npm install -g @pleri/olam-cli && olam init && olam bootstrap`.
|
|
4
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
5
|
+
import { runBootstrap, pullImageWithRetry, loadImageDigests, } from '../bootstrap.js';
|
|
6
|
+
import { EXIT_BOOTSTRAP_PULL_FAILED, EXIT_GENERIC_ERROR, EXIT_PROTOCOL_MISMATCH, } from '../../exit-codes.js';
|
|
7
|
+
// ── Test helpers ──────────────────────────────────────────────────
|
|
8
|
+
function mkDigest(seed) {
|
|
9
|
+
// Build a 64-hex digest deterministically from a seed for readable test
|
|
10
|
+
// output. Real digests are sha256 hex of the manifest content; tests
|
|
11
|
+
// just need stable strings of the right shape.
|
|
12
|
+
const hex = (seed + 'a'.repeat(64)).replace(/[^a-f0-9]/g, '0').slice(0, 64);
|
|
13
|
+
return `sha256:${hex}`;
|
|
14
|
+
}
|
|
15
|
+
const FIXTURE_DIGESTS = {
|
|
16
|
+
'host-cp': mkDigest('host-cp-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'),
|
|
17
|
+
auth: mkDigest('auth-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb'),
|
|
18
|
+
devbox: mkDigest('devbox-ccccccccccccccccccccccccccccccccccccccccccccccccccccccc'),
|
|
19
|
+
$registry: 'ghcr.io/pleri',
|
|
20
|
+
$published_version: '0.1.15',
|
|
21
|
+
};
|
|
22
|
+
function ok(stdout = '') {
|
|
23
|
+
return { exitCode: 0, stdout, stderr: '' };
|
|
24
|
+
}
|
|
25
|
+
function err(stderr, exitCode = 1) {
|
|
26
|
+
return { exitCode, stdout: '', stderr };
|
|
27
|
+
}
|
|
28
|
+
function makeMockDocker(opts = {}) {
|
|
29
|
+
const calls = opts.calls ?? { pull: [], inspect: [] };
|
|
30
|
+
return {
|
|
31
|
+
info: vi.fn(async () => (opts.infoOk === false ? err('cannot connect to docker') : ok())),
|
|
32
|
+
pull: vi.fn(async (ref) => {
|
|
33
|
+
calls.pull.push(ref);
|
|
34
|
+
return opts.pullResults?.get(ref) ?? ok();
|
|
35
|
+
}),
|
|
36
|
+
inspectLabel: vi.fn(async (ref, label) => {
|
|
37
|
+
calls.inspect.push(`${ref}#${label}`);
|
|
38
|
+
const value = opts.inspectLabels?.get(ref) ?? '1';
|
|
39
|
+
return ok(`${value}\n`);
|
|
40
|
+
}),
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
function silentSubprocess() {
|
|
44
|
+
const calls = [];
|
|
45
|
+
return {
|
|
46
|
+
calls,
|
|
47
|
+
runOlamSubcommand: (args) => {
|
|
48
|
+
calls.push([...args]);
|
|
49
|
+
return ok();
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
// Suppress real console output during tests — orchestrator uses ora +
|
|
54
|
+
// stdout/stderr writes that vitest reports as warnings otherwise.
|
|
55
|
+
beforeEach(() => {
|
|
56
|
+
vi.spyOn(process.stdout, 'write').mockImplementation(() => true);
|
|
57
|
+
vi.spyOn(process.stderr, 'write').mockImplementation(() => true);
|
|
58
|
+
});
|
|
59
|
+
afterEach(() => {
|
|
60
|
+
vi.restoreAllMocks();
|
|
61
|
+
});
|
|
62
|
+
// ── pullImageWithRetry — unit ────────────────────────────────────
|
|
63
|
+
describe('pullImageWithRetry — coalesce + retry', () => {
|
|
64
|
+
it('returns first attempt success without retrying', async () => {
|
|
65
|
+
let calls = 0;
|
|
66
|
+
const docker = {
|
|
67
|
+
info: async () => ok(),
|
|
68
|
+
pull: async () => {
|
|
69
|
+
calls++;
|
|
70
|
+
return ok();
|
|
71
|
+
},
|
|
72
|
+
inspectLabel: async () => ok('1'),
|
|
73
|
+
};
|
|
74
|
+
const result = await pullImageWithRetry('ghcr.io/test/img@sha256:xxx', docker);
|
|
75
|
+
expect(result.exitCode).toBe(0);
|
|
76
|
+
expect(calls).toBe(1);
|
|
77
|
+
});
|
|
78
|
+
it('retries once on transient (network) failure, then succeeds', async () => {
|
|
79
|
+
let calls = 0;
|
|
80
|
+
const docker = {
|
|
81
|
+
info: async () => ok(),
|
|
82
|
+
pull: async () => {
|
|
83
|
+
calls++;
|
|
84
|
+
if (calls === 1)
|
|
85
|
+
return err('connection refused', 1);
|
|
86
|
+
return ok();
|
|
87
|
+
},
|
|
88
|
+
inspectLabel: async () => ok('1'),
|
|
89
|
+
};
|
|
90
|
+
const result = await pullImageWithRetry('ghcr.io/test/img@sha256:xxx', docker);
|
|
91
|
+
expect(result.exitCode).toBe(0);
|
|
92
|
+
expect(calls).toBe(2);
|
|
93
|
+
});
|
|
94
|
+
it('does NOT retry on a non-transient failure (e.g. manifest unknown)', async () => {
|
|
95
|
+
let calls = 0;
|
|
96
|
+
const docker = {
|
|
97
|
+
info: async () => ok(),
|
|
98
|
+
pull: async () => {
|
|
99
|
+
calls++;
|
|
100
|
+
return err('manifest unknown', 1);
|
|
101
|
+
},
|
|
102
|
+
inspectLabel: async () => ok('1'),
|
|
103
|
+
};
|
|
104
|
+
const result = await pullImageWithRetry('ghcr.io/test/img@sha256:xxx', docker);
|
|
105
|
+
expect(result.exitCode).toBe(1);
|
|
106
|
+
expect(calls).toBe(1);
|
|
107
|
+
});
|
|
108
|
+
it('coalesces concurrent identical pulls into one in-flight promise', async () => {
|
|
109
|
+
let pullsStarted = 0;
|
|
110
|
+
const gate = new Promise((resolveGate) => setTimeout(resolveGate, 30));
|
|
111
|
+
const docker = {
|
|
112
|
+
info: async () => ok(),
|
|
113
|
+
pull: async () => {
|
|
114
|
+
pullsStarted++;
|
|
115
|
+
await gate;
|
|
116
|
+
return ok();
|
|
117
|
+
},
|
|
118
|
+
inspectLabel: async () => ok('1'),
|
|
119
|
+
};
|
|
120
|
+
const ref = 'ghcr.io/test/img@sha256:dedupeme';
|
|
121
|
+
const [a, b, c] = await Promise.all([
|
|
122
|
+
pullImageWithRetry(ref, docker),
|
|
123
|
+
pullImageWithRetry(ref, docker),
|
|
124
|
+
pullImageWithRetry(ref, docker),
|
|
125
|
+
]);
|
|
126
|
+
expect(a.exitCode).toBe(0);
|
|
127
|
+
expect(b.exitCode).toBe(0);
|
|
128
|
+
expect(c.exitCode).toBe(0);
|
|
129
|
+
expect(pullsStarted).toBe(1); // coalesced
|
|
130
|
+
});
|
|
131
|
+
it('does not coalesce different image refs', async () => {
|
|
132
|
+
let pullsStarted = 0;
|
|
133
|
+
const docker = {
|
|
134
|
+
info: async () => ok(),
|
|
135
|
+
pull: async () => {
|
|
136
|
+
pullsStarted++;
|
|
137
|
+
return ok();
|
|
138
|
+
},
|
|
139
|
+
inspectLabel: async () => ok('1'),
|
|
140
|
+
};
|
|
141
|
+
await Promise.all([
|
|
142
|
+
pullImageWithRetry('ghcr.io/a@sha256:111', docker),
|
|
143
|
+
pullImageWithRetry('ghcr.io/b@sha256:222', docker),
|
|
144
|
+
pullImageWithRetry('ghcr.io/c@sha256:333', docker),
|
|
145
|
+
]);
|
|
146
|
+
expect(pullsStarted).toBe(3);
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
// ── runBootstrap — orchestrator ───────────────────────────────────
|
|
150
|
+
describe('runBootstrap — happy path', () => {
|
|
151
|
+
it('exits 0 when daemon up + 3 pulls succeed + handshake passes + services start', async () => {
|
|
152
|
+
const docker = makeMockDocker();
|
|
153
|
+
const sub = silentSubprocess();
|
|
154
|
+
const result = await runBootstrap({ withSmoke: false, skipAuthLogin: true }, { docker, digests: FIXTURE_DIGESTS, runOlamSubcommand: sub.runOlamSubcommand });
|
|
155
|
+
expect(result.exitCode).toBe(0);
|
|
156
|
+
expect(sub.calls).toEqual([
|
|
157
|
+
['host-cp', 'start'],
|
|
158
|
+
['auth', 'up'],
|
|
159
|
+
]);
|
|
160
|
+
});
|
|
161
|
+
it('with --with-smoke, dispatches a smoke-test world create', async () => {
|
|
162
|
+
const docker = makeMockDocker();
|
|
163
|
+
const sub = silentSubprocess();
|
|
164
|
+
const result = await runBootstrap({ withSmoke: true, skipAuthLogin: true }, { docker, digests: FIXTURE_DIGESTS, runOlamSubcommand: sub.runOlamSubcommand });
|
|
165
|
+
expect(result.exitCode).toBe(0);
|
|
166
|
+
const lastCall = sub.calls[sub.calls.length - 1] ?? [];
|
|
167
|
+
expect(lastCall[0]).toBe('create');
|
|
168
|
+
expect(lastCall).toContain('--workspace');
|
|
169
|
+
expect(lastCall).toContain('smoke');
|
|
170
|
+
});
|
|
171
|
+
it('without --skip-auth-login (and OLAM_BOOTSTRAP_SKIP_AUTH_LOGIN unset), runs auth login', async () => {
|
|
172
|
+
const docker = makeMockDocker();
|
|
173
|
+
const sub = silentSubprocess();
|
|
174
|
+
delete process.env.OLAM_BOOTSTRAP_SKIP_AUTH_LOGIN;
|
|
175
|
+
const result = await runBootstrap({ withSmoke: false, skipAuthLogin: false }, { docker, digests: FIXTURE_DIGESTS, runOlamSubcommand: sub.runOlamSubcommand });
|
|
176
|
+
expect(result.exitCode).toBe(0);
|
|
177
|
+
expect(sub.calls).toContainEqual(['auth', 'login']);
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
describe('runBootstrap — failure paths', () => {
|
|
181
|
+
it('exits EXIT_GENERIC_ERROR (1) when docker daemon not reachable', async () => {
|
|
182
|
+
const docker = makeMockDocker({ infoOk: false });
|
|
183
|
+
const sub = silentSubprocess();
|
|
184
|
+
const result = await runBootstrap({ withSmoke: false, skipAuthLogin: true }, { docker, digests: FIXTURE_DIGESTS, runOlamSubcommand: sub.runOlamSubcommand });
|
|
185
|
+
expect(result.exitCode).toBe(EXIT_GENERIC_ERROR);
|
|
186
|
+
expect(result.summary).toContain('docker daemon not reachable');
|
|
187
|
+
// No subcommands run — bailed at step 1.
|
|
188
|
+
expect(sub.calls).toEqual([]);
|
|
189
|
+
});
|
|
190
|
+
it('exits EXIT_BOOTSTRAP_PULL_FAILED (3) when an image pull fails terminally', async () => {
|
|
191
|
+
const calls = { pull: [], inspect: [] };
|
|
192
|
+
const docker = makeMockDocker({
|
|
193
|
+
pullResults: new Map([
|
|
194
|
+
[`ghcr.io/pleri/olam-host-cp@${FIXTURE_DIGESTS['host-cp']}`, err('manifest unknown', 1)],
|
|
195
|
+
]),
|
|
196
|
+
calls,
|
|
197
|
+
});
|
|
198
|
+
const sub = silentSubprocess();
|
|
199
|
+
const result = await runBootstrap({ withSmoke: false, skipAuthLogin: true }, { docker, digests: FIXTURE_DIGESTS, runOlamSubcommand: sub.runOlamSubcommand });
|
|
200
|
+
expect(result.exitCode).toBe(EXIT_BOOTSTRAP_PULL_FAILED);
|
|
201
|
+
expect(result.summary).toContain('pull failed');
|
|
202
|
+
expect(sub.calls).toEqual([]);
|
|
203
|
+
});
|
|
204
|
+
it('exits EXIT_PROTOCOL_MISMATCH (4) when an image LABEL is missing', async () => {
|
|
205
|
+
const docker = makeMockDocker({
|
|
206
|
+
// Missing LABEL → docker inspect returns "<no value>".
|
|
207
|
+
inspectLabels: new Map([
|
|
208
|
+
[`ghcr.io/pleri/olam-devbox@${FIXTURE_DIGESTS.devbox}`, '<no value>'],
|
|
209
|
+
]),
|
|
210
|
+
});
|
|
211
|
+
const sub = silentSubprocess();
|
|
212
|
+
const result = await runBootstrap({ withSmoke: false, skipAuthLogin: true }, { docker, digests: FIXTURE_DIGESTS, runOlamSubcommand: sub.runOlamSubcommand });
|
|
213
|
+
expect(result.exitCode).toBe(EXIT_PROTOCOL_MISMATCH);
|
|
214
|
+
expect(result.summary).toContain('devbox');
|
|
215
|
+
});
|
|
216
|
+
it('exits EXIT_PROTOCOL_MISMATCH when an image LABEL declares non-overlapping versions', async () => {
|
|
217
|
+
const docker = makeMockDocker({
|
|
218
|
+
inspectLabels: new Map([
|
|
219
|
+
[`ghcr.io/pleri/olam-auth@${FIXTURE_DIGESTS.auth}`, '99'],
|
|
220
|
+
]),
|
|
221
|
+
});
|
|
222
|
+
const sub = silentSubprocess();
|
|
223
|
+
const result = await runBootstrap({ withSmoke: false, skipAuthLogin: true }, { docker, digests: FIXTURE_DIGESTS, runOlamSubcommand: sub.runOlamSubcommand });
|
|
224
|
+
expect(result.exitCode).toBe(EXIT_PROTOCOL_MISMATCH);
|
|
225
|
+
expect(result.summary).toContain('auth');
|
|
226
|
+
});
|
|
227
|
+
it('exits EXIT_GENERIC_ERROR when host-cp start fails', async () => {
|
|
228
|
+
const docker = makeMockDocker();
|
|
229
|
+
const calls = [];
|
|
230
|
+
const runOlamSubcommand = (args) => {
|
|
231
|
+
calls.push([...args]);
|
|
232
|
+
if (args[0] === 'host-cp')
|
|
233
|
+
return err('host-cp could not start', 1);
|
|
234
|
+
return ok();
|
|
235
|
+
};
|
|
236
|
+
const result = await runBootstrap({ withSmoke: false, skipAuthLogin: true }, { docker, digests: FIXTURE_DIGESTS, runOlamSubcommand });
|
|
237
|
+
expect(result.exitCode).toBe(EXIT_GENERIC_ERROR);
|
|
238
|
+
expect(result.summary).toContain('host-cp');
|
|
239
|
+
});
|
|
240
|
+
it('exits EXIT_GENERIC_ERROR when auth up fails', async () => {
|
|
241
|
+
const docker = makeMockDocker();
|
|
242
|
+
const runOlamSubcommand = (args) => {
|
|
243
|
+
if (args[0] === 'auth' && args[1] === 'up')
|
|
244
|
+
return err('auth-service docker port busy', 1);
|
|
245
|
+
return ok();
|
|
246
|
+
};
|
|
247
|
+
const result = await runBootstrap({ withSmoke: false, skipAuthLogin: true }, { docker, digests: FIXTURE_DIGESTS, runOlamSubcommand });
|
|
248
|
+
expect(result.exitCode).toBe(EXIT_GENERIC_ERROR);
|
|
249
|
+
expect(result.summary).toContain('auth up');
|
|
250
|
+
});
|
|
251
|
+
});
|
|
252
|
+
describe('runBootstrap — registry override + digest resolution', () => {
|
|
253
|
+
it('uses --registry flag when provided', async () => {
|
|
254
|
+
const calls = { pull: [], inspect: [] };
|
|
255
|
+
const docker = makeMockDocker({ calls });
|
|
256
|
+
const sub = silentSubprocess();
|
|
257
|
+
await runBootstrap({ withSmoke: false, skipAuthLogin: true, registry: 'localhost:5000/test' }, { docker, digests: FIXTURE_DIGESTS, runOlamSubcommand: sub.runOlamSubcommand });
|
|
258
|
+
expect(calls.pull[0]).toContain('localhost:5000/test/olam-host-cp');
|
|
259
|
+
});
|
|
260
|
+
it('falls back to digests.$registry when --registry not provided', async () => {
|
|
261
|
+
const calls = { pull: [], inspect: [] };
|
|
262
|
+
const docker = makeMockDocker({ calls });
|
|
263
|
+
const sub = silentSubprocess();
|
|
264
|
+
await runBootstrap({ withSmoke: false, skipAuthLogin: true }, {
|
|
265
|
+
docker,
|
|
266
|
+
digests: { ...FIXTURE_DIGESTS, $registry: 'ghcr.io/atlas-namespace' },
|
|
267
|
+
runOlamSubcommand: sub.runOlamSubcommand,
|
|
268
|
+
});
|
|
269
|
+
expect(calls.pull[0]).toContain('ghcr.io/atlas-namespace/olam-host-cp');
|
|
270
|
+
});
|
|
271
|
+
it('pulls all 3 images with the correct digest references', async () => {
|
|
272
|
+
const calls = { pull: [], inspect: [] };
|
|
273
|
+
const docker = makeMockDocker({ calls });
|
|
274
|
+
const sub = silentSubprocess();
|
|
275
|
+
await runBootstrap({ withSmoke: false, skipAuthLogin: true }, { docker, digests: FIXTURE_DIGESTS, runOlamSubcommand: sub.runOlamSubcommand });
|
|
276
|
+
expect(calls.pull).toHaveLength(3);
|
|
277
|
+
expect(calls.pull).toContain(`ghcr.io/pleri/olam-host-cp@${FIXTURE_DIGESTS['host-cp']}`);
|
|
278
|
+
expect(calls.pull).toContain(`ghcr.io/pleri/olam-auth@${FIXTURE_DIGESTS.auth}`);
|
|
279
|
+
expect(calls.pull).toContain(`ghcr.io/pleri/olam-devbox@${FIXTURE_DIGESTS.devbox}`);
|
|
280
|
+
});
|
|
281
|
+
});
|
|
282
|
+
// ── loadImageDigests — unit ───────────────────────────────────────
|
|
283
|
+
describe('loadImageDigests', () => {
|
|
284
|
+
it('throws when image-digests.json is absent', () => {
|
|
285
|
+
expect(() => loadImageDigests('/tmp/no-such-install-' + Date.now())).toThrow(/image-digests\.json missing/);
|
|
286
|
+
});
|
|
287
|
+
});
|
|
288
|
+
//# sourceMappingURL=bootstrap.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bootstrap.test.js","sourceRoot":"","sources":["../../../src/commands/__tests__/bootstrap.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -67,7 +67,7 @@ function buildFixtureImage(tag, labelValue) {
|
|
|
67
67
|
return result.status === 0;
|
|
68
68
|
}
|
|
69
69
|
function bakedSha(tag) {
|
|
70
|
-
const result = spawnSync('docker', ['inspect', '--format', '{{index .Config.Labels "
|
|
70
|
+
const result = spawnSync('docker', ['inspect', '--format', '{{index .Config.Labels "olam.build.sha"}}', tag], { encoding: 'utf-8', stdio: ['ignore', 'pipe', 'ignore'] });
|
|
71
71
|
if (result.status !== 0)
|
|
72
72
|
return null;
|
|
73
73
|
return (result.stdout ?? '').trim();
|
|
@@ -87,7 +87,7 @@ function imageExists(tag) {
|
|
|
87
87
|
return imageId(tag) !== null;
|
|
88
88
|
}
|
|
89
89
|
function bakedSha(tag) {
|
|
90
|
-
const result = spawnSync('docker', ['inspect', '--format', '{{index .Config.Labels "
|
|
90
|
+
const result = spawnSync('docker', ['inspect', '--format', '{{index .Config.Labels "olam.build.sha"}}', tag], { encoding: 'utf-8', stdio: ['ignore', 'pipe', 'ignore'] });
|
|
91
91
|
if (result.status !== 0)
|
|
92
92
|
return null;
|
|
93
93
|
return (result.stdout ?? '').trim();
|
|
@@ -9,14 +9,22 @@
|
|
|
9
9
|
* smoke test: POST /credentials/add provider=codex must NOT return a Claude URL
|
|
10
10
|
*/
|
|
11
11
|
import type { Command } from 'commander';
|
|
12
|
+
import { type DockerImpl, type ImageDigests } from './bootstrap.js';
|
|
12
13
|
export interface AuthUpgradeOpts {
|
|
13
14
|
readonly yes: boolean;
|
|
14
15
|
readonly skipRecreate: boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Phase C — C6: opt into the legacy source-build path for auth-service.
|
|
18
|
+
* Only works when isDevMode() is true. Default false → pull
|
|
19
|
+
* `olam-auth@sha256:<digest>` from ghcr.io and recreate.
|
|
20
|
+
*/
|
|
21
|
+
readonly fromSource: boolean;
|
|
15
22
|
}
|
|
16
23
|
/** Normalise raw Commander option object into typed opts. */
|
|
17
24
|
export declare function parseAuthUpgradeOpts(raw: {
|
|
18
25
|
yes?: boolean;
|
|
19
26
|
skipRecreate?: boolean;
|
|
27
|
+
fromSource?: boolean;
|
|
20
28
|
}): AuthUpgradeOpts;
|
|
21
29
|
/**
|
|
22
30
|
* Check cwd looks like the olam repo root for auth-service.
|
|
@@ -43,5 +51,38 @@ export declare function smokeTestCodexProvider(authSecret: string | null): Promi
|
|
|
43
51
|
ok: false;
|
|
44
52
|
error: string;
|
|
45
53
|
}>;
|
|
54
|
+
/**
|
|
55
|
+
* Phase C — C6: default `olam auth upgrade` flow for installed CLIs.
|
|
56
|
+
*
|
|
57
|
+
* Pulls `olam-auth@sha256:<digest>` from ghcr.io (via bootstrap.ts's
|
|
58
|
+
* retry-throttle-coalesce helper), runs the protocol-version handshake,
|
|
59
|
+
* re-tags to `olam-auth:local` (the canonical tag the
|
|
60
|
+
* AuthContainerController expects), then docker stop + docker rm +
|
|
61
|
+
* controller.start() — same recreate path as the legacy --from-source
|
|
62
|
+
* flow uses post-build.
|
|
63
|
+
*/
|
|
64
|
+
export interface AuthUpgradePullDeps {
|
|
65
|
+
readonly docker?: DockerImpl;
|
|
66
|
+
readonly digests?: ImageDigests;
|
|
67
|
+
readonly registry?: string;
|
|
68
|
+
/**
|
|
69
|
+
* Recreate driver. Default: stop + rm + AuthContainerController.start
|
|
70
|
+
* + waitForAuthHealth. Tests stub to avoid real docker calls.
|
|
71
|
+
*/
|
|
72
|
+
readonly recreateAuth?: () => Promise<{
|
|
73
|
+
ok: boolean;
|
|
74
|
+
error?: string;
|
|
75
|
+
}>;
|
|
76
|
+
/** Test-only: skip the docker-tag step (e.g. when injecting). */
|
|
77
|
+
readonly tagImpl?: (from: string, to: string) => {
|
|
78
|
+
ok: boolean;
|
|
79
|
+
error?: string;
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
export interface AuthUpgradePullResult {
|
|
83
|
+
readonly exitCode: number;
|
|
84
|
+
readonly summary: string;
|
|
85
|
+
}
|
|
86
|
+
export declare function runAuthUpgradePullByDigest(deps?: AuthUpgradePullDeps): Promise<AuthUpgradePullResult>;
|
|
46
87
|
export declare function registerAuthUpgrade(auth: Command): void;
|
|
47
88
|
//# sourceMappingURL=auth-upgrade.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth-upgrade.d.ts","sourceRoot":"","sources":["../../src/commands/auth-upgrade.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"auth-upgrade.d.ts","sourceRoot":"","sources":["../../src/commands/auth-upgrade.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AASzC,OAAO,EAIL,KAAK,UAAU,EACf,KAAK,YAAY,EAClB,MAAM,gBAAgB,CAAC;AAexB,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC;IACtB,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC;IAC/B;;;;OAIG;IACH,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;CAC9B;AAED,6DAA6D;AAC7D,wBAAgB,oBAAoB,CAAC,GAAG,EAAE;IACxC,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB,GAAG,eAAe,CAMlB;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG;IAAE,EAAE,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAW7F;AAED,uEAAuE;AACvE,wBAAsB,iBAAiB,CAAC,SAAS,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC,CAc5E;AAED;;;;;;GAMG;AACH,wBAAsB,sBAAsB,CAC1C,UAAU,EAAE,MAAM,GAAG,IAAI,GACxB,OAAO,CAAC;IAAE,EAAE,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAuCtD;AA4DD;;;;;;;;;GASG;AAEH,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,MAAM,CAAC,EAAE,UAAU,CAAC;IAC7B,QAAQ,CAAC,OAAO,CAAC,EAAE,YAAY,CAAC;IAChC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B;;;OAGG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,OAAO,CAAC;QAAE,EAAE,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACvE,iEAAiE;IACjE,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,KAAK;QAAE,EAAE,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CAClF;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED,wBAAsB,0BAA0B,CAC9C,IAAI,GAAE,mBAAwB,GAC7B,OAAO,CAAC,qBAAqB,CAAC,CAqFhC;AAgMD,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,CAqCvD"}
|
|
@@ -11,10 +11,15 @@
|
|
|
11
11
|
import * as fs from 'node:fs';
|
|
12
12
|
import * as path from 'node:path';
|
|
13
13
|
import { spawnSync } from 'node:child_process';
|
|
14
|
+
import ora from 'ora';
|
|
14
15
|
import pc from 'picocolors';
|
|
15
|
-
import { printError, printSuccess, printInfo, printWarning } from '../output.js';
|
|
16
|
+
import { printError, printSuccess, printInfo, printWarning, printHeader } from '../output.js';
|
|
16
17
|
import { readAuthSecret } from './host-cp.js';
|
|
17
18
|
import { AuthContainerController } from '@olam/core/src/auth/index.js';
|
|
19
|
+
import { loadImageDigests, pullImageWithRetry, realDocker, } from './bootstrap.js';
|
|
20
|
+
import { EXIT_BOOTSTRAP_PULL_FAILED, EXIT_GENERIC_ERROR, EXIT_PROTOCOL_MISMATCH, } from '../exit-codes.js';
|
|
21
|
+
import { checkProtocolOverlap, parseProtocolVersionsLabel } from '../protocol-version.js';
|
|
22
|
+
import { isDevMode, MissingBuildScriptError } from '../install-root.js';
|
|
18
23
|
const AUTH_PORT = 9999;
|
|
19
24
|
const AUTH_HEALTH_URL = `http://127.0.0.1:${AUTH_PORT}/health`;
|
|
20
25
|
const AUTH_ADD_URL = `http://127.0.0.1:${AUTH_PORT}/credentials/add`;
|
|
@@ -23,6 +28,7 @@ export function parseAuthUpgradeOpts(raw) {
|
|
|
23
28
|
return {
|
|
24
29
|
yes: raw.yes === true,
|
|
25
30
|
skipRecreate: raw.skipRecreate === true,
|
|
31
|
+
fromSource: raw.fromSource === true,
|
|
26
32
|
};
|
|
27
33
|
}
|
|
28
34
|
/**
|
|
@@ -143,6 +149,117 @@ function printTimings(timings) {
|
|
|
143
149
|
}
|
|
144
150
|
printInfo('total', `${(total / 1000).toFixed(1)}s`);
|
|
145
151
|
}
|
|
152
|
+
export async function runAuthUpgradePullByDigest(deps = {}) {
|
|
153
|
+
const docker = deps.docker ?? realDocker;
|
|
154
|
+
const digests = deps.digests ?? loadImageDigests();
|
|
155
|
+
const registry = deps.registry ?? digests.$registry ?? 'ghcr.io/pleri';
|
|
156
|
+
printHeader('olam auth upgrade (pull-by-digest)');
|
|
157
|
+
// Step 1 — daemon smoke.
|
|
158
|
+
const infoSpinner = ora('Checking docker daemon').start();
|
|
159
|
+
const info = await docker.info();
|
|
160
|
+
if (info.exitCode !== 0) {
|
|
161
|
+
infoSpinner.fail('docker daemon not reachable');
|
|
162
|
+
process.stderr.write(`${pc.red('error')} docker info exited with ${info.exitCode}.\n` +
|
|
163
|
+
' Ensure Docker is running, then retry.\n');
|
|
164
|
+
return { exitCode: EXIT_GENERIC_ERROR, summary: 'docker daemon not reachable' };
|
|
165
|
+
}
|
|
166
|
+
infoSpinner.succeed('docker daemon reachable');
|
|
167
|
+
// Step 2 — pull olam-auth by digest.
|
|
168
|
+
const ref = `${registry}/olam-auth@${digests.auth}`;
|
|
169
|
+
const pullSpinner = ora(`Pulling ${ref.slice(0, 60)}…`).start();
|
|
170
|
+
const pullResult = await pullImageWithRetry(ref, docker);
|
|
171
|
+
if (pullResult.exitCode !== 0) {
|
|
172
|
+
pullSpinner.fail('Pull failed');
|
|
173
|
+
process.stderr.write(`${pc.red('error')} pull failed (exit ${pullResult.exitCode}):\n` +
|
|
174
|
+
` ${pullResult.stderr.split('\n')[0] ?? '(empty)'}\n` +
|
|
175
|
+
'\n Remedy: re-run after resolving network / GHCR availability.\n' +
|
|
176
|
+
' For monorepo dev, `OLAM_DEV=1 olam auth upgrade --from-source` builds locally.\n');
|
|
177
|
+
return { exitCode: EXIT_BOOTSTRAP_PULL_FAILED, summary: 'pull failed: auth' };
|
|
178
|
+
}
|
|
179
|
+
pullSpinner.succeed('Image pulled');
|
|
180
|
+
// Step 3 — protocol-version handshake.
|
|
181
|
+
const handshakeSpinner = ora('Verifying olam.protocol.versions handshake').start();
|
|
182
|
+
const inspect = await docker.inspectLabel(ref, 'olam.protocol.versions');
|
|
183
|
+
if (inspect.exitCode !== 0) {
|
|
184
|
+
handshakeSpinner.fail('Could not inspect auth image');
|
|
185
|
+
process.stderr.write(`${pc.red('error')} docker inspect ${ref} failed: ${inspect.stderr}\n`);
|
|
186
|
+
return { exitCode: EXIT_GENERIC_ERROR, summary: 'inspect failed: auth' };
|
|
187
|
+
}
|
|
188
|
+
const labelValue = (inspect.stdout || '').trim();
|
|
189
|
+
const versions = parseProtocolVersionsLabel(labelValue === '<no value>' ? '' : labelValue);
|
|
190
|
+
const decision = checkProtocolOverlap(versions);
|
|
191
|
+
if (!decision.compatible) {
|
|
192
|
+
handshakeSpinner.fail('Protocol mismatch on auth image');
|
|
193
|
+
process.stderr.write(`${pc.red('error')} ${decision.remedy}\n`);
|
|
194
|
+
return { exitCode: EXIT_PROTOCOL_MISMATCH, summary: 'protocol mismatch: auth' };
|
|
195
|
+
}
|
|
196
|
+
handshakeSpinner.succeed('Protocol handshake passed');
|
|
197
|
+
// Step 4 — re-tag pulled-by-digest to olam-auth:local.
|
|
198
|
+
const tagSpinner = ora('Tagging digest → olam-auth:local').start();
|
|
199
|
+
const tagger = deps.tagImpl ?? defaultTagAuth;
|
|
200
|
+
const tagResult = tagger(ref, 'olam-auth:local');
|
|
201
|
+
if (!tagResult.ok) {
|
|
202
|
+
tagSpinner.fail('docker tag failed');
|
|
203
|
+
process.stderr.write(`${pc.red('error')} ${tagResult.error ?? 'docker tag failed'}\n`);
|
|
204
|
+
return { exitCode: EXIT_GENERIC_ERROR, summary: 'tag failed: auth' };
|
|
205
|
+
}
|
|
206
|
+
tagSpinner.succeed('Re-tagged → olam-auth:local');
|
|
207
|
+
// Step 5 — recreate auth-service container.
|
|
208
|
+
const authSpinner = ora('Recreating auth-service container').start();
|
|
209
|
+
const recreate = deps.recreateAuth ?? defaultRecreateAuth;
|
|
210
|
+
const recreateResult = await recreate();
|
|
211
|
+
if (!recreateResult.ok) {
|
|
212
|
+
authSpinner.fail('auth-service recreate failed');
|
|
213
|
+
process.stderr.write(`${pc.red('error')} ${recreateResult.error ?? 'unknown failure'}\n`);
|
|
214
|
+
return { exitCode: EXIT_GENERIC_ERROR, summary: 'auth recreate failed' };
|
|
215
|
+
}
|
|
216
|
+
authSpinner.succeed('auth-service running on new image');
|
|
217
|
+
printSuccess('Auth upgrade complete (pull-by-digest)');
|
|
218
|
+
printInfo('digest', digests.auth.slice(0, 32) + '…');
|
|
219
|
+
return { exitCode: 0, summary: 'auth upgraded' };
|
|
220
|
+
}
|
|
221
|
+
function defaultTagAuth(from, to) {
|
|
222
|
+
try {
|
|
223
|
+
const r = spawnSync('docker', ['tag', from, to], {
|
|
224
|
+
encoding: 'utf-8',
|
|
225
|
+
stdio: ['ignore', 'ignore', 'pipe'],
|
|
226
|
+
});
|
|
227
|
+
if (r.status === 0 && r.error === undefined)
|
|
228
|
+
return { ok: true };
|
|
229
|
+
return {
|
|
230
|
+
ok: false,
|
|
231
|
+
error: (r.stderr ?? '').trim() || r.error?.message || 'docker tag failed',
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
catch (err) {
|
|
235
|
+
return {
|
|
236
|
+
ok: false,
|
|
237
|
+
error: err instanceof Error ? `spawnSync threw: ${err.message}` : 'spawnSync threw',
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
async function defaultRecreateAuth() {
|
|
242
|
+
try {
|
|
243
|
+
spawnSync('docker', ['stop', 'olam-auth'], {
|
|
244
|
+
encoding: 'utf-8',
|
|
245
|
+
stdio: ['ignore', 'ignore', 'ignore'],
|
|
246
|
+
});
|
|
247
|
+
spawnSync('docker', ['rm', 'olam-auth'], {
|
|
248
|
+
encoding: 'utf-8',
|
|
249
|
+
stdio: ['ignore', 'ignore', 'ignore'],
|
|
250
|
+
});
|
|
251
|
+
const controller = new AuthContainerController();
|
|
252
|
+
controller.start();
|
|
253
|
+
const healthy = await waitForAuthHealth(15_000);
|
|
254
|
+
if (!healthy) {
|
|
255
|
+
return { ok: false, error: 'auth-service /health did not respond within 15s' };
|
|
256
|
+
}
|
|
257
|
+
return { ok: true };
|
|
258
|
+
}
|
|
259
|
+
catch (err) {
|
|
260
|
+
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
261
|
+
}
|
|
262
|
+
}
|
|
146
263
|
// ── Main handler ──────────────────────────────────────────────────
|
|
147
264
|
async function handleAuthUpgrade(opts) {
|
|
148
265
|
const cwd = process.cwd();
|
|
@@ -175,8 +292,23 @@ async function handleAuthUpgrade(opts) {
|
|
|
175
292
|
}
|
|
176
293
|
}
|
|
177
294
|
const timings = [];
|
|
178
|
-
// Step a: build the auth image.
|
|
179
|
-
|
|
295
|
+
// Step a: build the auth image. Phase B6: resolve the build script via
|
|
296
|
+
// installRoot + 2-condition dev-mode check (OLAM_DEV=1 + monorepo
|
|
297
|
+
// siblings). Outside dev mode, surface the named remedy from
|
|
298
|
+
// MissingBuildScriptError and exit cleanly — published-image upgrades
|
|
299
|
+
// (Phase C's C6) take the no-flag default path.
|
|
300
|
+
let buildScript;
|
|
301
|
+
try {
|
|
302
|
+
const { resolveBuildScript } = await import('../install-root.js');
|
|
303
|
+
buildScript = resolveBuildScript({
|
|
304
|
+
scriptRelPath: 'packages/adapters/src/docker/build-auth.sh',
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
catch (err) {
|
|
308
|
+
printError(err instanceof Error ? err.message : String(err));
|
|
309
|
+
process.exitCode = 1;
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
180
312
|
const imageResult = runStep('bash build-auth.sh', 'bash', [buildScript], { cwd });
|
|
181
313
|
timings.push({ label: 'docker image build', durationMs: imageResult.durationMs });
|
|
182
314
|
if (!imageResult.ok) {
|
|
@@ -267,11 +399,31 @@ async function handleAuthUpgrade(opts) {
|
|
|
267
399
|
export function registerAuthUpgrade(auth) {
|
|
268
400
|
auth
|
|
269
401
|
.command('upgrade')
|
|
270
|
-
.description('
|
|
402
|
+
.description('Upgrade the olam-auth container. Default: pull olam-auth@<digest> from ghcr.io and recreate. ' +
|
|
403
|
+
'Use --from-source to rebuild from monorepo source (requires OLAM_DEV=1).')
|
|
271
404
|
.option('-y, --yes', 'Skip the confirmation prompt')
|
|
272
|
-
.option('--skip-recreate', 'Build the image only; skip container stop/rm/start')
|
|
405
|
+
.option('--skip-recreate', 'Build the image only; skip container stop/rm/start (only with --from-source)')
|
|
406
|
+
.option('--from-source', 'Build olam-auth from monorepo source (legacy path).\n' +
|
|
407
|
+
' Requires OLAM_DEV=1 + a `packages/` sibling at the install root.')
|
|
273
408
|
.action(async (opts) => {
|
|
274
|
-
|
|
409
|
+
const parsed = parseAuthUpgradeOpts(opts);
|
|
410
|
+
if (parsed.fromSource) {
|
|
411
|
+
if (!isDevMode()) {
|
|
412
|
+
printError(new MissingBuildScriptError('packages/adapters/src/docker/build-auth.sh').message);
|
|
413
|
+
process.exitCode = 1;
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
416
|
+
await handleAuthUpgrade(parsed);
|
|
417
|
+
return;
|
|
418
|
+
}
|
|
419
|
+
try {
|
|
420
|
+
const result = await runAuthUpgradePullByDigest();
|
|
421
|
+
process.exitCode = result.exitCode;
|
|
422
|
+
}
|
|
423
|
+
catch (err) {
|
|
424
|
+
printError(err instanceof Error ? err.message : String(err));
|
|
425
|
+
process.exitCode = EXIT_GENERIC_ERROR;
|
|
426
|
+
}
|
|
275
427
|
});
|
|
276
428
|
}
|
|
277
429
|
//# sourceMappingURL=auth-upgrade.js.map
|