@smoothbricks/cli 0.10.6 โ 0.10.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +1 -0
- package/dist/github-ci/index.d.ts +1 -0
- package/dist/github-ci/index.d.ts.map +1 -1
- package/dist/github-ci/index.js +23 -2
- package/dist/lib/json.d.ts +1 -1
- package/dist/lib/json.d.ts.map +1 -1
- package/dist/lib/json.js +3 -18
- package/dist/lib/workspace.d.ts.map +1 -1
- package/dist/lib/workspace.js +1 -0
- package/dist/monorepo/managed-files.d.ts +6 -22
- package/dist/monorepo/managed-files.d.ts.map +1 -1
- package/dist/monorepo/managed-files.js +12 -47
- package/dist/monorepo/package-policy.js +2 -2
- package/dist/monorepo/packs/index.d.ts.map +1 -1
- package/dist/monorepo/packs/index.js +2 -0
- package/dist/monorepo/publish-workflow.js +1 -1
- package/dist/monorepo/runtime.d.ts +2 -2
- package/dist/monorepo/runtime.d.ts.map +1 -1
- package/dist/monorepo/runtime.js +11 -16
- package/dist/monorepo/tool-validation.d.ts.map +1 -1
- package/dist/monorepo/tool-validation.js +20 -23
- package/dist/nx/index.d.ts +7 -4
- package/dist/nx/index.d.ts.map +1 -1
- package/dist/nx/index.js +117 -56
- package/managed/raw/tooling/devenv +71 -0
- package/package.json +4 -4
- package/src/cli.ts +8 -1
- package/src/github-ci/index.test.ts +50 -1
- package/src/github-ci/index.ts +31 -2
- package/src/lib/json.ts +1 -1
- package/src/lib/workspace.ts +1 -0
- package/src/monorepo/__tests__/publish-workflow.test.ts +11 -3
- package/src/monorepo/managed-files.test.ts +31 -22
- package/src/monorepo/managed-files.ts +15 -68
- package/src/monorepo/package-policy.test.ts +31 -0
- package/src/monorepo/package-policy.ts +2 -2
- package/src/monorepo/packs/index.ts +2 -0
- package/src/monorepo/publish-workflow.ts +5 -0
- package/src/monorepo/runtime.test.ts +23 -17
- package/src/monorepo/runtime.ts +12 -17
- package/src/monorepo/tool-validation.test.ts +15 -8
- package/src/monorepo/tool-validation.ts +20 -25
- package/src/nx/index.test.ts +45 -14
- package/src/nx/index.ts +133 -64
- package/managed/raw/patches/ttsc@0.19.3.patch +0 -67
package/dist/nx/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { NxProjectJson, NxTargetConfig, NxTargetOptions } from '../lib/json.js';
|
|
2
2
|
export interface ProjectTargets {
|
|
3
3
|
project: string;
|
|
4
4
|
root?: string;
|
|
@@ -6,6 +6,7 @@ export interface ProjectTargets {
|
|
|
6
6
|
buildDependsOn?: string[];
|
|
7
7
|
targetDependencies?: Map<string, string[]>;
|
|
8
8
|
targetExecutors?: Map<string, string>;
|
|
9
|
+
targetOptions?: Map<string, NxTargetOptions>;
|
|
9
10
|
targetOutputs?: Map<string, string[]>;
|
|
10
11
|
targetScripts?: Map<string, string>;
|
|
11
12
|
}
|
|
@@ -14,24 +15,26 @@ export interface CommandInvocation {
|
|
|
14
15
|
args: string[];
|
|
15
16
|
}
|
|
16
17
|
export declare function nxResetCommand(): CommandInvocation;
|
|
17
|
-
export declare function nxShowProjectCommand(project: string): CommandInvocation;
|
|
18
18
|
export declare function nxCacheDirectories(root: string): string[];
|
|
19
19
|
export declare function targetNamesFromNxProjectJson(value: NxProjectJson | null | undefined): string[];
|
|
20
20
|
export declare function projectRootFromNxProjectJson(value: NxProjectJson | null | undefined): string | undefined;
|
|
21
|
-
export declare function buildDependsOnFromNxProjectJson(value: NxProjectJson | null | undefined): string[] | undefined;
|
|
22
21
|
export declare function targetDependenciesFromNxProjectJson(value: NxProjectJson | null | undefined): Map<string, string[]>;
|
|
23
22
|
export declare function targetExecutorsFromNxProjectJson(value: NxProjectJson | null | undefined): Map<string, string>;
|
|
23
|
+
export declare function targetOptionsFromNxProjectJson(value: NxProjectJson | null | undefined): Map<string, NxTargetOptions>;
|
|
24
24
|
export declare function targetOutputsFromNxProjectJson(value: NxProjectJson | null | undefined): Map<string, string[]>;
|
|
25
25
|
export declare function targetScriptsFromNxProjectJson(value: NxProjectJson | null | undefined): Map<string, string>;
|
|
26
26
|
export declare function formatProjectTargetLines(projects: ProjectTargets[]): string;
|
|
27
27
|
export declare function projectNamesWithTarget(projects: ProjectTargets[], target: string): string[];
|
|
28
|
-
export declare function projectNamesFromNxShowProjectsOutput(output: string): string[];
|
|
29
28
|
export declare function listTargets(root: string): Promise<void>;
|
|
30
29
|
export declare function listProjects(root: string, options: {
|
|
31
30
|
withTarget?: string;
|
|
32
31
|
}): Promise<void>;
|
|
33
32
|
export declare function resetCache(root: string): Promise<void>;
|
|
34
33
|
export declare function cleanCache(root: string): Promise<void>;
|
|
34
|
+
export type NxProjects = Readonly<Record<string, NxProjectJson>>;
|
|
35
|
+
export declare function loadNxProjects(root: string): Promise<NxProjects>;
|
|
36
|
+
export declare function targetNamesFromProjects(projects: NxProjects): string[];
|
|
37
|
+
export declare function projectTargetsFromNxProjects(projects: NxProjects): ProjectTargets[];
|
|
35
38
|
export declare function readProjectTargets(root: string): Promise<ProjectTargets[]>;
|
|
36
39
|
export type { NxProjectJson, NxTargetConfig };
|
|
37
40
|
//# sourceMappingURL=index.d.ts.map
|
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":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/nx/index.ts"],"names":[],"mappings":"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"}
|
package/dist/nx/index.js
CHANGED
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
import { existsSync } from 'node:fs';
|
|
2
2
|
import { lstat, rm } from 'node:fs/promises';
|
|
3
3
|
import { join } from 'node:path';
|
|
4
|
-
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
4
|
+
// Nx package exports are CLI-oriented; this is the module the CLI uses for graph + daemon IPC.
|
|
5
|
+
import { createProjectGraphAsync } from 'nx/src/project-graph/project-graph.js';
|
|
6
|
+
import { workspaceRoot as primaryWorkspaceRoot } from 'nx/src/utils/workspace-root.js';
|
|
7
|
+
import { run, runResult } from '../lib/run.js';
|
|
7
8
|
export function nxResetCommand() {
|
|
8
9
|
return { command: 'nx', args: ['reset'] };
|
|
9
10
|
}
|
|
10
|
-
export function nxShowProjectCommand(project) {
|
|
11
|
-
return { command: 'nx', args: ['show', 'project', project, '--json'] };
|
|
12
|
-
}
|
|
13
11
|
export function nxCacheDirectories(root) {
|
|
14
12
|
return [join(root, '.nx/cache'), join(root, '.nx/workspace-data'), join(root, 'node_modules/.cache/nx')];
|
|
15
13
|
}
|
|
@@ -20,9 +18,6 @@ export function targetNamesFromNxProjectJson(value) {
|
|
|
20
18
|
export function projectRootFromNxProjectJson(value) {
|
|
21
19
|
return typeof value?.root === 'string' ? value.root : undefined;
|
|
22
20
|
}
|
|
23
|
-
export function buildDependsOnFromNxProjectJson(value) {
|
|
24
|
-
return targetDependenciesFromNxProjectJson(value).get('build');
|
|
25
|
-
}
|
|
26
21
|
export function targetDependenciesFromNxProjectJson(value) {
|
|
27
22
|
const targets = value?.targets;
|
|
28
23
|
const dependencies = new Map();
|
|
@@ -79,6 +74,19 @@ export function targetExecutorsFromNxProjectJson(value) {
|
|
|
79
74
|
}
|
|
80
75
|
return executors;
|
|
81
76
|
}
|
|
77
|
+
export function targetOptionsFromNxProjectJson(value) {
|
|
78
|
+
const targets = value?.targets;
|
|
79
|
+
const options = new Map();
|
|
80
|
+
if (!targets) {
|
|
81
|
+
return options;
|
|
82
|
+
}
|
|
83
|
+
for (const [targetName, target] of Object.entries(targets)) {
|
|
84
|
+
if (target.options) {
|
|
85
|
+
options.set(targetName, target.options);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return options;
|
|
89
|
+
}
|
|
82
90
|
export function targetOutputsFromNxProjectJson(value) {
|
|
83
91
|
return targetStringArraysFromNxProjectJson(value, 'outputs');
|
|
84
92
|
}
|
|
@@ -127,28 +135,6 @@ export function projectNamesWithTarget(projects, target) {
|
|
|
127
135
|
.map((project) => project.project)
|
|
128
136
|
.sort((a, b) => a.localeCompare(b));
|
|
129
137
|
}
|
|
130
|
-
export function projectNamesFromNxShowProjectsOutput(output) {
|
|
131
|
-
const trimmed = output.trim();
|
|
132
|
-
if (!trimmed) {
|
|
133
|
-
return [];
|
|
134
|
-
}
|
|
135
|
-
if (trimmed.startsWith('[')) {
|
|
136
|
-
try {
|
|
137
|
-
const parsed = parseStringArrayText(trimmed);
|
|
138
|
-
if (parsed) {
|
|
139
|
-
return parsed.sort((a, b) => a.localeCompare(b));
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
catch {
|
|
143
|
-
// Fall through to legacy newline parsing for older Nx output shapes.
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
return trimmed
|
|
147
|
-
.split('\n')
|
|
148
|
-
.map((line) => line.trim())
|
|
149
|
-
.filter(Boolean)
|
|
150
|
-
.sort((a, b) => a.localeCompare(b));
|
|
151
|
-
}
|
|
152
138
|
export async function listTargets(root) {
|
|
153
139
|
const output = formatProjectTargetLines(await readProjectTargets(root));
|
|
154
140
|
if (output) {
|
|
@@ -182,30 +168,105 @@ export async function cleanCache(root) {
|
|
|
182
168
|
console.log(`Removed ${path}`);
|
|
183
169
|
}
|
|
184
170
|
}
|
|
171
|
+
/**
|
|
172
|
+
* Load the resolved project graph through Nx's API. The current workspace uses the
|
|
173
|
+
* daemon in-process; foreign roots use an isolated process because Nx snapshots its
|
|
174
|
+
* workspace cache paths when the module loads.
|
|
175
|
+
*/
|
|
176
|
+
let graphLoadTail = Promise.resolve();
|
|
177
|
+
export function loadNxProjects(root) {
|
|
178
|
+
const result = graphLoadTail.then(() => loadNxProjectsNow(root));
|
|
179
|
+
graphLoadTail = result.then(() => undefined, () => undefined);
|
|
180
|
+
return result;
|
|
181
|
+
}
|
|
182
|
+
async function loadNxProjectsNow(root) {
|
|
183
|
+
if (root !== primaryWorkspaceRoot) {
|
|
184
|
+
return loadForeignNxProjects(root);
|
|
185
|
+
}
|
|
186
|
+
const graph = await createProjectGraphAsync({ exitOnError: false });
|
|
187
|
+
return nxProjectsFromNodes(graph.nodes);
|
|
188
|
+
}
|
|
189
|
+
const FOREIGN_GRAPH_SCRIPT = `
|
|
190
|
+
import('nx/src/project-graph/project-graph.js')
|
|
191
|
+
.then(async ({ createProjectGraphAsync }) => {
|
|
192
|
+
const graph = await createProjectGraphAsync({ exitOnError: false });
|
|
193
|
+
const projects = Object.fromEntries(
|
|
194
|
+
Object.entries(graph.nodes).map(([name, node]) => [
|
|
195
|
+
name,
|
|
196
|
+
{ ...node.data, name: node.data?.name ?? name, targets: node.data?.targets ?? {} },
|
|
197
|
+
]),
|
|
198
|
+
);
|
|
199
|
+
process.stdout.write(JSON.stringify(projects));
|
|
200
|
+
})
|
|
201
|
+
.catch((error) => {
|
|
202
|
+
console.error(error);
|
|
203
|
+
process.exitCode = 1;
|
|
204
|
+
});
|
|
205
|
+
`;
|
|
206
|
+
async function loadForeignNxProjects(root) {
|
|
207
|
+
const result = await runResult(process.execPath, ['--eval', FOREIGN_GRAPH_SCRIPT], root, {
|
|
208
|
+
NX_DAEMON: 'false',
|
|
209
|
+
NX_ISOLATE_PLUGINS: 'false',
|
|
210
|
+
NX_WORKSPACE_ROOT_PATH: root,
|
|
211
|
+
});
|
|
212
|
+
if (result.exitCode !== 0) {
|
|
213
|
+
throw new Error(`Failed to load Nx project graph for ${root}: ${result.stderr.trim()}`);
|
|
214
|
+
}
|
|
215
|
+
const parsed = JSON.parse(result.stdout);
|
|
216
|
+
if (!isNxProjects(parsed)) {
|
|
217
|
+
throw new Error(`Nx returned an invalid project graph for ${root}`);
|
|
218
|
+
}
|
|
219
|
+
return parsed;
|
|
220
|
+
}
|
|
221
|
+
function nxProjectsFromNodes(nodes) {
|
|
222
|
+
const projects = {};
|
|
223
|
+
for (const [name, node] of Object.entries(nodes)) {
|
|
224
|
+
projects[name] = {
|
|
225
|
+
...node.data,
|
|
226
|
+
name: node.data.name ?? name,
|
|
227
|
+
targets: node.data.targets ?? {},
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
return projects;
|
|
231
|
+
}
|
|
232
|
+
function isNxProjects(value) {
|
|
233
|
+
if (!isRecord(value))
|
|
234
|
+
return false;
|
|
235
|
+
return Object.values(value).every((project) => isRecord(project) &&
|
|
236
|
+
(project.name === undefined || typeof project.name === 'string') &&
|
|
237
|
+
(project.root === undefined || typeof project.root === 'string') &&
|
|
238
|
+
(project.targets === undefined || (isRecord(project.targets) && Object.values(project.targets).every(isRecord))));
|
|
239
|
+
}
|
|
240
|
+
function isRecord(value) {
|
|
241
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
242
|
+
}
|
|
243
|
+
export function targetNamesFromProjects(projects) {
|
|
244
|
+
const names = new Set();
|
|
245
|
+
for (const project of Object.values(projects)) {
|
|
246
|
+
for (const targetName of targetNamesFromNxProjectJson(project)) {
|
|
247
|
+
names.add(targetName);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
return [...names];
|
|
251
|
+
}
|
|
252
|
+
export function projectTargetsFromNxProjects(projects) {
|
|
253
|
+
return Object.entries(projects)
|
|
254
|
+
.sort(([left], [right]) => left.localeCompare(right))
|
|
255
|
+
.map(([project, metadata]) => {
|
|
256
|
+
const targetDependencies = targetDependenciesFromNxProjectJson(metadata);
|
|
257
|
+
return {
|
|
258
|
+
project,
|
|
259
|
+
root: projectRootFromNxProjectJson(metadata),
|
|
260
|
+
targets: targetNamesFromNxProjectJson(metadata),
|
|
261
|
+
buildDependsOn: targetDependencies.get('build'),
|
|
262
|
+
targetDependencies,
|
|
263
|
+
targetExecutors: targetExecutorsFromNxProjectJson(metadata),
|
|
264
|
+
targetOptions: targetOptionsFromNxProjectJson(metadata),
|
|
265
|
+
targetOutputs: targetOutputsFromNxProjectJson(metadata),
|
|
266
|
+
targetScripts: targetScriptsFromNxProjectJson(metadata),
|
|
267
|
+
};
|
|
268
|
+
});
|
|
269
|
+
}
|
|
185
270
|
export async function readProjectTargets(root) {
|
|
186
|
-
|
|
187
|
-
return Promise.all(projects.map((project) => readProjectTarget(root, project)));
|
|
188
|
-
}
|
|
189
|
-
async function readNxProjectNames(root) {
|
|
190
|
-
const result = await $ `nx show projects`.cwd(root).quiet();
|
|
191
|
-
return projectNamesFromNxShowProjectsOutput(decode(result.stdout));
|
|
192
|
-
}
|
|
193
|
-
async function readProjectTarget(root, project) {
|
|
194
|
-
const command = nxShowProjectCommand(project);
|
|
195
|
-
const result = await $ `${command.command} ${command.args}`.cwd(root).quiet();
|
|
196
|
-
const parsed = parseNxProjectJsonText(decode(result.stdout));
|
|
197
|
-
if (!parsed) {
|
|
198
|
-
throw new Error(`Unable to inspect Nx project ${project}.`);
|
|
199
|
-
}
|
|
200
|
-
const targetDependencies = targetDependenciesFromNxProjectJson(parsed);
|
|
201
|
-
return {
|
|
202
|
-
project,
|
|
203
|
-
root: projectRootFromNxProjectJson(parsed),
|
|
204
|
-
targets: targetNamesFromNxProjectJson(parsed),
|
|
205
|
-
buildDependsOn: targetDependencies.get('build'),
|
|
206
|
-
targetDependencies,
|
|
207
|
-
targetExecutors: targetExecutorsFromNxProjectJson(parsed),
|
|
208
|
-
targetOutputs: targetOutputsFromNxProjectJson(parsed),
|
|
209
|
-
targetScripts: targetScriptsFromNxProjectJson(parsed),
|
|
210
|
-
};
|
|
271
|
+
return projectTargetsFromNxProjects(await loadNxProjects(root));
|
|
211
272
|
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Wrapper script to run devenv from anywhere in the monorepo
|
|
3
|
+
# This allows us to keep all devenv files in tooling/direnv
|
|
4
|
+
|
|
5
|
+
# Save the current working directory
|
|
6
|
+
ORIGINAL_PWD="$(pwd)"
|
|
7
|
+
SELF_PATH="$(realpath "$0")"
|
|
8
|
+
|
|
9
|
+
# Find the root of the git repository
|
|
10
|
+
TOOLING_DIR="$(dirname "$SELF_PATH")"
|
|
11
|
+
DEVENV_DIR="$TOOLING_DIR/direnv"
|
|
12
|
+
if [ ! -d "$DEVENV_DIR" ]; then
|
|
13
|
+
echo "Error: Could not find devenv directory at $DEVENV_DIR" >&2
|
|
14
|
+
exit 1
|
|
15
|
+
fi
|
|
16
|
+
|
|
17
|
+
# Find the actual Nix-built devenv binary, skipping every wrapper script.
|
|
18
|
+
# WHY: PATH routinely holds more than one of these wrappers at once โ this
|
|
19
|
+
# repo's tooling/devenv, sibling repos' wrappers, and direnv-stacked parent
|
|
20
|
+
# environments. A plain "not equal to SELF_PATH" check happily selects a
|
|
21
|
+
# *different* wrapper and exec's it, so `devenv` bounces into the wrong repo
|
|
22
|
+
# (or self-execs forever on cheap checks like `devenv version`). The real devenv
|
|
23
|
+
# is always the Nix-managed executable under /nix/store, so match that and ignore
|
|
24
|
+
# every wrapper regardless of PATH ordering. (Matching the store path is also more
|
|
25
|
+
# robust than a "is it a binary" test: Nix sometimes ships tools as makeWrapper
|
|
26
|
+
# shell scripts, which are still the correct entry point.)
|
|
27
|
+
DEVENV_BIN=""
|
|
28
|
+
while IFS= read -r candidate; do
|
|
29
|
+
candidate_path="$(realpath "$candidate")"
|
|
30
|
+
case "$candidate_path" in
|
|
31
|
+
/nix/store/*)
|
|
32
|
+
if [ -f "$candidate_path" ] && [ -x "$candidate_path" ]; then
|
|
33
|
+
DEVENV_BIN="$candidate_path"
|
|
34
|
+
break
|
|
35
|
+
fi
|
|
36
|
+
;;
|
|
37
|
+
esac
|
|
38
|
+
done < <(which -a devenv 2>/dev/null)
|
|
39
|
+
|
|
40
|
+
if [ -z "$DEVENV_BIN" ]; then
|
|
41
|
+
echo "Error: Could not find the Nix-built devenv binary (under /nix/store) in PATH" >&2
|
|
42
|
+
exit 1
|
|
43
|
+
fi
|
|
44
|
+
|
|
45
|
+
# If we're in a project, just run devenv
|
|
46
|
+
if [ -f devenv.nix ]; then
|
|
47
|
+
exec "$DEVENV_BIN" "$@"
|
|
48
|
+
fi
|
|
49
|
+
|
|
50
|
+
# Change to the tooling/direnv directory where devenv files are located
|
|
51
|
+
cd "$DEVENV_DIR" || exit 1
|
|
52
|
+
|
|
53
|
+
# Export the original directory so enterShell can use it
|
|
54
|
+
export DEVENV_SHELL_PWD="$ORIGINAL_PWD"
|
|
55
|
+
|
|
56
|
+
# Run the actual devenv command with all arguments passed through.
|
|
57
|
+
# For updates, refresh nvfetcher-managed overlay sources first so packages like
|
|
58
|
+
# jmunch-mcp and jcodemunch-mcp are current before devenv.lock is updated.
|
|
59
|
+
if [ "${1:-}" = "update" ]; then
|
|
60
|
+
if [ -f "$DEVENV_DIR/nixpkgs-overlay/nvfetcher.toml" ]; then
|
|
61
|
+
echo "Running nvfetcher for nixpkgs-overlay/..."
|
|
62
|
+
(cd "$DEVENV_DIR/nixpkgs-overlay" && echo "
|
|
63
|
+
[keys]
|
|
64
|
+
github = \"$(gh auth token)\"
|
|
65
|
+
" > .gh-auth && (nvfetcher -k .gh-auth -o _sources || true) && rm .gh-auth)
|
|
66
|
+
fi
|
|
67
|
+
|
|
68
|
+
exec "$DEVENV_BIN" "$@"
|
|
69
|
+
fi
|
|
70
|
+
|
|
71
|
+
exec "$DEVENV_BIN" "$@"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@smoothbricks/cli",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.7",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "SmoothBricks monorepo automation CLI",
|
|
6
6
|
"bin": {
|
|
@@ -64,8 +64,8 @@
|
|
|
64
64
|
"dependencies": {
|
|
65
65
|
"@arethetypeswrong/core": "^0.18.2",
|
|
66
66
|
"@prettier/sync": "^0.6.1",
|
|
67
|
-
"@smoothbricks/nx-plugin": "0.3.
|
|
68
|
-
"@smoothbricks/validation": "0.1.
|
|
67
|
+
"@smoothbricks/nx-plugin": "0.3.5",
|
|
68
|
+
"@smoothbricks/validation": "0.1.5",
|
|
69
69
|
"commander": "^14.0.3",
|
|
70
70
|
"prettier": "^3.6.1",
|
|
71
71
|
"publint": "^0.3.18",
|
|
@@ -73,7 +73,7 @@
|
|
|
73
73
|
"sherif": "^1.11.1",
|
|
74
74
|
"toml-eslint-parser": "^1.0.3",
|
|
75
75
|
"tslib": "^2.8.1",
|
|
76
|
-
"typia": "13.
|
|
76
|
+
"typia": "13.2.0"
|
|
77
77
|
},
|
|
78
78
|
"devDependencies": {
|
|
79
79
|
"fast-check": "^4.4.0"
|
package/src/cli.ts
CHANGED
|
@@ -348,10 +348,17 @@ function buildProgram(): Command {
|
|
|
348
348
|
.command('nx-run-many')
|
|
349
349
|
.requiredOption('--targets <targets>')
|
|
350
350
|
.option('--projects <projects>')
|
|
351
|
+
.option('--projects-with-targets <targets>', 'select projects owning any comma-separated target or target glob')
|
|
351
352
|
.option('--configuration <configuration>')
|
|
352
353
|
.option('--collect-outputs <directory>')
|
|
353
354
|
.action(
|
|
354
|
-
async (options: {
|
|
355
|
+
async (options: {
|
|
356
|
+
targets: string;
|
|
357
|
+
projects?: string;
|
|
358
|
+
projectsWithTargets?: string;
|
|
359
|
+
configuration?: string;
|
|
360
|
+
collectOutputs?: string;
|
|
361
|
+
}) => {
|
|
355
362
|
const { githubCiNxRunMany } = await import('./github-ci/index.js');
|
|
356
363
|
await githubCiNxRunMany(await findRepoRoot(), options);
|
|
357
364
|
},
|
|
@@ -53,6 +53,28 @@ describe('GitHub CI Nx target expansion', () => {
|
|
|
53
53
|
]);
|
|
54
54
|
});
|
|
55
55
|
|
|
56
|
+
it('selects test owners from target globs and fails if any selected project lacks test', () => {
|
|
57
|
+
const platformProjects = projects.map((project) =>
|
|
58
|
+
project.project === 'mobile' ? { ...project, targets: [...project.targets, 'test'] } : project,
|
|
59
|
+
);
|
|
60
|
+
const expanded = expandNxTargetRuns(platformProjects, {
|
|
61
|
+
targets: 'test',
|
|
62
|
+
projectsWithTargets: '*-macos,*-ios',
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
expect(expanded.runs.map((run) => [run.target, run.projects.map((project) => project.project)])).toEqual([
|
|
66
|
+
['test', ['desktop', 'mobile']],
|
|
67
|
+
]);
|
|
68
|
+
const [testRun] = expanded.runs;
|
|
69
|
+
if (!testRun) {
|
|
70
|
+
throw new Error('Expected a test target run.');
|
|
71
|
+
}
|
|
72
|
+
expect(nxRunManyArgs(testRun)).toEqual(['run-many', '-t', 'test', '--projects=desktop,mobile', '--parallel=100%']);
|
|
73
|
+
expect(() => expandNxTargetRuns(projects, { targets: 'test', projectsWithTargets: '*-macos,*-ios' })).toThrow(
|
|
74
|
+
'Nx target test is missing for project(s) selected by --projects-with-targets *-macos,*-ios: mobile.',
|
|
75
|
+
);
|
|
76
|
+
});
|
|
77
|
+
|
|
56
78
|
it('reports a zero-match glob as an empty run set', () => {
|
|
57
79
|
expect(expandNxTargetRuns(projects, { targets: '*-windows' })).toEqual({
|
|
58
80
|
runs: [],
|
|
@@ -155,6 +177,21 @@ describe('collected Nx outputs', () => {
|
|
|
155
177
|
});
|
|
156
178
|
});
|
|
157
179
|
|
|
180
|
+
it('runs test for projects selected by platform target ownership', async () => {
|
|
181
|
+
await withNxRunManyFixture(async ({ root }) => {
|
|
182
|
+
await writeFile(join(root, 'packages/app/test-target.ts'), "await Bun.write('test-ran.txt', 'tested');\n");
|
|
183
|
+
await writeFile(join(root, 'packages/app/build-target.ts'), "throw new Error('build must not run here');\n");
|
|
184
|
+
|
|
185
|
+
await githubCiNxRunMany(root, {
|
|
186
|
+
targets: 'test',
|
|
187
|
+
projectsWithTargets: '*-macos,*-ios',
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
await expect(readFile(join(root, 'test-ran.txt'), 'utf8')).resolves.toBe('tested');
|
|
191
|
+
await expect(readFile(join(root, 'build-ran.txt'), 'utf8')).rejects.toThrow();
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
|
|
158
195
|
it('applies an empty manifest after artifact transport omits its empty workspace directory', async () => {
|
|
159
196
|
await withOutputFixture(async ({ root, artifact }) => {
|
|
160
197
|
const manifest = await collectNxOutputs(root, artifact, [], SOURCE_SHA);
|
|
@@ -472,7 +509,19 @@ async function withNxRunManyFixture(
|
|
|
472
509
|
join(root, 'packages/app/package.json'),
|
|
473
510
|
JSON.stringify({
|
|
474
511
|
name: '@fixture/app',
|
|
475
|
-
nx: {
|
|
512
|
+
nx: {
|
|
513
|
+
name: 'app',
|
|
514
|
+
targets: {
|
|
515
|
+
'build-macos': {
|
|
516
|
+
executor: 'nx:run-commands',
|
|
517
|
+
options: { command: 'bun packages/app/build-target.ts', cwd: '.' },
|
|
518
|
+
},
|
|
519
|
+
test: {
|
|
520
|
+
executor: 'nx:run-commands',
|
|
521
|
+
options: { command: 'bun packages/app/test-target.ts', cwd: '.' },
|
|
522
|
+
},
|
|
523
|
+
},
|
|
524
|
+
},
|
|
476
525
|
}),
|
|
477
526
|
);
|
|
478
527
|
await $`git init --quiet`.cwd(root);
|
package/src/github-ci/index.ts
CHANGED
|
@@ -168,6 +168,7 @@ export interface NxRunManyOptions {
|
|
|
168
168
|
configuration?: string;
|
|
169
169
|
collectOutputs?: string;
|
|
170
170
|
allowEmptyProjects?: boolean;
|
|
171
|
+
projectsWithTargets?: string;
|
|
171
172
|
}
|
|
172
173
|
|
|
173
174
|
export interface ExpandedNxTargetRuns {
|
|
@@ -176,10 +177,18 @@ export interface ExpandedNxTargetRuns {
|
|
|
176
177
|
}
|
|
177
178
|
|
|
178
179
|
export function expandNxTargetRuns(projects: ProjectTargets[], options: NxRunManyOptions): ExpandedNxTargetRuns {
|
|
179
|
-
const
|
|
180
|
-
if (options.projects !== undefined &&
|
|
180
|
+
const namedProjects = selectProjects(projects, options.projects);
|
|
181
|
+
if (options.projects !== undefined && namedProjects.length === 0 && options.allowEmptyProjects !== true) {
|
|
181
182
|
throw new Error(`No Nx projects matched --projects ${options.projects}.`);
|
|
182
183
|
}
|
|
184
|
+
const selectedProjects = selectProjectsWithTargets(namedProjects, options.projectsWithTargets);
|
|
185
|
+
if (
|
|
186
|
+
options.projectsWithTargets !== undefined &&
|
|
187
|
+
selectedProjects.length === 0 &&
|
|
188
|
+
options.allowEmptyProjects !== true
|
|
189
|
+
) {
|
|
190
|
+
throw new Error(`No Nx projects own targets matching --projects-with-targets ${options.projectsWithTargets}.`);
|
|
191
|
+
}
|
|
183
192
|
const selectedTargetNames = [...new Set(selectedProjects.flatMap((project) => project.targets))].sort((a, b) =>
|
|
184
193
|
a.localeCompare(b),
|
|
185
194
|
);
|
|
@@ -195,6 +204,16 @@ export function expandNxTargetRuns(projects: ProjectTargets[], options: NxRunMan
|
|
|
195
204
|
unmatchedGlobs.push(targetPattern);
|
|
196
205
|
}
|
|
197
206
|
for (const target of targets) {
|
|
207
|
+
if (!isGlob && options.projectsWithTargets !== undefined) {
|
|
208
|
+
const missingProjects = selectedProjects
|
|
209
|
+
.filter((project) => !project.targets.includes(target))
|
|
210
|
+
.map((project) => project.project);
|
|
211
|
+
if (missingProjects.length > 0) {
|
|
212
|
+
throw new Error(
|
|
213
|
+
`Nx target ${target} is missing for project(s) selected by --projects-with-targets ${options.projectsWithTargets}: ${missingProjects.join(', ')}.`,
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
198
217
|
if (addedTargets.has(target)) {
|
|
199
218
|
continue;
|
|
200
219
|
}
|
|
@@ -321,6 +340,16 @@ function selectProjects(projects: ProjectTargets[], selectors: string | undefine
|
|
|
321
340
|
.sort((left, right) => left.project.localeCompare(right.project));
|
|
322
341
|
}
|
|
323
342
|
|
|
343
|
+
function selectProjectsWithTargets(projects: ProjectTargets[], selectors: string | undefined): ProjectTargets[] {
|
|
344
|
+
if (selectors === undefined) {
|
|
345
|
+
return projects;
|
|
346
|
+
}
|
|
347
|
+
const patterns = commaSeparatedValues(selectors);
|
|
348
|
+
return projects.filter((project) =>
|
|
349
|
+
project.targets.some((target) => patterns.some((pattern) => new Bun.Glob(pattern).match(target))),
|
|
350
|
+
);
|
|
351
|
+
}
|
|
352
|
+
|
|
324
353
|
function commaSeparatedValues(value: string): string[] {
|
|
325
354
|
return value
|
|
326
355
|
.split(',')
|
package/src/lib/json.ts
CHANGED
|
@@ -80,7 +80,7 @@ export type NxDependsOn = string | NxDependsOnObject;
|
|
|
80
80
|
/** Nx target options are executor-specific open bags. */
|
|
81
81
|
export type NxTargetOptions = Record<string, unknown>;
|
|
82
82
|
|
|
83
|
-
export type NxInput = string |
|
|
83
|
+
export type NxInput = string | object;
|
|
84
84
|
|
|
85
85
|
export interface NxTargetConfig {
|
|
86
86
|
executor?: string;
|
package/src/lib/workspace.ts
CHANGED
|
@@ -129,6 +129,7 @@ function listWorkspacePackageJsonPaths(root: string, workspacePatterns: string[]
|
|
|
129
129
|
const paths: string[] = [];
|
|
130
130
|
for (const pattern of workspacePatterns) {
|
|
131
131
|
if (!pattern.endsWith('/*')) {
|
|
132
|
+
paths.push(join(root, pattern, 'package.json'));
|
|
132
133
|
continue;
|
|
133
134
|
}
|
|
134
135
|
const parent = join(root, pattern.slice(0, -2));
|
|
@@ -99,7 +99,7 @@ describe('publish workflow definition', () => {
|
|
|
99
99
|
|
|
100
100
|
expect(stepAnchorNumbers(singleJob)).toEqual(Array.from({ length: 15 }, (_, index) => index + 1));
|
|
101
101
|
expect(stepAnchorNumbers(linuxCandidate)).toEqual(Array.from({ length: 19 }, (_, index) => index + 1));
|
|
102
|
-
expect(stepAnchorNumbers(macosPlatform)).toEqual(Array.from({ length:
|
|
102
|
+
expect(stepAnchorNumbers(macosPlatform)).toEqual(Array.from({ length: 8 }, (_, index) => index + 1));
|
|
103
103
|
expect(stepAnchorNumbers(finalJob)).toEqual(Array.from({ length: 14 }, (_, index) => index + 1));
|
|
104
104
|
expect(singleJob).toContain('# Step 14\n - name: ๐ฆ Publish release (${{ steps.version.outputs.mode }})');
|
|
105
105
|
expect(singleJob).toContain('# Step 15\n - name: ๐งน Cleanup and cache Nix/devenv');
|
|
@@ -109,7 +109,7 @@ describe('publish workflow definition', () => {
|
|
|
109
109
|
);
|
|
110
110
|
expect(linuxCandidate).toContain('# Step 19\n - name: ๐งน Cleanup and cache Nix/devenv');
|
|
111
111
|
expect(macosPlatform).toContain('# Step 5\n - name: ๐ Build selected macOS and iOS release outputs');
|
|
112
|
-
expect(macosPlatform).toContain('# Step
|
|
112
|
+
expect(macosPlatform).toContain('# Step 8\n - name: ๐งน Cleanup and cache Nix/devenv');
|
|
113
113
|
expect(finalJob).toContain('# Step 7\n - name: ๐งฏ Repair pending releases');
|
|
114
114
|
expect(finalJob).toContain('# Step 8\n - name: โป๏ธ Restore validated release state');
|
|
115
115
|
expect(finalJob).toContain('# Step 9\n - name: ๐ฆ Apply verified Linux outputs');
|
|
@@ -156,12 +156,20 @@ describe('publish workflow definition', () => {
|
|
|
156
156
|
expect(linuxCandidate).toContain(
|
|
157
157
|
`smoo github-ci nx-run-many --targets "${LINUX_PLATFORM_TARGET_GLOBS.join(',')}" --projects`,
|
|
158
158
|
);
|
|
159
|
-
expect(macosPlatform).not.toContain('smoo github-ci nx-run-many');
|
|
160
159
|
expect(foldedRunCommand(macosPlatform, '๐ Build selected macOS and iOS release outputs')).toBe(
|
|
161
160
|
`smoo release build-platform-outputs --bump "\${{ inputs.bump }}" --ref "\${{ github.sha }}" --targets "${MACOS_PLATFORM_TARGET_GLOBS.join(
|
|
162
161
|
',',
|
|
163
162
|
)}" --output "\${{ runner.temp }}/macos-platform-outputs"`,
|
|
164
163
|
);
|
|
164
|
+
expect(macosPlatform).toContain(
|
|
165
|
+
`run: smoo github-ci nx-run-many --targets test --projects-with-targets "${MACOS_PLATFORM_TARGET_GLOBS.join(',')}"`,
|
|
166
|
+
);
|
|
167
|
+
expect(macosPlatform.indexOf('- name: ๐ Build selected macOS and iOS release outputs')).toBeLessThan(
|
|
168
|
+
macosPlatform.indexOf('- name: ๐งช Unit test macOS and iOS packages'),
|
|
169
|
+
);
|
|
170
|
+
expect(macosPlatform.indexOf('- name: ๐งช Unit test macOS and iOS packages')).toBeLessThan(
|
|
171
|
+
macosPlatform.indexOf('- name: ๐ค Upload macOS platform outputs'),
|
|
172
|
+
);
|
|
165
173
|
expect(finalJob).not.toContain('smoo release version');
|
|
166
174
|
expect(finalJob).not.toContain('nx-run-many');
|
|
167
175
|
expect(foldedRunCommand(finalJob, '๐งฏ Repair pending releases')).toBe(
|