@pleri/olam-cli 0.1.12 → 0.1.13

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.
Files changed (85) hide show
  1. package/dist/__tests__/image-presence.test.d.ts +2 -0
  2. package/dist/__tests__/image-presence.test.d.ts.map +1 -0
  3. package/dist/__tests__/image-presence.test.js +44 -0
  4. package/dist/__tests__/image-presence.test.js.map +1 -0
  5. package/dist/__tests__/protocol-version.test.d.ts +2 -0
  6. package/dist/__tests__/protocol-version.test.d.ts.map +1 -0
  7. package/dist/__tests__/protocol-version.test.js +170 -0
  8. package/dist/__tests__/protocol-version.test.js.map +1 -0
  9. package/dist/__tests__/registry-allowlist.test.d.ts +2 -0
  10. package/dist/__tests__/registry-allowlist.test.d.ts.map +1 -0
  11. package/dist/__tests__/registry-allowlist.test.js +129 -0
  12. package/dist/__tests__/registry-allowlist.test.js.map +1 -0
  13. package/dist/commands/__tests__/upgrade.all-three.test.d.ts +19 -0
  14. package/dist/commands/__tests__/upgrade.all-three.test.d.ts.map +1 -0
  15. package/dist/commands/__tests__/upgrade.all-three.test.js +92 -0
  16. package/dist/commands/__tests__/upgrade.all-three.test.js.map +1 -0
  17. package/dist/commands/__tests__/upgrade.history.test.d.ts +15 -0
  18. package/dist/commands/__tests__/upgrade.history.test.d.ts.map +1 -0
  19. package/dist/commands/__tests__/upgrade.history.test.js +199 -0
  20. package/dist/commands/__tests__/upgrade.history.test.js.map +1 -0
  21. package/dist/commands/__tests__/upgrade.lock.test.d.ts +15 -0
  22. package/dist/commands/__tests__/upgrade.lock.test.d.ts.map +1 -0
  23. package/dist/commands/__tests__/upgrade.lock.test.js +253 -0
  24. package/dist/commands/__tests__/upgrade.lock.test.js.map +1 -0
  25. package/dist/commands/__tests__/upgrade.olam-tag.test.d.ts +21 -0
  26. package/dist/commands/__tests__/upgrade.olam-tag.test.d.ts.map +1 -0
  27. package/dist/commands/__tests__/upgrade.olam-tag.test.js +127 -0
  28. package/dist/commands/__tests__/upgrade.olam-tag.test.js.map +1 -0
  29. package/dist/commands/__tests__/upgrade.poll.test.d.ts +14 -0
  30. package/dist/commands/__tests__/upgrade.poll.test.d.ts.map +1 -0
  31. package/dist/commands/__tests__/upgrade.poll.test.js +136 -0
  32. package/dist/commands/__tests__/upgrade.poll.test.js.map +1 -0
  33. package/dist/commands/__tests__/upgrade.recreate.test.d.ts +17 -0
  34. package/dist/commands/__tests__/upgrade.recreate.test.d.ts.map +1 -0
  35. package/dist/commands/__tests__/upgrade.recreate.test.js +95 -0
  36. package/dist/commands/__tests__/upgrade.recreate.test.js.map +1 -0
  37. package/dist/commands/__tests__/upgrade.rollback.test.d.ts +12 -0
  38. package/dist/commands/__tests__/upgrade.rollback.test.d.ts.map +1 -0
  39. package/dist/commands/__tests__/upgrade.rollback.test.js +275 -0
  40. package/dist/commands/__tests__/upgrade.rollback.test.js.map +1 -0
  41. package/dist/commands/__tests__/upgrade.sha-capture.test.d.ts +12 -0
  42. package/dist/commands/__tests__/upgrade.sha-capture.test.d.ts.map +1 -0
  43. package/dist/commands/__tests__/upgrade.sha-capture.test.js +63 -0
  44. package/dist/commands/__tests__/upgrade.sha-capture.test.js.map +1 -0
  45. package/dist/commands/__tests__/upgrade.smoke.test.d.ts +19 -0
  46. package/dist/commands/__tests__/upgrade.smoke.test.d.ts.map +1 -0
  47. package/dist/commands/__tests__/upgrade.smoke.test.js +101 -0
  48. package/dist/commands/__tests__/upgrade.smoke.test.js.map +1 -0
  49. package/dist/commands/__tests__/upgrade.swap.test.d.ts +19 -0
  50. package/dist/commands/__tests__/upgrade.swap.test.d.ts.map +1 -0
  51. package/dist/commands/__tests__/upgrade.swap.test.js +333 -0
  52. package/dist/commands/__tests__/upgrade.swap.test.js.map +1 -0
  53. package/dist/commands/create.d.ts.map +1 -1
  54. package/dist/commands/create.js +31 -0
  55. package/dist/commands/create.js.map +1 -1
  56. package/dist/commands/upgrade-history.d.ts +17 -0
  57. package/dist/commands/upgrade-history.d.ts.map +1 -0
  58. package/dist/commands/upgrade-history.js +40 -0
  59. package/dist/commands/upgrade-history.js.map +1 -0
  60. package/dist/commands/upgrade-lock.d.ts +102 -0
  61. package/dist/commands/upgrade-lock.d.ts.map +1 -0
  62. package/dist/commands/upgrade-lock.js +225 -0
  63. package/dist/commands/upgrade-lock.js.map +1 -0
  64. package/dist/commands/upgrade-log.d.ts +86 -0
  65. package/dist/commands/upgrade-log.d.ts.map +1 -0
  66. package/dist/commands/upgrade-log.js +146 -0
  67. package/dist/commands/upgrade-log.js.map +1 -0
  68. package/dist/commands/upgrade.d.ts +265 -0
  69. package/dist/commands/upgrade.d.ts.map +1 -1
  70. package/dist/commands/upgrade.js +840 -10
  71. package/dist/commands/upgrade.js.map +1 -1
  72. package/dist/image-presence.d.ts +40 -0
  73. package/dist/image-presence.d.ts.map +1 -0
  74. package/dist/image-presence.js +39 -0
  75. package/dist/image-presence.js.map +1 -0
  76. package/dist/index.js +1015 -163
  77. package/dist/protocol-version.d.ts +79 -0
  78. package/dist/protocol-version.d.ts.map +1 -0
  79. package/dist/protocol-version.js +133 -0
  80. package/dist/protocol-version.js.map +1 -0
  81. package/dist/registry-allowlist.d.ts +47 -0
  82. package/dist/registry-allowlist.d.ts.map +1 -0
  83. package/dist/registry-allowlist.js +67 -0
  84. package/dist/registry-allowlist.js.map +1 -0
  85. package/package.json +1 -1
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=image-presence.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"image-presence.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/image-presence.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,44 @@
1
+ // C5 (Phase C — fresh-install self-bootstrap): image-presence pre-flight
2
+ // helper. Decision #6 (revised pass 3) — `olam bootstrap` is the SOLE
3
+ // on-ramp; commands that need an image fail-fast with a named remedy
4
+ // instead of auto-pulling.
5
+ import { describe, it, expect } from 'vitest';
6
+ import { checkImagePresent } from '../image-presence.js';
7
+ describe('checkImagePresent', () => {
8
+ it('returns present:true when docker image inspect exits 0', () => {
9
+ const inspect = () => ({ exitCode: 0 });
10
+ const result = checkImagePresent({ imageRef: 'ghcr.io/pleri/olam-devbox:0.2.0', callerCommand: 'olam create' }, inspect);
11
+ expect(result.present).toBe(true);
12
+ expect(result.stderrLine).toBe('');
13
+ });
14
+ it('returns present:false with named remedy when inspect exits non-zero', () => {
15
+ const inspect = () => ({ exitCode: 1 });
16
+ const result = checkImagePresent({ imageRef: 'ghcr.io/pleri/olam-devbox:0.2.0', callerCommand: 'olam create' }, inspect);
17
+ expect(result.present).toBe(false);
18
+ expect(result.stderrLine).toContain("'ghcr.io/pleri/olam-devbox:0.2.0' not found locally");
19
+ expect(result.stderrLine).toContain('olam create does not auto-pull');
20
+ expect(result.stderrLine).toContain('Remedy: run `olam bootstrap`');
21
+ });
22
+ it('names the caller command in the remedy (so operators know which command failed)', () => {
23
+ const inspect = () => ({ exitCode: 1 });
24
+ const create = checkImagePresent({ imageRef: 'ghcr.io/pleri/olam-devbox:0.2.0', callerCommand: 'olam create' }, inspect);
25
+ const hostCp = checkImagePresent({ imageRef: 'ghcr.io/pleri/olam-host-cp:0.2.0', callerCommand: 'olam host-cp start' }, inspect);
26
+ const authUp = checkImagePresent({ imageRef: 'ghcr.io/pleri/olam-auth:0.2.0', callerCommand: 'olam auth up' }, inspect);
27
+ expect(create.stderrLine).toContain('olam create');
28
+ expect(hostCp.stderrLine).toContain('olam host-cp start');
29
+ expect(authUp.stderrLine).toContain('olam auth up');
30
+ });
31
+ it('mentions Decision #6 lineage in the remedy (audit trail for support questions)', () => {
32
+ const inspect = () => ({ exitCode: 1 });
33
+ const result = checkImagePresent({ imageRef: 'foo:bar', callerCommand: 'olam x' }, inspect);
34
+ expect(result.stderrLine).toContain('Decision #6');
35
+ expect(result.stderrLine).toContain('olam-fresh-install-self-bootstrap');
36
+ });
37
+ it('handles inspect timeout / docker daemon down (negative exitCode)', () => {
38
+ const inspect = () => ({ exitCode: -1 });
39
+ const result = checkImagePresent({ imageRef: 'foo:bar', callerCommand: 'olam x' }, inspect);
40
+ expect(result.present).toBe(false);
41
+ expect(result.stderrLine).toContain('not found locally');
42
+ });
43
+ });
44
+ //# sourceMappingURL=image-presence.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"image-presence.test.js","sourceRoot":"","sources":["../../src/__tests__/image-presence.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=protocol-version.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"protocol-version.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/protocol-version.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,170 @@
1
+ // C2 (Phase C — fresh-install self-bootstrap): protocol-version
2
+ // handshake helper. Closes T13 (cross-namespace protocol skew between
3
+ // CLI and overridden devbox image) by giving the CLI a clean way to
4
+ // inspect a pulled image's `olam.protocol.versions` LABEL and refuse
5
+ // when no overlap exists.
6
+ import { describe, it, expect } from 'vitest';
7
+ import { OLAM_PROTOCOL_VERSIONS_SUPPORTED, parseProtocolVersionsLabel, checkProtocolOverlap, inspectImageProtocolVersions, } from '../protocol-version.js';
8
+ describe('parseProtocolVersionsLabel', () => {
9
+ it('returns empty array for undefined / null / empty string', () => {
10
+ expect(parseProtocolVersionsLabel(undefined)).toEqual([]);
11
+ expect(parseProtocolVersionsLabel(null)).toEqual([]);
12
+ expect(parseProtocolVersionsLabel('')).toEqual([]);
13
+ });
14
+ it('parses a single version', () => {
15
+ expect(parseProtocolVersionsLabel('1')).toEqual([1]);
16
+ });
17
+ it('parses comma-separated list', () => {
18
+ expect(parseProtocolVersionsLabel('1,2,3')).toEqual([1, 2, 3]);
19
+ });
20
+ it('tolerates whitespace around entries', () => {
21
+ expect(parseProtocolVersionsLabel('1, 2 , 3')).toEqual([1, 2, 3]);
22
+ });
23
+ it('deduplicates', () => {
24
+ expect(parseProtocolVersionsLabel('1,2,1,3,2')).toEqual([1, 2, 3]);
25
+ });
26
+ it('sorts ascending', () => {
27
+ expect(parseProtocolVersionsLabel('3,1,2')).toEqual([1, 2, 3]);
28
+ });
29
+ it('skips malformed entries silently', () => {
30
+ expect(parseProtocolVersionsLabel('1,abc,2,1.5,3')).toEqual([1, 2, 3]);
31
+ });
32
+ it('rejects zero and negatives', () => {
33
+ expect(parseProtocolVersionsLabel('0,-1,2')).toEqual([2]);
34
+ });
35
+ });
36
+ describe('checkProtocolOverlap', () => {
37
+ it('returns compatible when image and CLI share at least one version', () => {
38
+ const result = checkProtocolOverlap([1, 2], [1]);
39
+ expect(result.compatible).toBe(true);
40
+ expect(result.overlap).toEqual([1]);
41
+ expect(result.remedy).toBe('');
42
+ });
43
+ it('returns incompatible with empty image versions and surfaces "missing LABEL" remedy', () => {
44
+ const result = checkProtocolOverlap([], [1]);
45
+ expect(result.compatible).toBe(false);
46
+ expect(result.overlap).toEqual([]);
47
+ expect(result.remedy).toContain('missing the `olam.protocol.versions` LABEL');
48
+ expect(result.remedy).toContain('docs/architecture/devbox-contract.md');
49
+ });
50
+ it('returns incompatible when image is older than CLI supports', () => {
51
+ const result = checkProtocolOverlap([1], [3, 4]);
52
+ expect(result.compatible).toBe(false);
53
+ expect(result.remedy).toContain('older than this CLI supports');
54
+ });
55
+ it('returns incompatible when image is newer than CLI supports', () => {
56
+ const result = checkProtocolOverlap([5, 6], [1]);
57
+ expect(result.compatible).toBe(false);
58
+ expect(result.remedy).toContain('newer than this CLI supports');
59
+ expect(result.remedy).toContain('npm install -g @pleri/olam-cli');
60
+ });
61
+ it('returns incompatible with non-overlapping bands', () => {
62
+ // Image declares [2, 4], CLI supports [1, 3] — no shared major.
63
+ const result = checkProtocolOverlap([2, 4], [1, 3]);
64
+ expect(result.compatible).toBe(false);
65
+ expect(result.overlap).toEqual([]);
66
+ expect(result.remedy).toContain('Pin a compatible CLI version that overlaps');
67
+ });
68
+ it('uses OLAM_PROTOCOL_VERSIONS_SUPPORTED by default when CLI list omitted', () => {
69
+ const result = checkProtocolOverlap(OLAM_PROTOCOL_VERSIONS_SUPPORTED.slice());
70
+ expect(result.compatible).toBe(true);
71
+ expect(result.overlap).toEqual([...OLAM_PROTOCOL_VERSIONS_SUPPORTED]);
72
+ });
73
+ it('preserves immutability of input — does not mutate cliVersions', () => {
74
+ const cli = [1, 2];
75
+ checkProtocolOverlap([1], cli);
76
+ expect(cli).toEqual([1, 2]);
77
+ });
78
+ });
79
+ describe('inspectImageProtocolVersions', () => {
80
+ it('returns parsed versions when docker inspect succeeds with a LABEL value', () => {
81
+ const fakeInspect = () => ({
82
+ exitCode: 0,
83
+ stdout: '1,2\n',
84
+ stderr: '',
85
+ });
86
+ const result = inspectImageProtocolVersions('ghcr.io/example/img:1.0', fakeInspect);
87
+ expect(result.versions).toEqual([1, 2]);
88
+ expect(result.inspectFailed).toBe(false);
89
+ expect(result.imageRef).toBe('ghcr.io/example/img:1.0');
90
+ });
91
+ it('returns empty versions when docker inspect outputs "<no value>" (LABEL absent)', () => {
92
+ const fakeInspect = () => ({
93
+ exitCode: 0,
94
+ stdout: '<no value>\n',
95
+ stderr: '',
96
+ });
97
+ const result = inspectImageProtocolVersions('ghcr.io/example/img:1.0', fakeInspect);
98
+ expect(result.versions).toEqual([]);
99
+ expect(result.inspectFailed).toBe(false);
100
+ });
101
+ it('returns empty versions when docker inspect outputs empty string', () => {
102
+ const fakeInspect = () => ({
103
+ exitCode: 0,
104
+ stdout: '\n',
105
+ stderr: '',
106
+ });
107
+ const result = inspectImageProtocolVersions('ghcr.io/example/img:1.0', fakeInspect);
108
+ expect(result.versions).toEqual([]);
109
+ expect(result.inspectFailed).toBe(false);
110
+ });
111
+ it('reports inspect failure when exit code non-zero', () => {
112
+ const fakeInspect = () => ({
113
+ exitCode: 1,
114
+ stdout: '',
115
+ stderr: 'Error: No such object\n',
116
+ });
117
+ const result = inspectImageProtocolVersions('ghcr.io/missing/img:9.9', fakeInspect);
118
+ expect(result.inspectFailed).toBe(true);
119
+ expect(result.inspectError).toContain('No such object');
120
+ expect(result.versions).toEqual([]);
121
+ });
122
+ it('reports inspect failure with default message when stderr is empty', () => {
123
+ const fakeInspect = () => ({
124
+ exitCode: 125,
125
+ stdout: '',
126
+ stderr: '',
127
+ });
128
+ const result = inspectImageProtocolVersions('ghcr.io/missing/img:9.9', fakeInspect);
129
+ expect(result.inspectFailed).toBe(true);
130
+ expect(result.inspectError).toContain('exited 125');
131
+ });
132
+ });
133
+ describe('end-to-end handshake against the conformance fixtures (mock-only)', () => {
134
+ // Mirrors what Phase D's E2E will exercise against the real fixture
135
+ // images. Here we mock the docker calls so the test runs in any
136
+ // environment.
137
+ it('positive fixture (LABEL "1") + CLI v1 → compatible', () => {
138
+ const fakeInspect = () => ({
139
+ exitCode: 0,
140
+ stdout: '1\n',
141
+ stderr: '',
142
+ });
143
+ const inspected = inspectImageProtocolVersions('ghcr.io/pleri/contract-test-image:0.1.0', fakeInspect);
144
+ const handshake = checkProtocolOverlap(inspected.versions);
145
+ expect(handshake.compatible).toBe(true);
146
+ });
147
+ it('negative fixture (no LABEL) + CLI v1 → incompatible with missing-LABEL remedy', () => {
148
+ const fakeInspect = () => ({
149
+ exitCode: 0,
150
+ stdout: '<no value>\n',
151
+ stderr: '',
152
+ });
153
+ const inspected = inspectImageProtocolVersions('ghcr.io/pleri/non-conformant-image:0.1.0', fakeInspect);
154
+ const handshake = checkProtocolOverlap(inspected.versions);
155
+ expect(handshake.compatible).toBe(false);
156
+ expect(handshake.remedy).toContain('missing the `olam.protocol.versions` LABEL');
157
+ });
158
+ it('hypothetical future-CLI-only image (LABEL "2") + current CLI v1 → newer-than-cli remedy', () => {
159
+ const fakeInspect = () => ({
160
+ exitCode: 0,
161
+ stdout: '2\n',
162
+ stderr: '',
163
+ });
164
+ const inspected = inspectImageProtocolVersions('ghcr.io/pleri/future-image:9.9.9', fakeInspect);
165
+ const handshake = checkProtocolOverlap(inspected.versions);
166
+ expect(handshake.compatible).toBe(false);
167
+ expect(handshake.remedy).toContain('newer than this CLI supports');
168
+ });
169
+ });
170
+ //# sourceMappingURL=protocol-version.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"protocol-version.test.js","sourceRoot":"","sources":["../../src/__tests__/protocol-version.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=registry-allowlist.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry-allowlist.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/registry-allowlist.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,129 @@
1
+ // C4 (Phase C — fresh-install self-bootstrap): registry allowlist for
2
+ // OLAM_DEVBOX_IMAGE overrides. Flag-only override (env-var form was
3
+ // dropped per Decision #19 to avoid ambient bypass via shell inheritance).
4
+ import { describe, it, expect } from 'vitest';
5
+ import { decideAllowlist, resolveDevboxImageOverride } from '../registry-allowlist.js';
6
+ describe('decideAllowlist — default allowlist (ghcr.io/pleri/*)', () => {
7
+ it('accepts the canonical olam-devbox image with a tag', () => {
8
+ const d = decideAllowlist({
9
+ imageRef: 'ghcr.io/pleri/olam-devbox:0.2.0',
10
+ allowCustomRegistry: false,
11
+ });
12
+ expect(d.allowedByDefault).toBe(true);
13
+ expect(d.accepted).toBe(true);
14
+ expect(d.stderrLine).toBe('');
15
+ });
16
+ it('accepts olam-devbox@sha256: digest references', () => {
17
+ const d = decideAllowlist({
18
+ imageRef: 'ghcr.io/pleri/olam-devbox@sha256:abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789',
19
+ allowCustomRegistry: false,
20
+ });
21
+ expect(d.accepted).toBe(true);
22
+ });
23
+ it('accepts atlas-devbox under the pleri namespace (Decision #15)', () => {
24
+ const d = decideAllowlist({
25
+ imageRef: 'ghcr.io/pleri/olam-devbox-atlas:1.2.3',
26
+ allowCustomRegistry: false,
27
+ });
28
+ expect(d.allowedByDefault).toBe(true);
29
+ expect(d.accepted).toBe(true);
30
+ });
31
+ it('accepts host-cp + auth images for the publish-workflow', () => {
32
+ expect(decideAllowlist({ imageRef: 'ghcr.io/pleri/olam-host-cp:0.2.0', allowCustomRegistry: false }).accepted).toBe(true);
33
+ expect(decideAllowlist({ imageRef: 'ghcr.io/pleri/olam-auth:0.2.0', allowCustomRegistry: false }).accepted).toBe(true);
34
+ });
35
+ });
36
+ describe('decideAllowlist — out-of-allowlist refs', () => {
37
+ it('rejects a generic Docker Hub ref without the flag', () => {
38
+ const d = decideAllowlist({
39
+ imageRef: 'docker.io/library/ubuntu:24.04',
40
+ allowCustomRegistry: false,
41
+ });
42
+ expect(d.accepted).toBe(false);
43
+ expect(d.stderrLine).toContain('outside allowed registries');
44
+ expect(d.stderrLine).toContain('--allow-custom-registry');
45
+ });
46
+ it('rejects a different ghcr.io org without the flag', () => {
47
+ const d = decideAllowlist({
48
+ imageRef: 'ghcr.io/atlas/atlas-devbox:1.2.3',
49
+ allowCustomRegistry: false,
50
+ });
51
+ expect(d.accepted).toBe(false);
52
+ });
53
+ it('rejects an attacker-controlled registry without the flag', () => {
54
+ const d = decideAllowlist({
55
+ imageRef: 'evil.example/evil-devbox:latest',
56
+ allowCustomRegistry: false,
57
+ });
58
+ expect(d.accepted).toBe(false);
59
+ expect(d.stderrLine).toContain('outside allowed registries');
60
+ });
61
+ it('accepts an out-of-allowlist ref WITH the flag and emits a warning', () => {
62
+ const d = decideAllowlist({
63
+ imageRef: 'ghcr.io/atlas/atlas-devbox:1.2.3',
64
+ allowCustomRegistry: true,
65
+ });
66
+ expect(d.accepted).toBe(true);
67
+ expect(d.allowedByDefault).toBe(false);
68
+ expect(d.stderrLine).toContain('Warning');
69
+ expect(d.stderrLine).toContain("'ghcr.io/atlas/atlas-devbox:1.2.3'");
70
+ expect(d.stderrLine).toContain('--allow-custom-registry was specified');
71
+ });
72
+ it('keeps stderr empty for default-allowed refs even when the flag is set', () => {
73
+ const d = decideAllowlist({
74
+ imageRef: 'ghcr.io/pleri/olam-devbox:0.2.0',
75
+ allowCustomRegistry: true,
76
+ });
77
+ expect(d.accepted).toBe(true);
78
+ expect(d.stderrLine).toBe('');
79
+ });
80
+ });
81
+ describe('decideAllowlist — pattern boundaries', () => {
82
+ it('rejects look-alike namespace ghcr.io/pleri-evil/...', () => {
83
+ const d = decideAllowlist({
84
+ imageRef: 'ghcr.io/pleri-evil/olam-devbox:0.2.0',
85
+ allowCustomRegistry: false,
86
+ });
87
+ expect(d.accepted).toBe(false);
88
+ });
89
+ it('rejects look-alike registry ghcr.io.evil.com/pleri/...', () => {
90
+ const d = decideAllowlist({
91
+ imageRef: 'ghcr.io.evil.com/pleri/olam-devbox:0.2.0',
92
+ allowCustomRegistry: false,
93
+ });
94
+ expect(d.accepted).toBe(false);
95
+ });
96
+ it('rejects nested-namespace ghcr.io/pleri/foo/bar:tag (only one path segment after pleri/ allowed)', () => {
97
+ const d = decideAllowlist({
98
+ imageRef: 'ghcr.io/pleri/foo/bar:0.2.0',
99
+ allowCustomRegistry: false,
100
+ });
101
+ expect(d.accepted).toBe(false);
102
+ });
103
+ });
104
+ describe('resolveDevboxImageOverride — precedence', () => {
105
+ it('returns undefined when neither flag nor env var is set', () => {
106
+ expect(resolveDevboxImageOverride(undefined, {})).toBeUndefined();
107
+ });
108
+ it('returns the flag value when only the flag is set', () => {
109
+ expect(resolveDevboxImageOverride('ghcr.io/atlas/foo:1', {})).toBe('ghcr.io/atlas/foo:1');
110
+ });
111
+ it('returns the env value when only the env var is set', () => {
112
+ expect(resolveDevboxImageOverride(undefined, { OLAM_DEVBOX_IMAGE: 'ghcr.io/atlas/foo:1' })).toBe('ghcr.io/atlas/foo:1');
113
+ });
114
+ it('flag takes precedence over env var when both are set', () => {
115
+ expect(resolveDevboxImageOverride('ghcr.io/from-flag/img:1', {
116
+ OLAM_DEVBOX_IMAGE: 'ghcr.io/from-env/img:1',
117
+ })).toBe('ghcr.io/from-flag/img:1');
118
+ });
119
+ it('treats empty/whitespace flag value as unset and falls back to env', () => {
120
+ expect(resolveDevboxImageOverride(' ', { OLAM_DEVBOX_IMAGE: 'ghcr.io/from-env/img:1' })).toBe('ghcr.io/from-env/img:1');
121
+ });
122
+ it('treats empty/whitespace env value as unset', () => {
123
+ expect(resolveDevboxImageOverride(undefined, { OLAM_DEVBOX_IMAGE: ' ' })).toBeUndefined();
124
+ });
125
+ it('trims trailing whitespace in returned values', () => {
126
+ expect(resolveDevboxImageOverride(' ghcr.io/atlas/foo:1 ', {})).toBe('ghcr.io/atlas/foo:1');
127
+ });
128
+ });
129
+ //# sourceMappingURL=registry-allowlist.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry-allowlist.test.js","sourceRoot":"","sources":["../../src/__tests__/registry-allowlist.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Verifies Phase 2a A4 sequential build orchestration: `olam upgrade` invokes
3
+ * `bash build-auth.sh` → `bash build-devbox.sh` → `bash build-host-cp.sh` in
4
+ * that order with `OLAM_TAG=olam-next` env, and the devbox step uses inherit
5
+ * stdio (live tee) for cold-cache visibility.
6
+ *
7
+ * The CLI source is read as text and parsed for the load-bearing invariants:
8
+ * - Build scripts listed in correct order
9
+ * - OLAM_TAG=olam-next env propagated
10
+ * - Devbox uses tee (inherit stdio) per F13 audit fix
11
+ * - Other two use silent runStep capture
12
+ *
13
+ * Direct integration testing (spawning real `bash build-*.sh`) lives at
14
+ * tests/integration/build-scripts-olam-tag.sh — manual smoke; not in CI.
15
+ *
16
+ * Run: `cd packages/cli && npx vitest run src/commands/__tests__/upgrade.all-three.test.ts`
17
+ */
18
+ export {};
19
+ //# sourceMappingURL=upgrade.all-three.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upgrade.all-three.test.d.ts","sourceRoot":"","sources":["../../../src/commands/__tests__/upgrade.all-three.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG"}
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Verifies Phase 2a A4 sequential build orchestration: `olam upgrade` invokes
3
+ * `bash build-auth.sh` → `bash build-devbox.sh` → `bash build-host-cp.sh` in
4
+ * that order with `OLAM_TAG=olam-next` env, and the devbox step uses inherit
5
+ * stdio (live tee) for cold-cache visibility.
6
+ *
7
+ * The CLI source is read as text and parsed for the load-bearing invariants:
8
+ * - Build scripts listed in correct order
9
+ * - OLAM_TAG=olam-next env propagated
10
+ * - Devbox uses tee (inherit stdio) per F13 audit fix
11
+ * - Other two use silent runStep capture
12
+ *
13
+ * Direct integration testing (spawning real `bash build-*.sh`) lives at
14
+ * tests/integration/build-scripts-olam-tag.sh — manual smoke; not in CI.
15
+ *
16
+ * Run: `cd packages/cli && npx vitest run src/commands/__tests__/upgrade.all-three.test.ts`
17
+ */
18
+ import { describe, it, expect } from 'vitest';
19
+ import * as fs from 'node:fs';
20
+ import * as path from 'node:path';
21
+ function findRepoRoot(start) {
22
+ let dir = start;
23
+ for (let i = 0; i < 10; i++) {
24
+ if (fs.existsSync(path.join(dir, 'packages', 'host-cp', 'compose.yaml')))
25
+ return dir;
26
+ const parent = path.dirname(dir);
27
+ if (parent === dir)
28
+ break;
29
+ dir = parent;
30
+ }
31
+ throw new Error(`Could not find repo root from ${start}`);
32
+ }
33
+ const REPO_ROOT = findRepoRoot(process.cwd());
34
+ const UPGRADE_TS = path.join(REPO_ROOT, 'packages', 'cli', 'src', 'commands', 'upgrade.ts');
35
+ describe('Phase 2a A4 — sequential build orchestration', () => {
36
+ let body;
37
+ it('reads the upgrade.ts source', () => {
38
+ body = fs.readFileSync(UPGRADE_TS, 'utf-8');
39
+ expect(body.length).toBeGreaterThan(1000);
40
+ });
41
+ it('declares OLAM_TAG=olam-next env for the build steps', () => {
42
+ body ??= fs.readFileSync(UPGRADE_TS, 'utf-8');
43
+ expect(body).toMatch(/OLAM_TAG:\s*['"]olam-next['"]/);
44
+ });
45
+ it('lists the three build scripts in correct order (auth → devbox → host-cp) inside buildScripts[]', () => {
46
+ body ??= fs.readFileSync(UPGRADE_TS, 'utf-8');
47
+ // Scope the order check to the buildScripts array literal (other mentions
48
+ // of build-host-cp.sh exist in the file's doc comment + the printed plan).
49
+ const arrayMatch = body.match(/buildScripts:[\s\S]*?=\s*\[[\s\S]*?\];/);
50
+ expect(arrayMatch).not.toBeNull();
51
+ const arrayBody = arrayMatch[0];
52
+ const authIdx = arrayBody.indexOf('build-auth.sh');
53
+ const devboxIdx = arrayBody.indexOf('build-devbox.sh');
54
+ const hostCpIdx = arrayBody.indexOf('build-host-cp.sh');
55
+ expect(authIdx).toBeGreaterThan(0);
56
+ expect(devboxIdx).toBeGreaterThan(authIdx);
57
+ expect(hostCpIdx).toBeGreaterThan(devboxIdx);
58
+ });
59
+ it('marks the devbox step as tee:true (inherit stdio) for cold-cache visibility (F13 fix)', () => {
60
+ body ??= fs.readFileSync(UPGRADE_TS, 'utf-8');
61
+ // The devbox entry in the buildScripts array should have tee: true.
62
+ // Look for the literal "build-devbox.sh" entry and verify the same line
63
+ // (or nearby) sets tee: true.
64
+ const devboxLine = body
65
+ .split('\n')
66
+ .find((line) => line.includes('build-devbox.sh') && line.includes('tee'));
67
+ expect(devboxLine).toBeDefined();
68
+ expect(devboxLine).toMatch(/tee:\s*true/);
69
+ });
70
+ it('marks auth + host-cp steps as tee:false (silent runStep)', () => {
71
+ body ??= fs.readFileSync(UPGRADE_TS, 'utf-8');
72
+ const lines = body.split('\n');
73
+ const authLine = lines.find((line) => line.includes('build-auth.sh') && line.includes('tee'));
74
+ const hostCpLine = lines.find((line) => line.includes('build-host-cp.sh') && line.includes('tee'));
75
+ expect(authLine).toMatch(/tee:\s*false/);
76
+ expect(hostCpLine).toMatch(/tee:\s*false/);
77
+ });
78
+ it('uses spawnSync with stdio: inherit for the tee branch', () => {
79
+ body ??= fs.readFileSync(UPGRADE_TS, 'utf-8');
80
+ // The tee branch should call spawnSync with stdio: 'inherit'.
81
+ expect(body).toMatch(/spawnSync\([\s\S]*?stdio:\s*['"]inherit['"]/);
82
+ });
83
+ it('failure path exits with code 1 on any build script failure', () => {
84
+ body ??= fs.readFileSync(UPGRADE_TS, 'utf-8');
85
+ // The orchestration loop should set process.exitCode = 1 on failure.
86
+ // Look for the buildScripts loop body containing the exitCode = 1 pattern.
87
+ const loopMatch = body.match(/for\s*\(\s*const\s+step\s+of\s+buildScripts[\s\S]*?\n\s*\}\n/);
88
+ expect(loopMatch).not.toBeNull();
89
+ expect(loopMatch[0]).toMatch(/process\.exitCode\s*=\s*1/);
90
+ });
91
+ });
92
+ //# sourceMappingURL=upgrade.all-three.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upgrade.all-three.test.js","sourceRoot":"","sources":["../../../src/commands/__tests__/upgrade.all-three.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Phase 2c — verifies C1 (~/.olam/upgrade.log JSONL writer) + C2 (--history flag).
3
+ *
4
+ * Tests:
5
+ * - appendUpgradeLog: open-per-write, mkdir defensively
6
+ * - readUpgradeLog: ENOENT → empty; corrupt JSON skipped + stderr warn
7
+ * - formatHistoryTable / formatHistoryJson: render shape
8
+ * - formatDuration: ms / s / m / h
9
+ * - parseHistoryOpts: --history -n <N> --json
10
+ * - handleHistory wired from upgrade.ts
11
+ *
12
+ * Run: `cd packages/cli && npx vitest run src/commands/__tests__/upgrade.history.test.ts`
13
+ */
14
+ export {};
15
+ //# sourceMappingURL=upgrade.history.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upgrade.history.test.d.ts","sourceRoot":"","sources":["../../../src/commands/__tests__/upgrade.history.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG"}