@mnci/cli 1.5.0 → 1.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +66 -19
- package/dist/cli.js +90 -27
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -29,10 +29,12 @@ first-party (or established community) Nx equivalent:
|
|
|
29
29
|
```sh
|
|
30
30
|
mnci new my-repo # create a monorepo (prompts scope + registry)
|
|
31
31
|
mnci new my-repo --yes --registry npm --scope @my
|
|
32
|
+
mnci new my-repo --yes --registry npm --scope @my --nx-cloud # opt in to Nx Cloud
|
|
32
33
|
|
|
33
34
|
cd my-repo
|
|
34
35
|
mnci add react-app web # @nx/react (Vite + Jest)
|
|
35
36
|
mnci add node-app svc # @nx/node (plain Node app, esbuild)
|
|
37
|
+
mnci add node-app api --framework express # ...or fastify | koa | nest
|
|
36
38
|
mnci add node-function-app api # @nx/node + an Azure Functions v4 overlay
|
|
37
39
|
mnci add npm-lib sdk # @nx/js publishable lib -> packages/
|
|
38
40
|
mnci add internal-lib utils # @nx/js private lib -> libs/
|
|
@@ -42,6 +44,7 @@ mnci add python-app svc # app -> apps/ (wheel, zipped into the drop)
|
|
|
42
44
|
mnci add python-function-app fn # Azure Functions (Python v2) -> apps/
|
|
43
45
|
mnci add python-lib shared # publishable -> python-packages/ (twine upload)
|
|
44
46
|
mnci add python-internal-lib core # private shared lib -> libs/
|
|
47
|
+
mnci add python-vendor shared --lib core # wire core's module into shared's built wheel
|
|
45
48
|
|
|
46
49
|
mnci upgrade # re-apply the latest overlay (see below)
|
|
47
50
|
mnci upgrade --agent windows-latest # ...with an explicit override
|
|
@@ -58,6 +61,7 @@ each a single cross-platform command:
|
|
|
58
61
|
| `npm run affected` | `nx affected -t lint,test,build` (vs `main`) |
|
|
59
62
|
| `npm run graph` | `nx graph` |
|
|
60
63
|
| `npm run release:preview` | `nx release --dry-run` |
|
|
64
|
+
| `npm run python:install` | fixed Python toolchain (ruff/pytest/build/twine) + editable-install every Python project — the same two guards CI runs, for local dev |
|
|
61
65
|
| `prepare` | `husky` (commit-msg lint hook) |
|
|
62
66
|
|
|
63
67
|
## What `new` actually does
|
|
@@ -339,6 +343,30 @@ only for `github`/`both` — an `azure`-only workspace gets no
|
|
|
339
343
|
`.github/dependabot.yml`, matching every other GitHub-only file this CLI
|
|
340
344
|
writes.
|
|
341
345
|
|
|
346
|
+
### Nx Cloud (`--nx-cloud`, opt-in)
|
|
347
|
+
|
|
348
|
+
`mnci new` never connects to Nx Cloud unless asked — `--nx-cloud` (or
|
|
349
|
+
answering "yes" to the interactive prompt) opts in; the flagless/`--yes`
|
|
350
|
+
default stays fully disconnected, exactly as before this existed. When
|
|
351
|
+
opted in, `mnci` passes `create-nx-workspace` a **named** `--nxCloud`
|
|
352
|
+
provider value derived from the chosen `--ci` (`azure`→`azure`,
|
|
353
|
+
`github`/`both`→`github`) — never the bare `--nxCloud=yes`. Verified
|
|
354
|
+
empirically: bare `yes` prompts "Will you be using GitHub as your git
|
|
355
|
+
hosting provider?" even with `--no-interactive` set, and exits without
|
|
356
|
+
creating the workspace at all when stdin isn't a TTY — a real
|
|
357
|
+
`create-nx-workspace` inconsistency, not something `mnci` can configure
|
|
358
|
+
around. The named-provider value sidesteps it and completes non-interactively
|
|
359
|
+
every time. The only visible effect of *which* named value is chosen is a
|
|
360
|
+
throwaway CI workflow file `create-nx-workspace` writes as a side effect of
|
|
361
|
+
Cloud setup — this CLI's own overlay unconditionally overwrites whatever
|
|
362
|
+
lands at that path immediately after, so the pipeline you actually get is
|
|
363
|
+
always the same one described above, Nx Cloud or not.
|
|
364
|
+
|
|
365
|
+
Connecting still requires finishing setup in a browser: `create-nx-workspace`
|
|
366
|
+
prints a `https://cloud.nx.app/connect/…` URL to complete linking the
|
|
367
|
+
workspace to an Nx Cloud account (remote caching, CI insights, `nx
|
|
368
|
+
fix-ci`) — `mnci` does not automate that step.
|
|
369
|
+
|
|
342
370
|
## Dependency & risk notes
|
|
343
371
|
|
|
344
372
|
Being upfront about what mnci leans on, so it's a conscious trade-off rather
|
|
@@ -394,18 +422,34 @@ than a surprise:
|
|
|
394
422
|
Python project workspace-wide (see "Workspace-wide install" above),
|
|
395
423
|
straight into whatever `<python>` resolves to on the agent (`python3` on
|
|
396
424
|
POSIX, `python` on Windows — see above); locally, create your own venv
|
|
397
|
-
(`python3 -m venv` / `python -m venv` on Windows) and
|
|
398
|
-
|
|
399
|
-
|
|
425
|
+
(`python3 -m venv` / `python -m venv` on Windows) and run
|
|
426
|
+
`npm run python:install` to reproduce the same two installs — the root
|
|
427
|
+
script chains the identical two guards CI runs (see the scripts table
|
|
428
|
+
above).
|
|
400
429
|
|
|
401
430
|
## How Node apps work (plain `@nx/node:application`, no Azure Functions plugin)
|
|
402
431
|
|
|
403
432
|
`node-app` and `node-function-app` are both the **official**
|
|
404
|
-
`@nx/node:application` generator (`--bundler=esbuild
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
433
|
+
`@nx/node:application` generator (`--bundler=esbuild`) — no third-party Azure
|
|
434
|
+
Functions plugin, and no post-generation build-output rewiring.
|
|
435
|
+
`node-function-app` is exactly that generator plus a hand-written Azure
|
|
436
|
+
Functions v4 file overlay, the same split `python-app`/`python-function-app`
|
|
437
|
+
already use:
|
|
438
|
+
|
|
439
|
+
- **`node-app` framework choice** (`--framework`, default `none`): plain flag
|
|
440
|
+
plumbing to the generator's own `express`/`fastify`/`koa`/`nest`/`none`
|
|
441
|
+
choices — `mnci` adds no framework-specific logic of its own. Verified
|
|
442
|
+
empirically that all four scaffold, build and test cleanly on Nx 23.1.0.
|
|
443
|
+
One quirk worth knowing: `--framework=nest` silently overrides
|
|
444
|
+
`--bundler=esbuild` — NestJS needs its own webpack build (decorator/DI
|
|
445
|
+
metadata emission esbuild's transform-only approach can't produce), so a
|
|
446
|
+
`nest` app's `dist/main.js` is a single webpack bundle instead of the
|
|
447
|
+
esbuild non-bundled mirrored-tree + shim the other frameworks (and `none`)
|
|
448
|
+
produce. The `package` target needs no framework branch either way — both
|
|
449
|
+
shapes' runnable entry is `dist/main.js`, so zipping the whole `dist` folder
|
|
450
|
+
works unchanged. `node-function-app` never accepts `--framework`: the Azure
|
|
451
|
+
Functions v4 programming model (`app.http(...)` registration) runs its own
|
|
452
|
+
request lifecycle, so a full HTTP server framework doesn't apply there.
|
|
409
453
|
|
|
410
454
|
- `build` = the generator's own `@nx/esbuild:esbuild` target, **non-bundled**
|
|
411
455
|
(`bundle: false`): it transpiles each file individually and mirrors the
|
|
@@ -518,18 +562,21 @@ identically to a Linux/macOS one.
|
|
|
518
562
|
<name>` just like the TS apps.
|
|
519
563
|
- **Internal-lib vendoring** replaces `@nxlv/python`'s `bundleLocalDependencies`:
|
|
520
564
|
plain pip has no bundled-local-dependency feature, so a project that imports
|
|
521
|
-
a workspace-internal Python library needs a
|
|
565
|
+
a workspace-internal Python library needs a `vendor` entry (under
|
|
522
566
|
`[tool.mnci-python-pip]`) in its own `pyproject.toml` (the pip-world
|
|
523
|
-
counterpart of
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
567
|
+
counterpart of a `dependencies = [...]` entry — neither mnci nor the plugin
|
|
568
|
+
wires cross-project Python dependencies automatically). `mnci add
|
|
569
|
+
python-vendor <consumer> --lib <name>` automates writing that entry —
|
|
570
|
+
idempotent (safe to run twice), and works on any consumer with a
|
|
571
|
+
`pyproject.toml` (app, publishable lib, or another internal lib), not just
|
|
572
|
+
apps. The plugin's `build` executor reads the entry, resolves the named
|
|
573
|
+
project's root via the **Nx project graph**, copies its module into a
|
|
574
|
+
staged copy of the consuming project, and builds from there — so the
|
|
575
|
+
resulting wheel contains the vendored module as a real top-level package.
|
|
576
|
+
Verified empirically that this does **not** reproduce the old
|
|
577
|
+
`@nxlv/python` bug where combining a vendored internal lib and a real
|
|
578
|
+
external dependency on the same project silently dropped the external one
|
|
579
|
+
from the wheel's metadata — both survive correctly.
|
|
533
580
|
- **Workspace-wide install** (mnci's own CI step, not the plugin's) — pip has
|
|
534
581
|
no npm-workspaces-style hoisting, so mnci writes one: a guarded step
|
|
535
582
|
editable-installs every Python project (`apps/*`, `python-packages/*`,
|
package/dist/cli.js
CHANGED
|
@@ -35,13 +35,13 @@ __export(cli_exports, {
|
|
|
35
35
|
main: () => main
|
|
36
36
|
});
|
|
37
37
|
module.exports = __toCommonJS(cli_exports);
|
|
38
|
-
var
|
|
38
|
+
var import_node_fs4 = require("fs");
|
|
39
39
|
var import_node_path12 = require("path");
|
|
40
40
|
var import_commander = require("commander");
|
|
41
41
|
|
|
42
42
|
// src/commands/add.ts
|
|
43
43
|
var import_node_path7 = require("path");
|
|
44
|
-
var
|
|
44
|
+
var import_prompts4 = require("@inquirer/prompts");
|
|
45
45
|
|
|
46
46
|
// src/nx.ts
|
|
47
47
|
var import_cross_spawn = __toESM(require("cross-spawn"));
|
|
@@ -100,6 +100,12 @@ async function promptCi() {
|
|
|
100
100
|
]
|
|
101
101
|
});
|
|
102
102
|
}
|
|
103
|
+
async function promptNxCloud() {
|
|
104
|
+
return await (0, import_prompts.confirm)({
|
|
105
|
+
message: "Connect this workspace to Nx Cloud (remote caching + CI insights)?",
|
|
106
|
+
default: false
|
|
107
|
+
});
|
|
108
|
+
}
|
|
103
109
|
async function promptStack() {
|
|
104
110
|
const linter = await (0, import_prompts.select)({
|
|
105
111
|
message: "Linter",
|
|
@@ -204,7 +210,7 @@ function ensurePlugin(workspaceRoot, packageName) {
|
|
|
204
210
|
logger.step(`Installing Nx plugin ${packageName}`);
|
|
205
211
|
runNx(["add", packageName], workspaceRoot);
|
|
206
212
|
}
|
|
207
|
-
function runNodeApp(workspaceRoot, name, stack) {
|
|
213
|
+
function runNodeApp(workspaceRoot, name, stack, framework) {
|
|
208
214
|
ensurePlugin(workspaceRoot, "@nx/node");
|
|
209
215
|
runNx([
|
|
210
216
|
"g",
|
|
@@ -214,7 +220,7 @@ function runNodeApp(workspaceRoot, name, stack) {
|
|
|
214
220
|
`--unitTestRunner=${stack.testRunner}`,
|
|
215
221
|
`--linter=${stack.linter}`,
|
|
216
222
|
"--e2eTestRunner=none",
|
|
217
|
-
|
|
223
|
+
`--framework=${framework}`,
|
|
218
224
|
"--no-interactive"
|
|
219
225
|
], workspaceRoot);
|
|
220
226
|
}
|
|
@@ -229,8 +235,8 @@ function nodeAppPackageTarget(name) {
|
|
|
229
235
|
options: { command }
|
|
230
236
|
};
|
|
231
237
|
}
|
|
232
|
-
function addNodeApp(workspaceRoot, name, stack) {
|
|
233
|
-
runNodeApp(workspaceRoot, name, stack);
|
|
238
|
+
function addNodeApp(workspaceRoot, name, stack, framework = "none") {
|
|
239
|
+
runNodeApp(workspaceRoot, name, stack, framework);
|
|
234
240
|
ensureAdmZip(workspaceRoot);
|
|
235
241
|
addNxTargets((0, import_node_path3.join)(workspaceRoot, "apps", name, "package.json"), { package: nodeAppPackageTarget(name) });
|
|
236
242
|
}
|
|
@@ -306,7 +312,7 @@ function nodeFunctionAppPackageTarget(name) {
|
|
|
306
312
|
};
|
|
307
313
|
}
|
|
308
314
|
function addNodeFunctionApp(workspaceRoot, name, stack) {
|
|
309
|
-
runNodeApp(workspaceRoot, name, stack);
|
|
315
|
+
runNodeApp(workspaceRoot, name, stack, "none");
|
|
310
316
|
ensureAzureFunctionsPackage(workspaceRoot);
|
|
311
317
|
const nodeFunctionAppRoot = (0, import_node_path3.join)(workspaceRoot, "apps", name);
|
|
312
318
|
writeFileEnsured((0, import_node_path3.join)(nodeFunctionAppRoot, "src/main.ts"), NODE_FUNCTION_APP_MAIN);
|
|
@@ -385,6 +391,7 @@ function markPublic(manifestPath) {
|
|
|
385
391
|
}
|
|
386
392
|
|
|
387
393
|
// src/commands/add/python.ts
|
|
394
|
+
var import_node_fs2 = require("fs");
|
|
388
395
|
var import_node_path5 = require("path");
|
|
389
396
|
function ensurePython(workspaceRoot) {
|
|
390
397
|
const hasPython = runShell("python3", ["--version"], workspaceRoot) === 0 || runShell("python", ["--version"], workspaceRoot) === 0;
|
|
@@ -476,9 +483,57 @@ function addPythonInternalLib(workspaceRoot, name) {
|
|
|
476
483
|
ensureRequirementsDev(workspaceRoot);
|
|
477
484
|
runNx(["g", "@mnci/nx-python-pip:internal-library", name, `--directory=libs/${name}`, "--no-interactive"], workspaceRoot);
|
|
478
485
|
}
|
|
486
|
+
var PYTHON_PROJECT_DIRS = ["apps", "python-packages", "libs"];
|
|
487
|
+
function findPythonProjectDirectory(workspaceRoot, name) {
|
|
488
|
+
return PYTHON_PROJECT_DIRS.find((directory) => fileExists((0, import_node_path5.join)(workspaceRoot, directory, name, "pyproject.toml")));
|
|
489
|
+
}
|
|
490
|
+
function parseVendorEntries(pyprojectToml) {
|
|
491
|
+
const match = /^\s*vendor\s*=\s*\[([^\]]*)\]/m.exec(pyprojectToml);
|
|
492
|
+
if (!match) {
|
|
493
|
+
return [];
|
|
494
|
+
}
|
|
495
|
+
return match[1].split(",").map((entry) => entry.trim().replaceAll(/^["']|["']$/g, "")).filter(Boolean);
|
|
496
|
+
}
|
|
497
|
+
function addVendorEntry(pyprojectToml, lib) {
|
|
498
|
+
const existing = parseVendorEntries(pyprojectToml);
|
|
499
|
+
if (existing.length > 0) {
|
|
500
|
+
const merged = [...existing, lib].map((name) => `"${name}"`).join(", ");
|
|
501
|
+
return pyprojectToml.replace(/^(\s*vendor\s*=\s*)\[([^\]]*)\]/m, (_match, prefix) => `${prefix}[${merged}]`);
|
|
502
|
+
}
|
|
503
|
+
const table = `[tool.mnci-python-pip]
|
|
504
|
+
vendor = ["${lib}"]
|
|
505
|
+
|
|
506
|
+
`;
|
|
507
|
+
return pyprojectToml.includes("[tool.pytest.ini_options]") ? pyprojectToml.replace("[tool.pytest.ini_options]", () => `${table}[tool.pytest.ini_options]`) : `${pyprojectToml}
|
|
508
|
+
${table}`;
|
|
509
|
+
}
|
|
510
|
+
async function addPythonVendor(workspaceRoot, consumer, options, kindProvided) {
|
|
511
|
+
const lib = options.lib ?? (kindProvided ? void 0 : await promptText("Internal Python library to vendor (an existing libs/<name>)"));
|
|
512
|
+
if (!lib) {
|
|
513
|
+
throw new Error("`add python-vendor` needs the library to vendor: pass --lib <name> (an existing libs/<name> internal library).");
|
|
514
|
+
}
|
|
515
|
+
const consumerDirectory = findPythonProjectDirectory(workspaceRoot, consumer);
|
|
516
|
+
if (!consumerDirectory) {
|
|
517
|
+
throw new Error(`No Python project named '${consumer}' with a pyproject.toml found (checked apps/, python-packages/, libs/). A Python function app has no pyproject.toml \u2014 there is nothing to vendor into.`);
|
|
518
|
+
}
|
|
519
|
+
if (!fileExists((0, import_node_path5.join)(workspaceRoot, "libs", lib, "pyproject.toml"))) {
|
|
520
|
+
throw new Error(`No internal Python library named '${lib}' found at libs/${lib}/pyproject.toml. Only an internal library (added via 'mnci add python-internal-lib') can be vendored.`);
|
|
521
|
+
}
|
|
522
|
+
if (consumerDirectory === "libs" && consumer === lib) {
|
|
523
|
+
throw new Error(`'${consumer}' cannot vendor itself.`);
|
|
524
|
+
}
|
|
525
|
+
const pyprojectPath = (0, import_node_path5.join)(workspaceRoot, consumerDirectory, consumer, "pyproject.toml");
|
|
526
|
+
const pyprojectToml = (0, import_node_fs2.readFileSync)(pyprojectPath, "utf8");
|
|
527
|
+
if (parseVendorEntries(pyprojectToml).includes(lib)) {
|
|
528
|
+
logger.info(`'${consumer}' already vendors '${lib}' \u2014 nothing to do.`);
|
|
529
|
+
return;
|
|
530
|
+
}
|
|
531
|
+
writeFileEnsured(pyprojectPath, addVendorEntry(pyprojectToml, lib));
|
|
532
|
+
logger.success(`'${consumer}' now vendors '${lib}' \u2014 its wheel will include '${lib}''s module on the next build.`);
|
|
533
|
+
}
|
|
479
534
|
|
|
480
535
|
// src/commands/add/reactApp.ts
|
|
481
|
-
var
|
|
536
|
+
var import_node_fs3 = require("fs");
|
|
482
537
|
var import_node_path6 = require("path");
|
|
483
538
|
function ensurePlugin2(workspaceRoot, packageName) {
|
|
484
539
|
if (hasPlugin(workspaceRoot, packageName)) {
|
|
@@ -521,7 +576,7 @@ function allowEnvFiles(workspaceRoot) {
|
|
|
521
576
|
return;
|
|
522
577
|
}
|
|
523
578
|
const marker = "# MoNecromanCI: keep the committed per-environment Vite config (public VITE_ values)";
|
|
524
|
-
const current = (0,
|
|
579
|
+
const current = (0, import_node_fs3.readFileSync)(gitignorePath, "utf8");
|
|
525
580
|
if (current.includes(marker)) {
|
|
526
581
|
return;
|
|
527
582
|
}
|
|
@@ -563,7 +618,8 @@ var PROJECT_KINDS = [
|
|
|
563
618
|
"python-app",
|
|
564
619
|
"python-function-app",
|
|
565
620
|
"python-lib",
|
|
566
|
-
"python-internal-lib"
|
|
621
|
+
"python-internal-lib",
|
|
622
|
+
"python-vendor"
|
|
567
623
|
];
|
|
568
624
|
async function runAdd(kind, name, options) {
|
|
569
625
|
const workspaceRoot = process.cwd();
|
|
@@ -572,7 +628,7 @@ async function runAdd(kind, name, options) {
|
|
|
572
628
|
}
|
|
573
629
|
const stack = readWorkspaceStack(workspaceRoot);
|
|
574
630
|
const kindProvided = kind !== void 0;
|
|
575
|
-
const resolvedKind = kind ?? await (0,
|
|
631
|
+
const resolvedKind = kind ?? await (0, import_prompts4.select)({
|
|
576
632
|
message: "What kind of project?",
|
|
577
633
|
choices: PROJECT_KINDS.map((value) => ({ name: value, value }))
|
|
578
634
|
});
|
|
@@ -584,7 +640,7 @@ async function runAdd(kind, name, options) {
|
|
|
584
640
|
break;
|
|
585
641
|
}
|
|
586
642
|
case "node-app": {
|
|
587
|
-
addNodeApp(workspaceRoot, resolvedName, stack);
|
|
643
|
+
addNodeApp(workspaceRoot, resolvedName, stack, options.framework);
|
|
588
644
|
break;
|
|
589
645
|
}
|
|
590
646
|
case "node-function-app": {
|
|
@@ -624,6 +680,11 @@ async function runAdd(kind, name, options) {
|
|
|
624
680
|
addPythonInternalLib(workspaceRoot, resolvedName);
|
|
625
681
|
break;
|
|
626
682
|
}
|
|
683
|
+
case "python-vendor": {
|
|
684
|
+
await addPythonVendor(workspaceRoot, resolvedName, options, kindProvided);
|
|
685
|
+
syncProjectReferences(workspaceRoot);
|
|
686
|
+
return;
|
|
687
|
+
}
|
|
627
688
|
default: {
|
|
628
689
|
const exhaustive = resolvedKind;
|
|
629
690
|
throw new Error(`Unknown project kind '${exhaustive}'. Expected one of: ${PROJECT_KINDS.join(", ")}.`);
|
|
@@ -653,7 +714,7 @@ function markPrivate(manifestPath) {
|
|
|
653
714
|
|
|
654
715
|
// src/commands/interactive.ts
|
|
655
716
|
var import_node_path10 = require("path");
|
|
656
|
-
var
|
|
717
|
+
var import_prompts7 = require("@inquirer/prompts");
|
|
657
718
|
|
|
658
719
|
// src/commands/new.ts
|
|
659
720
|
var import_node_path9 = require("path");
|
|
@@ -730,15 +791,13 @@ var ROOT_SCRIPTS = {
|
|
|
730
791
|
prepare: "husky"
|
|
731
792
|
};
|
|
732
793
|
function rootScripts(stack) {
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
}
|
|
741
|
-
return { ...ROOT_SCRIPTS };
|
|
794
|
+
const scripts = stack.linter === "oxlint" ? {
|
|
795
|
+
...ROOT_SCRIPTS,
|
|
796
|
+
lint: "oxlint",
|
|
797
|
+
format: "oxfmt -c oxfmt.config.mts .",
|
|
798
|
+
"format:check": "oxfmt -c oxfmt.config.mts --check ."
|
|
799
|
+
} : { ...ROOT_SCRIPTS };
|
|
800
|
+
return { ...scripts, "python:install": `${PYTHON_INSTALL_GUARD} && ${PYTHON_WORKSPACE_INSTALL_GUARD}` };
|
|
742
801
|
}
|
|
743
802
|
function generatorDefaults(stack) {
|
|
744
803
|
const shared = { linter: stack.linter === "oxlint" ? "none" : "eslint", unitTestRunner: stack.testRunner };
|
|
@@ -1142,6 +1201,9 @@ function applyOverlay(workspaceRoot, options) {
|
|
|
1142
1201
|
}
|
|
1143
1202
|
|
|
1144
1203
|
// src/commands/new.ts
|
|
1204
|
+
function nxCloudProviderValue(ci) {
|
|
1205
|
+
return ci === "azure" ? "azure" : "github";
|
|
1206
|
+
}
|
|
1145
1207
|
async function resolveStack(options) {
|
|
1146
1208
|
const fromFlags = { linter: options.linter, testRunner: options.testRunner };
|
|
1147
1209
|
const complete = Boolean(fromFlags.linter && fromFlags.testRunner);
|
|
@@ -1190,6 +1252,7 @@ async function runNew(name, options) {
|
|
|
1190
1252
|
const agent = options.agent ?? (options.yes ? "ubuntu-latest" : await promptText("CI build agent/runner (vmImage, GitHub Actions runner label, or self-hosted pool name)", "ubuntu-latest"));
|
|
1191
1253
|
const variableGroup = ci === "github" ? options.variableGroup ?? "Build" : options.variableGroup ?? (options.yes ? "Build" : await promptText("Azure DevOps variable group holding the npm PAT", "Build"));
|
|
1192
1254
|
const stack = await resolveStack(options);
|
|
1255
|
+
const nxCloud = options.nxCloud ?? (options.yes ? false : await promptNxCloud());
|
|
1193
1256
|
logger.step(`Creating Nx workspace '${workspaceName}' (preset: ts)`);
|
|
1194
1257
|
runNpx([
|
|
1195
1258
|
"--yes",
|
|
@@ -1197,7 +1260,7 @@ async function runNew(name, options) {
|
|
|
1197
1260
|
workspaceName,
|
|
1198
1261
|
"--preset=ts",
|
|
1199
1262
|
"--pm=npm",
|
|
1200
|
-
"--nxCloud=skip",
|
|
1263
|
+
nxCloud ? `--nxCloud=${nxCloudProviderValue(ci)}` : "--nxCloud=skip",
|
|
1201
1264
|
"--no-interactive"
|
|
1202
1265
|
], process.cwd());
|
|
1203
1266
|
const workspaceRoot = (0, import_node_path9.join)(process.cwd(), workspaceName);
|
|
@@ -1222,7 +1285,7 @@ async function runInteractive() {
|
|
|
1222
1285
|
const inWorkspace = fileExists((0, import_node_path10.join)(process.cwd(), "nx.json"));
|
|
1223
1286
|
const newChoice = { name: "Create a new monorepo", value: "new" };
|
|
1224
1287
|
const addChoice = { name: "Add a project to this workspace", value: "add" };
|
|
1225
|
-
const action = await (0,
|
|
1288
|
+
const action = await (0, import_prompts7.select)({
|
|
1226
1289
|
message: "What would you like to do?",
|
|
1227
1290
|
choices: inWorkspace ? [addChoice, newChoice] : [newChoice, addChoice]
|
|
1228
1291
|
});
|
|
@@ -1290,7 +1353,7 @@ function runUpgrade(workspaceRoot, options) {
|
|
|
1290
1353
|
// src/cli.ts
|
|
1291
1354
|
function readVersion() {
|
|
1292
1355
|
try {
|
|
1293
|
-
const package_ = JSON.parse((0,
|
|
1356
|
+
const package_ = JSON.parse((0, import_node_fs4.readFileSync)((0, import_node_path12.join)(__dirname, "..", "package.json"), "utf8"));
|
|
1294
1357
|
return package_.version ?? "0.0.0";
|
|
1295
1358
|
} catch {
|
|
1296
1359
|
return "0.0.0";
|
|
@@ -1299,13 +1362,13 @@ function readVersion() {
|
|
|
1299
1362
|
function buildProgram() {
|
|
1300
1363
|
const program = new import_commander.Command();
|
|
1301
1364
|
program.name("mnci").description("MoNecromanCI \u2014 a thin CLI over official Nx plugins").version(readVersion(), "-v, --version", "display the version");
|
|
1302
|
-
program.command("new").argument("[name]", "workspace name").description("Create a new monorepo (Nx TS preset + release/pipeline overlay)").option("-y, --yes", "accept defaults for anything not passed as a flag").option("--scope <scope>", "npm scope for publishable packages (e.g. @demo)").option("--registry <kind>", "azure-artifacts | npm").option("--organization <name>", "Azure DevOps organization").option("--project <name>", "Azure DevOps project").option("--artifacts-feed <name>", "Azure Artifacts feed").option("--agent <pool>", "CI build agent: a vmImage (e.g. ubuntu-latest) or self-hosted pool name").option("--variable-group <name>", "Azure DevOps variable group holding the npm PAT").option("--ci <provider>", "CI provider: azure | github | both").option("--linter <linter>", "linter: eslint | oxlint").option("--test-runner <runner>", "unit-test runner: jest | vitest").action(async (name, options) => {
|
|
1365
|
+
program.command("new").argument("[name]", "workspace name").description("Create a new monorepo (Nx TS preset + release/pipeline overlay)").option("-y, --yes", "accept defaults for anything not passed as a flag").option("--scope <scope>", "npm scope for publishable packages (e.g. @demo)").option("--registry <kind>", "azure-artifacts | npm").option("--organization <name>", "Azure DevOps organization").option("--project <name>", "Azure DevOps project").option("--artifacts-feed <name>", "Azure Artifacts feed").option("--agent <pool>", "CI build agent: a vmImage (e.g. ubuntu-latest) or self-hosted pool name").option("--variable-group <name>", "Azure DevOps variable group holding the npm PAT").option("--ci <provider>", "CI provider: azure | github | both").option("--linter <linter>", "linter: eslint | oxlint").option("--test-runner <runner>", "unit-test runner: jest | vitest").option("--nx-cloud", "connect the workspace to Nx Cloud (remote caching + CI insights)").action(async (name, options) => {
|
|
1303
1366
|
await runNew(name, options);
|
|
1304
1367
|
});
|
|
1305
1368
|
program.command("upgrade").description("Re-apply the latest MoNecromanCI overlay to this workspace (release config, pipeline, npmrc, commitlint, husky hook, curated scripts)").option("--scope <scope>", "npm scope for publishable packages (overrides the persisted value)").option("--registry <kind>", "azure-artifacts | npm (overrides the persisted value)").option("--organization <name>", "Azure DevOps organization").option("--project <name>", "Azure DevOps project").option("--artifacts-feed <name>", "Azure Artifacts feed").option("--agent <pool>", "CI build agent (overrides the persisted value)").option("--variable-group <name>", "Azure DevOps variable group holding the npm PAT").option("--ci <provider>", "CI provider: azure | github | both (overrides the persisted value)").option("--linter <linter>", "linter: eslint | oxlint (overrides the persisted value)").option("--test-runner <runner>", "unit-test runner: jest | vitest (overrides the persisted value)").action((options) => {
|
|
1306
1369
|
runUpgrade(process.cwd(), options);
|
|
1307
1370
|
});
|
|
1308
|
-
program.command("add").addArgument(new import_commander.Argument("[kind]", "project kind").choices(PROJECT_KINDS)).argument("[name]", "project name").description("Add a project by delegating to the matching Nx plugin generator").option("--scope <scope>", "npm scope for a publishable lib (defaults to @<workspace name>)").action(async (kind, name, options) => {
|
|
1371
|
+
program.command("add").addArgument(new import_commander.Argument("[kind]", "project kind").choices(PROJECT_KINDS)).argument("[name]", "project name").description("Add a project by delegating to the matching Nx plugin generator").option("--scope <scope>", "npm scope for a publishable lib (defaults to @<workspace name>)").option("--framework <framework>", "node-app only: express | fastify | koa | nest | none (default: none)").option("--lib <name>", "python-vendor only: the internal Python library (libs/<name>) to vendor into <name>").action(async (kind, name, options) => {
|
|
1309
1372
|
await runAdd(kind, name, options);
|
|
1310
1373
|
});
|
|
1311
1374
|
program.action(async () => {
|