@smoothbricks/cli 0.10.15 → 0.10.17
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 +31 -3
- package/dist/github-ci/index.d.ts.map +1 -1
- package/dist/github-ci/index.js +2 -1
- package/dist/monorepo/package-policy.d.ts +8 -0
- package/dist/monorepo/package-policy.d.ts.map +1 -1
- package/dist/monorepo/package-policy.js +65 -1
- package/dist/monorepo/packs/index.d.ts.map +1 -1
- package/dist/monorepo/packs/index.js +3 -2
- package/dist/monorepo/tool-validation.js +1 -1
- package/dist/release/orchestration.d.ts +1 -1
- package/dist/release/orchestration.d.ts.map +1 -1
- package/dist/release/orchestration.js +1 -1
- package/package.json +2 -2
- package/src/github-ci/index.test.ts +2 -1
- package/src/github-ci/index.ts +3 -1
- package/src/monorepo/package-policy.test.ts +40 -0
- package/src/monorepo/package-policy.ts +70 -1
- package/src/monorepo/packs/index.ts +3 -1
- package/src/monorepo/tool-validation.test.ts +3 -1
- package/src/monorepo/tool-validation.ts +1 -1
- package/src/release/__tests__/orchestration.test.ts +2 -2
- package/src/release/orchestration.ts +2 -2
package/README.md
CHANGED
|
@@ -3,9 +3,10 @@
|
|
|
3
3
|
`@smoothbricks/cli` provides `smoo`, the SmoothBricks monorepo automation CLI. It is the control plane for shared CI,
|
|
4
4
|
release, Git hook, package metadata, and publish validation conventions across SmoothBricks-style repositories.
|
|
5
5
|
|
|
6
|
-
The tool is intentionally convention-over-configuration. SmoothBricks repos use [Nx], [Bun], [Nix], [devenv]
|
|
7
|
-
[direnv], so `smoo` assumes those pieces exist instead
|
|
8
|
-
|
|
6
|
+
The tool is intentionally convention-over-configuration. SmoothBricks repos use [Nx], [Bun], [Nix], and [devenv]
|
|
7
|
+
(activated natively, or through [direnv] in repos that have not migrated), so `smoo` assumes those pieces exist instead
|
|
8
|
+
of adding another local config file. Repos should be made correct by running the mutating initialization path, then kept
|
|
9
|
+
correct by the read-only validation path.
|
|
9
10
|
|
|
10
11
|
## Install
|
|
11
12
|
|
|
@@ -266,6 +267,33 @@ Use tool-output names for concrete targets, such as `tsc-js`, `tsdown-js`, and `
|
|
|
266
267
|
as aggregate targets. Package scripts may still use developer-friendly colon names, for example `build:wasm`, but those
|
|
267
268
|
scripts should delegate to unambiguous Nx targets such as `nx run pkg:cargo-wasm`.
|
|
268
269
|
|
|
270
|
+
## Package Structure
|
|
271
|
+
|
|
272
|
+
Workspace packages keep all TypeScript sources — and especially all test files — under a single root: `src/`.
|
|
273
|
+
|
|
274
|
+
```
|
|
275
|
+
packages/<name>/
|
|
276
|
+
src/
|
|
277
|
+
foo.ts
|
|
278
|
+
foo.test.ts # unit tests: colocated with the module they defend
|
|
279
|
+
__tests__/ # cross-module/integration TS tests, still under src/
|
|
280
|
+
crates/*/tests/ # Rust integration tests (cargo-owned convention, unaffected)
|
|
281
|
+
scripts/ # non-shipped tooling; never *.test.ts
|
|
282
|
+
dist/, target/ # generated output, never scanned
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
The test-location rule is not tidiness; it is what makes the generated tooling provably cover every test. The convention
|
|
286
|
+
is one _root_, not one path: `tsconfig.test.json` includes are generated as `src/**` patterns, and the bounded
|
|
287
|
+
`bun test` targets run with `cwd <package>/src` — both because `bun test <arg>` treats the argument as a filter over a
|
|
288
|
+
scan rooted at the cwd (scanning from the package root would walk a Rust package's entire cargo `target/` tree, tens of
|
|
289
|
+
seconds per run), and because a second test root would have to be threaded through every one of those consumers forever.
|
|
290
|
+
A test file outside `src/` is therefore neither typechecked nor executed, silently. `smoo monorepo validate` fails on
|
|
291
|
+
any `*.test.ts` / `*.spec.ts(x)` outside `src/` so that gap cannot reappear.
|
|
292
|
+
|
|
293
|
+
Within the single root, separation still exists where it matters: unit tests sit next to their modules, integration
|
|
294
|
+
tests live in `src/__tests__/`, and Rust crates keep cargo's own `tests/` directories, which the rule deliberately
|
|
295
|
+
ignores.
|
|
296
|
+
|
|
269
297
|
## Managed Files
|
|
270
298
|
|
|
271
299
|
`smoo monorepo update` writes the managed files into a repository.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/github-ci/index.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/github-ci/index.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,KAAK,cAAc,EAAsB,MAAM,gBAAgB,CAAC;AACzE,OAAO,EAAE,KAAK,eAAe,EAA8D,MAAM,sBAAsB,CAAC;AACxH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAEhD,MAAM,WAAW,yBAAyB;IACxC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE;QACX,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,CAAC;IACF,YAAY,CAAC,EAAE;QACb,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,IAAI,CAAC,EAAE;YACL,IAAI,CAAC,EAAE;gBACL,SAAS,CAAC,EAAE,MAAM,CAAC;aACpB,CAAC;SACH,CAAC;KACH,CAAC;CACH;AAmBD,KAAK,WAAW,GAAG,MAAM,GAAG,UAAU,GAAG,UAAU,CAAC;AAKpD,wBAAsB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAwBtE;AAmFD,wBAAgB,WAAW,CACzB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,UAAU,GAAG,UAAU,EAC7B,aAAa,CAAC,EAAE,MAAM,EACtB,KAAK,CAAC,EAAE,MAAM,EACd,YAAY,UAAQ,GACnB,MAAM,EAAE,CAaV;AAED,wBAAsB,eAAe,CACnC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE;IACP,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,WAAW,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB,GACA,OAAO,CAAC,IAAI,CAAC,CAWf;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,WAAW,EAAE,CAAC;IACpB,cAAc,EAAE,MAAM,EAAE,CAAC;CAC1B;AAED,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,cAAc,EAAE,EAAE,OAAO,EAAE,gBAAgB,GAAG,oBAAoB,CAmD9G;AAED,wBAAgB,4BAA4B,CAAC,IAAI,EAAE,WAAW,EAAE,GAAG,WAAW,EAAE,CAwC/E;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,WAAW,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAehF;AAED,wBAAsB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAGlE;AAED,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAc9G;AAED,wBAAsB,oBAAoB,CACxC,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,MAAM,EAAE,EACrB,iBAAiB,EAAE,MAAM,GACxB,OAAO,CAAC,IAAI,CAAC,CAGf;AA0CD,MAAM,WAAW,uBAAuB;IACtC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,WAAW,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,4BAA4B;IAC3C,YAAY,CAAC,EAAE,CACb,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,UAAU,GAAG,UAAU,EAC7B,KAAK,EAAE,eAAe,KACnB,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IACvB,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAC1D,aAAa,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACxE,YAAY,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACtE,iBAAiB,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,MAAM,EAAE,EAAE,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACzE,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,SAAS,GAAG,SAAS,GAAG,SAAS,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACxE,UAAU,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IAC/B,YAAY,CAAC,EAAE,yBAAyB,CAAC;CAC1C;AAED,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,uBAAuB,EAChC,YAAY,GAAE,4BAAiC,GAC9C,OAAO,CAAC,IAAI,CAAC,CA4Ef;AAiCD,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,WAAW,EAAE,MAAM,CAAC,UAAU,EAC9B,KAAK,EAAE,yBAAyB,GAAG,SAAS,GAC3C,eAAe,CAsBjB;AAwCD,wBAAsB,yBAAyB,CAC7C,UAAU,EAAE,MAAM,EAAE,EACpB,KAAK,EAAE,eAAe,EACtB,WAAW,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,GACjD,OAAO,CAAC,MAAM,EAAE,CAAC,CAmBnB;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,sBAAsB;IACrC,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAAC;CAClF;AAED,qBAAa,0BAA2B,YAAW,sBAAsB;IACvE,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAmB/E;CACF;AAED,wBAAsB,uBAAuB,CAC3C,WAAW,EAAE,KAAK,MAAM,EAAE,EAC1B,GAAG,EAAE,MAAM,EACX,kBAAkB,EAAE,MAAM,CAAC,UAAU,EACrC,MAAM,GAAE,sBAAyD,GAChE,OAAO,CAAC,IAAI,CAAC,CA+Df"}
|
package/dist/github-ci/index.js
CHANGED
|
@@ -491,7 +491,8 @@ export async function selectStageDeployProjects(candidates, stage, loadProject)
|
|
|
491
491
|
const tags = Array.isArray(definition.tags)
|
|
492
492
|
? definition.tags.filter((tag) => typeof tag === 'string')
|
|
493
493
|
: [];
|
|
494
|
-
const isStageDerived =
|
|
494
|
+
const isStageDerived = tags.includes('stage-deploy-target') ||
|
|
495
|
+
(typeof commandValue === 'string' && commandValue.includes('smoo wrangler deploy-stage'));
|
|
495
496
|
const isStagingOnly = tags.includes('staging-deploy-target');
|
|
496
497
|
if (isStageDerived || (stage === 'staging' && isStagingOnly)) {
|
|
497
498
|
selected.push(project);
|
|
@@ -15,6 +15,14 @@ export declare function validateRootPackagePolicy(root: string): number;
|
|
|
15
15
|
export declare function validateNxReleaseConfig(root: string): number;
|
|
16
16
|
export declare function validateNxProjectNames(root: string): number;
|
|
17
17
|
export declare function validatePublicTags(root: string): number;
|
|
18
|
+
/**
|
|
19
|
+
* Every test file must live under the package's `src/`. The convention is
|
|
20
|
+
* otherwise only encoded generatively — tsconfig.test.json includes are
|
|
21
|
+
* `src/**` and the bounded bun-test targets run with cwd `<package>/src` —
|
|
22
|
+
* so a test file anywhere else is neither typechecked nor executed, with no
|
|
23
|
+
* diagnostic. This check turns that silent gap into a validation failure.
|
|
24
|
+
*/
|
|
25
|
+
export declare function validateTestFileLocations(root: string): number;
|
|
18
26
|
export declare function validatePublicPackageMetadata(root: string): number;
|
|
19
27
|
export declare function validateWorkspaceDependencies(root: string, options?: PackageTargetPolicyOptions): number;
|
|
20
28
|
//# sourceMappingURL=package-policy.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"package-policy.d.ts","sourceRoot":"","sources":["../../src/monorepo/package-policy.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"package-policy.d.ts","sourceRoot":"","sources":["../../src/monorepo/package-policy.ts"],"names":[],"mappings":"AAMA,OAAO,EAGL,KAAK,0BAA0B,EAC/B,KAAK,sBAAsB,EAC5B,MAAM,+CAA+C,CAAC;AACvD,OAAO,EAGL,2BAA2B,EAC3B,uBAAuB,EACxB,MAAM,+CAA+C,CAAC;AAoCvD,YAAY,EAAE,0BAA0B,IAAI,iCAAiC,EAAE,sBAAsB,EAAE,CAAC;AACxG,OAAO,EAAE,2BAA2B,EAAE,uBAAuB,EAAE,CAAC;AAYhE,wBAAgB,4BAA4B,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAI/D;AAED,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAuB1D;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAMxD;AAED,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CA0C7D;AAED,wBAAgB,gCAAgC,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,0BAA+B,GAAG,IAAI,CAwB7G;AAED,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAMzD;AAED,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAoB7D;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC,MAAM,CAAC,CAEvE;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAczD;AAED,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAsC9D;AAED,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAW5D;AAED,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAwB3D;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAcvD;AAoBD;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAW9D;AAgCD,wBAAgB,6BAA6B,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAgElE;AAED,wBAAgB,6BAA6B,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,0BAA+B,GAAG,MAAM,CAkC5G"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { readdirSync } from 'node:fs';
|
|
2
|
+
import { join, relative } from 'node:path';
|
|
2
3
|
import { applyWorkspaceBoundedTestTargetPolicy, checkWorkspaceBoundedTestTargetPolicy, } from '@smoothbricks/nx-plugin/bounded-test-policy';
|
|
3
4
|
import { applyPackageTargetPolicy, checkPackageTargetPolicy, } from '@smoothbricks/nx-plugin/package-target-policy';
|
|
4
5
|
import { applyReleaseConfigPolicy, checkReleaseConfigPolicy, SMOO_NX_RELEASE_TAG_PATTERN, SMOO_NX_VERSION_ACTIONS, } from '@smoothbricks/nx-plugin/release-config-policy';
|
|
@@ -258,6 +259,69 @@ export function validatePublicTags(root) {
|
|
|
258
259
|
console.log('npm:public tags are valid.');
|
|
259
260
|
return 0;
|
|
260
261
|
}
|
|
262
|
+
const testFilePattern = /\.(test|spec)\.tsx?$/;
|
|
263
|
+
/** Directories that legitimately hold generated/vendored trees a test-file
|
|
264
|
+
* scan must never descend into (cargo target/ alone holds 100k+ files). */
|
|
265
|
+
const testScanSkippedDirectories = new Set([
|
|
266
|
+
'node_modules',
|
|
267
|
+
'dist',
|
|
268
|
+
'dist-test',
|
|
269
|
+
'target',
|
|
270
|
+
'coverage',
|
|
271
|
+
'tmp',
|
|
272
|
+
'.git',
|
|
273
|
+
'.devenv',
|
|
274
|
+
'.direnv',
|
|
275
|
+
'.nx',
|
|
276
|
+
'.cache',
|
|
277
|
+
]);
|
|
278
|
+
/**
|
|
279
|
+
* Every test file must live under the package's `src/`. The convention is
|
|
280
|
+
* otherwise only encoded generatively — tsconfig.test.json includes are
|
|
281
|
+
* `src/**` and the bounded bun-test targets run with cwd `<package>/src` —
|
|
282
|
+
* so a test file anywhere else is neither typechecked nor executed, with no
|
|
283
|
+
* diagnostic. This check turns that silent gap into a validation failure.
|
|
284
|
+
*/
|
|
285
|
+
export function validateTestFileLocations(root) {
|
|
286
|
+
let failures = 0;
|
|
287
|
+
for (const pkg of getWorkspacePackages(root)) {
|
|
288
|
+
for (const stray of strayTestFiles(join(root, pkg.path))) {
|
|
289
|
+
console.error(`${pkg.projectName}: test file outside src/ is neither typechecked nor run by the bounded test targets: ${stray}`);
|
|
290
|
+
failures++;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
return failures;
|
|
294
|
+
}
|
|
295
|
+
function strayTestFiles(packagePath) {
|
|
296
|
+
const stray = [];
|
|
297
|
+
const walk = (directory) => {
|
|
298
|
+
let entries;
|
|
299
|
+
try {
|
|
300
|
+
entries = readdirSync(directory, { withFileTypes: true });
|
|
301
|
+
}
|
|
302
|
+
catch {
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
for (const entry of entries) {
|
|
306
|
+
const path = join(directory, entry.name);
|
|
307
|
+
if (entry.isDirectory()) {
|
|
308
|
+
if (testScanSkippedDirectories.has(entry.name)) {
|
|
309
|
+
continue;
|
|
310
|
+
}
|
|
311
|
+
if (directory === packagePath && entry.name === 'src') {
|
|
312
|
+
continue; // src/ is where tests belong.
|
|
313
|
+
}
|
|
314
|
+
walk(path);
|
|
315
|
+
continue;
|
|
316
|
+
}
|
|
317
|
+
if (entry.isFile() && testFilePattern.test(entry.name)) {
|
|
318
|
+
stray.push(relative(packagePath, path));
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
};
|
|
322
|
+
walk(packagePath);
|
|
323
|
+
return stray;
|
|
324
|
+
}
|
|
261
325
|
export function validatePublicPackageMetadata(root) {
|
|
262
326
|
const rootPackage = readJsonObject(join(root, 'package.json'));
|
|
263
327
|
const rootRepository = rootPackage ? repositoryInfo(rootPackage) : null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/monorepo/packs/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,cAAc,EAAsB,MAAM,mBAAmB,CAAC;AAK5E,OAAO,EAKL,KAAK,sBAAsB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/monorepo/packs/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,cAAc,EAAsB,MAAM,mBAAmB,CAAC;AAK5E,OAAO,EAKL,KAAK,sBAAsB,EAQ5B,MAAM,sBAAsB,CAAC;AAU9B,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,OAAO,CAAC;IACrB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAClD,WAAW,CAAC,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IACzD,YAAY,CAAC,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAC1D,gBAAgB,CAAC,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC;IAClE,iBAAiB,CAAC,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC;CACpE;AAED,MAAM,WAAW,oBAAoB;IACnC,KAAK,CAAC,EAAE,SAAS,YAAY,EAAE,CAAC;IAChC,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,eAAe,EAAE,OAAO,CAAC,EAAE,mBAAmB,KAAK,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC;CAC9F;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;CACtB;AAsHD,wBAAsB,YAAY,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAItE;AAED,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,eAAe,EACpB,OAAO,GAAE,mBAAwB,EACjC,KAAK,GAAE,oBAAyB,GAC/B,OAAO,CAAC,kBAAkB,CAAC,CA0B7B;AAuMD,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,cAAc,EAAE,GAAG,GAAG,CAAC,MAAM,EAAE,sBAAsB,CAAC,CAexG"}
|
|
@@ -6,7 +6,7 @@ import { syncBunLockfileVersions, validateBunLockfileVersions } from '../lockfil
|
|
|
6
6
|
import { warnOnManagedFileDrift } from '../managed-files.js';
|
|
7
7
|
import { fixNxSync, validateNxSync } from '../nx-sync.js';
|
|
8
8
|
import { fixPackageHygiene, validatePackageHygiene } from '../package-hygiene.js';
|
|
9
|
-
import { applyFixableMonorepoDefaults, applyNxReleaseDefaults, applyPublicPackageDefaults, applyWorkspaceDependencyDefaults, validateNxProjectNames, validateNxReleaseConfig, validatePublicPackageMetadata, validatePublicTags, validateRootPackagePolicy, validateWorkspaceDependencies, } from '../package-policy.js';
|
|
9
|
+
import { applyFixableMonorepoDefaults, applyNxReleaseDefaults, applyPublicPackageDefaults, applyWorkspaceDependencyDefaults, validateNxProjectNames, validateNxReleaseConfig, validatePublicPackageMetadata, validatePublicTags, validateRootPackagePolicy, validateTestFileLocations, validateWorkspaceDependencies, } from '../package-policy.js';
|
|
10
10
|
import { validatePackedPublicPackageManifest, validatePackedPublicPackagePublint, validatePackedPublicPackageTypes, } from '../packed-package.js';
|
|
11
11
|
import { syncRootRuntimeVersions, validateRootRuntimeVersions } from '../runtime.js';
|
|
12
12
|
import { applyToolConfigDefaults, validateToolConfig } from '../tool-validation.js';
|
|
@@ -47,7 +47,8 @@ const packs = [
|
|
|
47
47
|
validateNxProjectNames(ctx.root) +
|
|
48
48
|
validateNxReleaseConfig(ctx.root) +
|
|
49
49
|
validateBunLockfileVersions(ctx.root) +
|
|
50
|
-
(await validateNxSync(ctx.root, ctx.verbose === true))
|
|
50
|
+
(await validateNxSync(ctx.root, ctx.verbose === true)) +
|
|
51
|
+
validateTestFileLocations(ctx.root));
|
|
51
52
|
},
|
|
52
53
|
},
|
|
53
54
|
{
|
|
@@ -388,7 +388,7 @@ function nixPackageComment(name) {
|
|
|
388
388
|
return '# Builds ttsc source plugins';
|
|
389
389
|
}
|
|
390
390
|
if (name === 'sccache') {
|
|
391
|
-
return '# Rust compiler
|
|
391
|
+
return '# Rust compiler cache; client of the host-owned daemon (cowshed sccache start)';
|
|
392
392
|
}
|
|
393
393
|
return '';
|
|
394
394
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ReleasePackageInfo, ReleaseTarget } from './core.js';
|
|
2
|
-
export type ReleaseVersionMode = 'new' | 'none';
|
|
2
|
+
export type ReleaseVersionMode = 'new' | 'dry-run' | 'none';
|
|
3
3
|
export interface ReleaseSummary<Package extends ReleasePackageInfo = ReleasePackageInfo> {
|
|
4
4
|
sha: string;
|
|
5
5
|
dryRun: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"orchestration.d.ts","sourceRoot":"","sources":["../../src/release/orchestration.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAGnE,MAAM,MAAM,kBAAkB,GAAG,KAAK,GAAG,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"orchestration.d.ts","sourceRoot":"","sources":["../../src/release/orchestration.ts"],"names":[],"mappings":"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"}
|
|
@@ -18,7 +18,7 @@ export async function runReleaseVersion(shell, options) {
|
|
|
18
18
|
const headBeforeVersioning = await shell.gitHead();
|
|
19
19
|
const dryRunPackages = await shell.runNxReleaseVersion(versionPackages, options.bump, options.dryRun);
|
|
20
20
|
if (options.dryRun) {
|
|
21
|
-
return { mode: '
|
|
21
|
+
return { mode: 'dry-run', packages: dryRunPackages, status: 'dry-run' };
|
|
22
22
|
}
|
|
23
23
|
await shell.assertCleanGitTree();
|
|
24
24
|
const headAfterVersioning = await shell.gitHead();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@smoothbricks/cli",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.17",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "SmoothBricks monorepo automation CLI",
|
|
6
6
|
"bin": {
|
|
@@ -71,7 +71,7 @@
|
|
|
71
71
|
"dependencies": {
|
|
72
72
|
"@arethetypeswrong/core": "^0.18.2",
|
|
73
73
|
"@prettier/sync": "^0.6.1",
|
|
74
|
-
"@smoothbricks/nx-plugin": "0.3.
|
|
74
|
+
"@smoothbricks/nx-plugin": "0.3.7",
|
|
75
75
|
"@smoothbricks/validation": "0.1.5",
|
|
76
76
|
"commander": "^14.0.3",
|
|
77
77
|
"prettier": "^3.6.1",
|
|
@@ -581,7 +581,8 @@ describe('event-aware stage deployment', () => {
|
|
|
581
581
|
},
|
|
582
582
|
},
|
|
583
583
|
'conloca-app-backend': {
|
|
584
|
-
|
|
584
|
+
tags: ['stage-deploy-target'],
|
|
585
|
+
targets: { deploy: { command: 'bun scripts/deploy-backend.ts --stage={args.stage}' } },
|
|
585
586
|
},
|
|
586
587
|
'conloca-e2e-mail-capture': {
|
|
587
588
|
tags: ['staging-deploy-target'],
|
package/src/github-ci/index.ts
CHANGED
|
@@ -613,7 +613,9 @@ export async function selectStageDeployProjects(
|
|
|
613
613
|
const tags = Array.isArray(definition.tags)
|
|
614
614
|
? definition.tags.filter((tag): tag is string => typeof tag === 'string')
|
|
615
615
|
: [];
|
|
616
|
-
const isStageDerived =
|
|
616
|
+
const isStageDerived =
|
|
617
|
+
tags.includes('stage-deploy-target') ||
|
|
618
|
+
(typeof commandValue === 'string' && commandValue.includes('smoo wrangler deploy-stage'));
|
|
617
619
|
const isStagingOnly = tags.includes('staging-deploy-target');
|
|
618
620
|
if (isStageDerived || (stage === 'staging' && isStagingOnly)) {
|
|
619
621
|
selected.push(project);
|
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
validateNxProjectNames,
|
|
23
23
|
validateNxReleaseConfig,
|
|
24
24
|
validateRootPackagePolicy,
|
|
25
|
+
validateTestFileLocations,
|
|
25
26
|
validateWorkspaceDependencies,
|
|
26
27
|
} from './package-policy.js';
|
|
27
28
|
|
|
@@ -200,6 +201,45 @@ describe('root smoo monorepo policy', () => {
|
|
|
200
201
|
});
|
|
201
202
|
});
|
|
202
203
|
|
|
204
|
+
describe('test file location policy', () => {
|
|
205
|
+
afterEach(() => {
|
|
206
|
+
console.error = originalConsoleError;
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
it('fails test files outside src/ and skips generated trees', async () => {
|
|
210
|
+
const root = await createWorkspace({
|
|
211
|
+
rootName: '@smoothbricks/codebase',
|
|
212
|
+
packages: [{ dir: 'app', name: '@smoothbricks/app' }],
|
|
213
|
+
});
|
|
214
|
+
try {
|
|
215
|
+
const app = join(root, 'packages/app');
|
|
216
|
+
// Allowed: anywhere under src/.
|
|
217
|
+
await writeSource(join(app, 'src/deep/feature.test.ts'));
|
|
218
|
+
// Ignored: generated/vendored trees are never scanned.
|
|
219
|
+
await writeSource(join(app, 'target/debug/build/foo/out/stray.test.ts'));
|
|
220
|
+
await writeSource(join(app, 'node_modules/dep/dep.test.ts'));
|
|
221
|
+
await writeSource(join(app, 'dist/bundled.test.ts'));
|
|
222
|
+
expect(validateTestFileLocations(root)).toBe(0);
|
|
223
|
+
|
|
224
|
+
// Stray: neither typechecked (tsconfig.test.json includes src/** only)
|
|
225
|
+
// nor run (bounded bun-test targets scan from src/).
|
|
226
|
+
await writeSource(join(app, 'scripts/helper.test.ts'));
|
|
227
|
+
await writeSource(join(app, 'stray.spec.tsx'));
|
|
228
|
+
const errors = captureConsoleErrors();
|
|
229
|
+
expect(validateTestFileLocations(root)).toBe(2);
|
|
230
|
+
expect(errors.join('\n')).toContain('scripts/helper.test.ts');
|
|
231
|
+
expect(errors.join('\n')).toContain('stray.spec.tsx');
|
|
232
|
+
} finally {
|
|
233
|
+
await rm(root, { recursive: true, force: true });
|
|
234
|
+
}
|
|
235
|
+
});
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
async function writeSource(path: string): Promise<void> {
|
|
239
|
+
await mkdir(dirname(path), { recursive: true });
|
|
240
|
+
await writeFile(path, 'export {};\n');
|
|
241
|
+
}
|
|
242
|
+
|
|
203
243
|
describe('Nx project name policy', () => {
|
|
204
244
|
it('fixes same-scope packages without touching external or unscoped packages', async () => {
|
|
205
245
|
const root = await createWorkspace({
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { type Dirent, readdirSync } from 'node:fs';
|
|
2
|
+
import { join, relative } from 'node:path';
|
|
2
3
|
import {
|
|
3
4
|
applyWorkspaceBoundedTestTargetPolicy,
|
|
4
5
|
checkWorkspaceBoundedTestTargetPolicy,
|
|
@@ -317,6 +318,74 @@ export function validatePublicTags(root: string): number {
|
|
|
317
318
|
return 0;
|
|
318
319
|
}
|
|
319
320
|
|
|
321
|
+
const testFilePattern = /\.(test|spec)\.tsx?$/;
|
|
322
|
+
|
|
323
|
+
/** Directories that legitimately hold generated/vendored trees a test-file
|
|
324
|
+
* scan must never descend into (cargo target/ alone holds 100k+ files). */
|
|
325
|
+
const testScanSkippedDirectories = new Set([
|
|
326
|
+
'node_modules',
|
|
327
|
+
'dist',
|
|
328
|
+
'dist-test',
|
|
329
|
+
'target',
|
|
330
|
+
'coverage',
|
|
331
|
+
'tmp',
|
|
332
|
+
'.git',
|
|
333
|
+
'.devenv',
|
|
334
|
+
'.direnv',
|
|
335
|
+
'.nx',
|
|
336
|
+
'.cache',
|
|
337
|
+
]);
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* Every test file must live under the package's `src/`. The convention is
|
|
341
|
+
* otherwise only encoded generatively — tsconfig.test.json includes are
|
|
342
|
+
* `src/**` and the bounded bun-test targets run with cwd `<package>/src` —
|
|
343
|
+
* so a test file anywhere else is neither typechecked nor executed, with no
|
|
344
|
+
* diagnostic. This check turns that silent gap into a validation failure.
|
|
345
|
+
*/
|
|
346
|
+
export function validateTestFileLocations(root: string): number {
|
|
347
|
+
let failures = 0;
|
|
348
|
+
for (const pkg of getWorkspacePackages(root)) {
|
|
349
|
+
for (const stray of strayTestFiles(join(root, pkg.path))) {
|
|
350
|
+
console.error(
|
|
351
|
+
`${pkg.projectName}: test file outside src/ is neither typechecked nor run by the bounded test targets: ${stray}`,
|
|
352
|
+
);
|
|
353
|
+
failures++;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
return failures;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
function strayTestFiles(packagePath: string): string[] {
|
|
360
|
+
const stray: string[] = [];
|
|
361
|
+
const walk = (directory: string): void => {
|
|
362
|
+
let entries: Dirent[];
|
|
363
|
+
try {
|
|
364
|
+
entries = readdirSync(directory, { withFileTypes: true });
|
|
365
|
+
} catch {
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
for (const entry of entries) {
|
|
369
|
+
const path = join(directory, entry.name);
|
|
370
|
+
if (entry.isDirectory()) {
|
|
371
|
+
if (testScanSkippedDirectories.has(entry.name)) {
|
|
372
|
+
continue;
|
|
373
|
+
}
|
|
374
|
+
if (directory === packagePath && entry.name === 'src') {
|
|
375
|
+
continue; // src/ is where tests belong.
|
|
376
|
+
}
|
|
377
|
+
walk(path);
|
|
378
|
+
continue;
|
|
379
|
+
}
|
|
380
|
+
if (entry.isFile() && testFilePattern.test(entry.name)) {
|
|
381
|
+
stray.push(relative(packagePath, path));
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
};
|
|
385
|
+
walk(packagePath);
|
|
386
|
+
return stray;
|
|
387
|
+
}
|
|
388
|
+
|
|
320
389
|
export function validatePublicPackageMetadata(root: string): number {
|
|
321
390
|
const rootPackage = readJsonObject(join(root, 'package.json'));
|
|
322
391
|
const rootRepository = rootPackage ? repositoryInfo(rootPackage) : null;
|
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
validatePublicPackageMetadata,
|
|
18
18
|
validatePublicTags,
|
|
19
19
|
validateRootPackagePolicy,
|
|
20
|
+
validateTestFileLocations,
|
|
20
21
|
validateWorkspaceDependencies,
|
|
21
22
|
} from '../package-policy.js';
|
|
22
23
|
import {
|
|
@@ -100,7 +101,8 @@ const packs: MonorepoPack[] = [
|
|
|
100
101
|
validateNxProjectNames(ctx.root) +
|
|
101
102
|
validateNxReleaseConfig(ctx.root) +
|
|
102
103
|
validateBunLockfileVersions(ctx.root) +
|
|
103
|
-
(await validateNxSync(ctx.root, ctx.verbose === true))
|
|
104
|
+
(await validateNxSync(ctx.root, ctx.verbose === true)) +
|
|
105
|
+
validateTestFileLocations(ctx.root)
|
|
104
106
|
);
|
|
105
107
|
},
|
|
106
108
|
},
|
|
@@ -169,7 +169,9 @@ describe('tool configuration validation', () => {
|
|
|
169
169
|
applyDevenvPackageDefaults(root);
|
|
170
170
|
expect(validateDevenvPackages(root)).toBe(0);
|
|
171
171
|
const devenv = await readFile(devenvPath, 'utf8');
|
|
172
|
-
expect(devenv).toContain(
|
|
172
|
+
expect(devenv).toContain(
|
|
173
|
+
'sccache # Rust compiler cache; client of the host-owned daemon (cowshed sccache start)',
|
|
174
|
+
);
|
|
173
175
|
expect(devenv).toContain('++ lib.optionals pkgs.stdenv.isLinux [');
|
|
174
176
|
expect(devenv).toContain('pkgs.stdenv.cc');
|
|
175
177
|
expect(devenv).toContain('stdenv.cc.cc.lib');
|
|
@@ -439,7 +439,7 @@ function nixPackageComment(name: string): string {
|
|
|
439
439
|
return '# Builds ttsc source plugins';
|
|
440
440
|
}
|
|
441
441
|
if (name === 'sccache') {
|
|
442
|
-
return '# Rust compiler
|
|
442
|
+
return '# Rust compiler cache; client of the host-owned daemon (cowshed sccache start)';
|
|
443
443
|
}
|
|
444
444
|
return '';
|
|
445
445
|
}
|
|
@@ -188,13 +188,13 @@ describe('release orchestration', () => {
|
|
|
188
188
|
expect(shell.nxRuns).toEqual([{ packages: ['@scope/stable'], bump: 'auto', dryRun: false }]);
|
|
189
189
|
});
|
|
190
190
|
|
|
191
|
-
it('reports dry-run version packages
|
|
191
|
+
it('reports dry-run version packages as a release candidate for validation', async () => {
|
|
192
192
|
const preview = { ...stable, version: '1.1.0' };
|
|
193
193
|
const shell = new RecordingVersionShell({ releasePackagesAtHead: [[]], releaseVersionPackages: [stable], preview });
|
|
194
194
|
|
|
195
195
|
const result = await runReleaseVersion(shell, { bump: 'auto', dryRun: true });
|
|
196
196
|
|
|
197
|
-
expect(result).toEqual({ mode: '
|
|
197
|
+
expect(result).toEqual({ mode: 'dry-run', packages: [preview], status: 'dry-run' });
|
|
198
198
|
expect(shell.nxRuns).toEqual([{ packages: ['@scope/stable'], bump: 'auto', dryRun: true }]);
|
|
199
199
|
});
|
|
200
200
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { ReleasePackageInfo, ReleaseTarget } from './core.js';
|
|
2
2
|
import { planPublishActions } from './publish-plan.js';
|
|
3
3
|
|
|
4
|
-
export type ReleaseVersionMode = 'new' | 'none';
|
|
4
|
+
export type ReleaseVersionMode = 'new' | 'dry-run' | 'none';
|
|
5
5
|
|
|
6
6
|
export interface ReleaseSummary<Package extends ReleasePackageInfo = ReleasePackageInfo> {
|
|
7
7
|
sha: string;
|
|
@@ -87,7 +87,7 @@ export async function runReleaseVersion<Package extends ReleasePackageInfo>(
|
|
|
87
87
|
const headBeforeVersioning = await shell.gitHead();
|
|
88
88
|
const dryRunPackages = await shell.runNxReleaseVersion(versionPackages, options.bump, options.dryRun);
|
|
89
89
|
if (options.dryRun) {
|
|
90
|
-
return { mode: '
|
|
90
|
+
return { mode: 'dry-run', packages: dryRunPackages, status: 'dry-run' };
|
|
91
91
|
}
|
|
92
92
|
|
|
93
93
|
await shell.assertCleanGitTree();
|