@mnci/cli 1.7.0 → 1.8.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 +140 -87
- package/dist/cli.js +735 -237
- package/dist/cli.js.map +1 -1
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -35,12 +35,10 @@ __export(cli_exports, {
|
|
|
35
35
|
main: () => main
|
|
36
36
|
});
|
|
37
37
|
module.exports = __toCommonJS(cli_exports);
|
|
38
|
-
var import_node_fs4 = require("fs");
|
|
39
|
-
var import_node_path12 = require("path");
|
|
40
38
|
var import_commander = require("commander");
|
|
41
39
|
|
|
42
40
|
// src/commands/add.ts
|
|
43
|
-
var
|
|
41
|
+
var import_node_path8 = require("path");
|
|
44
42
|
var import_prompts4 = require("@inquirer/prompts");
|
|
45
43
|
|
|
46
44
|
// src/nx.ts
|
|
@@ -107,13 +105,6 @@ async function promptNxCloud() {
|
|
|
107
105
|
});
|
|
108
106
|
}
|
|
109
107
|
async function promptStack() {
|
|
110
|
-
const linter = await (0, import_prompts.select)({
|
|
111
|
-
message: "Linter",
|
|
112
|
-
choices: [
|
|
113
|
-
{ name: "ESLint", value: "eslint" },
|
|
114
|
-
{ name: "Oxlint", value: "oxlint" }
|
|
115
|
-
]
|
|
116
|
-
});
|
|
117
108
|
const testRunner = await (0, import_prompts.select)({
|
|
118
109
|
message: "Unit-test runner",
|
|
119
110
|
choices: [
|
|
@@ -121,7 +112,7 @@ async function promptStack() {
|
|
|
121
112
|
{ name: "Vitest", value: "vitest" }
|
|
122
113
|
]
|
|
123
114
|
});
|
|
124
|
-
return {
|
|
115
|
+
return { testRunner };
|
|
125
116
|
}
|
|
126
117
|
|
|
127
118
|
// src/util/fsx.ts
|
|
@@ -167,11 +158,14 @@ var logger = {
|
|
|
167
158
|
// src/util/names.ts
|
|
168
159
|
function assertValidProjectName(name, label) {
|
|
169
160
|
if (!/^[a-z][a-z0-9-]*$/.test(name)) {
|
|
170
|
-
throw new Error(
|
|
161
|
+
throw new Error(
|
|
162
|
+
`${label} '${name}' is invalid \u2014 use lowercase letters, digits and hyphens, starting with a letter (e.g. 'my-project').`
|
|
163
|
+
);
|
|
171
164
|
}
|
|
172
165
|
}
|
|
173
166
|
|
|
174
|
-
// src/commands/add/
|
|
167
|
+
// src/commands/add/go.ts
|
|
168
|
+
var import_node_fs2 = require("fs");
|
|
175
169
|
var import_node_path3 = require("path");
|
|
176
170
|
|
|
177
171
|
// src/commands/add/shared.ts
|
|
@@ -186,7 +180,11 @@ function ensureAdmZip(workspaceRoot) {
|
|
|
186
180
|
return;
|
|
187
181
|
}
|
|
188
182
|
logger.step("Installing the app packager (adm-zip)");
|
|
189
|
-
if (runShell(
|
|
183
|
+
if (runShell(
|
|
184
|
+
"npm",
|
|
185
|
+
["install", "--save-dev", "adm-zip", "--no-audit", "--no-fund"],
|
|
186
|
+
workspaceRoot
|
|
187
|
+
) !== 0) {
|
|
190
188
|
throw new Error("npm install of adm-zip failed");
|
|
191
189
|
}
|
|
192
190
|
}
|
|
@@ -199,10 +197,179 @@ function addNxTargets(manifestPath, newTargets) {
|
|
|
199
197
|
const manifest = fileExists(manifestPath) ? readJson(manifestPath) : {};
|
|
200
198
|
const nx = manifest.nx ?? {};
|
|
201
199
|
const targets = nx.targets ?? {};
|
|
202
|
-
writeFileEnsured(
|
|
200
|
+
writeFileEnsured(
|
|
201
|
+
manifestPath,
|
|
202
|
+
toJson({ ...manifest, nx: { ...nx, targets: { ...targets, ...newTargets } } })
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// src/commands/add/go.ts
|
|
207
|
+
function ensureGo(workspaceRoot) {
|
|
208
|
+
if (runShell("go", ["version"], workspaceRoot) !== 0) {
|
|
209
|
+
throw new Error("Go not found. Install Go 1.21+ first: https://go.dev/dl/");
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
function warnIfNoGolangciLint(workspaceRoot) {
|
|
213
|
+
if (runShell("golangci-lint", ["--version"], workspaceRoot) !== 0) {
|
|
214
|
+
logger.warn(
|
|
215
|
+
"golangci-lint not found \u2014 the generated lint target needs it. Install: https://golangci-lint.run/welcome/install/"
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
function nxGoPluginSpec() {
|
|
220
|
+
return process.env.MNCI_NX_GO_SPEC ?? "@nx-go/nx-go";
|
|
221
|
+
}
|
|
222
|
+
function ensureNxGoPlugin(workspaceRoot) {
|
|
223
|
+
if (hasPlugin(workspaceRoot, "@nx-go/nx-go")) {
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
const spec = nxGoPluginSpec();
|
|
227
|
+
logger.step(`Installing the Go toolchain plugin (${spec})`);
|
|
228
|
+
if (runShell("npm", ["install", "--save-dev", spec, "--no-audit", "--no-fund"], workspaceRoot) !== 0) {
|
|
229
|
+
throw new Error("npm install of @nx-go/nx-go failed");
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
function ensureGoModule(workspaceRoot) {
|
|
233
|
+
if (fileExists((0, import_node_path3.join)(workspaceRoot, "go.mod"))) {
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
logger.step("Bootstrapping the workspace Go module (single root go.mod)");
|
|
237
|
+
runNx(["g", "@nx-go/nx-go:init", "--no-interactive"], workspaceRoot);
|
|
238
|
+
runNx(["g", "@nx-go/nx-go:convert-to-one-mod", "--no-interactive"], workspaceRoot);
|
|
239
|
+
}
|
|
240
|
+
function goModulePath(workspaceRoot) {
|
|
241
|
+
try {
|
|
242
|
+
const contents = (0, import_node_fs2.readFileSync)((0, import_node_path3.join)(workspaceRoot, "go.mod"), "utf8");
|
|
243
|
+
return /^module\s+(\S+)/m.exec(contents)?.[1];
|
|
244
|
+
} catch {
|
|
245
|
+
return void 0;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
function addProjectJsonTargets(projectJsonPath, newTargets) {
|
|
249
|
+
const project = readJson(projectJsonPath);
|
|
250
|
+
const targets = project.targets ?? {};
|
|
251
|
+
writeFileEnsured(projectJsonPath, toJson({ ...project, targets: { ...targets, ...newTargets } }));
|
|
252
|
+
}
|
|
253
|
+
function goTestTarget() {
|
|
254
|
+
return { executor: "@nx-go/nx-go:test" };
|
|
255
|
+
}
|
|
256
|
+
function goLintTarget() {
|
|
257
|
+
return { executor: "@nx-go/nx-go:lint", options: { linter: "golangci-lint", args: ["run"] } };
|
|
258
|
+
}
|
|
259
|
+
function goBuildTarget(name) {
|
|
260
|
+
return {
|
|
261
|
+
executor: "@nx-go/nx-go:build",
|
|
262
|
+
// eslint-disable-next-line unicorn/no-incorrect-template-string-interpolation -- {workspaceRoot} is an Nx output token
|
|
263
|
+
outputs: [`{workspaceRoot}/dist/apps/${name}`],
|
|
264
|
+
options: { outputPath: `../../dist/apps/${name}/${name}` }
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
function goPackageTarget(tag, name) {
|
|
268
|
+
const zip = `dist/drop/${tag}-${name}.zip`;
|
|
269
|
+
const command = `node -e "const fs=require('node:fs');fs.mkdirSync('dist/drop',{recursive:true});const A=require('adm-zip');const z=new A();z.addLocalFolder('dist/apps/${name}');z.writeZip('${zip}')"`;
|
|
270
|
+
return {
|
|
271
|
+
executor: "nx:run-commands",
|
|
272
|
+
dependsOn: ["build"],
|
|
273
|
+
// eslint-disable-next-line unicorn/no-incorrect-template-string-interpolation -- {workspaceRoot} is an Nx output token
|
|
274
|
+
outputs: [`{workspaceRoot}/${zip}`],
|
|
275
|
+
options: { command }
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
function prepareGo(workspaceRoot) {
|
|
279
|
+
ensureGo(workspaceRoot);
|
|
280
|
+
warnIfNoGolangciLint(workspaceRoot);
|
|
281
|
+
ensureNxGoPlugin(workspaceRoot);
|
|
282
|
+
ensureGoModule(workspaceRoot);
|
|
283
|
+
}
|
|
284
|
+
function addGoApp(workspaceRoot, name) {
|
|
285
|
+
prepareGo(workspaceRoot);
|
|
286
|
+
ensureAdmZip(workspaceRoot);
|
|
287
|
+
runNx(
|
|
288
|
+
[
|
|
289
|
+
"g",
|
|
290
|
+
"@nx-go/nx-go:application",
|
|
291
|
+
`apps/${name}`,
|
|
292
|
+
`--name=${name}`,
|
|
293
|
+
"--tags=type:go-app",
|
|
294
|
+
"--no-interactive"
|
|
295
|
+
],
|
|
296
|
+
workspaceRoot
|
|
297
|
+
);
|
|
298
|
+
addProjectJsonTargets((0, import_node_path3.join)(workspaceRoot, "apps", name, "project.json"), {
|
|
299
|
+
build: goBuildTarget(name),
|
|
300
|
+
test: goTestTarget(),
|
|
301
|
+
lint: goLintTarget(),
|
|
302
|
+
package: goPackageTarget("go-app", name)
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
function addGoFunctionApp(workspaceRoot, name) {
|
|
306
|
+
prepareGo(workspaceRoot);
|
|
307
|
+
ensureAdmZip(workspaceRoot);
|
|
308
|
+
runNx(
|
|
309
|
+
[
|
|
310
|
+
"g",
|
|
311
|
+
"@nx-go/nx-go:application",
|
|
312
|
+
`apps/${name}`,
|
|
313
|
+
`--name=${name}`,
|
|
314
|
+
"--tags=type:go-function-app",
|
|
315
|
+
"--no-interactive"
|
|
316
|
+
],
|
|
317
|
+
workspaceRoot
|
|
318
|
+
);
|
|
319
|
+
addProjectJsonTargets((0, import_node_path3.join)(workspaceRoot, "apps", name, "project.json"), {
|
|
320
|
+
build: goBuildTarget(name),
|
|
321
|
+
test: goTestTarget(),
|
|
322
|
+
lint: goLintTarget(),
|
|
323
|
+
package: goPackageTarget("go-function-app", name)
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
function addGoLib(workspaceRoot, name) {
|
|
327
|
+
prepareGo(workspaceRoot);
|
|
328
|
+
runNx(
|
|
329
|
+
[
|
|
330
|
+
"g",
|
|
331
|
+
"@nx-go/nx-go:library",
|
|
332
|
+
`packages/${name}`,
|
|
333
|
+
`--name=${name}`,
|
|
334
|
+
"--tags=type:go-lib",
|
|
335
|
+
"--no-interactive"
|
|
336
|
+
],
|
|
337
|
+
workspaceRoot
|
|
338
|
+
);
|
|
339
|
+
addProjectJsonTargets((0, import_node_path3.join)(workspaceRoot, "packages", name, "project.json"), {
|
|
340
|
+
test: goTestTarget(),
|
|
341
|
+
lint: goLintTarget()
|
|
342
|
+
});
|
|
343
|
+
const module2 = goModulePath(workspaceRoot);
|
|
344
|
+
if (module2) {
|
|
345
|
+
logger.step(`Import it as ${module2}/packages/${name}`);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
function addGoInternalLib(workspaceRoot, name) {
|
|
349
|
+
prepareGo(workspaceRoot);
|
|
350
|
+
runNx(
|
|
351
|
+
[
|
|
352
|
+
"g",
|
|
353
|
+
"@nx-go/nx-go:library",
|
|
354
|
+
`libs/${name}`,
|
|
355
|
+
`--name=${name}`,
|
|
356
|
+
"--tags=type:go-internal-lib",
|
|
357
|
+
"--no-interactive"
|
|
358
|
+
],
|
|
359
|
+
workspaceRoot
|
|
360
|
+
);
|
|
361
|
+
addProjectJsonTargets((0, import_node_path3.join)(workspaceRoot, "libs", name, "project.json"), {
|
|
362
|
+
test: goTestTarget(),
|
|
363
|
+
lint: goLintTarget()
|
|
364
|
+
});
|
|
365
|
+
const module2 = goModulePath(workspaceRoot);
|
|
366
|
+
if (module2) {
|
|
367
|
+
logger.step(`Import it as ${module2}/libs/${name}`);
|
|
368
|
+
}
|
|
203
369
|
}
|
|
204
370
|
|
|
205
371
|
// src/commands/add/node.ts
|
|
372
|
+
var import_node_path4 = require("path");
|
|
206
373
|
function ensurePlugin(workspaceRoot, packageName) {
|
|
207
374
|
if (hasPlugin(workspaceRoot, packageName)) {
|
|
208
375
|
return;
|
|
@@ -212,17 +379,20 @@ function ensurePlugin(workspaceRoot, packageName) {
|
|
|
212
379
|
}
|
|
213
380
|
function runNodeApp(workspaceRoot, name, stack, framework) {
|
|
214
381
|
ensurePlugin(workspaceRoot, "@nx/node");
|
|
215
|
-
runNx(
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
382
|
+
runNx(
|
|
383
|
+
[
|
|
384
|
+
"g",
|
|
385
|
+
"@nx/node:application",
|
|
386
|
+
`apps/${name}`,
|
|
387
|
+
"--bundler=esbuild",
|
|
388
|
+
`--unitTestRunner=${stack.testRunner}`,
|
|
389
|
+
"--linter=eslint",
|
|
390
|
+
"--e2eTestRunner=none",
|
|
391
|
+
`--framework=${framework}`,
|
|
392
|
+
"--no-interactive"
|
|
393
|
+
],
|
|
394
|
+
workspaceRoot
|
|
395
|
+
);
|
|
226
396
|
}
|
|
227
397
|
function nodeAppPackageTarget(name) {
|
|
228
398
|
const zip = `dist/drop/node-app-${name}.zip`;
|
|
@@ -238,7 +408,9 @@ function nodeAppPackageTarget(name) {
|
|
|
238
408
|
function addNodeApp(workspaceRoot, name, stack, framework = "none") {
|
|
239
409
|
runNodeApp(workspaceRoot, name, stack, framework);
|
|
240
410
|
ensureAdmZip(workspaceRoot);
|
|
241
|
-
addNxTargets((0,
|
|
411
|
+
addNxTargets((0, import_node_path4.join)(workspaceRoot, "apps", name, "package.json"), {
|
|
412
|
+
package: nodeAppPackageTarget(name)
|
|
413
|
+
});
|
|
242
414
|
}
|
|
243
415
|
function ensureAzureFunctionsPackage(workspaceRoot) {
|
|
244
416
|
if (hasPlugin(workspaceRoot, "@azure/functions")) {
|
|
@@ -289,15 +461,20 @@ describe('buildGreeting', () => {
|
|
|
289
461
|
})
|
|
290
462
|
`;
|
|
291
463
|
function repairNodeFunctionAppManifest(nodeFunctionAppRoot, workspaceRoot) {
|
|
292
|
-
const manifestPath = (0,
|
|
464
|
+
const manifestPath = (0, import_node_path4.join)(nodeFunctionAppRoot, "package.json");
|
|
293
465
|
const manifest = readJson(manifestPath);
|
|
294
|
-
const azureFunctionsVersion = readJson(
|
|
466
|
+
const azureFunctionsVersion = readJson(
|
|
467
|
+
(0, import_node_path4.join)(workspaceRoot, "node_modules/@azure/functions/package.json")
|
|
468
|
+
).version;
|
|
295
469
|
const dependencies = manifest.dependencies ?? {};
|
|
296
|
-
writeFileEnsured(
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
470
|
+
writeFileEnsured(
|
|
471
|
+
manifestPath,
|
|
472
|
+
toJson({
|
|
473
|
+
...manifest,
|
|
474
|
+
main: "main.js",
|
|
475
|
+
dependencies: { ...dependencies, "@azure/functions": `^${azureFunctionsVersion}` }
|
|
476
|
+
})
|
|
477
|
+
);
|
|
301
478
|
}
|
|
302
479
|
function nodeFunctionAppPackageTarget(name) {
|
|
303
480
|
const zip = `dist/drop/node-function-app-${name}.zip`;
|
|
@@ -314,19 +491,27 @@ function nodeFunctionAppPackageTarget(name) {
|
|
|
314
491
|
function addNodeFunctionApp(workspaceRoot, name, stack) {
|
|
315
492
|
runNodeApp(workspaceRoot, name, stack, "none");
|
|
316
493
|
ensureAzureFunctionsPackage(workspaceRoot);
|
|
317
|
-
const nodeFunctionAppRoot = (0,
|
|
318
|
-
writeFileEnsured((0,
|
|
319
|
-
writeFileEnsured((0,
|
|
320
|
-
writeFileEnsured(
|
|
321
|
-
|
|
322
|
-
|
|
494
|
+
const nodeFunctionAppRoot = (0, import_node_path4.join)(workspaceRoot, "apps", name);
|
|
495
|
+
writeFileEnsured((0, import_node_path4.join)(nodeFunctionAppRoot, "src/main.ts"), NODE_FUNCTION_APP_MAIN);
|
|
496
|
+
writeFileEnsured((0, import_node_path4.join)(nodeFunctionAppRoot, "src/functions/hello.ts"), NODE_FUNCTION_APP_HELLO);
|
|
497
|
+
writeFileEnsured(
|
|
498
|
+
(0, import_node_path4.join)(nodeFunctionAppRoot, "src/functions/greeting.ts"),
|
|
499
|
+
NODE_FUNCTION_APP_GREETING
|
|
500
|
+
);
|
|
501
|
+
writeFileEnsured(
|
|
502
|
+
(0, import_node_path4.join)(nodeFunctionAppRoot, "src/functions/greeting.spec.ts"),
|
|
503
|
+
NODE_FUNCTION_APP_GREETING_SPEC
|
|
504
|
+
);
|
|
505
|
+
writeFileEnsured((0, import_node_path4.join)(nodeFunctionAppRoot, "host.json"), NODE_FUNCTION_APP_HOST_JSON);
|
|
323
506
|
repairNodeFunctionAppManifest(nodeFunctionAppRoot, workspaceRoot);
|
|
324
507
|
ensureAdmZip(workspaceRoot);
|
|
325
|
-
addNxTargets((0,
|
|
508
|
+
addNxTargets((0, import_node_path4.join)(nodeFunctionAppRoot, "package.json"), {
|
|
509
|
+
package: nodeFunctionAppPackageTarget(name)
|
|
510
|
+
});
|
|
326
511
|
}
|
|
327
512
|
|
|
328
513
|
// src/commands/add/npmLib.ts
|
|
329
|
-
var
|
|
514
|
+
var import_node_path5 = require("path");
|
|
330
515
|
var NPM_LIB_ESLINT_CONFIG = `import { globSync, readFileSync } from 'node:fs';
|
|
331
516
|
import { dirname, join } from 'node:path';
|
|
332
517
|
import { fileURLToPath } from 'node:url';
|
|
@@ -369,21 +554,25 @@ export default [
|
|
|
369
554
|
`;
|
|
370
555
|
async function addNpmLib(workspaceRoot, name, options, kindProvided, stack) {
|
|
371
556
|
const scope = options.scope ?? (kindProvided ? defaultScope(workspaceRoot) : await promptText("npm scope for the published package", defaultScope(workspaceRoot)));
|
|
372
|
-
runNx(
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
557
|
+
runNx(
|
|
558
|
+
[
|
|
559
|
+
"g",
|
|
560
|
+
"@nx/js:lib",
|
|
561
|
+
`packages/${name}`,
|
|
562
|
+
"--publishable",
|
|
563
|
+
`--importPath=${scope}/${name}`,
|
|
564
|
+
"--bundler=rollup",
|
|
565
|
+
`--unitTestRunner=${stack.testRunner}`,
|
|
566
|
+
"--linter=eslint",
|
|
567
|
+
"--no-interactive"
|
|
568
|
+
],
|
|
569
|
+
workspaceRoot
|
|
570
|
+
);
|
|
571
|
+
markPublic((0, import_node_path5.join)(workspaceRoot, "packages", name, "package.json"));
|
|
572
|
+
writeFileEnsured(
|
|
573
|
+
(0, import_node_path5.join)(workspaceRoot, "packages", name, "eslint.config.mjs"),
|
|
574
|
+
NPM_LIB_ESLINT_CONFIG
|
|
575
|
+
);
|
|
387
576
|
}
|
|
388
577
|
function markPublic(manifestPath) {
|
|
389
578
|
const manifest = readJson(manifestPath);
|
|
@@ -391,12 +580,14 @@ function markPublic(manifestPath) {
|
|
|
391
580
|
}
|
|
392
581
|
|
|
393
582
|
// src/commands/add/python.ts
|
|
394
|
-
var
|
|
395
|
-
var
|
|
583
|
+
var import_node_fs3 = require("fs");
|
|
584
|
+
var import_node_path6 = require("path");
|
|
396
585
|
function ensurePython(workspaceRoot) {
|
|
397
586
|
const hasPython = runShell("python3", ["--version"], workspaceRoot) === 0 || runShell("python", ["--version"], workspaceRoot) === 0;
|
|
398
587
|
if (!hasPython) {
|
|
399
|
-
throw new Error(
|
|
588
|
+
throw new Error(
|
|
589
|
+
"Python not found. Install Python 3.9+ first: https://www.python.org/downloads/"
|
|
590
|
+
);
|
|
400
591
|
}
|
|
401
592
|
}
|
|
402
593
|
function pythonPipPluginSpec() {
|
|
@@ -419,7 +610,7 @@ pytest
|
|
|
419
610
|
pip-audit
|
|
420
611
|
`;
|
|
421
612
|
function ensureRequirementsDev(workspaceRoot) {
|
|
422
|
-
const requirementsDevPath = (0,
|
|
613
|
+
const requirementsDevPath = (0, import_node_path6.join)(workspaceRoot, "requirements-dev.txt");
|
|
423
614
|
if (!fileExists(requirementsDevPath)) {
|
|
424
615
|
writeFileEnsured(requirementsDevPath, PYTHON_REQUIREMENTS_DEV);
|
|
425
616
|
}
|
|
@@ -427,7 +618,7 @@ function ensureRequirementsDev(workspaceRoot) {
|
|
|
427
618
|
function pythonModuleDirectory(name) {
|
|
428
619
|
return name.replaceAll("-", "_");
|
|
429
620
|
}
|
|
430
|
-
function
|
|
621
|
+
function addProjectJsonTargets2(projectJsonPath, newTargets) {
|
|
431
622
|
const project = readJson(projectJsonPath);
|
|
432
623
|
const targets = project.targets ?? {};
|
|
433
624
|
writeFileEnsured(projectJsonPath, toJson({ ...project, targets: { ...targets, ...newTargets } }));
|
|
@@ -459,33 +650,69 @@ function addPythonApp(workspaceRoot, name) {
|
|
|
459
650
|
ensurePythonPipPlugin(workspaceRoot);
|
|
460
651
|
ensureRequirementsDev(workspaceRoot);
|
|
461
652
|
ensureAdmZip(workspaceRoot);
|
|
462
|
-
runNx(
|
|
463
|
-
|
|
653
|
+
runNx(
|
|
654
|
+
["g", "@mnci/nx-python-pip:application", name, `--directory=apps/${name}`, "--no-interactive"],
|
|
655
|
+
workspaceRoot
|
|
656
|
+
);
|
|
657
|
+
addProjectJsonTargets2((0, import_node_path6.join)(workspaceRoot, "apps", name, "project.json"), {
|
|
658
|
+
package: pythonAppPackageTarget(name)
|
|
659
|
+
});
|
|
464
660
|
}
|
|
465
661
|
function addPythonFunctionApp(workspaceRoot, name) {
|
|
466
662
|
ensurePython(workspaceRoot);
|
|
467
663
|
ensurePythonPipPlugin(workspaceRoot);
|
|
468
664
|
ensureRequirementsDev(workspaceRoot);
|
|
469
665
|
ensureAdmZip(workspaceRoot);
|
|
470
|
-
runNx(
|
|
666
|
+
runNx(
|
|
667
|
+
[
|
|
668
|
+
"g",
|
|
669
|
+
"@mnci/nx-python-pip:function-application",
|
|
670
|
+
name,
|
|
671
|
+
`--directory=apps/${name}`,
|
|
672
|
+
"--no-interactive"
|
|
673
|
+
],
|
|
674
|
+
workspaceRoot
|
|
675
|
+
);
|
|
471
676
|
const moduleDirectory = pythonModuleDirectory(name);
|
|
472
|
-
|
|
677
|
+
addProjectJsonTargets2((0, import_node_path6.join)(workspaceRoot, "apps", name, "project.json"), {
|
|
678
|
+
package: pythonFunctionAppPackageTarget(name, moduleDirectory)
|
|
679
|
+
});
|
|
473
680
|
}
|
|
474
681
|
function addPythonLib(workspaceRoot, name) {
|
|
475
682
|
ensurePython(workspaceRoot);
|
|
476
683
|
ensurePythonPipPlugin(workspaceRoot);
|
|
477
684
|
ensureRequirementsDev(workspaceRoot);
|
|
478
|
-
runNx(
|
|
685
|
+
runNx(
|
|
686
|
+
[
|
|
687
|
+
"g",
|
|
688
|
+
"@mnci/nx-python-pip:library",
|
|
689
|
+
name,
|
|
690
|
+
`--directory=python-packages/${name}`,
|
|
691
|
+
"--no-interactive"
|
|
692
|
+
],
|
|
693
|
+
workspaceRoot
|
|
694
|
+
);
|
|
479
695
|
}
|
|
480
696
|
function addPythonInternalLib(workspaceRoot, name) {
|
|
481
697
|
ensurePython(workspaceRoot);
|
|
482
698
|
ensurePythonPipPlugin(workspaceRoot);
|
|
483
699
|
ensureRequirementsDev(workspaceRoot);
|
|
484
|
-
runNx(
|
|
700
|
+
runNx(
|
|
701
|
+
[
|
|
702
|
+
"g",
|
|
703
|
+
"@mnci/nx-python-pip:internal-library",
|
|
704
|
+
name,
|
|
705
|
+
`--directory=libs/${name}`,
|
|
706
|
+
"--no-interactive"
|
|
707
|
+
],
|
|
708
|
+
workspaceRoot
|
|
709
|
+
);
|
|
485
710
|
}
|
|
486
711
|
var PYTHON_PROJECT_DIRS = ["apps", "python-packages", "libs"];
|
|
487
712
|
function findPythonProjectDirectory(workspaceRoot, name) {
|
|
488
|
-
return PYTHON_PROJECT_DIRS.find(
|
|
713
|
+
return PYTHON_PROJECT_DIRS.find(
|
|
714
|
+
(directory) => fileExists((0, import_node_path6.join)(workspaceRoot, directory, name, "pyproject.toml"))
|
|
715
|
+
);
|
|
489
716
|
}
|
|
490
717
|
function parseVendorEntries(pyprojectToml) {
|
|
491
718
|
const match = /^\s*vendor\s*=\s*\[([^\]]*)\]/m.exec(pyprojectToml);
|
|
@@ -498,7 +725,10 @@ function addVendorEntry(pyprojectToml, lib) {
|
|
|
498
725
|
const existing = parseVendorEntries(pyprojectToml);
|
|
499
726
|
if (existing.length > 0) {
|
|
500
727
|
const merged = [...existing, lib].map((name) => `"${name}"`).join(", ");
|
|
501
|
-
return pyprojectToml.replace(
|
|
728
|
+
return pyprojectToml.replace(
|
|
729
|
+
/^(\s*vendor\s*=\s*)\[([^\]]*)\]/m,
|
|
730
|
+
(_match, prefix) => `${prefix}[${merged}]`
|
|
731
|
+
);
|
|
502
732
|
}
|
|
503
733
|
const table = `[tool.mnci-python-pip]
|
|
504
734
|
vendor = ["${lib}"]
|
|
@@ -510,31 +740,39 @@ ${table}`;
|
|
|
510
740
|
async function addPythonVendor(workspaceRoot, consumer, options, kindProvided) {
|
|
511
741
|
const lib = options.lib ?? (kindProvided ? void 0 : await promptText("Internal Python library to vendor (an existing libs/<name>)"));
|
|
512
742
|
if (!lib) {
|
|
513
|
-
throw new Error(
|
|
743
|
+
throw new Error(
|
|
744
|
+
"`add python-vendor` needs the library to vendor: pass --lib <name> (an existing libs/<name> internal library)."
|
|
745
|
+
);
|
|
514
746
|
}
|
|
515
747
|
const consumerDirectory = findPythonProjectDirectory(workspaceRoot, consumer);
|
|
516
748
|
if (!consumerDirectory) {
|
|
517
|
-
throw new Error(
|
|
749
|
+
throw new Error(
|
|
750
|
+
`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.`
|
|
751
|
+
);
|
|
518
752
|
}
|
|
519
|
-
if (!fileExists((0,
|
|
520
|
-
throw new Error(
|
|
753
|
+
if (!fileExists((0, import_node_path6.join)(workspaceRoot, "libs", lib, "pyproject.toml"))) {
|
|
754
|
+
throw new Error(
|
|
755
|
+
`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.`
|
|
756
|
+
);
|
|
521
757
|
}
|
|
522
758
|
if (consumerDirectory === "libs" && consumer === lib) {
|
|
523
759
|
throw new Error(`'${consumer}' cannot vendor itself.`);
|
|
524
760
|
}
|
|
525
|
-
const pyprojectPath = (0,
|
|
526
|
-
const pyprojectToml = (0,
|
|
761
|
+
const pyprojectPath = (0, import_node_path6.join)(workspaceRoot, consumerDirectory, consumer, "pyproject.toml");
|
|
762
|
+
const pyprojectToml = (0, import_node_fs3.readFileSync)(pyprojectPath, "utf8");
|
|
527
763
|
if (parseVendorEntries(pyprojectToml).includes(lib)) {
|
|
528
764
|
logger.info(`'${consumer}' already vendors '${lib}' \u2014 nothing to do.`);
|
|
529
765
|
return;
|
|
530
766
|
}
|
|
531
767
|
writeFileEnsured(pyprojectPath, addVendorEntry(pyprojectToml, lib));
|
|
532
|
-
logger.success(
|
|
768
|
+
logger.success(
|
|
769
|
+
`'${consumer}' now vendors '${lib}' \u2014 its wheel will include '${lib}''s module on the next build.`
|
|
770
|
+
);
|
|
533
771
|
}
|
|
534
772
|
|
|
535
773
|
// src/commands/add/reactApp.ts
|
|
536
|
-
var
|
|
537
|
-
var
|
|
774
|
+
var import_node_fs4 = require("fs");
|
|
775
|
+
var import_node_path7 = require("path");
|
|
538
776
|
function ensurePlugin2(workspaceRoot, packageName) {
|
|
539
777
|
if (hasPlugin(workspaceRoot, packageName)) {
|
|
540
778
|
return;
|
|
@@ -557,26 +795,35 @@ function reactAppTargets(name) {
|
|
|
557
795
|
executor: "nx:run-commands",
|
|
558
796
|
// eslint-disable-next-line unicorn/no-incorrect-template-string-interpolation -- {workspaceRoot} is an Nx output token
|
|
559
797
|
outputs: [`{workspaceRoot}/apps/${name}/dist-${environment}`],
|
|
560
|
-
options: {
|
|
798
|
+
options: {
|
|
799
|
+
command: `vite build --mode ${environment} --outDir dist-${environment}`,
|
|
800
|
+
cwd: `apps/${name}`
|
|
801
|
+
}
|
|
561
802
|
};
|
|
562
803
|
}
|
|
563
|
-
const zipStatements = REACT_ENVIRONMENTS.map(
|
|
804
|
+
const zipStatements = REACT_ENVIRONMENTS.map(
|
|
805
|
+
(environment) => `z=new A();z.addLocalFolder('apps/${name}/dist-${environment}');z.writeZip('dist/drop/react-app-${name}-${environment}.zip')`
|
|
806
|
+
).join(";");
|
|
564
807
|
targets.package = {
|
|
565
808
|
executor: "nx:run-commands",
|
|
566
809
|
dependsOn: REACT_ENVIRONMENTS.map((environment) => `build-${environment}`),
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
810
|
+
outputs: REACT_ENVIRONMENTS.map(
|
|
811
|
+
// eslint-disable-next-line unicorn/no-incorrect-template-string-interpolation
|
|
812
|
+
(environment) => `{workspaceRoot}/dist/drop/react-app-${name}-${environment}.zip`
|
|
813
|
+
),
|
|
814
|
+
options: {
|
|
815
|
+
command: `node -e "const fs=require('node:fs');fs.mkdirSync('dist/drop',{recursive:true});const A=require('adm-zip');let z;${zipStatements}"`
|
|
816
|
+
}
|
|
570
817
|
};
|
|
571
818
|
return targets;
|
|
572
819
|
}
|
|
573
820
|
function allowEnvFiles(workspaceRoot) {
|
|
574
|
-
const gitignorePath = (0,
|
|
821
|
+
const gitignorePath = (0, import_node_path7.join)(workspaceRoot, ".gitignore");
|
|
575
822
|
if (!fileExists(gitignorePath)) {
|
|
576
823
|
return;
|
|
577
824
|
}
|
|
578
825
|
const marker = "# MoNecromanCI: keep the committed per-environment Vite config (public VITE_ values)";
|
|
579
|
-
const current = (0,
|
|
826
|
+
const current = (0, import_node_fs4.readFileSync)(gitignorePath, "utf8");
|
|
580
827
|
if (current.includes(marker)) {
|
|
581
828
|
return;
|
|
582
829
|
}
|
|
@@ -588,24 +835,27 @@ ${allow}
|
|
|
588
835
|
}
|
|
589
836
|
function addReactApp(workspaceRoot, name, stack) {
|
|
590
837
|
ensurePlugin2(workspaceRoot, "@nx/react");
|
|
591
|
-
runNx(
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
838
|
+
runNx(
|
|
839
|
+
[
|
|
840
|
+
"g",
|
|
841
|
+
"@nx/react:app",
|
|
842
|
+
`apps/${name}`,
|
|
843
|
+
"--bundler=vite",
|
|
844
|
+
`--unitTestRunner=${stack.testRunner}`,
|
|
845
|
+
"--linter=eslint",
|
|
846
|
+
"--style=css",
|
|
847
|
+
"--e2eTestRunner=none",
|
|
848
|
+
"--no-interactive"
|
|
849
|
+
],
|
|
850
|
+
workspaceRoot
|
|
851
|
+
);
|
|
602
852
|
ensureAdmZip(workspaceRoot);
|
|
603
|
-
const reactAppRoot = (0,
|
|
853
|
+
const reactAppRoot = (0, import_node_path7.join)(workspaceRoot, "apps", name);
|
|
604
854
|
for (const environment of REACT_ENVIRONMENTS) {
|
|
605
|
-
writeFileEnsured((0,
|
|
855
|
+
writeFileEnsured((0, import_node_path7.join)(reactAppRoot, `.env.${environment}`), reactEnvironmentFile(environment));
|
|
606
856
|
}
|
|
607
857
|
allowEnvFiles(workspaceRoot);
|
|
608
|
-
addNxTargets((0,
|
|
858
|
+
addNxTargets((0, import_node_path7.join)(reactAppRoot, "package.json"), reactAppTargets(name));
|
|
609
859
|
}
|
|
610
860
|
|
|
611
861
|
// src/commands/add.ts
|
|
@@ -619,11 +869,15 @@ var PROJECT_KINDS = [
|
|
|
619
869
|
"python-function-app",
|
|
620
870
|
"python-lib",
|
|
621
871
|
"python-internal-lib",
|
|
622
|
-
"python-vendor"
|
|
872
|
+
"python-vendor",
|
|
873
|
+
"go-app",
|
|
874
|
+
"go-function-app",
|
|
875
|
+
"go-lib",
|
|
876
|
+
"go-internal-lib"
|
|
623
877
|
];
|
|
624
878
|
async function runAdd(kind, name, options) {
|
|
625
879
|
const workspaceRoot = process.cwd();
|
|
626
|
-
if (!fileExists((0,
|
|
880
|
+
if (!fileExists((0, import_node_path8.join)(workspaceRoot, "nx.json"))) {
|
|
627
881
|
throw new Error("No nx.json found here. Run `add` from the workspace root.");
|
|
628
882
|
}
|
|
629
883
|
const stack = readWorkspaceStack(workspaceRoot);
|
|
@@ -652,16 +906,19 @@ async function runAdd(kind, name, options) {
|
|
|
652
906
|
break;
|
|
653
907
|
}
|
|
654
908
|
case "internal-lib": {
|
|
655
|
-
runNx(
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
909
|
+
runNx(
|
|
910
|
+
[
|
|
911
|
+
"g",
|
|
912
|
+
"@nx/js:lib",
|
|
913
|
+
`libs/${resolvedName}`,
|
|
914
|
+
"--bundler=tsc",
|
|
915
|
+
`--unitTestRunner=${stack.testRunner}`,
|
|
916
|
+
"--linter=eslint",
|
|
917
|
+
"--no-interactive"
|
|
918
|
+
],
|
|
919
|
+
workspaceRoot
|
|
920
|
+
);
|
|
921
|
+
markPrivate((0, import_node_path8.join)(workspaceRoot, "libs", resolvedName, "package.json"));
|
|
665
922
|
break;
|
|
666
923
|
}
|
|
667
924
|
case "python-app": {
|
|
@@ -680,6 +937,22 @@ async function runAdd(kind, name, options) {
|
|
|
680
937
|
addPythonInternalLib(workspaceRoot, resolvedName);
|
|
681
938
|
break;
|
|
682
939
|
}
|
|
940
|
+
case "go-app": {
|
|
941
|
+
addGoApp(workspaceRoot, resolvedName);
|
|
942
|
+
break;
|
|
943
|
+
}
|
|
944
|
+
case "go-function-app": {
|
|
945
|
+
addGoFunctionApp(workspaceRoot, resolvedName);
|
|
946
|
+
break;
|
|
947
|
+
}
|
|
948
|
+
case "go-lib": {
|
|
949
|
+
addGoLib(workspaceRoot, resolvedName);
|
|
950
|
+
break;
|
|
951
|
+
}
|
|
952
|
+
case "go-internal-lib": {
|
|
953
|
+
addGoInternalLib(workspaceRoot, resolvedName);
|
|
954
|
+
break;
|
|
955
|
+
}
|
|
683
956
|
case "python-vendor": {
|
|
684
957
|
await addPythonVendor(workspaceRoot, resolvedName, options, kindProvided);
|
|
685
958
|
syncProjectReferences(workspaceRoot);
|
|
@@ -687,7 +960,9 @@ async function runAdd(kind, name, options) {
|
|
|
687
960
|
}
|
|
688
961
|
default: {
|
|
689
962
|
const exhaustive = resolvedKind;
|
|
690
|
-
throw new Error(
|
|
963
|
+
throw new Error(
|
|
964
|
+
`Unknown project kind '${exhaustive}'. Expected one of: ${PROJECT_KINDS.join(", ")}.`
|
|
965
|
+
);
|
|
691
966
|
}
|
|
692
967
|
}
|
|
693
968
|
syncProjectReferences(workspaceRoot);
|
|
@@ -696,14 +971,17 @@ async function runAdd(kind, name, options) {
|
|
|
696
971
|
function syncProjectReferences(workspaceRoot) {
|
|
697
972
|
logger.step("Syncing TypeScript project references (nx sync)");
|
|
698
973
|
if (runShell("npx", ["nx", "sync"], workspaceRoot) !== 0) {
|
|
699
|
-
logger.warn(
|
|
974
|
+
logger.warn(
|
|
975
|
+
"nx sync did not complete \u2014 run `npx nx sync` yourself so cross-project imports resolve in your editor."
|
|
976
|
+
);
|
|
700
977
|
}
|
|
701
978
|
}
|
|
702
979
|
function readWorkspaceStack(workspaceRoot) {
|
|
703
|
-
const nxJson = readJson(
|
|
980
|
+
const nxJson = readJson(
|
|
981
|
+
(0, import_node_path8.join)(workspaceRoot, "nx.json")
|
|
982
|
+
);
|
|
704
983
|
const stack = nxJson.mnci?.stack;
|
|
705
984
|
return {
|
|
706
|
-
linter: stack?.linter === "oxlint" ? "none" : "eslint",
|
|
707
985
|
testRunner: stack?.testRunner === "vitest" ? "vitest" : "jest"
|
|
708
986
|
};
|
|
709
987
|
}
|
|
@@ -713,15 +991,16 @@ function markPrivate(manifestPath) {
|
|
|
713
991
|
}
|
|
714
992
|
|
|
715
993
|
// src/commands/interactive.ts
|
|
716
|
-
var
|
|
994
|
+
var import_node_path12 = require("path");
|
|
717
995
|
var import_prompts7 = require("@inquirer/prompts");
|
|
718
996
|
|
|
719
997
|
// src/commands/new.ts
|
|
720
|
-
var
|
|
998
|
+
var import_node_path10 = require("path");
|
|
721
999
|
|
|
722
1000
|
// src/overlay.ts
|
|
723
|
-
var
|
|
724
|
-
var DEFAULT_STACK = {
|
|
1001
|
+
var import_node_path9 = require("path");
|
|
1002
|
+
var DEFAULT_STACK = { testRunner: "jest" };
|
|
1003
|
+
var PRETTIER_VERSION = "^3.8.1";
|
|
725
1004
|
var TS_COMPILER_DEPENDENCIES = {
|
|
726
1005
|
"@typescript/native": "npm:typescript@^7.0.2",
|
|
727
1006
|
typescript: "npm:@typescript/typescript6@^6.0.2"
|
|
@@ -781,6 +1060,77 @@ var COMMITLINT_CONFIG = `export default { extends: ['@commitlint/config-conventi
|
|
|
781
1060
|
`;
|
|
782
1061
|
var COMMIT_MSG_HOOK = `npx --no -- commitlint --edit "$1"
|
|
783
1062
|
`;
|
|
1063
|
+
var PRETTIER_CONFIG = `{
|
|
1064
|
+
"semi": false,
|
|
1065
|
+
"singleQuote": true,
|
|
1066
|
+
"trailingComma": "es5",
|
|
1067
|
+
"arrowParens": "avoid",
|
|
1068
|
+
"printWidth": 100,
|
|
1069
|
+
"tabWidth": 2,
|
|
1070
|
+
"useTabs": false
|
|
1071
|
+
}
|
|
1072
|
+
`;
|
|
1073
|
+
var PRETTIER_IGNORE = `node_modules
|
|
1074
|
+
dist
|
|
1075
|
+
dist-dev
|
|
1076
|
+
dist-uat
|
|
1077
|
+
dist-prod
|
|
1078
|
+
coverage
|
|
1079
|
+
.next
|
|
1080
|
+
out
|
|
1081
|
+
.nx
|
|
1082
|
+
tmp
|
|
1083
|
+
package-lock.json
|
|
1084
|
+
*.lock
|
|
1085
|
+
`;
|
|
1086
|
+
function vscodeWorkspace(workspaceName) {
|
|
1087
|
+
return JSON.stringify(
|
|
1088
|
+
{
|
|
1089
|
+
folders: [
|
|
1090
|
+
{ path: ".", name: workspaceName },
|
|
1091
|
+
{ path: "packages/cli", name: "@mnci/cli" },
|
|
1092
|
+
{ path: "packages/nx-python-pip", name: "@mnci/nx-python-pip" },
|
|
1093
|
+
{ path: "libs/monecromanci-v2", name: "@mnci/monecromanci" }
|
|
1094
|
+
],
|
|
1095
|
+
settings: {
|
|
1096
|
+
"eslint.validate": [
|
|
1097
|
+
"javascript",
|
|
1098
|
+
"javascriptreact",
|
|
1099
|
+
"typescript",
|
|
1100
|
+
"typescriptreact",
|
|
1101
|
+
"json",
|
|
1102
|
+
"jsonc",
|
|
1103
|
+
"markdown",
|
|
1104
|
+
"yaml"
|
|
1105
|
+
],
|
|
1106
|
+
"editor.codeActionsOnSave": {
|
|
1107
|
+
"source.fixAll.eslint": "explicit"
|
|
1108
|
+
},
|
|
1109
|
+
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
|
1110
|
+
"editor.formatOnSave": true,
|
|
1111
|
+
"[json]": {
|
|
1112
|
+
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
|
1113
|
+
},
|
|
1114
|
+
"[jsonc]": {
|
|
1115
|
+
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
|
1116
|
+
},
|
|
1117
|
+
"[yaml]": {
|
|
1118
|
+
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
|
1119
|
+
}
|
|
1120
|
+
},
|
|
1121
|
+
extensions: {
|
|
1122
|
+
recommendations: [
|
|
1123
|
+
"dbaeumer.vscode-eslint",
|
|
1124
|
+
"esbenp.prettier-vscode",
|
|
1125
|
+
"nrwl.angular-console",
|
|
1126
|
+
"firsttris.vscode-jest-runner"
|
|
1127
|
+
]
|
|
1128
|
+
}
|
|
1129
|
+
},
|
|
1130
|
+
null,
|
|
1131
|
+
2
|
|
1132
|
+
);
|
|
1133
|
+
}
|
|
784
1134
|
var ROOT_SCRIPTS = {
|
|
785
1135
|
build: "nx run-many -t build",
|
|
786
1136
|
lint: "nx run-many -t lint",
|
|
@@ -790,17 +1140,19 @@ var ROOT_SCRIPTS = {
|
|
|
790
1140
|
"release:preview": "nx release --dry-run",
|
|
791
1141
|
prepare: "husky"
|
|
792
1142
|
};
|
|
793
|
-
function rootScripts(
|
|
794
|
-
|
|
1143
|
+
function rootScripts() {
|
|
1144
|
+
return {
|
|
795
1145
|
...ROOT_SCRIPTS,
|
|
796
|
-
|
|
797
|
-
format: "
|
|
798
|
-
"
|
|
799
|
-
}
|
|
800
|
-
return { ...scripts, "python:install": `${PYTHON_INSTALL_GUARD} && ${PYTHON_WORKSPACE_INSTALL_GUARD}` };
|
|
1146
|
+
format: "prettier --write .",
|
|
1147
|
+
"format:check": "prettier --check .",
|
|
1148
|
+
"python:install": `${PYTHON_INSTALL_GUARD} && ${PYTHON_WORKSPACE_INSTALL_GUARD}`
|
|
1149
|
+
};
|
|
801
1150
|
}
|
|
802
1151
|
function generatorDefaults(stack) {
|
|
803
|
-
const shared = {
|
|
1152
|
+
const shared = {
|
|
1153
|
+
linter: "eslint",
|
|
1154
|
+
unitTestRunner: stack.testRunner
|
|
1155
|
+
};
|
|
804
1156
|
return {
|
|
805
1157
|
"@nx/react:application": shared,
|
|
806
1158
|
"@nx/react:library": shared,
|
|
@@ -814,40 +1166,13 @@ function mnciConfig(options) {
|
|
|
814
1166
|
agent: options.agent,
|
|
815
1167
|
variableGroup: options.variableGroup,
|
|
816
1168
|
ci: options.ci,
|
|
817
|
-
stack: {
|
|
1169
|
+
stack: { testRunner: options.stack.testRunner }
|
|
818
1170
|
};
|
|
819
1171
|
}
|
|
820
1172
|
function readMnciConfig(workspaceRoot) {
|
|
821
|
-
const nxJson = readJson((0,
|
|
1173
|
+
const nxJson = readJson((0, import_node_path9.join)(workspaceRoot, "nx.json"));
|
|
822
1174
|
return nxJson.mnci ?? {};
|
|
823
1175
|
}
|
|
824
|
-
var OXLINT_CONFIG = `import { defineConfig } from 'oxlint'
|
|
825
|
-
import standard from 'oxc-standard/.oxlintrc.json' with { type: 'json' }
|
|
826
|
-
|
|
827
|
-
// Generated by MoNecromanCI. JavaScript Standard Style linting via the
|
|
828
|
-
// oxc-standard preset (unicorn + React + react-perf + TypeScript + oxc).
|
|
829
|
-
// Add project-specific rule overrides in the \`rules\` block below.
|
|
830
|
-
export default defineConfig({
|
|
831
|
-
extends: [standard],
|
|
832
|
-
rules: {
|
|
833
|
-
// The modern JSX transform (Nx's default) needs no React import in scope.
|
|
834
|
-
'react/react-in-jsx-scope': 'off',
|
|
835
|
-
},
|
|
836
|
-
})
|
|
837
|
-
`;
|
|
838
|
-
var OXFMT_CONFIG = `import { defineConfig } from 'oxfmt'
|
|
839
|
-
|
|
840
|
-
// Generated by MoNecromanCI. JavaScript Standard Style formatting, matching the
|
|
841
|
-
// oxc-standard lint preset: no semicolons, single quotes, 2-space indent.
|
|
842
|
-
export default defineConfig({
|
|
843
|
-
singleQuote: true,
|
|
844
|
-
semi: false,
|
|
845
|
-
printWidth: 100,
|
|
846
|
-
tabWidth: 2,
|
|
847
|
-
trailingComma: 'es5',
|
|
848
|
-
arrowParens: 'avoid',
|
|
849
|
-
})
|
|
850
|
-
`;
|
|
851
1176
|
function pythonPublishUrl(registry) {
|
|
852
1177
|
if (registry.kind === "azure-artifacts") {
|
|
853
1178
|
return `https://pkgs.dev.azure.com/${registry.organization}/${registry.project}/_packaging/${registry.artifactsFeed}/pypi/upload/`;
|
|
@@ -857,6 +1182,11 @@ function pythonPublishUrl(registry) {
|
|
|
857
1182
|
var PYTHON_INSTALL_GUARD = `node -e "if(!require('node:fs').existsSync('requirements-dev.txt')){console.log('No Python projects - skipping.');process.exit(0)}const py=process.platform==='win32'?'python':'python3';process.exit(require('node:child_process').spawnSync(py+' -m pip install -r requirements-dev.txt',{stdio:'inherit',shell:true}).status ?? 1)"`;
|
|
858
1183
|
var PYTHON_WORKSPACE_INSTALL_GUARD = `node -e "const fs=require('node:fs'),path=require('node:path');const editableDirs=[...fs.globSync('apps/*/pyproject.toml'),...fs.globSync('python-packages/*/pyproject.toml'),...fs.globSync('libs/*/pyproject.toml')].map((p)=>path.dirname(p));const requirementsFiles=fs.globSync('apps/*/requirements.txt');if(editableDirs.length===0&&requirementsFiles.length===0){console.log('No Python projects - skipping.');process.exit(0)}const args=['-m','pip','install','--quiet',...editableDirs.flatMap((d)=>['-e',d]),...requirementsFiles.flatMap((f)=>['-r',f])];const py=process.platform==='win32'?'python':'python3';process.exit(require('node:child_process').spawnSync(py,args,{stdio:'inherit'}).status ?? 1)"`;
|
|
859
1184
|
var PIP_AUDIT_GUARD = `node -e "if(!require('node:fs').existsSync('requirements-dev.txt')){console.log('No Python projects - skipping.');process.exit(0)}const py=process.platform==='win32'?'python':'python3';require('node:child_process').spawnSync(py,['-m','pip_audit'],{stdio:'inherit'});process.exit(0)"`;
|
|
1185
|
+
var GO_MODULE_DOWNLOAD_GUARD = `node -e "if(!require('node:fs').existsSync('go.mod')){console.log('No Go projects - skipping.');process.exit(0)}process.exit(require('node:child_process').spawnSync('go',['mod','download'],{stdio:'inherit'}).status ?? 1)"`;
|
|
1186
|
+
var GOLANGCI_LINT_INSTALL_GUARD = `node -e "const fs=require('node:fs'),cp=require('node:child_process');if(!fs.existsSync('go.mod')){console.log('No Go projects - skipping.');process.exit(0)}if(cp.spawnSync('golangci-lint',['--version'],{stdio:'ignore'}).status===0){console.log('golangci-lint already installed - skipping.');process.exit(0)}process.exit(cp.spawnSync('go',['install','github.com/golangci/golangci-lint/v2/cmd/golangci-lint@latest'],{stdio:'inherit'}).status ?? 1)"`;
|
|
1187
|
+
var GO_TOOL_PATH_PRELUDE = `const fs=require('node:fs'),cp=require('node:child_process');if(!fs.existsSync('go.mod')){console.log('No Go projects - skipping.');process.exit(0)}const r=cp.spawnSync('go',['env','GOPATH'],{encoding:'utf8'});if(r.status!==0){console.log('Could not resolve GOPATH - skipping.');process.exit(0)}const bin=require('node:path').join(r.stdout.trim(),'bin');`;
|
|
1188
|
+
var GO_TOOL_PATH_AZURE = `node -e "${GO_TOOL_PATH_PRELUDE}console.log('##vso[task.prependpath]'+bin)"`;
|
|
1189
|
+
var GO_TOOL_PATH_GITHUB = `node -e "${GO_TOOL_PATH_PRELUDE}if(!process.env.GITHUB_PATH){console.log('Not running in GitHub Actions - skipping.');process.exit(0)}fs.appendFileSync(process.env.GITHUB_PATH,bin+${String.raw`'\n'`})"`;
|
|
860
1190
|
var NPM_AUDIT_STEP = "npm audit --audit-level=high || echo npm audit found vulnerabilities, see log above - non-blocking, run npm audit locally to inspect";
|
|
861
1191
|
var PACK_APPS_GUARD = `node -e "const fs=require('node:fs');fs.mkdirSync('dist/drop',{recursive:true});if(fs.globSync('apps/*/project.json').length===0){console.log('No apps to pack - skipping.');process.exit(0)}process.exit(require('node:child_process').spawnSync('npx nx run-many -t package',{stdio:'inherit',shell:true}).status ?? 1)"`;
|
|
862
1192
|
function releaseGuard(pythonPublishEnv) {
|
|
@@ -956,6 +1286,22 @@ steps:
|
|
|
956
1286
|
- script: ${PIP_AUDIT_GUARD}
|
|
957
1287
|
displayName: pip-audit (non-blocking)
|
|
958
1288
|
|
|
1289
|
+
# Go, if the workspace has any: hosted agents ship the toolchain, so only
|
|
1290
|
+
# the module cache and the linter need seeding. Both guards skip cleanly on
|
|
1291
|
+
# a workspace with no root go.mod.
|
|
1292
|
+
- script: ${GO_MODULE_DOWNLOAD_GUARD}
|
|
1293
|
+
displayName: Download Go module dependencies
|
|
1294
|
+
|
|
1295
|
+
# golangci-lint is what the generated Go lint target actually runs (the
|
|
1296
|
+
# plugin's own default is 'go fmt', which only reformats). 'go install'
|
|
1297
|
+
# drops it in GOPATH/bin, which is not on PATH by default on a hosted
|
|
1298
|
+
# agent \u2014 so prepend it for every later step in the job.
|
|
1299
|
+
- script: ${GOLANGCI_LINT_INSTALL_GUARD}
|
|
1300
|
+
displayName: Install golangci-lint
|
|
1301
|
+
|
|
1302
|
+
- script: ${GO_TOOL_PATH_AZURE}
|
|
1303
|
+
displayName: Add Go tool bin to PATH
|
|
1304
|
+
|
|
959
1305
|
# Fails fast, with an unambiguous message, when a stale TypeScript project
|
|
960
1306
|
# reference (or another sync generator's drift) was never synced+committed
|
|
961
1307
|
# locally \u2014 sync.applyChanges (nx.json) only auto-applies interactively, so
|
|
@@ -1019,7 +1365,10 @@ steps:
|
|
|
1019
1365
|
}
|
|
1020
1366
|
function githubActionsYaml(agent, pythonPublishUrl2, registryKind = "azure-artifacts") {
|
|
1021
1367
|
const onMain = `github.event_name != 'pull_request' && github.ref_name == 'main'`;
|
|
1022
|
-
const [npmAuthName, npmAuthValue] = npmAuthEnvVariable(
|
|
1368
|
+
const [npmAuthName, npmAuthValue] = npmAuthEnvVariable(
|
|
1369
|
+
registryKind,
|
|
1370
|
+
(name) => `\${{ secrets.${name} }}`
|
|
1371
|
+
);
|
|
1023
1372
|
return `name: CI
|
|
1024
1373
|
|
|
1025
1374
|
# Generated by MoNecromanCI. Deliberately thin: Nx builds, 'nx release'
|
|
@@ -1089,6 +1438,22 @@ jobs:
|
|
|
1089
1438
|
- run: ${PIP_AUDIT_GUARD}
|
|
1090
1439
|
name: pip-audit (non-blocking)
|
|
1091
1440
|
|
|
1441
|
+
# Go, if the workspace has any: hosted runners ship the toolchain, so
|
|
1442
|
+
# only the module cache and the linter need seeding. Both guards skip
|
|
1443
|
+
# cleanly on a workspace with no root go.mod.
|
|
1444
|
+
- run: ${GO_MODULE_DOWNLOAD_GUARD}
|
|
1445
|
+
name: Download Go module dependencies
|
|
1446
|
+
|
|
1447
|
+
# golangci-lint is what the generated Go lint target actually runs (the
|
|
1448
|
+
# plugin's own default is 'go fmt', which only reformats). 'go install'
|
|
1449
|
+
# drops it in GOPATH/bin, which is not on PATH by default on a hosted
|
|
1450
|
+
# runner \u2014 so publish it for every later step in the job.
|
|
1451
|
+
- run: ${GOLANGCI_LINT_INSTALL_GUARD}
|
|
1452
|
+
name: Install golangci-lint
|
|
1453
|
+
|
|
1454
|
+
- run: ${GO_TOOL_PATH_GITHUB}
|
|
1455
|
+
name: Add Go tool bin to PATH
|
|
1456
|
+
|
|
1092
1457
|
# Fails fast, with an unambiguous message, when a stale TypeScript project
|
|
1093
1458
|
# reference (or another sync generator's drift) was never synced+committed
|
|
1094
1459
|
# locally \u2014 sync.applyChanges (nx.json) only auto-applies interactively, so
|
|
@@ -1170,33 +1535,54 @@ updates:
|
|
|
1170
1535
|
interval: weekly
|
|
1171
1536
|
`;
|
|
1172
1537
|
function applyOverlay(workspaceRoot, options) {
|
|
1173
|
-
const nxJsonPath = (0,
|
|
1538
|
+
const nxJsonPath = (0, import_node_path9.join)(workspaceRoot, "nx.json");
|
|
1174
1539
|
const nxJson = readJson(nxJsonPath);
|
|
1175
|
-
const generators = {
|
|
1540
|
+
const generators = {
|
|
1541
|
+
...nxJson.generators,
|
|
1542
|
+
...generatorDefaults(options.stack)
|
|
1543
|
+
};
|
|
1176
1544
|
const sync = { ...nxJson.sync, ...SYNC_CONFIG };
|
|
1177
1545
|
const mnci = { ...nxJson.mnci, ...mnciConfig(options) };
|
|
1178
1546
|
writeFileEnsured(nxJsonPath, toJson({ ...withReleaseConfig(nxJson), generators, sync, mnci }));
|
|
1179
|
-
const manifestPath = (0,
|
|
1547
|
+
const manifestPath = (0, import_node_path9.join)(workspaceRoot, "package.json");
|
|
1180
1548
|
const manifest = readJson(manifestPath);
|
|
1181
|
-
const scripts = {
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1549
|
+
const scripts = {
|
|
1550
|
+
...manifest.scripts,
|
|
1551
|
+
...rootScripts()
|
|
1552
|
+
};
|
|
1553
|
+
const devDeps = {
|
|
1554
|
+
...manifest.devDependencies,
|
|
1555
|
+
...TS_COMPILER_DEPENDENCIES,
|
|
1556
|
+
prettier: PRETTIER_VERSION
|
|
1557
|
+
};
|
|
1558
|
+
writeFileEnsured(
|
|
1559
|
+
manifestPath,
|
|
1560
|
+
toJson({ ...manifest, name: `${options.scope}/source`, scripts, devDependencies: devDeps })
|
|
1561
|
+
);
|
|
1562
|
+
writeFileEnsured((0, import_node_path9.join)(workspaceRoot, ".npmrc"), npmrcContent(options.registry, options.scope));
|
|
1563
|
+
writeFileEnsured((0, import_node_path9.join)(workspaceRoot, "commitlint.config.mjs"), COMMITLINT_CONFIG);
|
|
1564
|
+
const hookPath = (0, import_node_path9.join)(workspaceRoot, ".husky/commit-msg");
|
|
1187
1565
|
writeFileEnsured(hookPath, COMMIT_MSG_HOOK);
|
|
1188
1566
|
markExecutable(hookPath);
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1567
|
+
writeFileEnsured((0, import_node_path9.join)(workspaceRoot, ".prettierrc.json"), PRETTIER_CONFIG);
|
|
1568
|
+
writeFileEnsured((0, import_node_path9.join)(workspaceRoot, ".prettierignore"), PRETTIER_IGNORE);
|
|
1569
|
+
writeFileEnsured(
|
|
1570
|
+
(0, import_node_path9.join)(workspaceRoot, `${options.workspaceName}.code-workspace`),
|
|
1571
|
+
vscodeWorkspace(options.workspaceName)
|
|
1572
|
+
);
|
|
1193
1573
|
const publishUrl = pythonPublishUrl(options.registry);
|
|
1194
1574
|
if (options.ci === "azure" || options.ci === "both") {
|
|
1195
|
-
writeFileEnsured(
|
|
1575
|
+
writeFileEnsured(
|
|
1576
|
+
(0, import_node_path9.join)(workspaceRoot, "azure-pipelines.yml"),
|
|
1577
|
+
azurePipelinesYaml(options.agent, options.variableGroup, publishUrl, options.registry.kind)
|
|
1578
|
+
);
|
|
1196
1579
|
}
|
|
1197
1580
|
if (options.ci === "github" || options.ci === "both") {
|
|
1198
|
-
writeFileEnsured(
|
|
1199
|
-
|
|
1581
|
+
writeFileEnsured(
|
|
1582
|
+
(0, import_node_path9.join)(workspaceRoot, ".github/workflows/ci.yml"),
|
|
1583
|
+
githubActionsYaml(options.agent, publishUrl, options.registry.kind)
|
|
1584
|
+
);
|
|
1585
|
+
writeFileEnsured((0, import_node_path9.join)(workspaceRoot, ".github/dependabot.yml"), DEPENDABOT_CONFIG);
|
|
1200
1586
|
}
|
|
1201
1587
|
}
|
|
1202
1588
|
|
|
@@ -1205,18 +1591,14 @@ function nxCloudProviderValue(ci) {
|
|
|
1205
1591
|
return ci === "azure" ? "azure" : "github";
|
|
1206
1592
|
}
|
|
1207
1593
|
async function resolveStack(options) {
|
|
1208
|
-
|
|
1209
|
-
const complete = Boolean(fromFlags.linter && fromFlags.testRunner);
|
|
1210
|
-
if (complete || options.yes) {
|
|
1594
|
+
if (options.testRunner || options.yes) {
|
|
1211
1595
|
return {
|
|
1212
|
-
|
|
1213
|
-
testRunner: fromFlags.testRunner ?? DEFAULT_STACK.testRunner
|
|
1596
|
+
testRunner: options.testRunner ?? DEFAULT_STACK.testRunner
|
|
1214
1597
|
};
|
|
1215
1598
|
}
|
|
1216
1599
|
const prompted = await promptStack();
|
|
1217
1600
|
return {
|
|
1218
|
-
|
|
1219
|
-
testRunner: fromFlags.testRunner ?? prompted.testRunner
|
|
1601
|
+
testRunner: prompted.testRunner
|
|
1220
1602
|
};
|
|
1221
1603
|
}
|
|
1222
1604
|
var CI_PROVIDERS = /* @__PURE__ */ new Set(["azure", "github", "both"]);
|
|
@@ -1249,53 +1631,52 @@ async function runNew(name, options) {
|
|
|
1249
1631
|
const scope = options.scope ?? (options.yes ? `@${workspaceName}` : await promptText("npm scope for publishable packages", `@${workspaceName}`));
|
|
1250
1632
|
const registry = await resolveRegistry(options);
|
|
1251
1633
|
const ci = await resolveCi(options);
|
|
1252
|
-
const agent = options.agent ?? (options.yes ? "ubuntu-latest" : await promptText(
|
|
1634
|
+
const agent = options.agent ?? (options.yes ? "ubuntu-latest" : await promptText(
|
|
1635
|
+
"CI build agent/runner (vmImage, GitHub Actions runner label, or self-hosted pool name)",
|
|
1636
|
+
"ubuntu-latest"
|
|
1637
|
+
));
|
|
1253
1638
|
const variableGroup = ci === "github" ? options.variableGroup ?? "Build" : options.variableGroup ?? (options.yes ? "Build" : await promptText("Azure DevOps variable group holding the npm PAT", "Build"));
|
|
1254
1639
|
const stack = await resolveStack(options);
|
|
1255
1640
|
const nxCloud = options.nxCloud ?? (options.yes ? false : await promptNxCloud());
|
|
1256
1641
|
logger.step(`Creating Nx workspace '${workspaceName}' (preset: ts)`);
|
|
1257
|
-
runNpx(
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
const
|
|
1270
|
-
logger.step(
|
|
1271
|
-
|
|
1642
|
+
runNpx(
|
|
1643
|
+
[
|
|
1644
|
+
"--yes",
|
|
1645
|
+
"create-nx-workspace@latest",
|
|
1646
|
+
workspaceName,
|
|
1647
|
+
"--preset=ts",
|
|
1648
|
+
"--pm=npm",
|
|
1649
|
+
nxCloud ? `--nxCloud=${nxCloudProviderValue(ci)}` : "--nxCloud=skip",
|
|
1650
|
+
"--no-interactive"
|
|
1651
|
+
],
|
|
1652
|
+
process.cwd()
|
|
1653
|
+
);
|
|
1654
|
+
const workspaceRoot = (0, import_node_path10.join)(process.cwd(), workspaceName);
|
|
1655
|
+
logger.step(
|
|
1656
|
+
"Applying MoNecromanCI overlay (VS Code workspace, release config, .npmrc, commitlint, pipeline, stack)"
|
|
1657
|
+
);
|
|
1658
|
+
applyOverlay(workspaceRoot, { workspaceName, scope, registry, agent, variableGroup, ci, stack });
|
|
1659
|
+
logger.step("Installing the toolchain (commitlint + husky)");
|
|
1660
|
+
const installStatus = runShell(
|
|
1661
|
+
"npm",
|
|
1662
|
+
["install", "--save-dev", "husky", "@commitlint/cli", "@commitlint/config-conventional"],
|
|
1663
|
+
workspaceRoot
|
|
1664
|
+
);
|
|
1272
1665
|
if (installStatus !== 0) {
|
|
1273
1666
|
throw new Error(`npm install of the toolchain failed with exit code ${installStatus}`);
|
|
1274
1667
|
}
|
|
1275
1668
|
runShell("npx", ["husky"], workspaceRoot);
|
|
1276
1669
|
logger.success("Done. Next steps:");
|
|
1277
1670
|
logger.info(` cd ${workspaceName}`);
|
|
1278
|
-
logger.info(
|
|
1279
|
-
|
|
1671
|
+
logger.info(
|
|
1672
|
+
" mnci add react-app web # or: node-app, node-function-app, npm-lib, internal-lib,"
|
|
1673
|
+
);
|
|
1674
|
+
logger.info(
|
|
1675
|
+
" # python-app, python-function-app, python-lib, python-internal-lib"
|
|
1676
|
+
);
|
|
1280
1677
|
logger.info(' git add -A && git commit -m "feat: initial workspace"');
|
|
1281
1678
|
}
|
|
1282
1679
|
|
|
1283
|
-
// src/commands/interactive.ts
|
|
1284
|
-
async function runInteractive() {
|
|
1285
|
-
const inWorkspace = fileExists((0, import_node_path10.join)(process.cwd(), "nx.json"));
|
|
1286
|
-
const newChoice = { name: "Create a new monorepo", value: "new" };
|
|
1287
|
-
const addChoice = { name: "Add a project to this workspace", value: "add" };
|
|
1288
|
-
const action = await (0, import_prompts7.select)({
|
|
1289
|
-
message: "What would you like to do?",
|
|
1290
|
-
choices: inWorkspace ? [addChoice, newChoice] : [newChoice, addChoice]
|
|
1291
|
-
});
|
|
1292
|
-
if (action === "new") {
|
|
1293
|
-
await runNew(void 0, {});
|
|
1294
|
-
} else {
|
|
1295
|
-
await runAdd(void 0, void 0, {});
|
|
1296
|
-
}
|
|
1297
|
-
}
|
|
1298
|
-
|
|
1299
1680
|
// src/commands/upgrade.ts
|
|
1300
1681
|
var import_node_path11 = require("path");
|
|
1301
1682
|
function resolveRegistry2(options, persisted) {
|
|
@@ -1305,7 +1686,9 @@ function resolveRegistry2(options, persisted) {
|
|
|
1305
1686
|
const project = options.project ?? persistedAzure?.project;
|
|
1306
1687
|
const artifactsFeed = options.artifactsFeed ?? persistedAzure?.artifactsFeed;
|
|
1307
1688
|
if (!organization || !project || !artifactsFeed) {
|
|
1308
|
-
throw new Error(
|
|
1689
|
+
throw new Error(
|
|
1690
|
+
"Azure Artifacts registry needs --organization, --project and --artifacts-feed (none found in nx.json's persisted config either)."
|
|
1691
|
+
);
|
|
1309
1692
|
}
|
|
1310
1693
|
return { kind: "azure-artifacts", organization, project, artifactsFeed };
|
|
1311
1694
|
}
|
|
@@ -1315,7 +1698,9 @@ function resolveRegistry2(options, persisted) {
|
|
|
1315
1698
|
if (persisted) {
|
|
1316
1699
|
return persisted;
|
|
1317
1700
|
}
|
|
1318
|
-
throw new Error(
|
|
1701
|
+
throw new Error(
|
|
1702
|
+
"No registry found in nx.json's persisted config. Pass --registry npm or --registry azure-artifacts (with --organization/--project/--artifacts-feed)."
|
|
1703
|
+
);
|
|
1319
1704
|
}
|
|
1320
1705
|
function resolveOverlayOptions(options, persisted) {
|
|
1321
1706
|
const scope = options.scope ?? persisted.scope;
|
|
@@ -1325,52 +1710,157 @@ function resolveOverlayOptions(options, persisted) {
|
|
|
1325
1710
|
const registry = resolveRegistry2(options, persisted.registry);
|
|
1326
1711
|
const ci = options.ci ?? persisted.ci;
|
|
1327
1712
|
if (!ci) {
|
|
1328
|
-
throw new Error(
|
|
1713
|
+
throw new Error(
|
|
1714
|
+
"No CI provider found in nx.json's persisted config. Pass --ci azure|github|both explicitly."
|
|
1715
|
+
);
|
|
1329
1716
|
}
|
|
1330
1717
|
const agent = options.agent ?? persisted.agent;
|
|
1331
1718
|
if (!agent) {
|
|
1332
|
-
throw new Error(
|
|
1719
|
+
throw new Error(
|
|
1720
|
+
"No CI build agent found in nx.json's persisted config. Pass --agent explicitly."
|
|
1721
|
+
);
|
|
1333
1722
|
}
|
|
1334
1723
|
const variableGroup = options.variableGroup ?? persisted.variableGroup ?? "Build";
|
|
1335
|
-
const linter = options.linter ?? persisted.stack?.linter;
|
|
1336
1724
|
const testRunner = options.testRunner ?? persisted.stack?.testRunner;
|
|
1337
|
-
if (!
|
|
1338
|
-
throw new Error(
|
|
1725
|
+
if (!testRunner) {
|
|
1726
|
+
throw new Error(
|
|
1727
|
+
"No testRunner found in nx.json's persisted config. Pass --test-runner explicitly."
|
|
1728
|
+
);
|
|
1339
1729
|
}
|
|
1340
|
-
return { scope, registry, agent, variableGroup, ci, stack: {
|
|
1730
|
+
return { scope, registry, agent, variableGroup, ci, stack: { testRunner } };
|
|
1341
1731
|
}
|
|
1342
1732
|
function runUpgrade(workspaceRoot, options) {
|
|
1343
1733
|
if (!fileExists((0, import_node_path11.join)(workspaceRoot, "nx.json"))) {
|
|
1344
|
-
throw new Error(
|
|
1734
|
+
throw new Error(
|
|
1735
|
+
`No nx.json found in ${workspaceRoot} \u2014 this does not look like an Nx workspace. Run 'mnci upgrade' from the workspace root.`
|
|
1736
|
+
);
|
|
1345
1737
|
}
|
|
1346
1738
|
const persisted = readMnciConfig(workspaceRoot);
|
|
1347
1739
|
const resolved = resolveOverlayOptions(options, persisted);
|
|
1348
|
-
logger.step(
|
|
1740
|
+
logger.step(
|
|
1741
|
+
"Re-applying the MoNecromanCI overlay (release config, .npmrc, commitlint, pipeline, stack)"
|
|
1742
|
+
);
|
|
1349
1743
|
applyOverlay(workspaceRoot, resolved);
|
|
1350
1744
|
logger.success("Done. Review the changes with `git diff` before committing.");
|
|
1351
1745
|
}
|
|
1352
1746
|
|
|
1353
|
-
// src/
|
|
1354
|
-
function
|
|
1747
|
+
// src/commands/interactive.ts
|
|
1748
|
+
async function runInteractive() {
|
|
1749
|
+
const inWorkspace = fileExists((0, import_node_path12.join)(process.cwd(), "nx.json"));
|
|
1750
|
+
const newChoice = { name: "Create a new monorepo", value: "new" };
|
|
1751
|
+
const addChoice = { name: "Add a project to this workspace", value: "add" };
|
|
1752
|
+
const upgradeChoice = {
|
|
1753
|
+
name: "Upgrade this workspace (re-apply the latest overlay)",
|
|
1754
|
+
value: "upgrade"
|
|
1755
|
+
};
|
|
1756
|
+
const action = await (0, import_prompts7.select)({
|
|
1757
|
+
message: "What would you like to do?",
|
|
1758
|
+
choices: inWorkspace ? [addChoice, upgradeChoice, newChoice] : [newChoice, addChoice, upgradeChoice]
|
|
1759
|
+
});
|
|
1760
|
+
if (action === "new") {
|
|
1761
|
+
await runNew(void 0, {});
|
|
1762
|
+
} else if (action === "upgrade") {
|
|
1763
|
+
runUpgrade(process.cwd(), {});
|
|
1764
|
+
} else {
|
|
1765
|
+
await runAdd(void 0, void 0, {});
|
|
1766
|
+
}
|
|
1767
|
+
}
|
|
1768
|
+
|
|
1769
|
+
// src/util/versionChecker.ts
|
|
1770
|
+
var import_node_fs5 = require("fs");
|
|
1771
|
+
var import_node_path13 = require("path");
|
|
1772
|
+
var import_cross_spawn2 = __toESM(require("cross-spawn"));
|
|
1773
|
+
var PACKAGE_NAME = "@mnci/cli";
|
|
1774
|
+
var REGISTRY_TIMEOUT_MS = 2e3;
|
|
1775
|
+
function checkForUpdate(currentVersion) {
|
|
1776
|
+
try {
|
|
1777
|
+
if (!process.stdout.isTTY) {
|
|
1778
|
+
return;
|
|
1779
|
+
}
|
|
1780
|
+
setImmediate(() => {
|
|
1781
|
+
try {
|
|
1782
|
+
reportIfOutdated(currentVersion);
|
|
1783
|
+
} catch {
|
|
1784
|
+
}
|
|
1785
|
+
});
|
|
1786
|
+
} catch {
|
|
1787
|
+
}
|
|
1788
|
+
}
|
|
1789
|
+
function reportIfOutdated(currentVersion) {
|
|
1790
|
+
const result = import_cross_spawn2.default.sync("npm", ["view", PACKAGE_NAME, "version"], {
|
|
1791
|
+
encoding: "utf8",
|
|
1792
|
+
timeout: REGISTRY_TIMEOUT_MS,
|
|
1793
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
1794
|
+
});
|
|
1795
|
+
if (result.error || result.status !== 0) {
|
|
1796
|
+
return;
|
|
1797
|
+
}
|
|
1798
|
+
const latestVersion = (result.stdout ?? "").trim();
|
|
1799
|
+
if (!latestVersion || !isNewerVersion(latestVersion, currentVersion)) {
|
|
1800
|
+
return;
|
|
1801
|
+
}
|
|
1802
|
+
logger.info("");
|
|
1803
|
+
logger.info(`A new version of ${PACKAGE_NAME} is available: ${latestVersion}`);
|
|
1804
|
+
logger.info(` Current version: ${currentVersion}`);
|
|
1805
|
+
logger.info(` Update with: npm install -g ${PACKAGE_NAME}@latest`);
|
|
1806
|
+
logger.info("");
|
|
1807
|
+
}
|
|
1808
|
+
function isNewerVersion(candidate, current) {
|
|
1809
|
+
const candidateParts = releaseTriple(candidate);
|
|
1810
|
+
const currentParts = releaseTriple(current);
|
|
1811
|
+
if (!candidateParts || !currentParts) {
|
|
1812
|
+
return false;
|
|
1813
|
+
}
|
|
1814
|
+
for (const [index, candidatePart] of candidateParts.entries()) {
|
|
1815
|
+
if (candidatePart !== currentParts[index]) {
|
|
1816
|
+
return candidatePart > currentParts[index];
|
|
1817
|
+
}
|
|
1818
|
+
}
|
|
1819
|
+
return false;
|
|
1820
|
+
}
|
|
1821
|
+
function releaseTriple(version) {
|
|
1822
|
+
const match = /^(\d+)\.(\d+)\.(\d+)/.exec(version.trim());
|
|
1823
|
+
return match ? [Number(match[1]), Number(match[2]), Number(match[3])] : void 0;
|
|
1824
|
+
}
|
|
1825
|
+
function readCliVersion(packageDirectory) {
|
|
1355
1826
|
try {
|
|
1356
|
-
const
|
|
1357
|
-
|
|
1827
|
+
const manifestPath = (0, import_node_path13.join)(packageDirectory, "..", "package.json");
|
|
1828
|
+
const manifest = JSON.parse((0, import_node_fs5.readFileSync)(manifestPath, "utf8"));
|
|
1829
|
+
return manifest.version ?? "0.0.0";
|
|
1358
1830
|
} catch {
|
|
1359
1831
|
return "0.0.0";
|
|
1360
1832
|
}
|
|
1361
1833
|
}
|
|
1362
|
-
|
|
1834
|
+
|
|
1835
|
+
// src/cli.ts
|
|
1836
|
+
function buildProgram(cliVersion) {
|
|
1363
1837
|
const program = new import_commander.Command();
|
|
1364
|
-
program.name("mnci").description("MoNecromanCI \u2014 a thin CLI over official Nx plugins").version(
|
|
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(
|
|
1838
|
+
program.name("mnci").description("MoNecromanCI \u2014 a thin CLI over official Nx plugins").version(cliVersion, "-v, --version", "display the version");
|
|
1839
|
+
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(
|
|
1840
|
+
"--agent <pool>",
|
|
1841
|
+
"CI build agent: a vmImage (e.g. ubuntu-latest) or self-hosted pool name"
|
|
1842
|
+
).option("--variable-group <name>", "Azure DevOps variable group holding the npm PAT").option("--ci <provider>", "CI provider: azure | github | both").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) => {
|
|
1366
1843
|
await runNew(name, options);
|
|
1367
1844
|
});
|
|
1368
|
-
program.command("upgrade").description(
|
|
1845
|
+
program.command("upgrade").description(
|
|
1846
|
+
"Re-apply the latest MoNecromanCI overlay to this workspace (release config, pipeline, npmrc, commitlint, husky hook, curated scripts)"
|
|
1847
|
+
).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(
|
|
1848
|
+
"--test-runner <runner>",
|
|
1849
|
+
"unit-test runner: jest | vitest (overrides the persisted value)"
|
|
1850
|
+
).action((options) => {
|
|
1369
1851
|
runUpgrade(process.cwd(), options);
|
|
1370
1852
|
});
|
|
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(
|
|
1372
|
-
|
|
1373
|
-
|
|
1853
|
+
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(
|
|
1854
|
+
"--framework <framework>",
|
|
1855
|
+
"node-app only: express | fastify | koa | nest | none (default: none)"
|
|
1856
|
+
).option(
|
|
1857
|
+
"--lib <name>",
|
|
1858
|
+
"python-vendor only: the internal Python library (libs/<name>) to vendor into <name>"
|
|
1859
|
+
).action(
|
|
1860
|
+
async (kind, name, options) => {
|
|
1861
|
+
await runAdd(kind, name, options);
|
|
1862
|
+
}
|
|
1863
|
+
);
|
|
1374
1864
|
program.action(async () => {
|
|
1375
1865
|
await runInteractive();
|
|
1376
1866
|
});
|
|
@@ -1378,7 +1868,15 @@ function buildProgram() {
|
|
|
1378
1868
|
}
|
|
1379
1869
|
async function main() {
|
|
1380
1870
|
try {
|
|
1381
|
-
|
|
1871
|
+
const cliVersion = readCliVersion(__dirname);
|
|
1872
|
+
const program = buildProgram(cliVersion);
|
|
1873
|
+
checkForUpdate(cliVersion);
|
|
1874
|
+
const arguments_ = new Set(process.argv.slice(2));
|
|
1875
|
+
const requestedVersionFlag = arguments_.has("-v") || arguments_.has("--version");
|
|
1876
|
+
if (process.stdout.isTTY && !requestedVersionFlag) {
|
|
1877
|
+
logger.info(`mnci v${cliVersion}`);
|
|
1878
|
+
}
|
|
1879
|
+
await program.parseAsync(process.argv);
|
|
1382
1880
|
} catch (error) {
|
|
1383
1881
|
logger.error(error instanceof Error ? error.message : String(error));
|
|
1384
1882
|
process.exitCode = 1;
|