@smoothbricks/cli 0.4.0 → 0.4.2
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/lib/run.d.ts +1 -0
- package/dist/lib/run.d.ts.map +1 -1
- package/dist/lib/run.js +11 -0
- package/dist/monorepo/managed-files.d.ts.map +1 -1
- package/dist/monorepo/managed-files.js +11 -0
- package/dist/monorepo/nx-sync.d.ts.map +1 -1
- package/dist/monorepo/nx-sync.js +18 -4
- package/dist/monorepo/package-hygiene.d.ts +5 -0
- package/dist/monorepo/package-hygiene.d.ts.map +1 -1
- package/dist/monorepo/package-hygiene.js +18 -4
- package/dist/monorepo/packed-package.js +5 -4
- package/dist/monorepo/packs/index.js +8 -2
- package/dist/nx/index.d.ts +4 -0
- package/dist/nx/index.d.ts.map +1 -1
- package/dist/nx/index.js +32 -0
- package/managed/raw/envrc +18 -0
- package/managed/raw/tooling/direnv/setup-environment.ts +131 -0
- package/package.json +2 -2
- package/src/lib/run.ts +13 -0
- package/src/monorepo/managed-files.ts +11 -0
- package/src/monorepo/nx-sync.ts +18 -4
- package/src/monorepo/package-hygiene.test.ts +54 -1
- package/src/monorepo/package-hygiene.ts +34 -4
- package/src/monorepo/packed-package.ts +5 -5
- package/src/monorepo/packs/index.ts +8 -2
- package/src/nx/index.ts +36 -0
package/dist/lib/run.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ export declare function runResult(command: string, args: string[], cwd: string,
|
|
|
6
6
|
stdout: string;
|
|
7
7
|
stderr: string;
|
|
8
8
|
}>;
|
|
9
|
+
export declare function printCommandOutput(stdout: string, stderr: string): void;
|
|
9
10
|
export declare function findRepoRoot(): Promise<string>;
|
|
10
11
|
export declare function decode(bytes: Uint8Array): string;
|
|
11
12
|
//# sourceMappingURL=run.d.ts.map
|
package/dist/lib/run.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/lib/run.ts"],"names":[],"mappings":"AAMA,wBAAsB,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAKnH;AAED,wBAAsB,SAAS,CAC7B,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EAAE,EACd,GAAG,EAAE,MAAM,EACX,KAAK,UAAQ,EACb,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC3B,OAAO,CAAC,MAAM,CAAC,CAQjB;AAED,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EAAE,EACd,GAAG,EAAE,MAAM,EACX,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC3B,OAAO,CAAC,MAAM,CAAC,CAiBjB;AAED,wBAAsB,SAAS,CAC7B,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EAAE,EACd,GAAG,EAAE,MAAM,EACX,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC3B,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAY/D;AAuCD,wBAAsB,YAAY,IAAI,OAAO,CAAC,MAAM,CAAC,CAMpD;AAED,wBAAgB,MAAM,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAEhD"}
|
|
1
|
+
{"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/lib/run.ts"],"names":[],"mappings":"AAMA,wBAAsB,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAKnH;AAED,wBAAsB,SAAS,CAC7B,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EAAE,EACd,GAAG,EAAE,MAAM,EACX,KAAK,UAAQ,EACb,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC3B,OAAO,CAAC,MAAM,CAAC,CAQjB;AAED,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EAAE,EACd,GAAG,EAAE,MAAM,EACX,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC3B,OAAO,CAAC,MAAM,CAAC,CAiBjB;AAED,wBAAsB,SAAS,CAC7B,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EAAE,EACd,GAAG,EAAE,MAAM,EACX,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC3B,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAY/D;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAOvE;AAuCD,wBAAsB,YAAY,IAAI,OAAO,CAAC,MAAM,CAAC,CAMpD;AAED,wBAAgB,MAAM,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAEhD"}
|
package/dist/lib/run.js
CHANGED
|
@@ -49,6 +49,14 @@ export async function runResult(command, args, cwd, env) {
|
|
|
49
49
|
stderr: decode(result.stderr),
|
|
50
50
|
};
|
|
51
51
|
}
|
|
52
|
+
export function printCommandOutput(stdout, stderr) {
|
|
53
|
+
if (stdout.length > 0) {
|
|
54
|
+
console.log(trimTrailingNewline(stdout));
|
|
55
|
+
}
|
|
56
|
+
if (stderr.length > 0) {
|
|
57
|
+
console.error(trimTrailingNewline(stderr));
|
|
58
|
+
}
|
|
59
|
+
}
|
|
52
60
|
function mergeEnv(env) {
|
|
53
61
|
const merged = {};
|
|
54
62
|
for (const [key, value] of Object.entries(process.env)) {
|
|
@@ -94,3 +102,6 @@ export async function findRepoRoot() {
|
|
|
94
102
|
export function decode(bytes) {
|
|
95
103
|
return new TextDecoder().decode(bytes);
|
|
96
104
|
}
|
|
105
|
+
function trimTrailingNewline(value) {
|
|
106
|
+
return value.endsWith('\n') ? value.slice(0, -1) : value;
|
|
107
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"managed-files.d.ts","sourceRoot":"","sources":["../../src/monorepo/managed-files.ts"],"names":[],"mappings":"AAkBA,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,SAAS,GAAG,SAAS,GAAG,WAAW,GAAG,SAAS,GAAG,iBAAiB,GAAG,SAAS,GAAG,YAAY,CAAC;CACxG;
|
|
1
|
+
{"version":3,"file":"managed-files.d.ts","sourceRoot":"","sources":["../../src/monorepo/managed-files.ts"],"names":[],"mappings":"AAkBA,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,SAAS,GAAG,SAAS,GAAG,WAAW,GAAG,SAAS,GAAG,iBAAiB,GAAG,SAAS,GAAG,YAAY,CAAC;CACxG;AAmGD,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,UAAU,EAAE,CAG/F;AA4KD,wBAAgB,YAAY,CAAC,OAAO,EAAE,UAAU,EAAE,GAAG,IAAI,CAIxD;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAQzD"}
|
|
@@ -6,6 +6,11 @@ import { listReleasePackages, readPackageJson } from '../lib/workspace.js';
|
|
|
6
6
|
import { renderCiWorkflowYaml } from './ci-workflow.js';
|
|
7
7
|
import { renderPublishWorkflowYaml } from './publish-workflow.js';
|
|
8
8
|
const managedFiles = [
|
|
9
|
+
{
|
|
10
|
+
kind: 'raw',
|
|
11
|
+
source: 'envrc',
|
|
12
|
+
target: '.envrc',
|
|
13
|
+
},
|
|
9
14
|
{
|
|
10
15
|
kind: 'raw',
|
|
11
16
|
source: 'tooling/direnv/repo-path',
|
|
@@ -18,6 +23,12 @@ const managedFiles = [
|
|
|
18
23
|
target: 'tooling/direnv/github-actions-bootstrap.sh',
|
|
19
24
|
executable: true,
|
|
20
25
|
},
|
|
26
|
+
{
|
|
27
|
+
kind: 'raw',
|
|
28
|
+
source: 'tooling/direnv/setup-environment.ts',
|
|
29
|
+
target: 'tooling/direnv/setup-environment.ts',
|
|
30
|
+
executable: true,
|
|
31
|
+
},
|
|
21
32
|
{
|
|
22
33
|
kind: 'raw',
|
|
23
34
|
source: 'tooling/git-hooks/pre-commit.sh',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nx-sync.d.ts","sourceRoot":"","sources":["../../src/monorepo/nx-sync.ts"],"names":[],"mappings":"AAEA,wBAAsB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,UAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"nx-sync.d.ts","sourceRoot":"","sources":["../../src/monorepo/nx-sync.ts"],"names":[],"mappings":"AAEA,wBAAsB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,UAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAW5E;AAED,wBAAsB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,UAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,CAWnF"}
|
package/dist/monorepo/nx-sync.js
CHANGED
|
@@ -1,11 +1,25 @@
|
|
|
1
|
-
import { runStatus } from '../lib/run.js';
|
|
1
|
+
import { printCommandOutput, runResult, runStatus } from '../lib/run.js';
|
|
2
2
|
export async function fixNxSync(root, verbose = false) {
|
|
3
|
-
const
|
|
3
|
+
const result = verbose
|
|
4
|
+
? { exitCode: await runStatus('nx', ['sync'], root, false), stdout: '', stderr: '' }
|
|
5
|
+
: await runResult('nx', ['sync'], root);
|
|
6
|
+
const status = result.exitCode;
|
|
4
7
|
if (status !== 0) {
|
|
8
|
+
if (!verbose) {
|
|
9
|
+
printCommandOutput(result.stdout, result.stderr);
|
|
10
|
+
}
|
|
5
11
|
throw new Error(`nx sync failed with exit code ${status}`);
|
|
6
12
|
}
|
|
7
13
|
}
|
|
8
14
|
export async function validateNxSync(root, verbose = false) {
|
|
9
|
-
const
|
|
10
|
-
|
|
15
|
+
const result = verbose
|
|
16
|
+
? { exitCode: await runStatus('nx', ['sync:check'], root, false), stdout: '', stderr: '' }
|
|
17
|
+
: await runResult('nx', ['sync:check'], root);
|
|
18
|
+
if (result.exitCode === 0) {
|
|
19
|
+
return 0;
|
|
20
|
+
}
|
|
21
|
+
if (!verbose) {
|
|
22
|
+
printCommandOutput(result.stdout, result.stderr);
|
|
23
|
+
}
|
|
24
|
+
return 1;
|
|
11
25
|
}
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
export interface PackageHygieneShell {
|
|
2
2
|
run(command: string, args: string[], cwd: string): Promise<void>;
|
|
3
|
+
runResult?(command: string, args: string[], cwd: string): Promise<{
|
|
4
|
+
exitCode: number;
|
|
5
|
+
stdout: string;
|
|
6
|
+
stderr: string;
|
|
7
|
+
}>;
|
|
3
8
|
runStatus(command: string, args: string[], cwd: string, quiet?: boolean): Promise<number>;
|
|
4
9
|
}
|
|
5
10
|
export declare function fixPackageHygiene(root: string, verboseOrShell?: boolean | PackageHygieneShell, maybeShell?: PackageHygieneShell): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"package-hygiene.d.ts","sourceRoot":"","sources":["../../src/monorepo/package-hygiene.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,mBAAmB;IAClC,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACjE,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CAC3F;AAID,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,MAAM,EACZ,cAAc,GAAE,OAAO,GAAG,mBAA2B,EACrD,UAAU,GAAE,mBAAkC,GAC7C,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"package-hygiene.d.ts","sourceRoot":"","sources":["../../src/monorepo/package-hygiene.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,mBAAmB;IAClC,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACjE,SAAS,CAAC,CACR,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EAAE,EACd,GAAG,EAAE,MAAM,GACV,OAAO,CAAC;QACT,QAAQ,EAAE,MAAM,CAAC;QACjB,MAAM,EAAE,MAAM,CAAC;QACf,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC,CAAC;IACH,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CAC3F;AAID,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,MAAM,EACZ,cAAc,GAAE,OAAO,GAAG,mBAA2B,EACrD,UAAU,GAAE,mBAAkC,GAC7C,OAAO,CAAC,IAAI,CAAC,CAWf;AAED,wBAAsB,sBAAsB,CAC1C,IAAI,EAAE,MAAM,EACZ,cAAc,GAAE,OAAO,GAAG,mBAA2B,EACrD,UAAU,GAAE,mBAAkC,GAC7C,OAAO,CAAC,MAAM,CAAC,CAajB"}
|
|
@@ -1,20 +1,34 @@
|
|
|
1
|
-
import { run, runStatus } from '../lib/run.js';
|
|
2
|
-
const defaultShell = { run, runStatus };
|
|
1
|
+
import { printCommandOutput, run, runResult, runStatus } from '../lib/run.js';
|
|
2
|
+
const defaultShell = { run, runResult, runStatus };
|
|
3
3
|
export async function fixPackageHygiene(root, verboseOrShell = false, maybeShell = defaultShell) {
|
|
4
4
|
const verbose = typeof verboseOrShell === 'boolean' ? verboseOrShell : false;
|
|
5
5
|
const shell = typeof verboseOrShell === 'boolean' ? maybeShell : verboseOrShell;
|
|
6
|
-
const
|
|
6
|
+
const result = await runPackageHygieneCommand(shell, 'sherif', ['-f', '--select', 'highest'], root, verbose);
|
|
7
|
+
const status = result.exitCode;
|
|
7
8
|
if (status !== 0) {
|
|
9
|
+
if (!verbose) {
|
|
10
|
+
printCommandOutput(result.stdout, result.stderr);
|
|
11
|
+
}
|
|
8
12
|
throw new Error(`sherif -f --select highest failed with exit code ${status}`);
|
|
9
13
|
}
|
|
10
14
|
}
|
|
11
15
|
export async function validatePackageHygiene(root, verboseOrShell = false, maybeShell = defaultShell) {
|
|
12
16
|
const verbose = typeof verboseOrShell === 'boolean' ? verboseOrShell : false;
|
|
13
17
|
const shell = typeof verboseOrShell === 'boolean' ? maybeShell : verboseOrShell;
|
|
14
|
-
const
|
|
18
|
+
const result = await runPackageHygieneCommand(shell, 'sherif', ['--fail-on-warnings'], root, verbose);
|
|
19
|
+
const status = result.exitCode;
|
|
15
20
|
if (status !== 0) {
|
|
21
|
+
if (!verbose) {
|
|
22
|
+
printCommandOutput(result.stdout, result.stderr);
|
|
23
|
+
}
|
|
16
24
|
console.error('sherif package hygiene validation failed');
|
|
17
25
|
return 1;
|
|
18
26
|
}
|
|
19
27
|
return 0;
|
|
20
28
|
}
|
|
29
|
+
async function runPackageHygieneCommand(shell, command, args, root, verbose) {
|
|
30
|
+
if (!verbose && shell.runResult) {
|
|
31
|
+
return shell.runResult(command, args, root);
|
|
32
|
+
}
|
|
33
|
+
return { exitCode: await shell.runStatus(command, args, root, !verbose), stdout: '', stderr: '' };
|
|
34
|
+
}
|
|
@@ -3,7 +3,7 @@ import { join } from 'node:path';
|
|
|
3
3
|
import { publint } from 'publint';
|
|
4
4
|
import { formatMessage } from 'publint/utils';
|
|
5
5
|
import { isRecord } from '../lib/json.js';
|
|
6
|
-
import {
|
|
6
|
+
import { printCommandOutput, runResult } from '../lib/run.js';
|
|
7
7
|
import { listPublicPackages } from '../lib/workspace.js';
|
|
8
8
|
import { readPackedPackageJson, validatePackedWorkspaceDependencies } from './packed-manifest.js';
|
|
9
9
|
export async function validatePackedPublicPackages(root) {
|
|
@@ -116,9 +116,10 @@ async function packPackage(root, pkg) {
|
|
|
116
116
|
const tarballName = `.smoo-${process.pid}-${Date.now()}.tgz`;
|
|
117
117
|
const tarballPath = join(root, tarballName);
|
|
118
118
|
try {
|
|
119
|
-
const
|
|
120
|
-
if (
|
|
121
|
-
|
|
119
|
+
const result = await runResult('bun', ['pm', 'pack', '--filename', tarballName, '--ignore-scripts', '--quiet'], packageDir);
|
|
120
|
+
if (result.exitCode !== 0) {
|
|
121
|
+
printCommandOutput(result.stdout, result.stderr);
|
|
122
|
+
throw new Error(`bun pm pack failed with exit code ${result.exitCode}`);
|
|
122
123
|
}
|
|
123
124
|
const bytes = new Uint8Array(readFileSync(tarballPath));
|
|
124
125
|
return { path: tarballPath, arrayBuffer: bytes.slice().buffer };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { chmodSync, existsSync, statSync } from 'node:fs';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
|
-
import { runStatus } from '../../lib/run.js';
|
|
3
|
+
import { printCommandOutput, runResult, runStatus } from '../../lib/run.js';
|
|
4
4
|
import { readProjectTargets } from '../../nx/index.js';
|
|
5
5
|
import { syncBunLockfileVersions, validateBunLockfileVersions } from '../lockfile.js';
|
|
6
6
|
import { validateManagedFiles } from '../managed-files.js';
|
|
@@ -221,10 +221,14 @@ async function runBuild(ctx, options = {}) {
|
|
|
221
221
|
if (options.verbose) {
|
|
222
222
|
printCheckHeading('build', true);
|
|
223
223
|
}
|
|
224
|
-
const
|
|
224
|
+
const result = options.verbose
|
|
225
|
+
? { exitCode: await runStatus('nx', ['run-many', '-t', 'build'], ctx.root, false), stdout: '', stderr: '' }
|
|
226
|
+
: await runResult('nx', ['run-many', '-t', 'build'], ctx.root);
|
|
227
|
+
const status = result.exitCode;
|
|
225
228
|
if (status !== 0) {
|
|
226
229
|
if (!options.verbose) {
|
|
227
230
|
printCheckHeading('build', true);
|
|
231
|
+
printCommandOutput(result.stdout, result.stderr);
|
|
228
232
|
}
|
|
229
233
|
console.error('nx run-many -t build failed');
|
|
230
234
|
printCheckStatus('build', 1);
|
|
@@ -292,6 +296,8 @@ async function readResolvedTargetsByProject(ctx) {
|
|
|
292
296
|
{
|
|
293
297
|
targets: new Set(project.targets),
|
|
294
298
|
...(project.buildDependsOn ? { buildDependsOn: project.buildDependsOn } : {}),
|
|
299
|
+
...(project.targetExecutors ? { targetExecutors: project.targetExecutors } : {}),
|
|
300
|
+
...(project.targetScripts ? { targetScripts: project.targetScripts } : {}),
|
|
295
301
|
},
|
|
296
302
|
]));
|
|
297
303
|
}
|
package/dist/nx/index.d.ts
CHANGED
|
@@ -2,6 +2,8 @@ export interface ProjectTargets {
|
|
|
2
2
|
project: string;
|
|
3
3
|
targets: string[];
|
|
4
4
|
buildDependsOn?: string[];
|
|
5
|
+
targetExecutors?: Map<string, string>;
|
|
6
|
+
targetScripts?: Map<string, string>;
|
|
5
7
|
}
|
|
6
8
|
export interface CommandInvocation {
|
|
7
9
|
command: string;
|
|
@@ -12,6 +14,8 @@ export declare function nxShowProjectCommand(project: string): CommandInvocation
|
|
|
12
14
|
export declare function nxCacheDirectories(root: string): string[];
|
|
13
15
|
export declare function targetNamesFromNxProjectJson(value: unknown): string[];
|
|
14
16
|
export declare function buildDependsOnFromNxProjectJson(value: unknown): string[] | undefined;
|
|
17
|
+
export declare function targetExecutorsFromNxProjectJson(value: unknown): Map<string, string>;
|
|
18
|
+
export declare function targetScriptsFromNxProjectJson(value: unknown): Map<string, string>;
|
|
15
19
|
export declare function formatProjectTargetLines(projects: ProjectTargets[]): string;
|
|
16
20
|
export declare function projectNamesWithTarget(projects: ProjectTargets[], target: string): string[];
|
|
17
21
|
export declare function projectNamesFromNxShowProjectsOutput(output: string): string[];
|
package/dist/nx/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/nx/index.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/nx/index.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,eAAe,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,aAAa,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACrC;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB;AAED,wBAAgB,cAAc,IAAI,iBAAiB,CAElD;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,iBAAiB,CAEvE;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAEzD;AAED,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,EAAE,CAGrE;AAED,wBAAgB,+BAA+B,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,EAAE,GAAG,SAAS,CAOpF;AAED,wBAAgB,gCAAgC,CAAC,KAAK,EAAE,OAAO,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAYpF;AAED,wBAAgB,8BAA8B,CAAC,KAAK,EAAE,OAAO,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAgBlF;AAED,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,cAAc,EAAE,GAAG,MAAM,CAK3E;AAED,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,cAAc,EAAE,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAK3F;AAED,wBAAgB,oCAAoC,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAsB7E;AAED,wBAAsB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAK7D;AAED,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE;IAAE,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAQhG;AAED,wBAAsB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAG5D;AAED,wBAAsB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAa5D;AAED,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC,CAGhF"}
|
package/dist/nx/index.js
CHANGED
|
@@ -25,6 +25,36 @@ export function buildDependsOnFromNxProjectJson(value) {
|
|
|
25
25
|
}
|
|
26
26
|
return build.dependsOn;
|
|
27
27
|
}
|
|
28
|
+
export function targetExecutorsFromNxProjectJson(value) {
|
|
29
|
+
const targets = isRecord(value) ? recordProperty(value, 'targets') : null;
|
|
30
|
+
const executors = new Map();
|
|
31
|
+
if (!targets) {
|
|
32
|
+
return executors;
|
|
33
|
+
}
|
|
34
|
+
for (const [targetName, target] of Object.entries(targets)) {
|
|
35
|
+
if (isRecord(target) && typeof target.executor === 'string') {
|
|
36
|
+
executors.set(targetName, target.executor);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return executors;
|
|
40
|
+
}
|
|
41
|
+
export function targetScriptsFromNxProjectJson(value) {
|
|
42
|
+
const targets = isRecord(value) ? recordProperty(value, 'targets') : null;
|
|
43
|
+
const scripts = new Map();
|
|
44
|
+
if (!targets) {
|
|
45
|
+
return scripts;
|
|
46
|
+
}
|
|
47
|
+
for (const [targetName, target] of Object.entries(targets)) {
|
|
48
|
+
if (!isRecord(target)) {
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
const options = recordProperty(target, 'options');
|
|
52
|
+
if (typeof options?.script === 'string') {
|
|
53
|
+
scripts.set(targetName, options.script);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return scripts;
|
|
57
|
+
}
|
|
28
58
|
export function formatProjectTargetLines(projects) {
|
|
29
59
|
return projects
|
|
30
60
|
.flatMap((project) => project.targets.map((target) => `${project.project}:${target}`))
|
|
@@ -108,5 +138,7 @@ async function readProjectTarget(root, project) {
|
|
|
108
138
|
project,
|
|
109
139
|
targets: targetNamesFromNxProjectJson(parsed),
|
|
110
140
|
buildDependsOn: buildDependsOnFromNxProjectJson(parsed),
|
|
141
|
+
targetExecutors: targetExecutorsFromNxProjectJson(parsed),
|
|
142
|
+
targetScripts: targetScriptsFromNxProjectJson(parsed),
|
|
111
143
|
};
|
|
112
144
|
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Worktrees inherit the real project .envrc once, then stop. The same file is
|
|
2
|
+
# checked out inside .jcode-worktrees, so continuing after source_up_if_exists
|
|
3
|
+
# would run the devenv setup twice while sandbox blocks access to tooling/.
|
|
4
|
+
case "$PWD" in
|
|
5
|
+
*/.jcode-worktrees/*)
|
|
6
|
+
;;
|
|
7
|
+
*)
|
|
8
|
+
source_up_if_exists
|
|
9
|
+
;;
|
|
10
|
+
esac
|
|
11
|
+
|
|
12
|
+
watch_file tooling/direnv/envrc.sh
|
|
13
|
+
|
|
14
|
+
cd tooling/direnv
|
|
15
|
+
. envrc.sh
|
|
16
|
+
|
|
17
|
+
# Load local extensions and overrides
|
|
18
|
+
source_env_if_exists .envrc-local
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
import { existsSync } from 'node:fs';
|
|
3
|
+
import { mkdir, rmdir } from 'node:fs/promises';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { $ } from 'bun';
|
|
6
|
+
|
|
7
|
+
const devenvRoot = process.env.DEVENV_ROOT;
|
|
8
|
+
const projectRoot = path.resolve(`${devenvRoot}/../..`);
|
|
9
|
+
const startedWithoutNodeModules = !existsSync(path.join(projectRoot, 'node_modules'));
|
|
10
|
+
|
|
11
|
+
class CapturedCommandError extends Error {
|
|
12
|
+
constructor(
|
|
13
|
+
public readonly command: string,
|
|
14
|
+
public readonly exitCode: number,
|
|
15
|
+
public readonly stdout: Uint8Array,
|
|
16
|
+
public readonly stderr: Uint8Array,
|
|
17
|
+
) {
|
|
18
|
+
super(`${command} failed with exit code ${exitCode}`);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Go to project root
|
|
23
|
+
process.chdir(projectRoot);
|
|
24
|
+
|
|
25
|
+
try {
|
|
26
|
+
// Install dependencies first so node_modules/.bin tools are available
|
|
27
|
+
if (process.env.CI) {
|
|
28
|
+
try {
|
|
29
|
+
await runSetupCommand('bun install --frozen-lockfile', $`bun install --frozen-lockfile`, { quiet: false });
|
|
30
|
+
} catch (error) {
|
|
31
|
+
console.error('! Failed to install dependencies with frozen lockfile');
|
|
32
|
+
replayCapturedOutput(error);
|
|
33
|
+
await runSetupCommand('bun install', $`bun install`, { quiet: false });
|
|
34
|
+
console.error('git diff after install:');
|
|
35
|
+
await runSetupCommand('git diff', $`git diff`, { quiet: false, allowNonzero: true });
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
} else {
|
|
39
|
+
await installLocalDependencies();
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Bun selects its resolver mode at process startup. If this process started
|
|
43
|
+
// before node_modules existed, imports below can stay in auto-install mode
|
|
44
|
+
// even after bun install succeeds, so restart once into the real workspace.
|
|
45
|
+
if (startedWithoutNodeModules) {
|
|
46
|
+
await runSetupCommand('restart setup-environment.ts', $`bun --no-install ${import.meta.path}`, { quiet: false });
|
|
47
|
+
process.exit(0);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (!process.env.CI) {
|
|
51
|
+
const { syncRootRuntimeVersions } = await import('@smoothbricks/cli/monorepo/runtime');
|
|
52
|
+
await syncRootRuntimeVersions(projectRoot);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const { applyWorkspaceGitConfig } = await import('@smoothbricks/cli/monorepo/git-config');
|
|
56
|
+
await applyWorkspaceGitConfig(projectRoot);
|
|
57
|
+
} catch (error) {
|
|
58
|
+
if (error instanceof CapturedCommandError) {
|
|
59
|
+
console.error(`--- ERROR: setup-environment.ts failed while running: ${error.command}`);
|
|
60
|
+
console.error(`exit code: ${error.exitCode}`);
|
|
61
|
+
} else {
|
|
62
|
+
console.error(`--- ERROR: setup-environment.ts failed: ${error}`);
|
|
63
|
+
}
|
|
64
|
+
replayCapturedOutput(error);
|
|
65
|
+
console.error('\n---');
|
|
66
|
+
process.exit(1);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async function installLocalDependencies(): Promise<void> {
|
|
70
|
+
// bun install runs the root prepare script, which patches TypeScript with
|
|
71
|
+
// ts-patch. Multiple concurrent direnv activations can otherwise race while
|
|
72
|
+
// mutating the same files under node_modules.
|
|
73
|
+
await withSetupLock(async () => {
|
|
74
|
+
await runSetupCommand('bun install --no-summary', $`bun install --no-summary`);
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async function runSetupCommand(
|
|
79
|
+
command: string,
|
|
80
|
+
shell: ReturnType<typeof $>,
|
|
81
|
+
options: { quiet?: boolean; allowNonzero?: boolean } = {},
|
|
82
|
+
): Promise<void> {
|
|
83
|
+
const result = await shell
|
|
84
|
+
.quiet(options.quiet ?? true)
|
|
85
|
+
.nothrow()
|
|
86
|
+
.cwd(projectRoot);
|
|
87
|
+
if (result.exitCode !== 0 && options.allowNonzero !== true) {
|
|
88
|
+
throw new CapturedCommandError(command, result.exitCode, result.stdout, result.stderr);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async function withSetupLock<T>(fn: () => Promise<T>): Promise<T> {
|
|
93
|
+
const lockDir = path.join(projectRoot, 'tooling/direnv/.devenv/setup-environment.lock');
|
|
94
|
+
await acquireLock(lockDir);
|
|
95
|
+
try {
|
|
96
|
+
return await fn();
|
|
97
|
+
} finally {
|
|
98
|
+
await rmdir(lockDir).catch(() => undefined);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
async function acquireLock(lockDir: string): Promise<void> {
|
|
103
|
+
const deadline = Date.now() + 120_000;
|
|
104
|
+
while (true) {
|
|
105
|
+
try {
|
|
106
|
+
await mkdir(lockDir, { recursive: false });
|
|
107
|
+
return;
|
|
108
|
+
} catch (error) {
|
|
109
|
+
if (!isFileExistsError(error) || Date.now() > deadline) {
|
|
110
|
+
throw error;
|
|
111
|
+
}
|
|
112
|
+
await Bun.sleep(100);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function isFileExistsError(error: unknown): boolean {
|
|
118
|
+
return error instanceof Error && 'code' in error && error.code === 'EEXIST';
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function replayCapturedOutput(error: unknown): void {
|
|
122
|
+
if (!(error instanceof CapturedCommandError)) {
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
if (error.stdout.length > 0) {
|
|
126
|
+
process.stdout.write(error.stdout);
|
|
127
|
+
}
|
|
128
|
+
if (error.stderr.length > 0) {
|
|
129
|
+
process.stderr.write(error.stderr);
|
|
130
|
+
}
|
|
131
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@smoothbricks/cli",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "SmoothBricks monorepo automation CLI",
|
|
6
6
|
"bin": {
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
],
|
|
46
46
|
"dependencies": {
|
|
47
47
|
"@arethetypeswrong/cli": "^0.18.2",
|
|
48
|
-
"@smoothbricks/nx-plugin": "0.2.
|
|
48
|
+
"@smoothbricks/nx-plugin": "0.2.5",
|
|
49
49
|
"@smoothbricks/validation": "0.1.1",
|
|
50
50
|
"commander": "^14.0.3",
|
|
51
51
|
"publint": "^0.3.18",
|
package/src/lib/run.ts
CHANGED
|
@@ -70,6 +70,15 @@ export async function runResult(
|
|
|
70
70
|
};
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
+
export function printCommandOutput(stdout: string, stderr: string): void {
|
|
74
|
+
if (stdout.length > 0) {
|
|
75
|
+
console.log(trimTrailingNewline(stdout));
|
|
76
|
+
}
|
|
77
|
+
if (stderr.length > 0) {
|
|
78
|
+
console.error(trimTrailingNewline(stderr));
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
73
82
|
function mergeEnv(env: Record<string, string>): Record<string, string> {
|
|
74
83
|
const merged: Record<string, string> = {};
|
|
75
84
|
for (const [key, value] of Object.entries(process.env)) {
|
|
@@ -118,3 +127,7 @@ export async function findRepoRoot(): Promise<string> {
|
|
|
118
127
|
export function decode(bytes: Uint8Array): string {
|
|
119
128
|
return new TextDecoder().decode(bytes);
|
|
120
129
|
}
|
|
130
|
+
|
|
131
|
+
function trimTrailingNewline(value: string): string {
|
|
132
|
+
return value.endsWith('\n') ? value.slice(0, -1) : value;
|
|
133
|
+
}
|
|
@@ -38,6 +38,11 @@ interface DeployTargetInfo {
|
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
const managedFiles: ManagedFile[] = [
|
|
41
|
+
{
|
|
42
|
+
kind: 'raw',
|
|
43
|
+
source: 'envrc',
|
|
44
|
+
target: '.envrc',
|
|
45
|
+
},
|
|
41
46
|
{
|
|
42
47
|
kind: 'raw',
|
|
43
48
|
source: 'tooling/direnv/repo-path',
|
|
@@ -50,6 +55,12 @@ const managedFiles: ManagedFile[] = [
|
|
|
50
55
|
target: 'tooling/direnv/github-actions-bootstrap.sh',
|
|
51
56
|
executable: true,
|
|
52
57
|
},
|
|
58
|
+
{
|
|
59
|
+
kind: 'raw',
|
|
60
|
+
source: 'tooling/direnv/setup-environment.ts',
|
|
61
|
+
target: 'tooling/direnv/setup-environment.ts',
|
|
62
|
+
executable: true,
|
|
63
|
+
},
|
|
53
64
|
{
|
|
54
65
|
kind: 'raw',
|
|
55
66
|
source: 'tooling/git-hooks/pre-commit.sh',
|
package/src/monorepo/nx-sync.ts
CHANGED
|
@@ -1,13 +1,27 @@
|
|
|
1
|
-
import { runStatus } from '../lib/run.js';
|
|
1
|
+
import { printCommandOutput, runResult, runStatus } from '../lib/run.js';
|
|
2
2
|
|
|
3
3
|
export async function fixNxSync(root: string, verbose = false): Promise<void> {
|
|
4
|
-
const
|
|
4
|
+
const result = verbose
|
|
5
|
+
? { exitCode: await runStatus('nx', ['sync'], root, false), stdout: '', stderr: '' }
|
|
6
|
+
: await runResult('nx', ['sync'], root);
|
|
7
|
+
const status = result.exitCode;
|
|
5
8
|
if (status !== 0) {
|
|
9
|
+
if (!verbose) {
|
|
10
|
+
printCommandOutput(result.stdout, result.stderr);
|
|
11
|
+
}
|
|
6
12
|
throw new Error(`nx sync failed with exit code ${status}`);
|
|
7
13
|
}
|
|
8
14
|
}
|
|
9
15
|
|
|
10
16
|
export async function validateNxSync(root: string, verbose = false): Promise<number> {
|
|
11
|
-
const
|
|
12
|
-
|
|
17
|
+
const result = verbose
|
|
18
|
+
? { exitCode: await runStatus('nx', ['sync:check'], root, false), stdout: '', stderr: '' }
|
|
19
|
+
: await runResult('nx', ['sync:check'], root);
|
|
20
|
+
if (result.exitCode === 0) {
|
|
21
|
+
return 0;
|
|
22
|
+
}
|
|
23
|
+
if (!verbose) {
|
|
24
|
+
printCommandOutput(result.stdout, result.stderr);
|
|
25
|
+
}
|
|
26
|
+
return 1;
|
|
13
27
|
}
|
|
@@ -1,7 +1,12 @@
|
|
|
1
|
-
import { describe, expect, it } from 'bun:test';
|
|
1
|
+
import { afterEach, describe, expect, it, spyOn } from 'bun:test';
|
|
2
2
|
import { fixPackageHygiene, type PackageHygieneShell, validatePackageHygiene } from './package-hygiene.js';
|
|
3
3
|
|
|
4
4
|
describe('package hygiene', () => {
|
|
5
|
+
afterEach(() => {
|
|
6
|
+
console.log = originalConsoleLog;
|
|
7
|
+
console.error = originalConsoleError;
|
|
8
|
+
});
|
|
9
|
+
|
|
5
10
|
it('runs sherif in autofix mode and selects highest dependency versions', async () => {
|
|
6
11
|
const shell = new RecordingShell();
|
|
7
12
|
|
|
@@ -17,8 +22,39 @@ describe('package hygiene', () => {
|
|
|
17
22
|
|
|
18
23
|
expect(shell.statuses).toEqual([{ command: 'sherif', args: ['--fail-on-warnings'], cwd: '/repo' }]);
|
|
19
24
|
});
|
|
25
|
+
|
|
26
|
+
it('prints captured sherif output when quiet validation fails', async () => {
|
|
27
|
+
const shell = new FailingResultShell('sherif stdout', 'sherif stderr');
|
|
28
|
+
const logs = captureConsoleLogs();
|
|
29
|
+
const errors = captureConsoleErrors();
|
|
30
|
+
|
|
31
|
+
const failures = await validatePackageHygiene('/repo', shell);
|
|
32
|
+
|
|
33
|
+
expect(failures).toBe(1);
|
|
34
|
+
expect(logs).toEqual(['sherif stdout']);
|
|
35
|
+
expect(errors).toEqual(['sherif stderr', 'sherif package hygiene validation failed']);
|
|
36
|
+
});
|
|
20
37
|
});
|
|
21
38
|
|
|
39
|
+
const originalConsoleLog = console.log;
|
|
40
|
+
const originalConsoleError = console.error;
|
|
41
|
+
|
|
42
|
+
function captureConsoleLogs(): string[] {
|
|
43
|
+
const logs: string[] = [];
|
|
44
|
+
spyOn(console, 'log').mockImplementation((...args: unknown[]) => {
|
|
45
|
+
logs.push(args.join(' '));
|
|
46
|
+
});
|
|
47
|
+
return logs;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function captureConsoleErrors(): string[] {
|
|
51
|
+
const errors: string[] = [];
|
|
52
|
+
spyOn(console, 'error').mockImplementation((...args: unknown[]) => {
|
|
53
|
+
errors.push(args.join(' '));
|
|
54
|
+
});
|
|
55
|
+
return errors;
|
|
56
|
+
}
|
|
57
|
+
|
|
22
58
|
class RecordingShell implements PackageHygieneShell {
|
|
23
59
|
readonly runs: { command: string; args: string[]; cwd: string }[] = [];
|
|
24
60
|
readonly statuses: { command: string; args: string[]; cwd: string }[] = [];
|
|
@@ -32,3 +68,20 @@ class RecordingShell implements PackageHygieneShell {
|
|
|
32
68
|
return 0;
|
|
33
69
|
}
|
|
34
70
|
}
|
|
71
|
+
|
|
72
|
+
class FailingResultShell implements PackageHygieneShell {
|
|
73
|
+
constructor(
|
|
74
|
+
private readonly stdout: string,
|
|
75
|
+
private readonly stderr: string,
|
|
76
|
+
) {}
|
|
77
|
+
|
|
78
|
+
async run(): Promise<void> {}
|
|
79
|
+
|
|
80
|
+
async runResult(): Promise<{ exitCode: number; stdout: string; stderr: string }> {
|
|
81
|
+
return { exitCode: 1, stdout: this.stdout, stderr: this.stderr };
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async runStatus(): Promise<number> {
|
|
85
|
+
return 1;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
@@ -1,11 +1,20 @@
|
|
|
1
|
-
import { run, runStatus } from '../lib/run.js';
|
|
1
|
+
import { printCommandOutput, run, runResult, runStatus } from '../lib/run.js';
|
|
2
2
|
|
|
3
3
|
export interface PackageHygieneShell {
|
|
4
4
|
run(command: string, args: string[], cwd: string): Promise<void>;
|
|
5
|
+
runResult?(
|
|
6
|
+
command: string,
|
|
7
|
+
args: string[],
|
|
8
|
+
cwd: string,
|
|
9
|
+
): Promise<{
|
|
10
|
+
exitCode: number;
|
|
11
|
+
stdout: string;
|
|
12
|
+
stderr: string;
|
|
13
|
+
}>;
|
|
5
14
|
runStatus(command: string, args: string[], cwd: string, quiet?: boolean): Promise<number>;
|
|
6
15
|
}
|
|
7
16
|
|
|
8
|
-
const defaultShell: PackageHygieneShell = { run, runStatus };
|
|
17
|
+
const defaultShell: PackageHygieneShell = { run, runResult, runStatus };
|
|
9
18
|
|
|
10
19
|
export async function fixPackageHygiene(
|
|
11
20
|
root: string,
|
|
@@ -14,8 +23,12 @@ export async function fixPackageHygiene(
|
|
|
14
23
|
): Promise<void> {
|
|
15
24
|
const verbose = typeof verboseOrShell === 'boolean' ? verboseOrShell : false;
|
|
16
25
|
const shell = typeof verboseOrShell === 'boolean' ? maybeShell : verboseOrShell;
|
|
17
|
-
const
|
|
26
|
+
const result = await runPackageHygieneCommand(shell, 'sherif', ['-f', '--select', 'highest'], root, verbose);
|
|
27
|
+
const status = result.exitCode;
|
|
18
28
|
if (status !== 0) {
|
|
29
|
+
if (!verbose) {
|
|
30
|
+
printCommandOutput(result.stdout, result.stderr);
|
|
31
|
+
}
|
|
19
32
|
throw new Error(`sherif -f --select highest failed with exit code ${status}`);
|
|
20
33
|
}
|
|
21
34
|
}
|
|
@@ -27,10 +40,27 @@ export async function validatePackageHygiene(
|
|
|
27
40
|
): Promise<number> {
|
|
28
41
|
const verbose = typeof verboseOrShell === 'boolean' ? verboseOrShell : false;
|
|
29
42
|
const shell = typeof verboseOrShell === 'boolean' ? maybeShell : verboseOrShell;
|
|
30
|
-
const
|
|
43
|
+
const result = await runPackageHygieneCommand(shell, 'sherif', ['--fail-on-warnings'], root, verbose);
|
|
44
|
+
const status = result.exitCode;
|
|
31
45
|
if (status !== 0) {
|
|
46
|
+
if (!verbose) {
|
|
47
|
+
printCommandOutput(result.stdout, result.stderr);
|
|
48
|
+
}
|
|
32
49
|
console.error('sherif package hygiene validation failed');
|
|
33
50
|
return 1;
|
|
34
51
|
}
|
|
35
52
|
return 0;
|
|
36
53
|
}
|
|
54
|
+
|
|
55
|
+
async function runPackageHygieneCommand(
|
|
56
|
+
shell: PackageHygieneShell,
|
|
57
|
+
command: string,
|
|
58
|
+
args: string[],
|
|
59
|
+
root: string,
|
|
60
|
+
verbose: boolean,
|
|
61
|
+
): Promise<{ exitCode: number; stdout: string; stderr: string }> {
|
|
62
|
+
if (!verbose && shell.runResult) {
|
|
63
|
+
return shell.runResult(command, args, root);
|
|
64
|
+
}
|
|
65
|
+
return { exitCode: await shell.runStatus(command, args, root, !verbose), stdout: '', stderr: '' };
|
|
66
|
+
}
|
|
@@ -3,7 +3,7 @@ import { join } from 'node:path';
|
|
|
3
3
|
import { publint } from 'publint';
|
|
4
4
|
import { formatMessage } from 'publint/utils';
|
|
5
5
|
import { isRecord } from '../lib/json.js';
|
|
6
|
-
import {
|
|
6
|
+
import { printCommandOutput, runResult } from '../lib/run.js';
|
|
7
7
|
import type { PackageInfo } from '../lib/workspace.js';
|
|
8
8
|
import { listPublicPackages } from '../lib/workspace.js';
|
|
9
9
|
import { readPackedPackageJson, validatePackedWorkspaceDependencies } from './packed-manifest.js';
|
|
@@ -142,14 +142,14 @@ async function packPackage(root: string, pkg: PackageInfo): Promise<{ path: stri
|
|
|
142
142
|
const tarballName = `.smoo-${process.pid}-${Date.now()}.tgz`;
|
|
143
143
|
const tarballPath = join(root, tarballName);
|
|
144
144
|
try {
|
|
145
|
-
const
|
|
145
|
+
const result = await runResult(
|
|
146
146
|
'bun',
|
|
147
147
|
['pm', 'pack', '--filename', tarballName, '--ignore-scripts', '--quiet'],
|
|
148
148
|
packageDir,
|
|
149
|
-
true,
|
|
150
149
|
);
|
|
151
|
-
if (
|
|
152
|
-
|
|
150
|
+
if (result.exitCode !== 0) {
|
|
151
|
+
printCommandOutput(result.stdout, result.stderr);
|
|
152
|
+
throw new Error(`bun pm pack failed with exit code ${result.exitCode}`);
|
|
153
153
|
}
|
|
154
154
|
const bytes = new Uint8Array(readFileSync(tarballPath));
|
|
155
155
|
return { path: tarballPath, arrayBuffer: bytes.slice().buffer };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { chmodSync, existsSync, statSync } from 'node:fs';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
|
-
import { runStatus } from '../../lib/run.js';
|
|
3
|
+
import { printCommandOutput, runResult, runStatus } from '../../lib/run.js';
|
|
4
4
|
import { readProjectTargets } from '../../nx/index.js';
|
|
5
5
|
import { syncBunLockfileVersions, validateBunLockfileVersions } from '../lockfile.js';
|
|
6
6
|
import { validateManagedFiles } from '../managed-files.js';
|
|
@@ -315,10 +315,14 @@ async function runBuild(ctx: MonorepoContext, options: ValidatePackOptions = {})
|
|
|
315
315
|
if (options.verbose) {
|
|
316
316
|
printCheckHeading('build', true);
|
|
317
317
|
}
|
|
318
|
-
const
|
|
318
|
+
const result = options.verbose
|
|
319
|
+
? { exitCode: await runStatus('nx', ['run-many', '-t', 'build'], ctx.root, false), stdout: '', stderr: '' }
|
|
320
|
+
: await runResult('nx', ['run-many', '-t', 'build'], ctx.root);
|
|
321
|
+
const status = result.exitCode;
|
|
319
322
|
if (status !== 0) {
|
|
320
323
|
if (!options.verbose) {
|
|
321
324
|
printCheckHeading('build', true);
|
|
325
|
+
printCommandOutput(result.stdout, result.stderr);
|
|
322
326
|
}
|
|
323
327
|
console.error('nx run-many -t build failed');
|
|
324
328
|
printCheckStatus('build', 1);
|
|
@@ -391,6 +395,8 @@ async function readResolvedTargetsByProject(ctx: MonorepoContext): Promise<Map<s
|
|
|
391
395
|
{
|
|
392
396
|
targets: new Set(project.targets),
|
|
393
397
|
...(project.buildDependsOn ? { buildDependsOn: project.buildDependsOn } : {}),
|
|
398
|
+
...(project.targetExecutors ? { targetExecutors: project.targetExecutors } : {}),
|
|
399
|
+
...(project.targetScripts ? { targetScripts: project.targetScripts } : {}),
|
|
394
400
|
},
|
|
395
401
|
]),
|
|
396
402
|
);
|
package/src/nx/index.ts
CHANGED
|
@@ -9,6 +9,8 @@ export interface ProjectTargets {
|
|
|
9
9
|
project: string;
|
|
10
10
|
targets: string[];
|
|
11
11
|
buildDependsOn?: string[];
|
|
12
|
+
targetExecutors?: Map<string, string>;
|
|
13
|
+
targetScripts?: Map<string, string>;
|
|
12
14
|
}
|
|
13
15
|
|
|
14
16
|
export interface CommandInvocation {
|
|
@@ -42,6 +44,38 @@ export function buildDependsOnFromNxProjectJson(value: unknown): string[] | unde
|
|
|
42
44
|
return build.dependsOn;
|
|
43
45
|
}
|
|
44
46
|
|
|
47
|
+
export function targetExecutorsFromNxProjectJson(value: unknown): Map<string, string> {
|
|
48
|
+
const targets = isRecord(value) ? recordProperty(value, 'targets') : null;
|
|
49
|
+
const executors = new Map<string, string>();
|
|
50
|
+
if (!targets) {
|
|
51
|
+
return executors;
|
|
52
|
+
}
|
|
53
|
+
for (const [targetName, target] of Object.entries(targets)) {
|
|
54
|
+
if (isRecord(target) && typeof target.executor === 'string') {
|
|
55
|
+
executors.set(targetName, target.executor);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return executors;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function targetScriptsFromNxProjectJson(value: unknown): Map<string, string> {
|
|
62
|
+
const targets = isRecord(value) ? recordProperty(value, 'targets') : null;
|
|
63
|
+
const scripts = new Map<string, string>();
|
|
64
|
+
if (!targets) {
|
|
65
|
+
return scripts;
|
|
66
|
+
}
|
|
67
|
+
for (const [targetName, target] of Object.entries(targets)) {
|
|
68
|
+
if (!isRecord(target)) {
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
const options = recordProperty(target, 'options');
|
|
72
|
+
if (typeof options?.script === 'string') {
|
|
73
|
+
scripts.set(targetName, options.script);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return scripts;
|
|
77
|
+
}
|
|
78
|
+
|
|
45
79
|
export function formatProjectTargetLines(projects: ProjectTargets[]): string {
|
|
46
80
|
return projects
|
|
47
81
|
.flatMap((project) => project.targets.map((target) => `${project.project}:${target}`))
|
|
@@ -135,5 +169,7 @@ async function readProjectTarget(root: string, project: string): Promise<Project
|
|
|
135
169
|
project,
|
|
136
170
|
targets: targetNamesFromNxProjectJson(parsed),
|
|
137
171
|
buildDependsOn: buildDependsOnFromNxProjectJson(parsed),
|
|
172
|
+
targetExecutors: targetExecutorsFromNxProjectJson(parsed),
|
|
173
|
+
targetScripts: targetScriptsFromNxProjectJson(parsed),
|
|
138
174
|
};
|
|
139
175
|
}
|