@celilo/cli 0.13.0 → 0.13.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@celilo/cli",
3
- "version": "0.13.0",
3
+ "version": "0.13.1",
4
4
  "description": "Celilo — home lab orchestration CLI",
5
5
  "type": "module",
6
6
  "bin": {
@@ -152,4 +152,49 @@ describe('bumpUpdatesFor', () => {
152
152
  '@celilo/event-bus',
153
153
  ]);
154
154
  });
155
+
156
+ // #399: pins are now written during the version phase from WORKSPACE versions,
157
+ // which run ahead of npm until the publish lands. The publish-phase pass still
158
+ // reads npm-latest, so without a guard it would walk those pins BACKWARDS and
159
+ // ship modules bundling the previous capability code — the exact bundle/repo
160
+ // drift ISS-0104 exists to prevent.
161
+ describe('never downgrades a pin', () => {
162
+ test('an older npm-latest leaves a newer workspace pin alone', () => {
163
+ const pkg: PackageJson = { dependencies: { '@celilo/capabilities': '^0.7.1' } };
164
+ expect(bumpUpdatesFor(pkg, latest({ '@celilo/capabilities': '0.7.0' }))).toEqual([]);
165
+ });
166
+
167
+ test('across a minor and a major', () => {
168
+ expect(
169
+ bumpUpdatesFor(
170
+ { dependencies: { '@celilo/cli': '^0.13.0' } },
171
+ latest({ '@celilo/cli': '0.12.1' }),
172
+ ),
173
+ ).toEqual([]);
174
+ expect(
175
+ bumpUpdatesFor(
176
+ { dependencies: { '@celilo/cli': '^1.0.0' } },
177
+ latest({ '@celilo/cli': '0.99.9' }),
178
+ ),
179
+ ).toEqual([]);
180
+ });
181
+
182
+ test('still bumps forward', () => {
183
+ const updates = bumpUpdatesFor(
184
+ { dependencies: { '@celilo/capabilities': '^0.7.0' } },
185
+ latest({ '@celilo/capabilities': '0.7.1' }),
186
+ );
187
+ expect(updates).toHaveLength(1);
188
+ expect(updates[0].newSpec).toBe('^0.7.1');
189
+ });
190
+
191
+ test('a prerelease suffix does not confuse the comparison', () => {
192
+ expect(
193
+ bumpUpdatesFor(
194
+ { dependencies: { '@celilo/cli': '^0.13.0' } },
195
+ latest({ '@celilo/cli': '0.12.1-alpha.3' }),
196
+ ),
197
+ ).toEqual([]);
198
+ });
199
+ });
155
200
  });
@@ -32,8 +32,8 @@ import type { ConsumerPinItem, PackageJson } from './types';
32
32
  * to current npm-latest. Returns the file paths + per-bucket update
33
33
  * lists; doesn't write anything.
34
34
  */
35
- export function planConsumerPins(): ConsumerPinItem[] {
36
- const latestMap = fetchLatestVersions();
35
+ export function planConsumerPins(versions?: Map<string, string>): ConsumerPinItem[] {
36
+ const latestMap = versions ?? fetchLatestVersions();
37
37
  if (latestMap.size === 0) return [];
38
38
 
39
39
  const externalPaths = readExternalProjectPaths();
@@ -68,6 +68,23 @@ export function planConsumerPins(): ConsumerPinItem[] {
68
68
  * - deps already at the target bare version (operator-class match,
69
69
  * `^1.0.0` and `1.0.0` both count as "at version 1.0.0").
70
70
  */
71
+ /**
72
+ * Compare `major.minor.patch`, ignoring any prerelease suffix. Returns
73
+ * -1 / 0 / 1. Deliberately tiny: pins only ever move between released
74
+ * `@celilo/*` versions, so full semver precedence is not needed.
75
+ */
76
+ function compareVersions(a: string, b: string): number {
77
+ const parse = (v: string) => (v.split('-')[0] ?? '').split('.').map(Number);
78
+ const pa = parse(a);
79
+ const pb = parse(b);
80
+ for (let i = 0; i < 3; i++) {
81
+ const x = pa[i] ?? 0;
82
+ const y = pb[i] ?? 0;
83
+ if (x !== y) return x < y ? -1 : 1;
84
+ }
85
+ return 0;
86
+ }
87
+
71
88
  export function bumpUpdatesFor(
72
89
  pkg: PackageJson,
73
90
  published: Map<string, string>,
@@ -89,6 +106,12 @@ export function bumpUpdatesFor(
89
106
  if (oldSpec.startsWith('workspace:')) continue;
90
107
  if (/^[a-z]+:/.test(oldSpec) && !oldSpec.startsWith('npm:')) continue;
91
108
  if (bareVersion(oldSpec) === newVersion) continue;
109
+ // Never walk a pin backwards. Pins are now written during the version
110
+ // phase from WORKSPACE versions, which are ahead of npm until the publish
111
+ // lands. Without this, the publish-phase pass — which still reads
112
+ // npm-latest — would "helpfully" downgrade every pin the version PR just
113
+ // set, and ship modules bundling the previous capability code.
114
+ if (compareVersions(newVersion, bareVersion(oldSpec)) < 0) continue;
92
115
  const newSpec = withOperator(oldSpec, newVersion);
93
116
  if (newSpec === oldSpec) continue;
94
117
  updates.push({ bucket, depName: name, oldSpec, newSpec });