@smoothbricks/cli 0.10.4 → 0.10.6
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/cli.js +5 -5
- package/dist/github-ci/index.d.ts.map +1 -1
- package/dist/github-ci/index.js +5 -3
- package/dist/lib/json.d.ts +5 -1
- package/dist/lib/json.d.ts.map +1 -1
- package/dist/lib/json.js +18 -16
- package/dist/monorepo/ci-workflow.d.ts +2 -0
- package/dist/monorepo/ci-workflow.d.ts.map +1 -1
- package/dist/monorepo/ci-workflow.js +10 -4
- package/dist/monorepo/github-runs-on.d.ts +14 -0
- package/dist/monorepo/github-runs-on.d.ts.map +1 -0
- package/dist/monorepo/github-runs-on.js +43 -0
- package/dist/monorepo/index.d.ts +3 -3
- package/dist/monorepo/index.d.ts.map +1 -1
- package/dist/monorepo/index.js +20 -8
- package/dist/monorepo/lockfile.d.ts +2 -1
- package/dist/monorepo/lockfile.d.ts.map +1 -1
- package/dist/monorepo/lockfile.js +1 -1
- package/dist/monorepo/managed-files.d.ts +30 -3
- package/dist/monorepo/managed-files.d.ts.map +1 -1
- package/dist/monorepo/managed-files.js +83 -63
- package/dist/monorepo/packs/index.js +2 -2
- package/dist/monorepo/publish-workflow.d.ts +3 -0
- package/dist/monorepo/publish-workflow.d.ts.map +1 -1
- package/dist/monorepo/publish-workflow.js +17 -14
- package/dist/monorepo/tool-validation.d.ts +2 -2
- package/dist/monorepo/tool-validation.d.ts.map +1 -1
- package/dist/monorepo/tool-validation.js +110 -16
- package/dist/release/index.d.ts.map +1 -1
- package/dist/release/index.js +6 -0
- package/managed/raw/envrc +5 -3
- package/managed/raw/patches/ttsc@0.19.3.patch +67 -0
- package/managed/raw/tooling/direnv/github-actions-bootstrap.sh +24 -7
- package/managed/raw/tooling/direnv/setup-environment.ts +23 -3
- package/managed/templates/github/actions/cache-nix-devenv/action.yml +13 -11
- package/managed/templates/github/actions/save-nix-devenv/action.yml +34 -3
- package/managed/templates/github/actions/setup-devenv/action.yml +44 -15
- package/managed/templates/github/workflows/managed-files.yml +64 -12
- package/package.json +2 -2
- package/src/cli.ts +6 -6
- package/src/github-ci/index.test.ts +3 -3
- package/src/github-ci/index.ts +6 -3
- package/src/lib/json.ts +5 -1
- package/src/monorepo/__tests__/ci-workflow.test.ts +37 -1
- package/src/monorepo/__tests__/publish-workflow.test.ts +28 -1
- package/src/monorepo/ci-workflow.ts +14 -4
- package/src/monorepo/github-runs-on.ts +45 -0
- package/src/monorepo/index.ts +20 -8
- package/src/monorepo/lockfile.ts +3 -2
- package/src/monorepo/managed-files.test.ts +49 -0
- package/src/monorepo/managed-files.ts +105 -64
- package/src/monorepo/packs/index.ts +2 -2
- package/src/monorepo/publish-workflow.ts +23 -15
- package/src/monorepo/tool-validation.test.ts +38 -16
- package/src/monorepo/tool-validation.ts +114 -16
- package/src/release/index.ts +6 -0
|
@@ -13,13 +13,20 @@ import {
|
|
|
13
13
|
const registryVersions: Record<string, string[]> = {
|
|
14
14
|
'@biomejs/biome': ['2.3.5'],
|
|
15
15
|
'@nx/js': ['23.1.0'],
|
|
16
|
+
'@smoothbricks/cli': ['0.10.3', '0.10.4'],
|
|
16
17
|
'@smoothbricks/nx-plugin': ['0.3.0'],
|
|
17
18
|
eslint: ['9.39.1'],
|
|
18
19
|
'eslint-stdout': ['1.1.1', '1.1.2'],
|
|
19
20
|
nx: ['23.1.0'],
|
|
20
21
|
prettier: ['3.6.1'],
|
|
21
|
-
ttsc: ['0.
|
|
22
|
-
typescript: ['
|
|
22
|
+
ttsc: ['0.19.3'],
|
|
23
|
+
typescript: ['6.0.3'],
|
|
24
|
+
// alias package is not fetched from registry under this name when using fallback
|
|
25
|
+
'@typescript/native': ['7.0.2'],
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const registryLatestTags: Record<string, string> = {
|
|
29
|
+
'@smoothbricks/cli': '0.10.4',
|
|
23
30
|
};
|
|
24
31
|
|
|
25
32
|
const realFetch = globalThis.fetch;
|
|
@@ -58,9 +65,9 @@ describe('tool configuration validation', () => {
|
|
|
58
65
|
`,
|
|
59
66
|
);
|
|
60
67
|
|
|
61
|
-
expect(validateToolConfig(root)).toBeGreaterThan(0);
|
|
68
|
+
expect(await validateToolConfig(root)).toBeGreaterThan(0);
|
|
62
69
|
await applyToolConfigDefaults(root);
|
|
63
|
-
expect(validateToolConfig(root)).toBe(0);
|
|
70
|
+
expect(await validateToolConfig(root)).toBe(0);
|
|
64
71
|
|
|
65
72
|
const rootPackage = JSON.parse(await readFile(join(root, 'package.json'), 'utf8'));
|
|
66
73
|
const toolingPackage = JSON.parse(await readFile(join(root, 'tooling/package.json'), 'utf8'));
|
|
@@ -69,6 +76,10 @@ describe('tool configuration validation', () => {
|
|
|
69
76
|
expect(rootPackage.devDependencies['@smoothbricks/nx-plugin']).toBe('workspace:*');
|
|
70
77
|
expect(rootPackage.devDependencies['eslint-stdout']).toBe('workspace:*');
|
|
71
78
|
expect(rootPackage.devDependencies.nx).toBe('23.1.0');
|
|
79
|
+
expect(rootPackage.devDependencies.ttsc).toBe('0.19.3');
|
|
80
|
+
expect(rootPackage.devDependencies.typescript).toBe('^6.0.3');
|
|
81
|
+
expect(rootPackage.devDependencies['@typescript/native']).toBe('npm:typescript@^7.0.2');
|
|
82
|
+
expect(rootPackage.patchedDependencies['ttsc@0.19.3']).toBe('patches/ttsc@0.19.3.patch');
|
|
72
83
|
expect(rootPackage.workspaces).toContain('tooling');
|
|
73
84
|
expect(toolingPackage.name).toBe('@smoothbricks/tooling');
|
|
74
85
|
expect(toolingPackage.dependencies['@smoothbricks/cli']).toBe('workspace:*');
|
|
@@ -97,8 +108,12 @@ describe('tool configuration validation', () => {
|
|
|
97
108
|
'eslint-stdout': await currentEslintStdoutRange(),
|
|
98
109
|
nx: '24.0.0',
|
|
99
110
|
prettier: '^3.7.0',
|
|
100
|
-
ttsc: '
|
|
111
|
+
ttsc: '0.19.3',
|
|
101
112
|
typescript: '^6.0.0',
|
|
113
|
+
'@typescript/native': 'npm:typescript@^7.0.2',
|
|
114
|
+
},
|
|
115
|
+
patchedDependencies: {
|
|
116
|
+
'ttsc@0.19.3': 'patches/ttsc@0.19.3.patch',
|
|
102
117
|
},
|
|
103
118
|
});
|
|
104
119
|
await writeJson(join(root, 'tooling/package.json'), {
|
|
@@ -128,18 +143,17 @@ describe('tool configuration validation', () => {
|
|
|
128
143
|
`,
|
|
129
144
|
);
|
|
130
145
|
|
|
131
|
-
expect(validateToolConfig(root)).toBe(0);
|
|
146
|
+
expect(await validateToolConfig(root)).toBe(0);
|
|
132
147
|
} finally {
|
|
133
148
|
await rm(root, { recursive: true, force: true });
|
|
134
149
|
}
|
|
135
150
|
});
|
|
136
151
|
|
|
137
|
-
it('
|
|
152
|
+
it('bumps consumer CLI pin to npm latest even when the running CLI is a linked prerelease', async () => {
|
|
138
153
|
const root = await mkdtemp(join(tmpdir(), 'smoo-tool-validation-'));
|
|
139
154
|
try {
|
|
140
155
|
const configuredRange = '^0.10.1';
|
|
141
|
-
const
|
|
142
|
-
const expectedRange = runningRange.includes('-') ? configuredRange : runningRange;
|
|
156
|
+
const expectedRange = `^${registryLatestTags['@smoothbricks/cli']}`;
|
|
143
157
|
await writeJson(join(root, 'package.json'), {
|
|
144
158
|
name: '@fixture/app',
|
|
145
159
|
version: '0.0.0',
|
|
@@ -152,7 +166,7 @@ describe('tool configuration validation', () => {
|
|
|
152
166
|
dependencies: { '@smoothbricks/cli': configuredRange },
|
|
153
167
|
});
|
|
154
168
|
|
|
155
|
-
const context = readToolContext(root);
|
|
169
|
+
const context = await readToolContext(root);
|
|
156
170
|
expect(context.policy.cliDependencyRange).toBe(expectedRange);
|
|
157
171
|
applyToolingPackageDefaults(root, context.policy);
|
|
158
172
|
|
|
@@ -172,7 +186,7 @@ describe('tool configuration validation', () => {
|
|
|
172
186
|
private: true,
|
|
173
187
|
workspaces: ['packages/*', 'tooling'],
|
|
174
188
|
});
|
|
175
|
-
applyToolingPackageDefaults(root, readToolContext(root).policy);
|
|
189
|
+
applyToolingPackageDefaults(root, (await readToolContext(root)).policy);
|
|
176
190
|
|
|
177
191
|
const toolingPackage = JSON.parse(await readFile(join(root, 'tooling/package.json'), 'utf8'));
|
|
178
192
|
expect(toolingPackage.name).toBe('@smoothbricks/tooling');
|
|
@@ -196,12 +210,12 @@ describe('tool configuration validation', () => {
|
|
|
196
210
|
eslint: '^10.0.0',
|
|
197
211
|
nx: '24.0.0',
|
|
198
212
|
prettier: '^3.7.0',
|
|
199
|
-
ttsc: '
|
|
213
|
+
ttsc: '0.19.3',
|
|
200
214
|
typescript: '^6.0.0',
|
|
201
215
|
},
|
|
202
216
|
});
|
|
203
217
|
|
|
204
|
-
const context = readToolContext(root);
|
|
218
|
+
const context = await readToolContext(root);
|
|
205
219
|
await applyRootDevDependencyDefaults(root, context);
|
|
206
220
|
applyToolingPackageDefaults(root, context.policy);
|
|
207
221
|
|
|
@@ -209,6 +223,8 @@ describe('tool configuration validation', () => {
|
|
|
209
223
|
const toolingPackage = JSON.parse(await readFile(join(root, 'tooling/package.json'), 'utf8'));
|
|
210
224
|
expect(rootPackage.devDependencies['eslint-stdout']).toBe(await currentEslintStdoutRange());
|
|
211
225
|
expect(rootPackage.devDependencies['@smoothbricks/nx-plugin']).toBe(await currentNxPluginRange());
|
|
226
|
+
expect(rootPackage.devDependencies['@typescript/native']).toBe('npm:typescript@^7.0.2');
|
|
227
|
+
expect(rootPackage.patchedDependencies?.['ttsc@0.19.3']).toBe('patches/ttsc@0.19.3.patch');
|
|
212
228
|
expect(toolingPackage.name).toBe('@fixture/tooling');
|
|
213
229
|
expect(toolingPackage.dependencies['@smoothbricks/cli']).toBe(await currentCliRange());
|
|
214
230
|
} finally {
|
|
@@ -218,8 +234,7 @@ describe('tool configuration validation', () => {
|
|
|
218
234
|
});
|
|
219
235
|
|
|
220
236
|
async function currentCliRange(): Promise<string> {
|
|
221
|
-
|
|
222
|
-
return `^${pkg.version}`;
|
|
237
|
+
return `^${registryLatestTags['@smoothbricks/cli']}`;
|
|
223
238
|
}
|
|
224
239
|
|
|
225
240
|
async function currentEslintStdoutRange(): Promise<string> {
|
|
@@ -235,8 +250,15 @@ const mockRegistryFetch = Object.assign(
|
|
|
235
250
|
if (!versions) {
|
|
236
251
|
return Promise.resolve(new Response('{}', { status: 404 }));
|
|
237
252
|
}
|
|
253
|
+
const body: Record<string, unknown> = {
|
|
254
|
+
versions: Object.fromEntries(versions.map((version) => [version, {}])),
|
|
255
|
+
};
|
|
256
|
+
const latest = registryLatestTags[packageName];
|
|
257
|
+
if (latest) {
|
|
258
|
+
body['dist-tags'] = { latest };
|
|
259
|
+
}
|
|
238
260
|
return Promise.resolve(
|
|
239
|
-
new Response(JSON.stringify(
|
|
261
|
+
new Response(JSON.stringify(body), {
|
|
240
262
|
status: 200,
|
|
241
263
|
headers: { 'content-type': 'application/json' },
|
|
242
264
|
}),
|
|
@@ -32,6 +32,7 @@ export interface ToolContext {
|
|
|
32
32
|
|
|
33
33
|
interface RegistryPackument {
|
|
34
34
|
versions: Record<string, unknown>;
|
|
35
|
+
'dist-tags'?: Record<string, string>;
|
|
35
36
|
}
|
|
36
37
|
|
|
37
38
|
const isRegistryPackument = typia.createIs<RegistryPackument>();
|
|
@@ -55,10 +56,14 @@ const rootDevDependencies: RequiredDependency[] = [
|
|
|
55
56
|
},
|
|
56
57
|
{ name: 'nx', fallbackVersion: '23.1.0', minimumVersion: '23.1.0' },
|
|
57
58
|
{ name: 'prettier', fallbackVersion: '^3.6.1', minimumVersion: '3.6.0', prefix: '^' },
|
|
58
|
-
|
|
59
|
+
// Exact pin: Bun patchedDependencies keys are version-locked (see patches/ttsc@0.19.3.patch).
|
|
60
|
+
{ name: 'ttsc', fallbackVersion: '0.19.3' },
|
|
59
61
|
// Nx and typescript-eslint still load the TypeScript JS API (6.x).
|
|
60
62
|
// Compilation is exclusively delegated to ttsc by the Nx plugin targets.
|
|
61
|
-
{ name: 'typescript', fallbackVersion: '^
|
|
63
|
+
{ name: 'typescript', fallbackVersion: '^6.0.3', minimumVersion: '6.0.0', prefix: '^' },
|
|
64
|
+
// TS7 native compiler for ttsc (TTSC_TSGO_BINARY). Not the unscoped API package.
|
|
65
|
+
// npm alias form required — Bun cannot install @typescript/typescript6 cleanly (bun#33834).
|
|
66
|
+
{ name: '@typescript/native', fallbackVersion: 'npm:typescript@^7.0.2' },
|
|
62
67
|
];
|
|
63
68
|
|
|
64
69
|
const cliPackageName = '@smoothbricks/cli';
|
|
@@ -70,17 +75,21 @@ const requiredDevenvPackages = ['bun', 'git', 'git-format-staged', 'jq', 'alejan
|
|
|
70
75
|
// never against a version template here.
|
|
71
76
|
const nodePackagePattern = /(^|\s)nodejs(_\d+|_latest)?(\s|#|$)/m;
|
|
72
77
|
|
|
78
|
+
const TTSC_PATCHED_DEPENDENCY_KEY = 'ttsc@0.19.3';
|
|
79
|
+
const TTSC_PATCH_PATH = 'patches/ttsc@0.19.3.patch';
|
|
80
|
+
|
|
73
81
|
export async function applyToolConfigDefaults(root: string): Promise<void> {
|
|
74
|
-
const context = readToolContext(root);
|
|
82
|
+
const context = await readToolContext(root);
|
|
75
83
|
await applyRootPackageToolDefaults(root, context);
|
|
76
84
|
applyToolingPackageDefaults(root, context.policy);
|
|
77
85
|
applyDevenvPackageDefaults(root);
|
|
78
86
|
}
|
|
79
87
|
|
|
80
|
-
export function validateToolConfig(root: string): number {
|
|
81
|
-
const context = readToolContext(root);
|
|
88
|
+
export async function validateToolConfig(root: string): Promise<number> {
|
|
89
|
+
const context = await readToolContext(root);
|
|
82
90
|
return (
|
|
83
91
|
validateRootDevDependencies(context.policy, context.rootPackage) +
|
|
92
|
+
validateTtscPatchedDependency(context.rootPackage) +
|
|
84
93
|
validateToolingPackage(root, context.policy) +
|
|
85
94
|
validateToolingWorkspace(context.rootPackage) +
|
|
86
95
|
validateDevenvPackages(root)
|
|
@@ -104,6 +113,7 @@ export async function applyRootDevDependencyDefaults(root: string, context: Tool
|
|
|
104
113
|
if (delete devDependencies['@smoothbricks/cli']) {
|
|
105
114
|
changed = true;
|
|
106
115
|
}
|
|
116
|
+
changed = ensureTtscPatchedDependency(pkg) || changed;
|
|
107
117
|
if (changed) {
|
|
108
118
|
writeJsonObject(join(root, 'package.json'), pkg);
|
|
109
119
|
console.log('updated package.json workspace tool dependencies');
|
|
@@ -119,6 +129,7 @@ async function applyRootPackageToolDefaults(root: string, context: ToolContext):
|
|
|
119
129
|
}
|
|
120
130
|
let dependencyChanged = false;
|
|
121
131
|
let workspaceChanged = false;
|
|
132
|
+
let patchChanged = false;
|
|
122
133
|
const devDependencies = ensureDependencyMap(pkg, 'devDependencies');
|
|
123
134
|
for (const dependency of rootDevDependencies) {
|
|
124
135
|
const current = devDependencies[dependency.name];
|
|
@@ -131,7 +142,8 @@ async function applyRootPackageToolDefaults(root: string, context: ToolContext):
|
|
|
131
142
|
dependencyChanged = true;
|
|
132
143
|
}
|
|
133
144
|
workspaceChanged = addWorkspacePattern(pkg, 'tooling');
|
|
134
|
-
|
|
145
|
+
patchChanged = ensureTtscPatchedDependency(pkg);
|
|
146
|
+
if (dependencyChanged || workspaceChanged || patchChanged) {
|
|
135
147
|
writeJsonObject(join(root, 'package.json'), pkg);
|
|
136
148
|
}
|
|
137
149
|
console.log(
|
|
@@ -144,6 +156,11 @@ async function applyRootPackageToolDefaults(root: string, context: ToolContext):
|
|
|
144
156
|
? 'updated package.json tooling workspace'
|
|
145
157
|
: 'unchanged package.json tooling workspace',
|
|
146
158
|
);
|
|
159
|
+
console.log(
|
|
160
|
+
patchChanged
|
|
161
|
+
? 'updated package.json patchedDependencies for ttsc'
|
|
162
|
+
: 'unchanged package.json patchedDependencies for ttsc',
|
|
163
|
+
);
|
|
147
164
|
}
|
|
148
165
|
|
|
149
166
|
export function applyToolingPackageDefaults(root: string, policy: ToolPolicy): void {
|
|
@@ -235,6 +252,26 @@ export function validateRootDevDependencies(policy: ToolPolicy, rootPackage: Pac
|
|
|
235
252
|
return failures;
|
|
236
253
|
}
|
|
237
254
|
|
|
255
|
+
function ensureTtscPatchedDependency(pkg: PackageJson): boolean {
|
|
256
|
+
const patched = ensureDependencyMap(pkg, 'patchedDependencies');
|
|
257
|
+
return setStringProperty(patched, TTSC_PATCHED_DEPENDENCY_KEY, TTSC_PATCH_PATH);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function validateTtscPatchedDependency(rootPackage: PackageJson | null): number {
|
|
261
|
+
if (!rootPackage) {
|
|
262
|
+
return 0;
|
|
263
|
+
}
|
|
264
|
+
const actual = rootPackage.patchedDependencies?.[TTSC_PATCHED_DEPENDENCY_KEY];
|
|
265
|
+
if (actual === TTSC_PATCH_PATH) {
|
|
266
|
+
return 0;
|
|
267
|
+
}
|
|
268
|
+
console.error(
|
|
269
|
+
`package.json patchedDependencies.${TTSC_PATCHED_DEPENDENCY_KEY} must be ${TTSC_PATCH_PATH}` +
|
|
270
|
+
(typeof actual === 'string' ? `; found ${actual}` : ' (required for declaration-emit fix)'),
|
|
271
|
+
);
|
|
272
|
+
return 1;
|
|
273
|
+
}
|
|
274
|
+
|
|
238
275
|
export function validateToolingPackage(root: string, policy: ToolPolicy): number {
|
|
239
276
|
const path = join(root, 'tooling', 'package.json');
|
|
240
277
|
const pkg = readJsonObject(path);
|
|
@@ -331,6 +368,10 @@ function satisfiesDependencyPolicy(policy: ToolPolicy, version: string, dependen
|
|
|
331
368
|
if (workspaceDependencyExpected(policy, dependency)) {
|
|
332
369
|
return version === 'workspace:*';
|
|
333
370
|
}
|
|
371
|
+
// Dual-package native compiler: must stay an npm:typescript@7 alias, never unscoped "typescript".
|
|
372
|
+
if (dependency.name === '@typescript/native') {
|
|
373
|
+
return isTypeScriptNativeAlias(version);
|
|
374
|
+
}
|
|
334
375
|
if (dependency.minimumVersion === undefined) {
|
|
335
376
|
return version === dependency.fallbackVersion;
|
|
336
377
|
}
|
|
@@ -342,37 +383,41 @@ function satisfiesDependencyPolicy(policy: ToolPolicy, version: string, dependen
|
|
|
342
383
|
return compareVersions(parsed, minimum) >= 0;
|
|
343
384
|
}
|
|
344
385
|
|
|
386
|
+
function isTypeScriptNativeAlias(version: string): boolean {
|
|
387
|
+
// Accept npm:typescript@7, npm:typescript@^7.0.2, npm:typescript@~7.0.2, etc.
|
|
388
|
+
return /^npm:typescript@(?:\^|~)?7(?:\.|$)/.test(version.trim());
|
|
389
|
+
}
|
|
390
|
+
|
|
345
391
|
function formatExpectedDependency(policy: ToolPolicy, dependency: RequiredDependency): string {
|
|
346
392
|
if (workspaceDependencyExpected(policy, dependency)) {
|
|
347
393
|
return 'workspace:*';
|
|
348
394
|
}
|
|
395
|
+
if (dependency.name === '@typescript/native') {
|
|
396
|
+
return 'npm:typescript@^7 (TypeScript 7 native compiler alias for ttsc)';
|
|
397
|
+
}
|
|
349
398
|
return dependency.minimumVersion ? `>= ${dependency.minimumVersion}` : dependency.fallbackVersion;
|
|
350
399
|
}
|
|
351
400
|
|
|
352
|
-
export function readToolContext(root: string): ToolContext {
|
|
401
|
+
export async function readToolContext(root: string): Promise<ToolContext> {
|
|
353
402
|
const rootPackage = readPackageJsonObject(join(root, 'package.json'));
|
|
354
403
|
const toolingPackage = readJsonObject(join(root, 'tooling', 'package.json'));
|
|
355
404
|
const configuredCliRange = toolingPackage?.dependencies?.[cliPackageName];
|
|
356
405
|
return {
|
|
357
406
|
rootPackage,
|
|
358
|
-
policy: toolPolicy(rootPackage, typeof configuredCliRange === 'string' ? configuredCliRange : null),
|
|
407
|
+
policy: await toolPolicy(rootPackage, typeof configuredCliRange === 'string' ? configuredCliRange : null),
|
|
359
408
|
};
|
|
360
409
|
}
|
|
361
410
|
|
|
362
|
-
function toolPolicy(rootPackage: PackageJson | null, configuredCliRange: string | null): ToolPolicy {
|
|
411
|
+
async function toolPolicy(rootPackage: PackageJson | null, configuredCliRange: string | null): Promise<ToolPolicy> {
|
|
363
412
|
const name = rootPackage?.name ?? null;
|
|
364
413
|
const isCodebase = isSmoothBricksCodebasePackageName(name ?? undefined);
|
|
365
414
|
const toolingName = toolingPackageName(name);
|
|
366
|
-
// A locally linked prerelease can be newer than every published CLI. Keep an existing
|
|
367
|
-
// installable range instead of rewriting the consumer manifest to an unpublished version.
|
|
368
|
-
const publishedCliRange =
|
|
369
|
-
cliPackageVersion.includes('-') && configuredCliRange && parseVersion(configuredCliRange)
|
|
370
|
-
? configuredCliRange
|
|
371
|
-
: `^${cliPackageVersion}`;
|
|
372
415
|
return {
|
|
373
416
|
isSmoothBricksCodebase: isCodebase,
|
|
374
417
|
toolingPackageName: toolingName,
|
|
375
|
-
|
|
418
|
+
// Consumers pin the latest *published* CLI. Running a linked prerelease must not
|
|
419
|
+
// freeze tooling/package.json on an older range or rewrite it to an unpublished -next.
|
|
420
|
+
cliDependencyRange: isCodebase ? 'workspace:*' : await resolvePublishedCliDependencyRange(configuredCliRange),
|
|
376
421
|
};
|
|
377
422
|
}
|
|
378
423
|
|
|
@@ -397,6 +442,59 @@ async function resolveDependencyVersion(policy: ToolPolicy, dependency: Required
|
|
|
397
442
|
return `${dependency.prefix ?? ''}${latest ?? stripRangePrefix(dependency.fallbackVersion)}`;
|
|
398
443
|
}
|
|
399
444
|
|
|
445
|
+
/**
|
|
446
|
+
* Consumer monorepos pin `@smoothbricks/cli` to the latest stable release.
|
|
447
|
+
* Prefer npm `dist-tags.latest`. Never write a running `-next` package version into
|
|
448
|
+
* consumer manifests; never leave an older pin just because the running CLI is prerelease.
|
|
449
|
+
*/
|
|
450
|
+
async function resolvePublishedCliDependencyRange(configuredCliRange: string | null): Promise<string> {
|
|
451
|
+
const latest = await fetchLatestStableVersion(cliPackageName);
|
|
452
|
+
if (latest) {
|
|
453
|
+
return `^${latest}`;
|
|
454
|
+
}
|
|
455
|
+
// Registry unavailable: if the running CLI is a stable publish, use it.
|
|
456
|
+
if (!cliPackageVersion.includes('-')) {
|
|
457
|
+
return `^${cliPackageVersion}`;
|
|
458
|
+
}
|
|
459
|
+
// Linked prerelease + offline down: keep an existing installable stable pin.
|
|
460
|
+
if (configuredCliRange && parseVersion(configuredCliRange)) {
|
|
461
|
+
return configuredCliRange;
|
|
462
|
+
}
|
|
463
|
+
// Last resort: caret of the prerelease base (0.10.5-next.0 → ^0.10.5) so manifests stay valid.
|
|
464
|
+
const base = parseVersion(cliPackageVersion);
|
|
465
|
+
return base ? `^${base.major}.${base.minor}.${base.patch}` : `^${cliPackageVersion}`;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
async function fetchLatestStableVersion(packageName: string): Promise<string | null> {
|
|
469
|
+
const url = `https://registry.npmjs.org/${encodeURIComponent(packageName).replace('%40', '@')}`;
|
|
470
|
+
const response = await fetch(url, { headers: { accept: 'application/vnd.npm.install-v1+json' } });
|
|
471
|
+
if (!response.ok) {
|
|
472
|
+
return null;
|
|
473
|
+
}
|
|
474
|
+
const body: unknown = await response.json();
|
|
475
|
+
if (!isRegistryPackument(body)) {
|
|
476
|
+
return null;
|
|
477
|
+
}
|
|
478
|
+
const tagged = body['dist-tags']?.latest;
|
|
479
|
+
if (typeof tagged === 'string' && parseVersion(tagged) && !tagged.includes('-')) {
|
|
480
|
+
return tagged;
|
|
481
|
+
}
|
|
482
|
+
let latest: Version | null = null;
|
|
483
|
+
for (const raw of Object.keys(body.versions)) {
|
|
484
|
+
if (raw.includes('-')) {
|
|
485
|
+
continue;
|
|
486
|
+
}
|
|
487
|
+
const version = parseVersion(raw);
|
|
488
|
+
if (!version) {
|
|
489
|
+
continue;
|
|
490
|
+
}
|
|
491
|
+
if (!latest || compareVersions(version, latest) > 0) {
|
|
492
|
+
latest = version;
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
return latest ? `${latest.major}.${latest.minor}.${latest.patch}` : null;
|
|
496
|
+
}
|
|
497
|
+
|
|
400
498
|
async function fetchLatestPatchVersion(packageName: string, minimumVersion: string): Promise<string | null> {
|
|
401
499
|
const minorRange = sameMajorMinorRange(minimumVersion);
|
|
402
500
|
const url = `https://registry.npmjs.org/${encodeURIComponent(packageName).replace('%40', '@')}`;
|
package/src/release/index.ts
CHANGED
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
} from '../lib/json.js';
|
|
21
21
|
import { decode, run, runInteractiveStatus, runResult } from '../lib/run.js';
|
|
22
22
|
import { listReleasePackages, readPackageJson, repositoryInfo } from '../lib/workspace.js';
|
|
23
|
+
import { syncBunLockfileVersions } from '../monorepo/lockfile.js';
|
|
23
24
|
import { readPackedPackageJson, validatePackedWorkspaceDependencies } from '../monorepo/packed-manifest.js';
|
|
24
25
|
import {
|
|
25
26
|
type BootstrapNpmPackagesOptions,
|
|
@@ -529,6 +530,10 @@ async function listUnpublishedPackages(root: string, packages: ReleasePackage[])
|
|
|
529
530
|
async function publishPackedPackage(root: string, pkg: ReleasePackage, tag: string, dryRun: boolean): Promise<void> {
|
|
530
531
|
const tempDir = await mkdtemp(join(tmpdir(), 'smoo-publish-'));
|
|
531
532
|
const tarball = join(tempDir, `${safeTarballPrefix(pkg.name)}-${pkg.version}.tgz`);
|
|
533
|
+
// bun pm pack resolves workspace:* from bun.lock. Day-to-day lock keeps -next
|
|
534
|
+
// (install/CI). For publish, temporarily rewrite unpublished -next entries to
|
|
535
|
+
// the last stable tag so the tarball embeds installable versions, then restore.
|
|
536
|
+
syncBunLockfileVersions(root, { mode: 'publish', log: true });
|
|
532
537
|
try {
|
|
533
538
|
console.log(`${pkg.name}@${pkg.version}: packing with bun pm pack`);
|
|
534
539
|
await run('bun', ['pm', 'pack', '--filename', tarball, '--ignore-scripts', '--quiet'], join(root, pkg.path));
|
|
@@ -560,6 +565,7 @@ async function publishPackedPackage(root: string, pkg: ReleasePackage, tag: stri
|
|
|
560
565
|
},
|
|
561
566
|
);
|
|
562
567
|
} finally {
|
|
568
|
+
syncBunLockfileVersions(root, { mode: 'install', log: true });
|
|
563
569
|
await rm(tempDir, { recursive: true, force: true });
|
|
564
570
|
}
|
|
565
571
|
}
|