@smoothbricks/cli 0.1.1 → 0.2.0
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/README.md +155 -30
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +91 -7
- package/dist/devenv/index.d.ts +10 -0
- package/dist/devenv/index.d.ts.map +1 -0
- package/dist/devenv/index.js +27 -0
- package/dist/lib/cli-package.d.ts +4 -0
- package/dist/lib/cli-package.d.ts.map +1 -0
- package/dist/lib/cli-package.js +15 -0
- package/dist/lib/run.d.ts +5 -0
- package/dist/lib/run.d.ts.map +1 -1
- package/dist/lib/run.js +13 -0
- package/dist/lib/workspace.d.ts +15 -1
- package/dist/lib/workspace.d.ts.map +1 -1
- package/dist/lib/workspace.js +25 -2
- package/dist/monorepo/commit-msg.d.ts +8 -1
- package/dist/monorepo/commit-msg.d.ts.map +1 -1
- package/dist/monorepo/commit-msg.js +85 -7
- package/dist/monorepo/index.d.ts +19 -2
- package/dist/monorepo/index.d.ts.map +1 -1
- package/dist/monorepo/index.js +104 -16
- package/dist/monorepo/managed-files.js +1 -1
- package/dist/monorepo/nx-sync.d.ts +2 -1
- package/dist/monorepo/nx-sync.d.ts.map +1 -1
- package/dist/monorepo/nx-sync.js +8 -2
- package/dist/monorepo/package-hygiene.d.ts +6 -2
- package/dist/monorepo/package-hygiene.d.ts.map +1 -1
- package/dist/monorepo/package-hygiene.js +12 -4
- package/dist/monorepo/package-policy.d.ts +22 -2
- package/dist/monorepo/package-policy.d.ts.map +1 -1
- package/dist/monorepo/package-policy.js +1167 -8
- package/dist/monorepo/packed-package.d.ts +3 -0
- package/dist/monorepo/packed-package.d.ts.map +1 -1
- package/dist/monorepo/packed-package.js +68 -36
- package/dist/monorepo/packs/index.d.ts +20 -1
- package/dist/monorepo/packs/index.d.ts.map +1 -1
- package/dist/monorepo/packs/index.js +238 -19
- package/dist/monorepo/publish-workflow.d.ts +0 -4
- package/dist/monorepo/publish-workflow.d.ts.map +1 -1
- package/dist/monorepo/publish-workflow.js +5 -12
- package/dist/monorepo/tool-validation.d.ts +22 -0
- package/dist/monorepo/tool-validation.d.ts.map +1 -0
- package/dist/monorepo/tool-validation.js +399 -0
- package/dist/nx/index.d.ts +24 -0
- package/dist/nx/index.d.ts.map +1 -0
- package/dist/nx/index.js +94 -0
- package/dist/release/bootstrap-npm-packages.d.ts +19 -0
- package/dist/release/bootstrap-npm-packages.d.ts.map +1 -0
- package/dist/release/bootstrap-npm-packages.js +58 -0
- package/dist/release/candidates.d.ts +11 -0
- package/dist/release/candidates.d.ts.map +1 -0
- package/dist/release/candidates.js +181 -0
- package/dist/release/core.d.ts +1 -0
- package/dist/release/core.d.ts.map +1 -1
- package/dist/release/github-release.js +2 -2
- package/dist/release/index.d.ts +25 -0
- package/dist/release/index.d.ts.map +1 -1
- package/dist/release/index.js +247 -49
- package/dist/release/npm-auth.d.ts +0 -1
- package/dist/release/npm-auth.d.ts.map +1 -1
- package/dist/release/npm-auth.js +4 -16
- package/dist/release/orchestration.d.ts +2 -1
- package/dist/release/orchestration.d.ts.map +1 -1
- package/dist/release/orchestration.js +9 -1
- package/dist/release/publish-plan.js +1 -1
- package/managed/raw/git-format-staged.yml +54 -0
- package/managed/raw/tooling/git-hooks/commit-msg.sh +1 -1
- package/managed/templates/github/actions/setup-devenv/action.yml +1 -7
- package/managed/templates/github/workflows/ci.yml +1 -0
- package/package.json +15 -3
- package/src/cli.ts +134 -20
- package/src/devenv/index.test.ts +49 -0
- package/src/devenv/index.ts +38 -0
- package/src/lib/cli-package.ts +18 -0
- package/src/lib/run.ts +19 -0
- package/src/lib/workspace.ts +41 -2
- package/src/monorepo/__tests__/publish-workflow.test.ts +16 -9
- package/src/monorepo/commit-msg.test.ts +45 -0
- package/src/monorepo/commit-msg.ts +107 -7
- package/src/monorepo/index.ts +142 -16
- package/src/monorepo/managed-files.ts +1 -1
- package/src/monorepo/nx-sync.ts +9 -2
- package/src/monorepo/package-hygiene.test.ts +34 -0
- package/src/monorepo/package-hygiene.ts +26 -4
- package/src/monorepo/package-policy.test.ts +902 -0
- package/src/monorepo/package-policy.ts +1393 -7
- package/src/monorepo/packed-package.ts +89 -37
- package/src/monorepo/packs/index.test.ts +189 -0
- package/src/monorepo/packs/index.ts +311 -20
- package/src/monorepo/publish-workflow.ts +8 -16
- package/src/monorepo/setup-test-tracing.test.ts +120 -0
- package/src/monorepo/tool-validation.test.ts +261 -0
- package/src/monorepo/tool-validation.ts +462 -0
- package/src/nx/index.test.ts +64 -0
- package/src/nx/index.ts +119 -0
- package/src/release/__tests__/bootstrap-npm-packages.test.ts +127 -0
- package/src/release/__tests__/candidates.test.ts +217 -0
- package/src/release/__tests__/core-properties.test.ts +4 -4
- package/src/release/__tests__/core-scenarios.test.ts +15 -5
- package/src/release/__tests__/core.test.ts +2 -2
- package/src/release/__tests__/fixture-repo.test.ts +22 -13
- package/src/release/__tests__/github-release.test.ts +13 -8
- package/src/release/__tests__/helpers/fixture-repo.ts +1 -0
- package/src/release/__tests__/npm-auth.test.ts +12 -21
- package/src/release/__tests__/orchestration.test.ts +51 -7
- package/src/release/__tests__/publish-plan.test.ts +9 -3
- package/src/release/__tests__/retag-unpublished.test.ts +1 -1
- package/src/release/__tests__/trust-publisher.test.ts +133 -0
- package/src/release/bootstrap-npm-packages.ts +88 -0
- package/src/release/candidates.ts +225 -0
- package/src/release/core.ts +1 -0
- package/src/release/github-release.ts +2 -2
- package/src/release/index.ts +346 -56
- package/src/release/npm-auth.ts +4 -33
- package/src/release/orchestration.ts +12 -2
- package/src/release/publish-plan.ts +1 -1
- package/managed/raw/.git-format-staged.yml +0 -47
|
@@ -1,35 +1,66 @@
|
|
|
1
1
|
import { chmodSync, existsSync, statSync } from 'node:fs';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
|
-
import {
|
|
3
|
+
import { runStatus } from '../../lib/run.js';
|
|
4
|
+
import { readProjectTargets } from '../../nx/index.js';
|
|
5
|
+
import { syncBunLockfileVersions, validateBunLockfileVersions } from '../lockfile.js';
|
|
4
6
|
import { validateManagedFiles } from '../managed-files.js';
|
|
5
|
-
import { validateNxSync } from '../nx-sync.js';
|
|
7
|
+
import { fixNxSync, validateNxSync } from '../nx-sync.js';
|
|
6
8
|
import { fixPackageHygiene, validatePackageHygiene } from '../package-hygiene.js';
|
|
7
9
|
import {
|
|
10
|
+
applyFixableMonorepoDefaults,
|
|
8
11
|
applyNxReleaseDefaults,
|
|
9
12
|
applyPublicPackageDefaults,
|
|
10
13
|
applyWorkspaceDependencyDefaults,
|
|
14
|
+
type ResolvedProjectTargets,
|
|
15
|
+
validateNxProjectNames,
|
|
11
16
|
validateNxReleaseConfig,
|
|
12
17
|
validatePublicPackageMetadata,
|
|
13
18
|
validatePublicTags,
|
|
14
19
|
validateRootPackagePolicy,
|
|
15
20
|
validateWorkspaceDependencies,
|
|
16
21
|
} from '../package-policy.js';
|
|
17
|
-
import {
|
|
22
|
+
import {
|
|
23
|
+
validatePackedPublicPackageManifest,
|
|
24
|
+
validatePackedPublicPackagePublint,
|
|
25
|
+
validatePackedPublicPackageTypes,
|
|
26
|
+
} from '../packed-package.js';
|
|
18
27
|
import { syncRootRuntimeVersions } from '../runtime.js';
|
|
28
|
+
import { applyToolConfigDefaults, validateToolConfig } from '../tool-validation.js';
|
|
19
29
|
|
|
20
30
|
export interface MonorepoContext {
|
|
21
31
|
root: string;
|
|
22
32
|
syncRuntime: boolean;
|
|
33
|
+
verbose?: boolean;
|
|
23
34
|
}
|
|
24
35
|
|
|
25
36
|
export interface ValidatePackOptions {
|
|
26
37
|
failFast?: boolean;
|
|
38
|
+
fix?: boolean;
|
|
39
|
+
verbose?: boolean;
|
|
27
40
|
}
|
|
28
41
|
|
|
29
|
-
interface MonorepoPack {
|
|
42
|
+
export interface MonorepoPack {
|
|
30
43
|
name: string;
|
|
31
44
|
init?(ctx: MonorepoContext): Promise<void> | void;
|
|
32
|
-
|
|
45
|
+
fixPreBuild?(ctx: MonorepoContext): Promise<void> | void;
|
|
46
|
+
fixPostBuild?(ctx: MonorepoContext): Promise<void> | void;
|
|
47
|
+
validatePreBuild?(ctx: MonorepoContext): Promise<number> | number;
|
|
48
|
+
validatePostBuild?(ctx: MonorepoContext): Promise<number> | number;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface ValidatePackRunHooks {
|
|
52
|
+
packs?: readonly MonorepoPack[];
|
|
53
|
+
runBuild?: (ctx: MonorepoContext, options?: ValidatePackOptions) => Promise<number> | number;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface ValidatePackResult {
|
|
57
|
+
failures: number;
|
|
58
|
+
failedChecks: number;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
interface CapturedOutput {
|
|
62
|
+
logs: string[];
|
|
63
|
+
errors: string[];
|
|
33
64
|
}
|
|
34
65
|
|
|
35
66
|
const packs: MonorepoPack[] = [
|
|
@@ -44,13 +75,22 @@ const packs: MonorepoPack[] = [
|
|
|
44
75
|
console.log('skip root runtime versions (outside devenv; pass --sync-runtime to force)');
|
|
45
76
|
}
|
|
46
77
|
},
|
|
47
|
-
async
|
|
78
|
+
async fixPreBuild(ctx) {
|
|
79
|
+
applyFixableMonorepoDefaults(ctx.root);
|
|
80
|
+
await applyToolConfigDefaults(ctx.root);
|
|
81
|
+
syncBunLockfileVersions(ctx.root);
|
|
82
|
+
await fixNxSync(ctx.root, ctx.verbose === true);
|
|
83
|
+
applyWorkspaceDependencyDefaults(ctx.root, { resolvedTargetsByProject: await readResolvedTargetsByProject(ctx) });
|
|
84
|
+
},
|
|
85
|
+
async validatePreBuild(ctx) {
|
|
48
86
|
return (
|
|
49
87
|
validateManagedFiles(ctx.root) +
|
|
50
88
|
validateRootPackagePolicy(ctx.root) +
|
|
89
|
+
validateToolConfig(ctx.root) +
|
|
90
|
+
validateNxProjectNames(ctx.root) +
|
|
51
91
|
validateNxReleaseConfig(ctx.root) +
|
|
52
92
|
validateBunLockfileVersions(ctx.root) +
|
|
53
|
-
(await validateNxSync(ctx.root))
|
|
93
|
+
(await validateNxSync(ctx.root, ctx.verbose === true))
|
|
54
94
|
);
|
|
55
95
|
},
|
|
56
96
|
},
|
|
@@ -59,24 +99,55 @@ const packs: MonorepoPack[] = [
|
|
|
59
99
|
init(ctx) {
|
|
60
100
|
applyPublicPackageDefaults(ctx.root);
|
|
61
101
|
},
|
|
62
|
-
|
|
102
|
+
fixPreBuild(ctx) {
|
|
103
|
+
applyPublicPackageDefaults(ctx.root);
|
|
104
|
+
},
|
|
105
|
+
validatePreBuild(ctx) {
|
|
63
106
|
return validatePublicTags(ctx.root) + validatePublicPackageMetadata(ctx.root);
|
|
64
107
|
},
|
|
65
108
|
},
|
|
66
109
|
{
|
|
67
110
|
name: 'workspace-dependencies',
|
|
68
|
-
|
|
111
|
+
init(ctx) {
|
|
69
112
|
applyWorkspaceDependencyDefaults(ctx.root);
|
|
113
|
+
},
|
|
114
|
+
async fixPostBuild(ctx) {
|
|
115
|
+
applyWorkspaceDependencyDefaults(ctx.root, { resolvedTargetsByProject: await readResolvedTargetsByProject(ctx) });
|
|
116
|
+
},
|
|
117
|
+
async validatePostBuild(ctx) {
|
|
118
|
+
return validateWorkspaceDependencies(ctx.root, {
|
|
119
|
+
resolvedTargetsByProject: await readResolvedTargetsByProject(ctx),
|
|
120
|
+
});
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
name: 'package-hygiene',
|
|
125
|
+
async init(ctx) {
|
|
126
|
+
await fixPackageHygiene(ctx.root, ctx.verbose === true);
|
|
127
|
+
},
|
|
128
|
+
async fixPostBuild(ctx) {
|
|
70
129
|
await fixPackageHygiene(ctx.root);
|
|
71
130
|
},
|
|
72
|
-
async
|
|
73
|
-
return
|
|
131
|
+
async validatePostBuild(ctx) {
|
|
132
|
+
return validatePackageHygiene(ctx.root, ctx.verbose === true);
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
name: 'packed-package-publint',
|
|
137
|
+
validatePostBuild(ctx) {
|
|
138
|
+
return validatePackedPublicPackagePublint(ctx.root);
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
name: 'packed-package-manifest',
|
|
143
|
+
validatePostBuild(ctx) {
|
|
144
|
+
return validatePackedPublicPackageManifest(ctx.root);
|
|
74
145
|
},
|
|
75
146
|
},
|
|
76
147
|
{
|
|
77
|
-
name: 'packed-
|
|
78
|
-
|
|
79
|
-
return
|
|
148
|
+
name: 'packed-package-types',
|
|
149
|
+
validatePostBuild(ctx) {
|
|
150
|
+
return validatePackedPublicPackageTypes(ctx.root);
|
|
80
151
|
},
|
|
81
152
|
},
|
|
82
153
|
];
|
|
@@ -87,22 +158,242 @@ export async function runInitPacks(ctx: MonorepoContext): Promise<void> {
|
|
|
87
158
|
}
|
|
88
159
|
}
|
|
89
160
|
|
|
90
|
-
export async function runValidatePacks(
|
|
161
|
+
export async function runValidatePacks(
|
|
162
|
+
ctx: MonorepoContext,
|
|
163
|
+
options: ValidatePackOptions = {},
|
|
164
|
+
hooks: ValidatePackRunHooks = {},
|
|
165
|
+
): Promise<ValidatePackResult> {
|
|
166
|
+
const selectedPacks = hooks.packs ?? packs;
|
|
167
|
+
const build = hooks.runBuild ?? runBuild;
|
|
168
|
+
|
|
169
|
+
if (options.fix) {
|
|
170
|
+
await runFixPhase(ctx, selectedPacks, 'pre-build', 'fixPreBuild');
|
|
171
|
+
const buildFailures = await build(ctx, options);
|
|
172
|
+
if (buildFailures > 0) {
|
|
173
|
+
return { failures: buildFailures, failedChecks: 1 };
|
|
174
|
+
}
|
|
175
|
+
await runFixPhase(ctx, selectedPacks, 'post-build', 'fixPostBuild');
|
|
176
|
+
return runValidationPhases(ctx, selectedPacks, options, false);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const preBuild = await runValidationPhase(ctx, selectedPacks, options, 'pre-build', 'validatePreBuild', false);
|
|
180
|
+
if (preBuild.failures > 0 && options.failFast) {
|
|
181
|
+
return preBuild;
|
|
182
|
+
}
|
|
183
|
+
const buildFailures = await build(ctx, options);
|
|
184
|
+
if (buildFailures > 0) {
|
|
185
|
+
return { failures: preBuild.failures + buildFailures, failedChecks: preBuild.failedChecks + 1 };
|
|
186
|
+
}
|
|
187
|
+
return sumResults(
|
|
188
|
+
preBuild,
|
|
189
|
+
await runValidationPhase(ctx, selectedPacks, options, 'post-build', 'validatePostBuild', true),
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
async function runValidationPhases(
|
|
194
|
+
ctx: MonorepoContext,
|
|
195
|
+
selectedPacks: readonly MonorepoPack[],
|
|
196
|
+
options: ValidatePackOptions,
|
|
197
|
+
printedBefore: boolean,
|
|
198
|
+
): Promise<ValidatePackResult> {
|
|
199
|
+
const preBuild = await runValidationPhase(
|
|
200
|
+
ctx,
|
|
201
|
+
selectedPacks,
|
|
202
|
+
options,
|
|
203
|
+
'pre-build',
|
|
204
|
+
'validatePreBuild',
|
|
205
|
+
printedBefore,
|
|
206
|
+
);
|
|
207
|
+
if (preBuild.failures > 0 && options.failFast) {
|
|
208
|
+
return preBuild;
|
|
209
|
+
}
|
|
210
|
+
return sumResults(
|
|
211
|
+
preBuild,
|
|
212
|
+
await runValidationPhase(ctx, selectedPacks, options, 'post-build', 'validatePostBuild', true),
|
|
213
|
+
);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
async function runValidationPhase(
|
|
217
|
+
ctx: MonorepoContext,
|
|
218
|
+
selectedPacks: readonly MonorepoPack[],
|
|
219
|
+
options: ValidatePackOptions,
|
|
220
|
+
phase: 'pre-build' | 'post-build',
|
|
221
|
+
method: 'validatePreBuild' | 'validatePostBuild',
|
|
222
|
+
printedBefore: boolean,
|
|
223
|
+
): Promise<ValidatePackResult> {
|
|
91
224
|
let failures = 0;
|
|
225
|
+
let failedChecks = 0;
|
|
92
226
|
let checkedPacks = 0;
|
|
93
|
-
|
|
94
|
-
|
|
227
|
+
let hasPrinted = printedBefore;
|
|
228
|
+
for (const pack of selectedPacks) {
|
|
229
|
+
const validate = pack[method];
|
|
230
|
+
if (!validate) {
|
|
95
231
|
continue;
|
|
96
232
|
}
|
|
97
|
-
|
|
233
|
+
const label = `${pack.name} (${phase})`;
|
|
234
|
+
if (options.verbose) {
|
|
235
|
+
printCheckHeading(label, hasPrinted || checkedPacks > 0);
|
|
236
|
+
}
|
|
237
|
+
hasPrinted = true;
|
|
98
238
|
checkedPacks++;
|
|
99
|
-
|
|
239
|
+
let packFailures: number;
|
|
240
|
+
let output = emptyCapturedOutput();
|
|
241
|
+
try {
|
|
242
|
+
if (options.verbose) {
|
|
243
|
+
packFailures = await validate(ctx);
|
|
244
|
+
} else {
|
|
245
|
+
const captured = await captureConsoleOutput(() => validate(ctx));
|
|
246
|
+
packFailures = captured.result;
|
|
247
|
+
output = captured.output;
|
|
248
|
+
}
|
|
249
|
+
} catch (error) {
|
|
250
|
+
if (!options.verbose) {
|
|
251
|
+
printCheckHeading(label, true);
|
|
252
|
+
if (error instanceof CapturedConsoleError) {
|
|
253
|
+
output = error.output;
|
|
254
|
+
}
|
|
255
|
+
printCapturedOutput(output);
|
|
256
|
+
}
|
|
257
|
+
throw error instanceof CapturedConsoleError ? error.cause : error;
|
|
258
|
+
}
|
|
100
259
|
failures += packFailures;
|
|
260
|
+
if (packFailures > 0) {
|
|
261
|
+
failedChecks++;
|
|
262
|
+
}
|
|
263
|
+
if (!options.verbose && packFailures > 0) {
|
|
264
|
+
printCheckHeading(label, true);
|
|
265
|
+
printCapturedOutput(output);
|
|
266
|
+
}
|
|
267
|
+
printCheckStatus(label, packFailures);
|
|
101
268
|
if (packFailures > 0 && options.failFast) {
|
|
102
269
|
break;
|
|
103
270
|
}
|
|
104
271
|
}
|
|
105
|
-
return failures;
|
|
272
|
+
return { failures, failedChecks };
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
function sumResults(left: ValidatePackResult, right: ValidatePackResult): ValidatePackResult {
|
|
276
|
+
return { failures: left.failures + right.failures, failedChecks: left.failedChecks + right.failedChecks };
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
async function runFixPhase(
|
|
280
|
+
ctx: MonorepoContext,
|
|
281
|
+
selectedPacks: readonly MonorepoPack[],
|
|
282
|
+
phase: 'pre-build' | 'post-build',
|
|
283
|
+
method: 'fixPreBuild' | 'fixPostBuild',
|
|
284
|
+
): Promise<void> {
|
|
285
|
+
let checkedPacks = 0;
|
|
286
|
+
for (const pack of selectedPacks) {
|
|
287
|
+
const fix = pack[method];
|
|
288
|
+
if (!fix) {
|
|
289
|
+
continue;
|
|
290
|
+
}
|
|
291
|
+
const label = `${pack.name} (${phase} fix)`;
|
|
292
|
+
if (ctx.verbose) {
|
|
293
|
+
printCheckHeading(label, checkedPacks > 0);
|
|
294
|
+
}
|
|
295
|
+
checkedPacks++;
|
|
296
|
+
if (ctx.verbose) {
|
|
297
|
+
await fix(ctx);
|
|
298
|
+
continue;
|
|
299
|
+
}
|
|
300
|
+
let output = emptyCapturedOutput();
|
|
301
|
+
try {
|
|
302
|
+
output = (await captureConsoleOutput(() => fix(ctx))).output;
|
|
303
|
+
} catch (error) {
|
|
304
|
+
if (error instanceof CapturedConsoleError) {
|
|
305
|
+
output = error.output;
|
|
306
|
+
}
|
|
307
|
+
printCheckHeading(label, true);
|
|
308
|
+
printCapturedOutput(output);
|
|
309
|
+
throw error instanceof CapturedConsoleError ? error.cause : error;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
async function runBuild(ctx: MonorepoContext, options: ValidatePackOptions = {}): Promise<number> {
|
|
315
|
+
if (options.verbose) {
|
|
316
|
+
printCheckHeading('build', true);
|
|
317
|
+
}
|
|
318
|
+
const status = await runStatus('nx', ['run-many', '-t', 'build'], ctx.root, options.verbose !== true);
|
|
319
|
+
if (status !== 0) {
|
|
320
|
+
if (!options.verbose) {
|
|
321
|
+
printCheckHeading('build', true);
|
|
322
|
+
}
|
|
323
|
+
console.error('nx run-many -t build failed');
|
|
324
|
+
printCheckStatus('build', 1);
|
|
325
|
+
return 1;
|
|
326
|
+
}
|
|
327
|
+
printCheckStatus('build', 0);
|
|
328
|
+
return 0;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
function printCheckStatus(label: string, failures: number): void {
|
|
332
|
+
if (failures === 0) {
|
|
333
|
+
console.log(`🆗 ${label}`);
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
const noun = failures === 1 ? 'problem' : 'problems';
|
|
337
|
+
console.log(`👎 ${label} (${failures} ${noun})\n`);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
function printCheckHeading(label: string, separate: boolean): void {
|
|
341
|
+
console.log(`${separate ? '\n' : ''}== ${label} ==`);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
function emptyCapturedOutput(): CapturedOutput {
|
|
345
|
+
return { logs: [], errors: [] };
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
async function captureConsoleOutput<T>(fn: () => Promise<T> | T): Promise<{ result: T; output: CapturedOutput }> {
|
|
349
|
+
const output = emptyCapturedOutput();
|
|
350
|
+
const originalLog = console.log;
|
|
351
|
+
const originalError = console.error;
|
|
352
|
+
console.log = (...args: unknown[]) => {
|
|
353
|
+
output.logs.push(args.join(' '));
|
|
354
|
+
};
|
|
355
|
+
console.error = (...args: unknown[]) => {
|
|
356
|
+
output.errors.push(args.join(' '));
|
|
357
|
+
};
|
|
358
|
+
try {
|
|
359
|
+
return { result: await fn(), output };
|
|
360
|
+
} catch (error) {
|
|
361
|
+
throw new CapturedConsoleError(error, output);
|
|
362
|
+
} finally {
|
|
363
|
+
console.log = originalLog;
|
|
364
|
+
console.error = originalError;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
class CapturedConsoleError extends Error {
|
|
369
|
+
constructor(
|
|
370
|
+
public override readonly cause: unknown,
|
|
371
|
+
public readonly output: CapturedOutput,
|
|
372
|
+
) {
|
|
373
|
+
super(cause instanceof Error ? cause.message : String(cause));
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
function printCapturedOutput(output: CapturedOutput): void {
|
|
378
|
+
for (const line of output.logs) {
|
|
379
|
+
console.log(line);
|
|
380
|
+
}
|
|
381
|
+
for (const line of output.errors) {
|
|
382
|
+
console.log(line);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
async function readResolvedTargetsByProject(ctx: MonorepoContext): Promise<Map<string, ResolvedProjectTargets>> {
|
|
387
|
+
const projects = await readProjectTargets(ctx.root);
|
|
388
|
+
return new Map(
|
|
389
|
+
projects.map((project) => [
|
|
390
|
+
project.project,
|
|
391
|
+
{
|
|
392
|
+
targets: new Set(project.targets),
|
|
393
|
+
...(project.buildDependsOn ? { buildDependsOn: project.buildDependsOn } : {}),
|
|
394
|
+
},
|
|
395
|
+
]),
|
|
396
|
+
);
|
|
106
397
|
}
|
|
107
398
|
|
|
108
399
|
function ensureLocalSmooShim(root: string): void {
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { isSmoothBricksCodebasePackageName } from '../lib/cli-package.js';
|
|
2
|
+
|
|
1
3
|
export type PublishWorkflowBump = 'auto' | 'patch' | 'minor' | 'major' | 'prerelease';
|
|
2
4
|
export type PublishWorkflowCondition = 'version-mode-not-none' | 'failure' | 'always';
|
|
3
5
|
export type PublishWorkflowNxTarget = 'build' | 'lint' | 'test';
|
|
@@ -26,7 +28,6 @@ export interface PublishWorkflowStep {
|
|
|
26
28
|
id?: string;
|
|
27
29
|
condition?: PublishWorkflowCondition;
|
|
28
30
|
nxTarget?: PublishWorkflowNxTarget;
|
|
29
|
-
needsNodeAuthToken?: boolean;
|
|
30
31
|
}
|
|
31
32
|
|
|
32
33
|
export interface PublishWorkflowDefinition {
|
|
@@ -57,19 +58,18 @@ export interface PublishWorkflowCallbacks {
|
|
|
57
58
|
setupDevenv(): Promise<PublishWorkflowSetupOutputs>;
|
|
58
59
|
configureReleaseAuthor(): Promise<void>;
|
|
59
60
|
buildSelfHostedCli(): Promise<void>;
|
|
60
|
-
repairPendingReleases(input: { dryRun: boolean
|
|
61
|
+
repairPendingReleases(input: { dryRun: boolean }): Promise<void>;
|
|
61
62
|
versionRelease(input: { bump: PublishWorkflowBump; dryRun: boolean }): Promise<PublishWorkflowVersionOutputs>;
|
|
62
63
|
checkManagedMonorepoFiles(): Promise<void>;
|
|
63
64
|
nxRunMany(input: { target: PublishWorkflowNxTarget; projects: string[] }): Promise<void>;
|
|
64
65
|
uploadTraceDbs(): Promise<void>;
|
|
65
66
|
validateMonorepoConfig(): Promise<void>;
|
|
66
|
-
publishRelease(input: { bump: PublishWorkflowBump; dryRun: boolean
|
|
67
|
+
publishRelease(input: { bump: PublishWorkflowBump; dryRun: boolean }): Promise<void>;
|
|
67
68
|
saveNixDevenv(input: PublishWorkflowSetupOutputs): Promise<void>;
|
|
68
69
|
}
|
|
69
70
|
|
|
70
71
|
export interface PublishWorkflowRunContext {
|
|
71
72
|
inputs: PublishWorkflowInputs;
|
|
72
|
-
nodeAuthToken: boolean;
|
|
73
73
|
callbacks: PublishWorkflowCallbacks;
|
|
74
74
|
}
|
|
75
75
|
|
|
@@ -87,7 +87,7 @@ export function definePublishWorkflow(options: PublishWorkflowDefinitionOptions
|
|
|
87
87
|
{ kind: PublishWorkflowStepKind.SetupDevenv, name: '🧱 Setup Nix/devenv', id: 'setup' },
|
|
88
88
|
{ kind: PublishWorkflowStepKind.ConfigureReleaseAuthor, name: '🤖 Configure release author' },
|
|
89
89
|
];
|
|
90
|
-
if (options.repoName
|
|
90
|
+
if (isSmoothBricksCodebasePackageName(options.repoName)) {
|
|
91
91
|
setupSteps.push({ kind: PublishWorkflowStepKind.BuildSelfHostedCli, name: '🏗️ Build self-hosted smoo' });
|
|
92
92
|
}
|
|
93
93
|
return {
|
|
@@ -96,7 +96,6 @@ export function definePublishWorkflow(options: PublishWorkflowDefinitionOptions
|
|
|
96
96
|
{
|
|
97
97
|
kind: PublishWorkflowStepKind.RepairPendingReleases,
|
|
98
98
|
name: '🧯 Repair pending releases',
|
|
99
|
-
needsNodeAuthToken: true,
|
|
100
99
|
},
|
|
101
100
|
{ kind: PublishWorkflowStepKind.VersionRelease, name: '🏷️ Version release', id: 'version' },
|
|
102
101
|
{
|
|
@@ -131,7 +130,6 @@ export function definePublishWorkflow(options: PublishWorkflowDefinitionOptions
|
|
|
131
130
|
{
|
|
132
131
|
kind: PublishWorkflowStepKind.PublishRelease,
|
|
133
132
|
name: `📦 Publish release (${versionMode})`,
|
|
134
|
-
needsNodeAuthToken: true,
|
|
135
133
|
},
|
|
136
134
|
{
|
|
137
135
|
kind: PublishWorkflowStepKind.SaveNixDevenv,
|
|
@@ -175,7 +173,6 @@ export async function runPublishWorkflow(
|
|
|
175
173
|
case PublishWorkflowStepKind.RepairPendingReleases:
|
|
176
174
|
await context.callbacks.repairPendingReleases({
|
|
177
175
|
dryRun: context.inputs.dryRun,
|
|
178
|
-
nodeAuthToken: context.nodeAuthToken,
|
|
179
176
|
});
|
|
180
177
|
break;
|
|
181
178
|
case PublishWorkflowStepKind.VersionRelease:
|
|
@@ -202,7 +199,6 @@ export async function runPublishWorkflow(
|
|
|
202
199
|
await context.callbacks.publishRelease({
|
|
203
200
|
bump: context.inputs.bump,
|
|
204
201
|
dryRun: context.inputs.dryRun,
|
|
205
|
-
nodeAuthToken: context.nodeAuthToken,
|
|
206
202
|
});
|
|
207
203
|
break;
|
|
208
204
|
case PublishWorkflowStepKind.SaveNixDevenv:
|
|
@@ -347,13 +343,11 @@ function yamlLinesForStep(step: PublishWorkflowStep): string[] {
|
|
|
347
343
|
` - name: ${step.name}`,
|
|
348
344
|
' # SmoothBricks self-hosts smoo from source, but Nx release loads',
|
|
349
345
|
' # @smoothbricks/cli/nx-version-actions through the built package export.',
|
|
350
|
-
' run: nx build
|
|
346
|
+
' run: nx build cli',
|
|
351
347
|
];
|
|
352
348
|
case PublishWorkflowStepKind.RepairPendingReleases:
|
|
353
349
|
return [
|
|
354
350
|
` - name: ${step.name}`,
|
|
355
|
-
' env:',
|
|
356
|
-
` NODE_AUTH_TOKEN: ${githubExpression('secrets.NPM_TOKEN')}`,
|
|
357
351
|
` run: smoo release repair-pending --dry-run "${githubExpression('inputs.dry_run')}"`,
|
|
358
352
|
];
|
|
359
353
|
case PublishWorkflowStepKind.VersionRelease:
|
|
@@ -399,10 +393,8 @@ function yamlLinesForStep(step: PublishWorkflowStep): string[] {
|
|
|
399
393
|
return [
|
|
400
394
|
` - name: ${step.name}`,
|
|
401
395
|
' # smoo packs with Bun, then publishes tarballs with npm. Existing',
|
|
402
|
-
' # packages use trusted publishing/OIDC
|
|
403
|
-
' #
|
|
404
|
-
' env:',
|
|
405
|
-
` NODE_AUTH_TOKEN: ${githubExpression('secrets.NPM_TOKEN')}`,
|
|
396
|
+
' # packages must already exist on npm and use trusted publishing/OIDC.',
|
|
397
|
+
' # Missing package names are bootstrapped locally before trust setup.',
|
|
406
398
|
` run: smoo release publish --bump "${githubExpression('inputs.bump')}" --dry-run "${githubExpression('inputs.dry_run')}"`,
|
|
407
399
|
];
|
|
408
400
|
case PublishWorkflowStepKind.SaveNixDevenv:
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { describe, expect, it } from 'bun:test';
|
|
2
|
+
import { mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises';
|
|
3
|
+
import { tmpdir } from 'node:os';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
import { type SetupTestTracingShell, setupTestTracing } from './index.js';
|
|
6
|
+
|
|
7
|
+
describe('LMAO test tracing setup', () => {
|
|
8
|
+
it('runs the Bun test tracing generator for every workspace package', async () => {
|
|
9
|
+
await withWorkspace(async (root) => {
|
|
10
|
+
await writeWorkspacePackage(root, 'packages/a', '@scope/a', 'a');
|
|
11
|
+
await writeWorkspacePackage(root, 'packages/b', '@scope/b', 'b');
|
|
12
|
+
const shell = new RecordingSetupShell();
|
|
13
|
+
|
|
14
|
+
await setupTestTracing(root, { all: true }, shell);
|
|
15
|
+
|
|
16
|
+
expect(shell.runs).toEqual([generatorRun(root, 'a', '@scope/a'), generatorRun(root, 'b', '@scope/b')]);
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('selects packages by project name, package name, or package root', async () => {
|
|
21
|
+
await withWorkspace(async (root) => {
|
|
22
|
+
await writeWorkspacePackage(root, 'packages/a', '@scope/a', 'a');
|
|
23
|
+
await writeWorkspacePackage(root, 'packages/b', '@scope/b', 'b');
|
|
24
|
+
await writeWorkspacePackage(root, 'packages/c', '@scope/c', 'c');
|
|
25
|
+
const shell = new RecordingSetupShell();
|
|
26
|
+
|
|
27
|
+
await setupTestTracing(root, { projects: 'a,@scope/b,packages/c', opContextExport: 'customContext' }, shell);
|
|
28
|
+
|
|
29
|
+
expect(shell.runs).toEqual([
|
|
30
|
+
generatorRun(root, 'a', '@scope/a', 'customContext'),
|
|
31
|
+
generatorRun(root, 'b', '@scope/b', 'customContext'),
|
|
32
|
+
generatorRun(root, 'c', '@scope/c', 'customContext'),
|
|
33
|
+
]);
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('prints generator invocations in dry-run mode without running Nx', async () => {
|
|
38
|
+
await withWorkspace(async (root) => {
|
|
39
|
+
await writeWorkspacePackage(root, 'packages/a', '@scope/a', 'a');
|
|
40
|
+
const shell = new RecordingSetupShell();
|
|
41
|
+
|
|
42
|
+
await setupTestTracing(root, { all: true, dryRun: true }, shell);
|
|
43
|
+
|
|
44
|
+
expect(shell.runs).toEqual([]);
|
|
45
|
+
expect(shell.logs).toEqual([
|
|
46
|
+
'would run nx g @smoothbricks/nx-plugin:bun-test-tracing --project a --opContextModule @scope/a --opContextExport opContext --tracerModule @smoothbricks/lmao/testing/bun',
|
|
47
|
+
]);
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
class RecordingSetupShell implements SetupTestTracingShell {
|
|
53
|
+
readonly runs: { command: string; args: string[]; cwd: string }[] = [];
|
|
54
|
+
readonly logs: string[] = [];
|
|
55
|
+
|
|
56
|
+
async run(command: string, args: string[], cwd: string): Promise<void> {
|
|
57
|
+
this.runs.push({ command, args, cwd });
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
log(message: string): void {
|
|
61
|
+
this.logs.push(message);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async function withWorkspace(run: (root: string) => Promise<void>): Promise<void> {
|
|
66
|
+
const root = await mkdtemp(join(tmpdir(), 'smoo-test-tracing-'));
|
|
67
|
+
try {
|
|
68
|
+
await writeJson(join(root, 'package.json'), {
|
|
69
|
+
name: '@scope/workspace',
|
|
70
|
+
version: '0.0.0',
|
|
71
|
+
private: true,
|
|
72
|
+
workspaces: ['packages/*'],
|
|
73
|
+
});
|
|
74
|
+
await run(root);
|
|
75
|
+
} finally {
|
|
76
|
+
await rm(root, { recursive: true, force: true });
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async function writeWorkspacePackage(
|
|
81
|
+
root: string,
|
|
82
|
+
packagePath: string,
|
|
83
|
+
name: string,
|
|
84
|
+
projectName: string,
|
|
85
|
+
): Promise<void> {
|
|
86
|
+
await mkdir(join(root, packagePath), { recursive: true });
|
|
87
|
+
await writeJson(join(root, packagePath, 'package.json'), {
|
|
88
|
+
name,
|
|
89
|
+
version: '1.0.0',
|
|
90
|
+
nx: { name: projectName },
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
async function writeJson(path: string, value: unknown): Promise<void> {
|
|
95
|
+
await writeFile(path, `${JSON.stringify(value, null, 2)}\n`);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function generatorRun(
|
|
99
|
+
root: string,
|
|
100
|
+
project: string,
|
|
101
|
+
opContextModule: string,
|
|
102
|
+
opContextExport = 'opContext',
|
|
103
|
+
): { command: string; args: string[]; cwd: string } {
|
|
104
|
+
return {
|
|
105
|
+
command: 'nx',
|
|
106
|
+
args: [
|
|
107
|
+
'g',
|
|
108
|
+
'@smoothbricks/nx-plugin:bun-test-tracing',
|
|
109
|
+
'--project',
|
|
110
|
+
project,
|
|
111
|
+
'--opContextModule',
|
|
112
|
+
opContextModule,
|
|
113
|
+
'--opContextExport',
|
|
114
|
+
opContextExport,
|
|
115
|
+
'--tracerModule',
|
|
116
|
+
'@smoothbricks/lmao/testing/bun',
|
|
117
|
+
],
|
|
118
|
+
cwd: root,
|
|
119
|
+
};
|
|
120
|
+
}
|