@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
package/src/monorepo/lockfile.ts
CHANGED
|
@@ -8,9 +8,10 @@ export interface SyncBunLockfileVersionsOptions {
|
|
|
8
8
|
/** `git add` bun.lock when versions were resynced. */
|
|
9
9
|
stage?: boolean;
|
|
10
10
|
/**
|
|
11
|
-
* `install` (default
|
|
11
|
+
* `install` (default): lockfile must match package.json (including -next).
|
|
12
12
|
* `publish`: rewrite unpublished prerelease package.json versions to the last
|
|
13
13
|
* stable git tag so `bun pm pack` embeds installable dependency versions.
|
|
14
|
+
* Only use publish mode around pack; never leave the tree in publish mode.
|
|
14
15
|
*/
|
|
15
16
|
mode?: 'install' | 'publish';
|
|
16
17
|
}
|
|
@@ -34,7 +35,7 @@ export interface SyncBunLockfileVersionsOptions {
|
|
|
34
35
|
// rewrites lockfile back to package.json and frozen CI then fails.
|
|
35
36
|
export function syncBunLockfileVersions(root: string, options: SyncBunLockfileVersionsOptions = {}): number {
|
|
36
37
|
const log = options.log ?? true;
|
|
37
|
-
const mode = options.mode ?? '
|
|
38
|
+
const mode = options.mode ?? 'install';
|
|
38
39
|
const lockfilePath = join(root, 'bun.lock');
|
|
39
40
|
if (!existsSync(lockfilePath)) {
|
|
40
41
|
throw new Error('bun.lock not found');
|
|
@@ -5,13 +5,16 @@ import { join } from 'node:path';
|
|
|
5
5
|
import { LINUX_PLATFORM_TARGET_GLOBS, PLATFORM_TARGET_GLOBS } from '@smoothbricks/nx-plugin/workspace-config-policy';
|
|
6
6
|
import fc from 'fast-check';
|
|
7
7
|
import {
|
|
8
|
+
deployTargetInfoFromProjects,
|
|
8
9
|
extractInlineLocalBlocksForTest,
|
|
9
10
|
INLINE_LOCAL_BEGIN,
|
|
10
11
|
INLINE_LOCAL_END,
|
|
11
12
|
LOCAL_SECTION_MARKER,
|
|
13
|
+
type NxGraphProjectNode,
|
|
12
14
|
platformTargetGlobsForTest,
|
|
13
15
|
reinsertInlineLocalBlocksForTest,
|
|
14
16
|
splitLocalSectionForTest,
|
|
17
|
+
targetNamesFromProjects,
|
|
15
18
|
} from './managed-files.js';
|
|
16
19
|
|
|
17
20
|
const MANAGED = '# managed content\npath merge=driver\n';
|
|
@@ -172,6 +175,52 @@ describe('managed publish platform discovery', () => {
|
|
|
172
175
|
});
|
|
173
176
|
});
|
|
174
177
|
|
|
178
|
+
describe('nx graph project helpers', () => {
|
|
179
|
+
const sampleNodes: Record<string, NxGraphProjectNode> = {
|
|
180
|
+
lib: {
|
|
181
|
+
data: {
|
|
182
|
+
targets: {
|
|
183
|
+
build: {},
|
|
184
|
+
'bundle-linux': {},
|
|
185
|
+
lint: {},
|
|
186
|
+
},
|
|
187
|
+
},
|
|
188
|
+
},
|
|
189
|
+
app: {
|
|
190
|
+
data: {
|
|
191
|
+
targets: {
|
|
192
|
+
deploy: {
|
|
193
|
+
options: { command: 'echo no' },
|
|
194
|
+
configurations: {
|
|
195
|
+
staging: { command: 'wrangler deploy --env staging' },
|
|
196
|
+
production: { options: { command: 'wrangler deploy --env production' } },
|
|
197
|
+
},
|
|
198
|
+
},
|
|
199
|
+
'package-macos': {},
|
|
200
|
+
},
|
|
201
|
+
},
|
|
202
|
+
},
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
it('collects target names from graph nodes without project names', () => {
|
|
206
|
+
expect(targetNamesFromProjects(sampleNodes).sort()).toEqual(
|
|
207
|
+
['build', 'bundle-linux', 'deploy', 'lint', 'package-macos'].sort(),
|
|
208
|
+
);
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
it('detects deploy configurations and cloudflare provider from graph nodes', () => {
|
|
212
|
+
expect(deployTargetInfoFromProjects(sampleNodes, 'staging')).toEqual({
|
|
213
|
+
exists: true,
|
|
214
|
+
provider: 'cloudflare',
|
|
215
|
+
});
|
|
216
|
+
expect(deployTargetInfoFromProjects(sampleNodes, 'production')).toEqual({
|
|
217
|
+
exists: true,
|
|
218
|
+
provider: 'cloudflare',
|
|
219
|
+
});
|
|
220
|
+
expect(deployTargetInfoFromProjects(sampleNodes, 'preview')).toEqual({ exists: false });
|
|
221
|
+
});
|
|
222
|
+
});
|
|
223
|
+
|
|
175
224
|
describe('managed cache actions', () => {
|
|
176
225
|
it('renders the checked-in action copies from their managed templates', async () => {
|
|
177
226
|
for (const action of CACHE_ACTIONS) {
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { execFileSync } from 'node:child_process';
|
|
2
1
|
import { appendFileSync, existsSync, lstatSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
3
2
|
import { dirname, join, resolve } from 'node:path';
|
|
4
3
|
import { fileURLToPath } from 'node:url';
|
|
5
4
|
import { PLATFORM_TARGET_GLOBS } from '@smoothbricks/nx-plugin/workspace-config-policy';
|
|
6
|
-
|
|
5
|
+
// Nx package exports are CLI-oriented; this is the module the CLI uses for graph + daemon IPC.
|
|
6
|
+
import { createProjectGraphAsync } from 'nx/src/project-graph/project-graph.js';
|
|
7
|
+
import type { PackageJson } from '../lib/json.js';
|
|
7
8
|
import { listReleasePackages, readPackageJson } from '../lib/workspace.js';
|
|
8
9
|
import { renderCiWorkflowYaml } from './ci-workflow.js';
|
|
9
10
|
import { renderPublishWorkflowYaml } from './publish-workflow.js';
|
|
@@ -137,6 +138,7 @@ interface ManagedFileContext {
|
|
|
137
138
|
stagingDeployProvider?: 'cloudflare';
|
|
138
139
|
productionDeployProvider?: 'cloudflare';
|
|
139
140
|
ciPushBranches: string[];
|
|
141
|
+
ciRunsOn: string | string[];
|
|
140
142
|
nodeModulesCacheKey: string;
|
|
141
143
|
repoName: string;
|
|
142
144
|
platformTargetGlobs: string[];
|
|
@@ -199,6 +201,11 @@ const managedFiles: ManagedFile[] = [
|
|
|
199
201
|
target: 'tooling/direnv/merge-newer-pins.sh',
|
|
200
202
|
executable: true,
|
|
201
203
|
},
|
|
204
|
+
{
|
|
205
|
+
kind: 'raw',
|
|
206
|
+
source: 'patches/ttsc@0.19.3.patch',
|
|
207
|
+
target: 'patches/ttsc@0.19.3.patch',
|
|
208
|
+
},
|
|
202
209
|
{
|
|
203
210
|
kind: 'generated',
|
|
204
211
|
source: 'ci-workflow',
|
|
@@ -249,8 +256,8 @@ const managedFiles: ManagedFile[] = [
|
|
|
249
256
|
|
|
250
257
|
const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..', '..');
|
|
251
258
|
|
|
252
|
-
export function applyManagedFiles(root: string, mode: 'update' | 'check' | 'diff'): FileResult[] {
|
|
253
|
-
const context = getManagedFileContext(root);
|
|
259
|
+
export async function applyManagedFiles(root: string, mode: 'update' | 'check' | 'diff'): Promise<FileResult[]> {
|
|
260
|
+
const context = await getManagedFileContext(root);
|
|
254
261
|
return managedFiles.map((file) => applyManagedFile(root, file, mode, context));
|
|
255
262
|
}
|
|
256
263
|
|
|
@@ -301,6 +308,7 @@ function getManagedContent(file: ManagedFile, context: ManagedFileContext): stri
|
|
|
301
308
|
deploy: context.hasStagingDeployTargets,
|
|
302
309
|
deployProvider: context.stagingDeployProvider,
|
|
303
310
|
pushBranches: context.ciPushBranches,
|
|
311
|
+
runsOn: context.ciRunsOn,
|
|
304
312
|
});
|
|
305
313
|
}
|
|
306
314
|
if (file.source === 'publish-workflow') {
|
|
@@ -309,6 +317,7 @@ function getManagedContent(file: ManagedFile, context: ManagedFileContext): stri
|
|
|
309
317
|
deployProvider: context.productionDeployProvider,
|
|
310
318
|
repoName: context.repoName,
|
|
311
319
|
platformTargetGlobs: context.platformTargetGlobs,
|
|
320
|
+
runsOn: context.ciRunsOn,
|
|
312
321
|
});
|
|
313
322
|
}
|
|
314
323
|
throw new Error(`Unknown generated managed file source ${file.source}`);
|
|
@@ -322,13 +331,16 @@ function getManagedContent(file: ManagedFile, context: ManagedFileContext): stri
|
|
|
322
331
|
return renderTemplate(context, content);
|
|
323
332
|
}
|
|
324
333
|
|
|
325
|
-
function getManagedFileContext(root: string): ManagedFileContext {
|
|
334
|
+
async function getManagedFileContext(root: string): Promise<ManagedFileContext> {
|
|
326
335
|
const packageJson = readPackageJson(join(root, 'package.json'));
|
|
327
336
|
const repoName = packageJson?.name ?? 'monorepo';
|
|
328
337
|
const ciPushBranches = getCiPushBranches(packageJson?.json);
|
|
329
|
-
const
|
|
330
|
-
|
|
331
|
-
const
|
|
338
|
+
const ciRunsOn = getCiRunsOn(packageJson?.json);
|
|
339
|
+
// In-process Nx API โ daemon socket (no second Node/`nx` CLI process).
|
|
340
|
+
const nxProjects = await loadNxGraphProjects(root);
|
|
341
|
+
const stagingDeploy = deployTargetInfoFromProjects(nxProjects, 'staging');
|
|
342
|
+
const productionDeploy = deployTargetInfoFromProjects(nxProjects, 'production');
|
|
343
|
+
const platformTargetGlobs = platformTargetGlobsForTest(targetNamesFromProjects(nxProjects));
|
|
332
344
|
const nodeModulesCacheKey = existsSync(join(root, 'bun.lock'))
|
|
333
345
|
? `$${"{{ hashFiles('bun.lock', 'package.json', 'packages/*/package.json') }}"}`
|
|
334
346
|
: `$${"{{ hashFiles('bun.lockb', 'package.json', 'packages/*/package.json') }}"}`;
|
|
@@ -339,6 +351,7 @@ function getManagedFileContext(root: string): ManagedFileContext {
|
|
|
339
351
|
stagingDeployProvider: stagingDeploy.provider,
|
|
340
352
|
productionDeployProvider: productionDeploy.provider,
|
|
341
353
|
ciPushBranches,
|
|
354
|
+
ciRunsOn,
|
|
342
355
|
nodeModulesCacheKey,
|
|
343
356
|
repoName,
|
|
344
357
|
platformTargetGlobs,
|
|
@@ -353,81 +366,109 @@ export function platformTargetGlobsForTest(targetNames: Iterable<string>): strin
|
|
|
353
366
|
});
|
|
354
367
|
}
|
|
355
368
|
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
369
|
+
/** Resolved Nx project node as returned under `graph.nodes` / project-graph.json. */
|
|
370
|
+
export type NxGraphProjectNode = {
|
|
371
|
+
data?: { targets?: Record<string, NxGraphTarget> };
|
|
372
|
+
targets?: Record<string, NxGraphTarget>;
|
|
373
|
+
};
|
|
374
|
+
|
|
375
|
+
type NxGraphTarget = {
|
|
376
|
+
command?: string;
|
|
377
|
+
options?: { command?: string };
|
|
378
|
+
configurations?: Record<string, { command?: string; options?: { command?: string } }>;
|
|
379
|
+
};
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* Load resolved project nodes via Nx's programmatic API.
|
|
383
|
+
*
|
|
384
|
+
* `createProjectGraphAsync` is what the CLI uses: with the daemon up it speaks the
|
|
385
|
+
* daemon unix socket instead of rebuilding the graph in a fresh Node process.
|
|
386
|
+
* Spawning `nx graph` / `nx show` only added CLI boot + plugin-worker overhead on
|
|
387
|
+
* top of that. smoo runs under Bun; Bun can import this CJS module directly.
|
|
388
|
+
*/
|
|
389
|
+
async function loadNxGraphProjects(root: string): Promise<Record<string, NxGraphProjectNode>> {
|
|
390
|
+
const prevCwd = process.cwd();
|
|
391
|
+
process.chdir(root);
|
|
392
|
+
try {
|
|
393
|
+
const graph = (await createProjectGraphAsync()) as {
|
|
394
|
+
nodes: Record<string, { data?: { targets?: Record<string, NxGraphTarget> } }>;
|
|
395
|
+
};
|
|
396
|
+
const out: Record<string, NxGraphProjectNode> = {};
|
|
397
|
+
for (const [name, node] of Object.entries(graph.nodes)) {
|
|
398
|
+
out[name] = { data: { targets: node.data?.targets ?? {} } };
|
|
379
399
|
}
|
|
400
|
+
return out;
|
|
401
|
+
} finally {
|
|
402
|
+
process.chdir(prevCwd);
|
|
380
403
|
}
|
|
381
|
-
return [...targetNames];
|
|
382
404
|
}
|
|
383
405
|
|
|
384
|
-
function
|
|
385
|
-
return
|
|
386
|
-
.replaceAll('{{REPO_NAME}}', context.repoName)
|
|
387
|
-
.replaceAll('__SMOO_CI_PUSH_BRANCHES__', renderYamlFlowList(context.ciPushBranches))
|
|
388
|
-
.replaceAll('{{NODE_MODULES_CACHE_KEY}}', context.nodeModulesCacheKey);
|
|
406
|
+
function targetsOfProject(node: NxGraphProjectNode): Record<string, NxGraphTarget> {
|
|
407
|
+
return node.data?.targets ?? node.targets ?? {};
|
|
389
408
|
}
|
|
390
409
|
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
if (!projects) {
|
|
399
|
-
return { exists: false };
|
|
410
|
+
/** Test seam: collect target names from a graph.nodes map. */
|
|
411
|
+
export function targetNamesFromProjects(nodes: Record<string, NxGraphProjectNode>): string[] {
|
|
412
|
+
const targetNames = new Set<string>();
|
|
413
|
+
for (const node of Object.values(nodes)) {
|
|
414
|
+
for (const name of Object.keys(targetsOfProject(node))) {
|
|
415
|
+
targetNames.add(name);
|
|
416
|
+
}
|
|
400
417
|
}
|
|
418
|
+
return [...targetNames];
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
/** Test seam: deploy configuration presence/provider from graph nodes. */
|
|
422
|
+
export function deployTargetInfoFromProjects(
|
|
423
|
+
nodes: Record<string, NxGraphProjectNode>,
|
|
424
|
+
configuration: string,
|
|
425
|
+
): DeployTargetInfo {
|
|
401
426
|
let exists = false;
|
|
402
427
|
let provider: DeployTargetInfo['provider'];
|
|
403
|
-
for (const
|
|
404
|
-
const
|
|
405
|
-
if (!
|
|
428
|
+
for (const node of Object.values(nodes)) {
|
|
429
|
+
const info = deployTargetInfoFromTargets(targetsOfProject(node), configuration);
|
|
430
|
+
if (!info.exists) {
|
|
406
431
|
continue;
|
|
407
432
|
}
|
|
408
433
|
exists = true;
|
|
409
|
-
provider ??=
|
|
434
|
+
provider ??= info.provider;
|
|
410
435
|
}
|
|
411
436
|
return { exists, provider };
|
|
412
437
|
}
|
|
413
438
|
|
|
414
|
-
function
|
|
415
|
-
const
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
const projectJson = parseNxProjectJsonText(output);
|
|
421
|
-
const deploy = projectJson?.targets?.deploy;
|
|
422
|
-
const config = deploy?.configurations?.[configuration];
|
|
439
|
+
function deployTargetInfoFromTargets(targets: Record<string, NxGraphTarget>, configuration: string): DeployTargetInfo {
|
|
440
|
+
const deploy = targets.deploy;
|
|
441
|
+
if (!deploy) {
|
|
442
|
+
return { exists: false };
|
|
443
|
+
}
|
|
444
|
+
const config = deploy.configurations?.[configuration];
|
|
423
445
|
if (!config) {
|
|
424
446
|
return { exists: false };
|
|
425
447
|
}
|
|
426
|
-
const commandValue = config.command ?? config.options?.command ?? deploy
|
|
448
|
+
const commandValue = config.command ?? config.options?.command ?? deploy.options?.command ?? deploy.command;
|
|
427
449
|
const command = typeof commandValue === 'string' ? commandValue : '';
|
|
428
450
|
return { exists: true, provider: command.includes('wrangler ') ? 'cloudflare' : undefined };
|
|
429
451
|
}
|
|
430
452
|
|
|
453
|
+
function renderTemplate(context: ManagedFileContext, template: string): string {
|
|
454
|
+
return template
|
|
455
|
+
.replaceAll('{{REPO_NAME}}', context.repoName)
|
|
456
|
+
.replaceAll('__SMOO_CI_PUSH_BRANCHES__', renderYamlFlowList(context.ciPushBranches))
|
|
457
|
+
.replaceAll('{{NODE_MODULES_CACHE_KEY}}', context.nodeModulesCacheKey);
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
function getCiRunsOn(packageJson: PackageJson | null | undefined): string | string[] {
|
|
461
|
+
const configured = packageJson?.smoo?.github?.runsOn;
|
|
462
|
+
if (configured === undefined) {
|
|
463
|
+
return 'ubuntu-latest';
|
|
464
|
+
}
|
|
465
|
+
if (typeof configured === 'string') {
|
|
466
|
+
return configured.length > 0 ? configured : 'ubuntu-latest';
|
|
467
|
+
}
|
|
468
|
+
const labels = configured.filter((label) => label.length > 0);
|
|
469
|
+
return labels.length > 0 ? labels : 'ubuntu-latest';
|
|
470
|
+
}
|
|
471
|
+
|
|
431
472
|
function getCiPushBranches(packageJson: PackageJson | null | undefined): string[] {
|
|
432
473
|
const configured = readCiPushBranches(packageJson);
|
|
433
474
|
return configured.length > 0 ? configured : ['main'];
|
|
@@ -453,8 +494,8 @@ export function printResults(results: FileResult[]): void {
|
|
|
453
494
|
}
|
|
454
495
|
}
|
|
455
496
|
|
|
456
|
-
export function validateManagedFiles(root: string): number {
|
|
457
|
-
const results = applyManagedFiles(root, 'check');
|
|
497
|
+
export async function validateManagedFiles(root: string): Promise<number> {
|
|
498
|
+
const results = await applyManagedFiles(root, 'check');
|
|
458
499
|
printResults(results);
|
|
459
500
|
const failures = results.filter((result) => result.action === 'drifted').length;
|
|
460
501
|
if (failures > 0) {
|
|
@@ -473,8 +514,8 @@ export function validateManagedFiles(root: string): number {
|
|
|
473
514
|
* Under GitHub Actions the drifted-file count is published as the step output
|
|
474
515
|
* `drifted`, so downstream steps gate declaratively instead of parsing logs.
|
|
475
516
|
*/
|
|
476
|
-
export function warnOnManagedFileDrift(root: string): void {
|
|
477
|
-
const results = applyManagedFiles(root, 'check');
|
|
517
|
+
export async function warnOnManagedFileDrift(root: string): Promise<void> {
|
|
518
|
+
const results = await applyManagedFiles(root, 'check');
|
|
478
519
|
printResults(results);
|
|
479
520
|
const drifted = results.filter((result) => result.action === 'drifted');
|
|
480
521
|
if (process.env.GITHUB_OUTPUT) {
|
|
@@ -92,11 +92,11 @@ const packs: MonorepoPack[] = [
|
|
|
92
92
|
// Managed-file drift is derived state (CLI template x pinned version) with
|
|
93
93
|
// its own remediation flow; it warns instead of failing so validation only
|
|
94
94
|
// blocks on actual package issues. See warnOnManagedFileDrift.
|
|
95
|
-
warnOnManagedFileDrift(ctx.root);
|
|
95
|
+
await warnOnManagedFileDrift(ctx.root);
|
|
96
96
|
return (
|
|
97
97
|
runtimeFailures +
|
|
98
98
|
validateRootPackagePolicy(ctx.root) +
|
|
99
|
-
validateToolConfig(ctx.root) +
|
|
99
|
+
(await validateToolConfig(ctx.root)) +
|
|
100
100
|
validateNxProjectNames(ctx.root) +
|
|
101
101
|
validateNxReleaseConfig(ctx.root) +
|
|
102
102
|
validateBunLockfileVersions(ctx.root) +
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
} from '@smoothbricks/nx-plugin/workspace-config-policy';
|
|
8
8
|
import type { Options as PrettierOptions } from 'prettier';
|
|
9
9
|
import { isSmoothBricksCodebasePackageName } from '../lib/cli-package.js';
|
|
10
|
+
import { renderRunsOnLine, type WorkflowRunsOn } from './github-runs-on.js';
|
|
10
11
|
|
|
11
12
|
const PUBLISH_WORKFLOW_FORMAT_OPTIONS = Object.freeze({
|
|
12
13
|
parser: 'yaml',
|
|
@@ -55,6 +56,8 @@ export interface PublishWorkflowDefinitionOptions {
|
|
|
55
56
|
deployProvider?: 'cloudflare';
|
|
56
57
|
repoName?: string;
|
|
57
58
|
platformTargetGlobs?: readonly string[];
|
|
59
|
+
/** Linux jobs only. Default ubuntu-latest. Same smoo.github.runsOn as CI. */
|
|
60
|
+
runsOn?: WorkflowRunsOn;
|
|
58
61
|
}
|
|
59
62
|
|
|
60
63
|
export interface PublishWorkflowInputs {
|
|
@@ -441,7 +444,8 @@ function yamlLinesForStep(step: PublishWorkflowStep, options: PublishWorkflowDef
|
|
|
441
444
|
case PublishWorkflowStepKind.UploadTraceDbs:
|
|
442
445
|
return [
|
|
443
446
|
` - name: ${step.name}`,
|
|
444
|
-
|
|
447
|
+
' # Default is success(); failure() uploads traces only when the job already failed.',
|
|
448
|
+
' if: failure()',
|
|
445
449
|
' uses: actions/upload-artifact@v7.0.1',
|
|
446
450
|
' with:',
|
|
447
451
|
` name: trace-results-${githubExpression('github.run_id')}`,
|
|
@@ -465,7 +469,8 @@ function yamlLinesForStep(step: PublishWorkflowStep, options: PublishWorkflowDef
|
|
|
465
469
|
case PublishWorkflowStepKind.SaveNixDevenv:
|
|
466
470
|
return [
|
|
467
471
|
` - name: ${step.name}`,
|
|
468
|
-
|
|
472
|
+
' # success() is default; always() still saves GH Nix cache after a red job.',
|
|
473
|
+
' if: always()',
|
|
469
474
|
' uses: ./.github/actions/save-nix-devenv',
|
|
470
475
|
' with:',
|
|
471
476
|
` nix-cache-hit: ${githubExpression('steps.setup.outputs.nix-cache-hit')}`,
|
|
@@ -498,7 +503,7 @@ function deployEnvLines(options: PublishWorkflowDefinitionOptions): string[] {
|
|
|
498
503
|
|
|
499
504
|
function conditionalRunStep(step: PublishWorkflowStep, run: string): string[] {
|
|
500
505
|
const condition = "steps.version.outputs.mode != 'none'";
|
|
501
|
-
return [` - name: ${step.name}`, ` if: ${
|
|
506
|
+
return [` - name: ${step.name}`, ` if: ${condition}`, ` run: ${run}`];
|
|
502
507
|
}
|
|
503
508
|
|
|
504
509
|
function renderSingleJobPublishWorkflowSteps(
|
|
@@ -514,7 +519,7 @@ function renderSingleJobPublishWorkflowSteps(
|
|
|
514
519
|
if (step.kind === PublishWorkflowStepKind.Build) {
|
|
515
520
|
lines.push(
|
|
516
521
|
' - name: ๐ง Build supplemental Linux targets',
|
|
517
|
-
|
|
522
|
+
" if: steps.version.outputs.mode != 'none'",
|
|
518
523
|
` run: smoo github-ci nx-run-many --targets "${LINUX_PLATFORM_TARGET_GLOBS.join(',')}" --projects "${githubExpression(
|
|
519
524
|
'steps.version.outputs.projects',
|
|
520
525
|
)}"`,
|
|
@@ -566,7 +571,7 @@ defaults:
|
|
|
566
571
|
|
|
567
572
|
jobs:
|
|
568
573
|
linux-release-candidate:
|
|
569
|
-
|
|
574
|
+
${renderRunsOnLine(options.runsOn)}
|
|
570
575
|
permissions:
|
|
571
576
|
contents: write
|
|
572
577
|
id-token: none
|
|
@@ -642,7 +647,7 @@ function renderLinuxReleaseCandidateSteps(
|
|
|
642
647
|
} else if (step.kind === PublishWorkflowStepKind.Build) {
|
|
643
648
|
lines.push(
|
|
644
649
|
` - name: ${step.name}`,
|
|
645
|
-
|
|
650
|
+
" if: steps.version.outputs.mode != 'none'",
|
|
646
651
|
' run:',
|
|
647
652
|
` smoo github-ci nx-run-many --targets build --projects "${githubExpression(
|
|
648
653
|
'steps.version.outputs.projects',
|
|
@@ -667,7 +672,7 @@ function renderLinuxReleaseCandidateSteps(
|
|
|
667
672
|
'',
|
|
668
673
|
` # Step ${stepNumber}`,
|
|
669
674
|
' - name: ๐ง Build supplemental Linux targets',
|
|
670
|
-
|
|
675
|
+
" if: steps.version.outputs.mode != 'none'",
|
|
671
676
|
' run:',
|
|
672
677
|
` smoo github-ci nx-run-many --targets "${LINUX_PLATFORM_TARGET_GLOBS.join(
|
|
673
678
|
',',
|
|
@@ -700,7 +705,7 @@ function renderLinuxReleaseCandidateSteps(
|
|
|
700
705
|
'',
|
|
701
706
|
` # Step ${stepNumber++}`,
|
|
702
707
|
' - name: ๐ค Upload validated build outputs',
|
|
703
|
-
|
|
708
|
+
" if: steps.version.outputs.mode != 'none'",
|
|
704
709
|
' uses: actions/upload-artifact@v7.0.1',
|
|
705
710
|
' with:',
|
|
706
711
|
` name: publish-release-outputs-${githubExpression('github.run_id')}`,
|
|
@@ -714,7 +719,7 @@ function renderLinuxReleaseCandidateSteps(
|
|
|
714
719
|
'',
|
|
715
720
|
` # Step ${stepNumber++}`,
|
|
716
721
|
' - name: ๐ค Upload supplemental Linux outputs',
|
|
717
|
-
|
|
722
|
+
" if: steps.version.outputs.mode != 'none'",
|
|
718
723
|
' uses: actions/upload-artifact@v7.0.1',
|
|
719
724
|
' with:',
|
|
720
725
|
` name: publish-linux-outputs-${githubExpression('github.run_id')}`,
|
|
@@ -730,7 +735,8 @@ function renderLinuxReleaseCandidateSteps(
|
|
|
730
735
|
'',
|
|
731
736
|
` # Step ${stepNumber}`,
|
|
732
737
|
' - name: ๐งน Cleanup and cache Nix/devenv',
|
|
733
|
-
|
|
738
|
+
' # success() is default; always() still saves GH Nix cache after a red job.',
|
|
739
|
+
' if: always()',
|
|
734
740
|
' uses: ./.github/actions/save-nix-devenv',
|
|
735
741
|
' with:',
|
|
736
742
|
` nix-cache-hit: ${githubExpression('steps.setup.outputs.nix-cache-hit')}`,
|
|
@@ -792,7 +798,8 @@ function renderMacosPlatformSteps(options: PublishWorkflowDefinitionOptions): st
|
|
|
792
798
|
'',
|
|
793
799
|
` # Step ${stepNumber}`,
|
|
794
800
|
' - name: ๐งน Cleanup and cache Nix/devenv',
|
|
795
|
-
|
|
801
|
+
' # success() is default; always() still saves GH Nix cache after a red job.',
|
|
802
|
+
' if: always()',
|
|
796
803
|
' uses: ./.github/actions/save-nix-devenv',
|
|
797
804
|
' with:',
|
|
798
805
|
` nix-cache-hit: ${githubExpression('steps.setup.outputs.nix-cache-hit')}`,
|
|
@@ -876,7 +883,7 @@ function renderFinalLinuxPublishSteps(options: PublishWorkflowDefinitionOptions)
|
|
|
876
883
|
'',
|
|
877
884
|
` # Step ${stepNumber++}`,
|
|
878
885
|
' - name: ๐ฆ Apply verified Linux outputs',
|
|
879
|
-
` if: ${
|
|
886
|
+
` if: ${mode} != 'none'`,
|
|
880
887
|
' run:',
|
|
881
888
|
` smoo github-ci apply-outputs --source-sha "${githubExpression(
|
|
882
889
|
'needs.linux-release-candidate.outputs.release-sha',
|
|
@@ -896,7 +903,7 @@ function renderFinalLinuxPublishSteps(options: PublishWorkflowDefinitionOptions)
|
|
|
896
903
|
'',
|
|
897
904
|
` # Step ${stepNumber++}`,
|
|
898
905
|
' - name: ๐ Apply verified macOS outputs',
|
|
899
|
-
` if: ${
|
|
906
|
+
` if: ${mode} != 'none'`,
|
|
900
907
|
' run:',
|
|
901
908
|
` smoo github-ci apply-outputs --source-sha "${githubExpression('github.sha')}"`,
|
|
902
909
|
` "${githubExpression('runner.temp')}/publish-artifacts/publish-macos-outputs-${githubExpression(
|
|
@@ -905,7 +912,7 @@ function renderFinalLinuxPublishSteps(options: PublishWorkflowDefinitionOptions)
|
|
|
905
912
|
'',
|
|
906
913
|
` # Step ${stepNumber++}`,
|
|
907
914
|
` - name: โ
Validate restored release (${githubExpression(mode)})`,
|
|
908
|
-
` if: ${
|
|
915
|
+
` if: ${mode} != 'none'`,
|
|
909
916
|
' run: smoo monorepo validate',
|
|
910
917
|
'',
|
|
911
918
|
' # --- Release ------------------------------------------------------------',
|
|
@@ -937,7 +944,8 @@ function renderFinalLinuxPublishSteps(options: PublishWorkflowDefinitionOptions)
|
|
|
937
944
|
'',
|
|
938
945
|
` # Step ${stepNumber}`,
|
|
939
946
|
' - name: ๐งน Cleanup and cache Nix/devenv',
|
|
940
|
-
|
|
947
|
+
' # success() is default; always() still saves GH Nix cache after a red job.',
|
|
948
|
+
' if: always()',
|
|
941
949
|
' uses: ./.github/actions/save-nix-devenv',
|
|
942
950
|
' with:',
|
|
943
951
|
` nix-cache-hit: ${githubExpression('steps.setup.outputs.nix-cache-hit')}`,
|