@mnci/cli 1.7.0 → 1.9.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 +157 -90
- package/dist/cli.js +780 -265
- package/dist/cli.js.map +1 -1
- package/package.json +3 -3
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"
|
|
@@ -755,32 +1034,109 @@ function npmrcContent(registry, scope) {
|
|
|
755
1034
|
lines.push("");
|
|
756
1035
|
return lines.join("\n");
|
|
757
1036
|
}
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
}
|
|
1037
|
+
function releaseConfig(ci) {
|
|
1038
|
+
const githubReleases = ci === "github";
|
|
1039
|
+
return {
|
|
1040
|
+
projectsRelationship: "independent",
|
|
1041
|
+
projects: ["packages/*", "python-packages/*"],
|
|
1042
|
+
releaseTag: { pattern: "{projectName}@{version}" },
|
|
1043
|
+
git: { commit: false, tag: true, push: githubReleases },
|
|
1044
|
+
version: {
|
|
1045
|
+
conventionalCommits: true,
|
|
1046
|
+
fallbackCurrentVersionResolver: "disk",
|
|
1047
|
+
// Build only what is being released. Without this, @nx/js:lib's generator
|
|
1048
|
+
// defaults the pre-version command to building EVERY project, so a broken
|
|
1049
|
+
// (or merely slow) app build would block releasing unrelated packages.
|
|
1050
|
+
// Set here at `new` time it wins: the generator only fills this in when
|
|
1051
|
+
// absent (it spreads the existing release.version over its default). Both
|
|
1052
|
+
// globs are listed; `nx run-many` no-ops cleanly when one matches nothing.
|
|
1053
|
+
preVersionCommand: "npx nx run-many -t build --projects=packages/*,python-packages/*"
|
|
1054
|
+
},
|
|
1055
|
+
changelog: githubReleases ? {
|
|
1056
|
+
workspaceChangelog: false,
|
|
1057
|
+
projectChangelogs: { createRelease: "github", file: false }
|
|
1058
|
+
} : { workspaceChangelog: false }
|
|
1059
|
+
};
|
|
1060
|
+
}
|
|
776
1061
|
var SYNC_CONFIG = { applyChanges: true };
|
|
777
|
-
function withReleaseConfig(nxJson) {
|
|
778
|
-
return { ...nxJson, defaultBase: "main", release:
|
|
1062
|
+
function withReleaseConfig(nxJson, ci) {
|
|
1063
|
+
return { ...nxJson, defaultBase: "main", release: releaseConfig(ci) };
|
|
779
1064
|
}
|
|
780
1065
|
var COMMITLINT_CONFIG = `export default { extends: ['@commitlint/config-conventional'] }
|
|
781
1066
|
`;
|
|
782
1067
|
var COMMIT_MSG_HOOK = `npx --no -- commitlint --edit "$1"
|
|
783
1068
|
`;
|
|
1069
|
+
var PRETTIER_CONFIG = `{
|
|
1070
|
+
"semi": false,
|
|
1071
|
+
"singleQuote": true,
|
|
1072
|
+
"trailingComma": "es5",
|
|
1073
|
+
"arrowParens": "avoid",
|
|
1074
|
+
"printWidth": 100,
|
|
1075
|
+
"tabWidth": 2,
|
|
1076
|
+
"useTabs": false
|
|
1077
|
+
}
|
|
1078
|
+
`;
|
|
1079
|
+
var PRETTIER_IGNORE = `node_modules
|
|
1080
|
+
dist
|
|
1081
|
+
dist-dev
|
|
1082
|
+
dist-uat
|
|
1083
|
+
dist-prod
|
|
1084
|
+
coverage
|
|
1085
|
+
.next
|
|
1086
|
+
out
|
|
1087
|
+
.nx
|
|
1088
|
+
tmp
|
|
1089
|
+
package-lock.json
|
|
1090
|
+
*.lock
|
|
1091
|
+
`;
|
|
1092
|
+
function vscodeWorkspace(workspaceName) {
|
|
1093
|
+
return JSON.stringify(
|
|
1094
|
+
{
|
|
1095
|
+
folders: [
|
|
1096
|
+
{ path: ".", name: workspaceName },
|
|
1097
|
+
{ path: "packages/cli", name: "@mnci/cli" },
|
|
1098
|
+
{ path: "packages/nx-python-pip", name: "@mnci/nx-python-pip" },
|
|
1099
|
+
{ path: "libs/monecromanci-v2", name: "@mnci/monecromanci" }
|
|
1100
|
+
],
|
|
1101
|
+
settings: {
|
|
1102
|
+
"eslint.validate": [
|
|
1103
|
+
"javascript",
|
|
1104
|
+
"javascriptreact",
|
|
1105
|
+
"typescript",
|
|
1106
|
+
"typescriptreact",
|
|
1107
|
+
"json",
|
|
1108
|
+
"jsonc",
|
|
1109
|
+
"markdown",
|
|
1110
|
+
"yaml"
|
|
1111
|
+
],
|
|
1112
|
+
"editor.codeActionsOnSave": {
|
|
1113
|
+
"source.fixAll.eslint": "explicit"
|
|
1114
|
+
},
|
|
1115
|
+
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
|
1116
|
+
"editor.formatOnSave": true,
|
|
1117
|
+
"[json]": {
|
|
1118
|
+
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
|
1119
|
+
},
|
|
1120
|
+
"[jsonc]": {
|
|
1121
|
+
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
|
1122
|
+
},
|
|
1123
|
+
"[yaml]": {
|
|
1124
|
+
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
|
1125
|
+
}
|
|
1126
|
+
},
|
|
1127
|
+
extensions: {
|
|
1128
|
+
recommendations: [
|
|
1129
|
+
"dbaeumer.vscode-eslint",
|
|
1130
|
+
"esbenp.prettier-vscode",
|
|
1131
|
+
"nrwl.angular-console",
|
|
1132
|
+
"firsttris.vscode-jest-runner"
|
|
1133
|
+
]
|
|
1134
|
+
}
|
|
1135
|
+
},
|
|
1136
|
+
null,
|
|
1137
|
+
2
|
|
1138
|
+
);
|
|
1139
|
+
}
|
|
784
1140
|
var ROOT_SCRIPTS = {
|
|
785
1141
|
build: "nx run-many -t build",
|
|
786
1142
|
lint: "nx run-many -t lint",
|
|
@@ -790,17 +1146,19 @@ var ROOT_SCRIPTS = {
|
|
|
790
1146
|
"release:preview": "nx release --dry-run",
|
|
791
1147
|
prepare: "husky"
|
|
792
1148
|
};
|
|
793
|
-
function rootScripts(
|
|
794
|
-
|
|
1149
|
+
function rootScripts() {
|
|
1150
|
+
return {
|
|
795
1151
|
...ROOT_SCRIPTS,
|
|
796
|
-
|
|
797
|
-
format: "
|
|
798
|
-
"
|
|
799
|
-
}
|
|
800
|
-
return { ...scripts, "python:install": `${PYTHON_INSTALL_GUARD} && ${PYTHON_WORKSPACE_INSTALL_GUARD}` };
|
|
1152
|
+
format: "prettier --write .",
|
|
1153
|
+
"format:check": "prettier --check .",
|
|
1154
|
+
"python:install": `${PYTHON_INSTALL_GUARD} && ${PYTHON_WORKSPACE_INSTALL_GUARD}`
|
|
1155
|
+
};
|
|
801
1156
|
}
|
|
802
1157
|
function generatorDefaults(stack) {
|
|
803
|
-
const shared = {
|
|
1158
|
+
const shared = {
|
|
1159
|
+
linter: "eslint",
|
|
1160
|
+
unitTestRunner: stack.testRunner
|
|
1161
|
+
};
|
|
804
1162
|
return {
|
|
805
1163
|
"@nx/react:application": shared,
|
|
806
1164
|
"@nx/react:library": shared,
|
|
@@ -814,40 +1172,13 @@ function mnciConfig(options) {
|
|
|
814
1172
|
agent: options.agent,
|
|
815
1173
|
variableGroup: options.variableGroup,
|
|
816
1174
|
ci: options.ci,
|
|
817
|
-
stack: {
|
|
1175
|
+
stack: { testRunner: options.stack.testRunner }
|
|
818
1176
|
};
|
|
819
1177
|
}
|
|
820
1178
|
function readMnciConfig(workspaceRoot) {
|
|
821
|
-
const nxJson = readJson((0,
|
|
1179
|
+
const nxJson = readJson((0, import_node_path9.join)(workspaceRoot, "nx.json"));
|
|
822
1180
|
return nxJson.mnci ?? {};
|
|
823
1181
|
}
|
|
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
1182
|
function pythonPublishUrl(registry) {
|
|
852
1183
|
if (registry.kind === "azure-artifacts") {
|
|
853
1184
|
return `https://pkgs.dev.azure.com/${registry.organization}/${registry.project}/_packaging/${registry.artifactsFeed}/pypi/upload/`;
|
|
@@ -857,6 +1188,11 @@ function pythonPublishUrl(registry) {
|
|
|
857
1188
|
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
1189
|
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
1190
|
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)"`;
|
|
1191
|
+
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)"`;
|
|
1192
|
+
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)"`;
|
|
1193
|
+
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');`;
|
|
1194
|
+
var GO_TOOL_PATH_AZURE = `node -e "${GO_TOOL_PATH_PRELUDE}console.log('##vso[task.prependpath]'+bin)"`;
|
|
1195
|
+
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
1196
|
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
1197
|
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
1198
|
function releaseGuard(pythonPublishEnv) {
|
|
@@ -956,6 +1292,22 @@ steps:
|
|
|
956
1292
|
- script: ${PIP_AUDIT_GUARD}
|
|
957
1293
|
displayName: pip-audit (non-blocking)
|
|
958
1294
|
|
|
1295
|
+
# Go, if the workspace has any: hosted agents ship the toolchain, so only
|
|
1296
|
+
# the module cache and the linter need seeding. Both guards skip cleanly on
|
|
1297
|
+
# a workspace with no root go.mod.
|
|
1298
|
+
- script: ${GO_MODULE_DOWNLOAD_GUARD}
|
|
1299
|
+
displayName: Download Go module dependencies
|
|
1300
|
+
|
|
1301
|
+
# golangci-lint is what the generated Go lint target actually runs (the
|
|
1302
|
+
# plugin's own default is 'go fmt', which only reformats). 'go install'
|
|
1303
|
+
# drops it in GOPATH/bin, which is not on PATH by default on a hosted
|
|
1304
|
+
# agent \u2014 so prepend it for every later step in the job.
|
|
1305
|
+
- script: ${GOLANGCI_LINT_INSTALL_GUARD}
|
|
1306
|
+
displayName: Install golangci-lint
|
|
1307
|
+
|
|
1308
|
+
- script: ${GO_TOOL_PATH_AZURE}
|
|
1309
|
+
displayName: Add Go tool bin to PATH
|
|
1310
|
+
|
|
959
1311
|
# Fails fast, with an unambiguous message, when a stale TypeScript project
|
|
960
1312
|
# reference (or another sync generator's drift) was never synced+committed
|
|
961
1313
|
# locally \u2014 sync.applyChanges (nx.json) only auto-applies interactively, so
|
|
@@ -1017,9 +1369,13 @@ steps:
|
|
|
1017
1369
|
condition: ${onMain}
|
|
1018
1370
|
`;
|
|
1019
1371
|
}
|
|
1020
|
-
function githubActionsYaml(agent, pythonPublishUrl2, registryKind = "azure-artifacts") {
|
|
1372
|
+
function githubActionsYaml(agent, pythonPublishUrl2, registryKind = "azure-artifacts", ci = "github") {
|
|
1021
1373
|
const onMain = `github.event_name != 'pull_request' && github.ref_name == 'main'`;
|
|
1022
|
-
const [npmAuthName, npmAuthValue] = npmAuthEnvVariable(
|
|
1374
|
+
const [npmAuthName, npmAuthValue] = npmAuthEnvVariable(
|
|
1375
|
+
registryKind,
|
|
1376
|
+
(name) => `\${{ secrets.${name} }}`
|
|
1377
|
+
);
|
|
1378
|
+
const githubReleases = ci === "github";
|
|
1023
1379
|
return `name: CI
|
|
1024
1380
|
|
|
1025
1381
|
# Generated by MoNecromanCI. Deliberately thin: Nx builds, 'nx release'
|
|
@@ -1035,7 +1391,8 @@ on:
|
|
|
1035
1391
|
branches: [main]
|
|
1036
1392
|
|
|
1037
1393
|
permissions:
|
|
1038
|
-
# Lets the release step push the version tag nx release creates back to main
|
|
1394
|
+
# Lets the release step push the version tag nx release creates back to main,
|
|
1395
|
+
# and (github-only provider) create the GitHub Release itself.
|
|
1039
1396
|
contents: write
|
|
1040
1397
|
|
|
1041
1398
|
jobs:
|
|
@@ -1089,6 +1446,22 @@ jobs:
|
|
|
1089
1446
|
- run: ${PIP_AUDIT_GUARD}
|
|
1090
1447
|
name: pip-audit (non-blocking)
|
|
1091
1448
|
|
|
1449
|
+
# Go, if the workspace has any: hosted runners ship the toolchain, so
|
|
1450
|
+
# only the module cache and the linter need seeding. Both guards skip
|
|
1451
|
+
# cleanly on a workspace with no root go.mod.
|
|
1452
|
+
- run: ${GO_MODULE_DOWNLOAD_GUARD}
|
|
1453
|
+
name: Download Go module dependencies
|
|
1454
|
+
|
|
1455
|
+
# golangci-lint is what the generated Go lint target actually runs (the
|
|
1456
|
+
# plugin's own default is 'go fmt', which only reformats). 'go install'
|
|
1457
|
+
# drops it in GOPATH/bin, which is not on PATH by default on a hosted
|
|
1458
|
+
# runner \u2014 so publish it for every later step in the job.
|
|
1459
|
+
- run: ${GOLANGCI_LINT_INSTALL_GUARD}
|
|
1460
|
+
name: Install golangci-lint
|
|
1461
|
+
|
|
1462
|
+
- run: ${GO_TOOL_PATH_GITHUB}
|
|
1463
|
+
name: Add Go tool bin to PATH
|
|
1464
|
+
|
|
1092
1465
|
# Fails fast, with an unambiguous message, when a stale TypeScript project
|
|
1093
1466
|
# reference (or another sync generator's drift) was never synced+committed
|
|
1094
1467
|
# locally \u2014 sync.applyChanges (nx.json) only auto-applies interactively, so
|
|
@@ -1126,13 +1499,19 @@ jobs:
|
|
|
1126
1499
|
# (python-packages/*) \u2014 conventional commits, tag-only push. Portable guard:
|
|
1127
1500
|
# nx release errors on an empty scope, so skip cleanly when there is nothing
|
|
1128
1501
|
# to release. When there are Python packages and an Azure feed, twine
|
|
1129
|
-
# publish credentials are exported (raw PAT, decoded from the base64 secret)
|
|
1502
|
+
# publish credentials are exported (raw PAT, decoded from the base64 secret).${githubReleases ? `
|
|
1503
|
+
# This provider also creates a per-project GitHub Release (changelog
|
|
1504
|
+
# generated from conventional commits since that project's last tag),
|
|
1505
|
+
# which is why 'nx release' pushes the tag itself here \u2014 see
|
|
1506
|
+
# releaseConfig's remarks for why that is safe on this Nx version and
|
|
1507
|
+
# why every other provider combination keeps the explicit push step below.` : ""}
|
|
1130
1508
|
- run: ${releaseGuard(pythonPublishEnvFragment(pythonPublishUrl2))}
|
|
1131
|
-
name: Release \u2014 version, tag and publish (npm + Python)
|
|
1509
|
+
name: Release \u2014 version, tag${githubReleases ? ", publish and GitHub Release" : " and publish"} (npm + Python)
|
|
1132
1510
|
if: \${{ ${onMain} }}
|
|
1133
1511
|
env:
|
|
1134
|
-
${npmAuthName}: ${npmAuthValue}
|
|
1135
|
-
|
|
1512
|
+
${npmAuthName}: ${npmAuthValue}${githubReleases ? `
|
|
1513
|
+
GITHUB_TOKEN: \${{ secrets.GITHUB_TOKEN }}` : ""}
|
|
1514
|
+
${githubReleases ? "" : `
|
|
1136
1515
|
# nx release's own git push (release.git.push) is deliberately left off: it
|
|
1137
1516
|
# only runs when a remote GitHub/GitLab Release is configured, which this
|
|
1138
1517
|
# workflow never does, so it would never push the tag the step above just
|
|
@@ -1141,7 +1520,7 @@ jobs:
|
|
|
1141
1520
|
- run: git push origin --tags
|
|
1142
1521
|
name: Push release tags (nx release's own push never runs without a remote Release configured)
|
|
1143
1522
|
if: \${{ ${onMain} }}
|
|
1144
|
-
`;
|
|
1523
|
+
`}`;
|
|
1145
1524
|
}
|
|
1146
1525
|
var DEPENDABOT_CONFIG = `# Generated by MoNecromanCI. Weekly dependency-update PRs so vulnerable or
|
|
1147
1526
|
# stale dependencies surface as a reviewable PR instead of only a push-time
|
|
@@ -1170,33 +1549,57 @@ updates:
|
|
|
1170
1549
|
interval: weekly
|
|
1171
1550
|
`;
|
|
1172
1551
|
function applyOverlay(workspaceRoot, options) {
|
|
1173
|
-
const nxJsonPath = (0,
|
|
1552
|
+
const nxJsonPath = (0, import_node_path9.join)(workspaceRoot, "nx.json");
|
|
1174
1553
|
const nxJson = readJson(nxJsonPath);
|
|
1175
|
-
const generators = {
|
|
1554
|
+
const generators = {
|
|
1555
|
+
...nxJson.generators,
|
|
1556
|
+
...generatorDefaults(options.stack)
|
|
1557
|
+
};
|
|
1176
1558
|
const sync = { ...nxJson.sync, ...SYNC_CONFIG };
|
|
1177
1559
|
const mnci = { ...nxJson.mnci, ...mnciConfig(options) };
|
|
1178
|
-
writeFileEnsured(
|
|
1179
|
-
|
|
1560
|
+
writeFileEnsured(
|
|
1561
|
+
nxJsonPath,
|
|
1562
|
+
toJson({ ...withReleaseConfig(nxJson, options.ci), generators, sync, mnci })
|
|
1563
|
+
);
|
|
1564
|
+
const manifestPath = (0, import_node_path9.join)(workspaceRoot, "package.json");
|
|
1180
1565
|
const manifest = readJson(manifestPath);
|
|
1181
|
-
const scripts = {
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1566
|
+
const scripts = {
|
|
1567
|
+
...manifest.scripts,
|
|
1568
|
+
...rootScripts()
|
|
1569
|
+
};
|
|
1570
|
+
const devDeps = {
|
|
1571
|
+
...manifest.devDependencies,
|
|
1572
|
+
...TS_COMPILER_DEPENDENCIES,
|
|
1573
|
+
prettier: PRETTIER_VERSION
|
|
1574
|
+
};
|
|
1575
|
+
writeFileEnsured(
|
|
1576
|
+
manifestPath,
|
|
1577
|
+
toJson({ ...manifest, name: `${options.scope}/source`, scripts, devDependencies: devDeps })
|
|
1578
|
+
);
|
|
1579
|
+
writeFileEnsured((0, import_node_path9.join)(workspaceRoot, ".npmrc"), npmrcContent(options.registry, options.scope));
|
|
1580
|
+
writeFileEnsured((0, import_node_path9.join)(workspaceRoot, "commitlint.config.mjs"), COMMITLINT_CONFIG);
|
|
1581
|
+
const hookPath = (0, import_node_path9.join)(workspaceRoot, ".husky/commit-msg");
|
|
1187
1582
|
writeFileEnsured(hookPath, COMMIT_MSG_HOOK);
|
|
1188
1583
|
markExecutable(hookPath);
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1584
|
+
writeFileEnsured((0, import_node_path9.join)(workspaceRoot, ".prettierrc.json"), PRETTIER_CONFIG);
|
|
1585
|
+
writeFileEnsured((0, import_node_path9.join)(workspaceRoot, ".prettierignore"), PRETTIER_IGNORE);
|
|
1586
|
+
writeFileEnsured(
|
|
1587
|
+
(0, import_node_path9.join)(workspaceRoot, `${options.workspaceName}.code-workspace`),
|
|
1588
|
+
vscodeWorkspace(options.workspaceName)
|
|
1589
|
+
);
|
|
1193
1590
|
const publishUrl = pythonPublishUrl(options.registry);
|
|
1194
1591
|
if (options.ci === "azure" || options.ci === "both") {
|
|
1195
|
-
writeFileEnsured(
|
|
1592
|
+
writeFileEnsured(
|
|
1593
|
+
(0, import_node_path9.join)(workspaceRoot, "azure-pipelines.yml"),
|
|
1594
|
+
azurePipelinesYaml(options.agent, options.variableGroup, publishUrl, options.registry.kind)
|
|
1595
|
+
);
|
|
1196
1596
|
}
|
|
1197
1597
|
if (options.ci === "github" || options.ci === "both") {
|
|
1198
|
-
writeFileEnsured(
|
|
1199
|
-
|
|
1598
|
+
writeFileEnsured(
|
|
1599
|
+
(0, import_node_path9.join)(workspaceRoot, ".github/workflows/ci.yml"),
|
|
1600
|
+
githubActionsYaml(options.agent, publishUrl, options.registry.kind, options.ci)
|
|
1601
|
+
);
|
|
1602
|
+
writeFileEnsured((0, import_node_path9.join)(workspaceRoot, ".github/dependabot.yml"), DEPENDABOT_CONFIG);
|
|
1200
1603
|
}
|
|
1201
1604
|
}
|
|
1202
1605
|
|
|
@@ -1205,18 +1608,14 @@ function nxCloudProviderValue(ci) {
|
|
|
1205
1608
|
return ci === "azure" ? "azure" : "github";
|
|
1206
1609
|
}
|
|
1207
1610
|
async function resolveStack(options) {
|
|
1208
|
-
|
|
1209
|
-
const complete = Boolean(fromFlags.linter && fromFlags.testRunner);
|
|
1210
|
-
if (complete || options.yes) {
|
|
1611
|
+
if (options.testRunner || options.yes) {
|
|
1211
1612
|
return {
|
|
1212
|
-
|
|
1213
|
-
testRunner: fromFlags.testRunner ?? DEFAULT_STACK.testRunner
|
|
1613
|
+
testRunner: options.testRunner ?? DEFAULT_STACK.testRunner
|
|
1214
1614
|
};
|
|
1215
1615
|
}
|
|
1216
1616
|
const prompted = await promptStack();
|
|
1217
1617
|
return {
|
|
1218
|
-
|
|
1219
|
-
testRunner: fromFlags.testRunner ?? prompted.testRunner
|
|
1618
|
+
testRunner: prompted.testRunner
|
|
1220
1619
|
};
|
|
1221
1620
|
}
|
|
1222
1621
|
var CI_PROVIDERS = /* @__PURE__ */ new Set(["azure", "github", "both"]);
|
|
@@ -1249,53 +1648,52 @@ async function runNew(name, options) {
|
|
|
1249
1648
|
const scope = options.scope ?? (options.yes ? `@${workspaceName}` : await promptText("npm scope for publishable packages", `@${workspaceName}`));
|
|
1250
1649
|
const registry = await resolveRegistry(options);
|
|
1251
1650
|
const ci = await resolveCi(options);
|
|
1252
|
-
const agent = options.agent ?? (options.yes ? "ubuntu-latest" : await promptText(
|
|
1651
|
+
const agent = options.agent ?? (options.yes ? "ubuntu-latest" : await promptText(
|
|
1652
|
+
"CI build agent/runner (vmImage, GitHub Actions runner label, or self-hosted pool name)",
|
|
1653
|
+
"ubuntu-latest"
|
|
1654
|
+
));
|
|
1253
1655
|
const variableGroup = ci === "github" ? options.variableGroup ?? "Build" : options.variableGroup ?? (options.yes ? "Build" : await promptText("Azure DevOps variable group holding the npm PAT", "Build"));
|
|
1254
1656
|
const stack = await resolveStack(options);
|
|
1255
1657
|
const nxCloud = options.nxCloud ?? (options.yes ? false : await promptNxCloud());
|
|
1256
1658
|
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
|
-
|
|
1659
|
+
runNpx(
|
|
1660
|
+
[
|
|
1661
|
+
"--yes",
|
|
1662
|
+
"create-nx-workspace@latest",
|
|
1663
|
+
workspaceName,
|
|
1664
|
+
"--preset=ts",
|
|
1665
|
+
"--pm=npm",
|
|
1666
|
+
nxCloud ? `--nxCloud=${nxCloudProviderValue(ci)}` : "--nxCloud=skip",
|
|
1667
|
+
"--no-interactive"
|
|
1668
|
+
],
|
|
1669
|
+
process.cwd()
|
|
1670
|
+
);
|
|
1671
|
+
const workspaceRoot = (0, import_node_path10.join)(process.cwd(), workspaceName);
|
|
1672
|
+
logger.step(
|
|
1673
|
+
"Applying MoNecromanCI overlay (VS Code workspace, release config, .npmrc, commitlint, pipeline, stack)"
|
|
1674
|
+
);
|
|
1675
|
+
applyOverlay(workspaceRoot, { workspaceName, scope, registry, agent, variableGroup, ci, stack });
|
|
1676
|
+
logger.step("Installing the toolchain (commitlint + husky)");
|
|
1677
|
+
const installStatus = runShell(
|
|
1678
|
+
"npm",
|
|
1679
|
+
["install", "--save-dev", "husky", "@commitlint/cli", "@commitlint/config-conventional"],
|
|
1680
|
+
workspaceRoot
|
|
1681
|
+
);
|
|
1272
1682
|
if (installStatus !== 0) {
|
|
1273
1683
|
throw new Error(`npm install of the toolchain failed with exit code ${installStatus}`);
|
|
1274
1684
|
}
|
|
1275
1685
|
runShell("npx", ["husky"], workspaceRoot);
|
|
1276
1686
|
logger.success("Done. Next steps:");
|
|
1277
1687
|
logger.info(` cd ${workspaceName}`);
|
|
1278
|
-
logger.info(
|
|
1279
|
-
|
|
1688
|
+
logger.info(
|
|
1689
|
+
" mnci add react-app web # or: node-app, node-function-app, npm-lib, internal-lib,"
|
|
1690
|
+
);
|
|
1691
|
+
logger.info(
|
|
1692
|
+
" # python-app, python-function-app, python-lib, python-internal-lib"
|
|
1693
|
+
);
|
|
1280
1694
|
logger.info(' git add -A && git commit -m "feat: initial workspace"');
|
|
1281
1695
|
}
|
|
1282
1696
|
|
|
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
1697
|
// src/commands/upgrade.ts
|
|
1300
1698
|
var import_node_path11 = require("path");
|
|
1301
1699
|
function resolveRegistry2(options, persisted) {
|
|
@@ -1305,7 +1703,9 @@ function resolveRegistry2(options, persisted) {
|
|
|
1305
1703
|
const project = options.project ?? persistedAzure?.project;
|
|
1306
1704
|
const artifactsFeed = options.artifactsFeed ?? persistedAzure?.artifactsFeed;
|
|
1307
1705
|
if (!organization || !project || !artifactsFeed) {
|
|
1308
|
-
throw new Error(
|
|
1706
|
+
throw new Error(
|
|
1707
|
+
"Azure Artifacts registry needs --organization, --project and --artifacts-feed (none found in nx.json's persisted config either)."
|
|
1708
|
+
);
|
|
1309
1709
|
}
|
|
1310
1710
|
return { kind: "azure-artifacts", organization, project, artifactsFeed };
|
|
1311
1711
|
}
|
|
@@ -1315,7 +1715,9 @@ function resolveRegistry2(options, persisted) {
|
|
|
1315
1715
|
if (persisted) {
|
|
1316
1716
|
return persisted;
|
|
1317
1717
|
}
|
|
1318
|
-
throw new Error(
|
|
1718
|
+
throw new Error(
|
|
1719
|
+
"No registry found in nx.json's persisted config. Pass --registry npm or --registry azure-artifacts (with --organization/--project/--artifacts-feed)."
|
|
1720
|
+
);
|
|
1319
1721
|
}
|
|
1320
1722
|
function resolveOverlayOptions(options, persisted) {
|
|
1321
1723
|
const scope = options.scope ?? persisted.scope;
|
|
@@ -1325,52 +1727,157 @@ function resolveOverlayOptions(options, persisted) {
|
|
|
1325
1727
|
const registry = resolveRegistry2(options, persisted.registry);
|
|
1326
1728
|
const ci = options.ci ?? persisted.ci;
|
|
1327
1729
|
if (!ci) {
|
|
1328
|
-
throw new Error(
|
|
1730
|
+
throw new Error(
|
|
1731
|
+
"No CI provider found in nx.json's persisted config. Pass --ci azure|github|both explicitly."
|
|
1732
|
+
);
|
|
1329
1733
|
}
|
|
1330
1734
|
const agent = options.agent ?? persisted.agent;
|
|
1331
1735
|
if (!agent) {
|
|
1332
|
-
throw new Error(
|
|
1736
|
+
throw new Error(
|
|
1737
|
+
"No CI build agent found in nx.json's persisted config. Pass --agent explicitly."
|
|
1738
|
+
);
|
|
1333
1739
|
}
|
|
1334
1740
|
const variableGroup = options.variableGroup ?? persisted.variableGroup ?? "Build";
|
|
1335
|
-
const linter = options.linter ?? persisted.stack?.linter;
|
|
1336
1741
|
const testRunner = options.testRunner ?? persisted.stack?.testRunner;
|
|
1337
|
-
if (!
|
|
1338
|
-
throw new Error(
|
|
1742
|
+
if (!testRunner) {
|
|
1743
|
+
throw new Error(
|
|
1744
|
+
"No testRunner found in nx.json's persisted config. Pass --test-runner explicitly."
|
|
1745
|
+
);
|
|
1339
1746
|
}
|
|
1340
|
-
return { scope, registry, agent, variableGroup, ci, stack: {
|
|
1747
|
+
return { scope, registry, agent, variableGroup, ci, stack: { testRunner } };
|
|
1341
1748
|
}
|
|
1342
1749
|
function runUpgrade(workspaceRoot, options) {
|
|
1343
1750
|
if (!fileExists((0, import_node_path11.join)(workspaceRoot, "nx.json"))) {
|
|
1344
|
-
throw new Error(
|
|
1751
|
+
throw new Error(
|
|
1752
|
+
`No nx.json found in ${workspaceRoot} \u2014 this does not look like an Nx workspace. Run 'mnci upgrade' from the workspace root.`
|
|
1753
|
+
);
|
|
1345
1754
|
}
|
|
1346
1755
|
const persisted = readMnciConfig(workspaceRoot);
|
|
1347
1756
|
const resolved = resolveOverlayOptions(options, persisted);
|
|
1348
|
-
logger.step(
|
|
1757
|
+
logger.step(
|
|
1758
|
+
"Re-applying the MoNecromanCI overlay (release config, .npmrc, commitlint, pipeline, stack)"
|
|
1759
|
+
);
|
|
1349
1760
|
applyOverlay(workspaceRoot, resolved);
|
|
1350
1761
|
logger.success("Done. Review the changes with `git diff` before committing.");
|
|
1351
1762
|
}
|
|
1352
1763
|
|
|
1353
|
-
// src/
|
|
1354
|
-
function
|
|
1764
|
+
// src/commands/interactive.ts
|
|
1765
|
+
async function runInteractive() {
|
|
1766
|
+
const inWorkspace = fileExists((0, import_node_path12.join)(process.cwd(), "nx.json"));
|
|
1767
|
+
const newChoice = { name: "Create a new monorepo", value: "new" };
|
|
1768
|
+
const addChoice = { name: "Add a project to this workspace", value: "add" };
|
|
1769
|
+
const upgradeChoice = {
|
|
1770
|
+
name: "Upgrade this workspace (re-apply the latest overlay)",
|
|
1771
|
+
value: "upgrade"
|
|
1772
|
+
};
|
|
1773
|
+
const action = await (0, import_prompts7.select)({
|
|
1774
|
+
message: "What would you like to do?",
|
|
1775
|
+
choices: inWorkspace ? [addChoice, upgradeChoice, newChoice] : [newChoice, addChoice, upgradeChoice]
|
|
1776
|
+
});
|
|
1777
|
+
if (action === "new") {
|
|
1778
|
+
await runNew(void 0, {});
|
|
1779
|
+
} else if (action === "upgrade") {
|
|
1780
|
+
runUpgrade(process.cwd(), {});
|
|
1781
|
+
} else {
|
|
1782
|
+
await runAdd(void 0, void 0, {});
|
|
1783
|
+
}
|
|
1784
|
+
}
|
|
1785
|
+
|
|
1786
|
+
// src/util/versionChecker.ts
|
|
1787
|
+
var import_node_fs5 = require("fs");
|
|
1788
|
+
var import_node_path13 = require("path");
|
|
1789
|
+
var import_cross_spawn2 = __toESM(require("cross-spawn"));
|
|
1790
|
+
var PACKAGE_NAME = "@mnci/cli";
|
|
1791
|
+
var REGISTRY_TIMEOUT_MS = 2e3;
|
|
1792
|
+
function checkForUpdate(currentVersion) {
|
|
1793
|
+
try {
|
|
1794
|
+
if (!process.stdout.isTTY) {
|
|
1795
|
+
return;
|
|
1796
|
+
}
|
|
1797
|
+
setImmediate(() => {
|
|
1798
|
+
try {
|
|
1799
|
+
reportIfOutdated(currentVersion);
|
|
1800
|
+
} catch {
|
|
1801
|
+
}
|
|
1802
|
+
});
|
|
1803
|
+
} catch {
|
|
1804
|
+
}
|
|
1805
|
+
}
|
|
1806
|
+
function reportIfOutdated(currentVersion) {
|
|
1807
|
+
const result = import_cross_spawn2.default.sync("npm", ["view", PACKAGE_NAME, "version"], {
|
|
1808
|
+
encoding: "utf8",
|
|
1809
|
+
timeout: REGISTRY_TIMEOUT_MS,
|
|
1810
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
1811
|
+
});
|
|
1812
|
+
if (result.error || result.status !== 0) {
|
|
1813
|
+
return;
|
|
1814
|
+
}
|
|
1815
|
+
const latestVersion = (result.stdout ?? "").trim();
|
|
1816
|
+
if (!latestVersion || !isNewerVersion(latestVersion, currentVersion)) {
|
|
1817
|
+
return;
|
|
1818
|
+
}
|
|
1819
|
+
logger.info("");
|
|
1820
|
+
logger.info(`A new version of ${PACKAGE_NAME} is available: ${latestVersion}`);
|
|
1821
|
+
logger.info(` Current version: ${currentVersion}`);
|
|
1822
|
+
logger.info(` Update with: npm install -g ${PACKAGE_NAME}@latest`);
|
|
1823
|
+
logger.info("");
|
|
1824
|
+
}
|
|
1825
|
+
function isNewerVersion(candidate, current) {
|
|
1826
|
+
const candidateParts = releaseTriple(candidate);
|
|
1827
|
+
const currentParts = releaseTriple(current);
|
|
1828
|
+
if (!candidateParts || !currentParts) {
|
|
1829
|
+
return false;
|
|
1830
|
+
}
|
|
1831
|
+
for (const [index, candidatePart] of candidateParts.entries()) {
|
|
1832
|
+
if (candidatePart !== currentParts[index]) {
|
|
1833
|
+
return candidatePart > currentParts[index];
|
|
1834
|
+
}
|
|
1835
|
+
}
|
|
1836
|
+
return false;
|
|
1837
|
+
}
|
|
1838
|
+
function releaseTriple(version) {
|
|
1839
|
+
const match = /^(\d+)\.(\d+)\.(\d+)/.exec(version.trim());
|
|
1840
|
+
return match ? [Number(match[1]), Number(match[2]), Number(match[3])] : void 0;
|
|
1841
|
+
}
|
|
1842
|
+
function readCliVersion(packageDirectory) {
|
|
1355
1843
|
try {
|
|
1356
|
-
const
|
|
1357
|
-
|
|
1844
|
+
const manifestPath = (0, import_node_path13.join)(packageDirectory, "..", "package.json");
|
|
1845
|
+
const manifest = JSON.parse((0, import_node_fs5.readFileSync)(manifestPath, "utf8"));
|
|
1846
|
+
return manifest.version ?? "0.0.0";
|
|
1358
1847
|
} catch {
|
|
1359
1848
|
return "0.0.0";
|
|
1360
1849
|
}
|
|
1361
1850
|
}
|
|
1362
|
-
|
|
1851
|
+
|
|
1852
|
+
// src/cli.ts
|
|
1853
|
+
function buildProgram(cliVersion) {
|
|
1363
1854
|
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(
|
|
1855
|
+
program.name("mnci").description("MoNecromanCI \u2014 a thin CLI over official Nx plugins").version(cliVersion, "-v, --version", "display the version");
|
|
1856
|
+
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(
|
|
1857
|
+
"--agent <pool>",
|
|
1858
|
+
"CI build agent: a vmImage (e.g. ubuntu-latest) or self-hosted pool name"
|
|
1859
|
+
).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
1860
|
await runNew(name, options);
|
|
1367
1861
|
});
|
|
1368
|
-
program.command("upgrade").description(
|
|
1862
|
+
program.command("upgrade").description(
|
|
1863
|
+
"Re-apply the latest MoNecromanCI overlay to this workspace (release config, pipeline, npmrc, commitlint, husky hook, curated scripts)"
|
|
1864
|
+
).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(
|
|
1865
|
+
"--test-runner <runner>",
|
|
1866
|
+
"unit-test runner: jest | vitest (overrides the persisted value)"
|
|
1867
|
+
).action((options) => {
|
|
1369
1868
|
runUpgrade(process.cwd(), options);
|
|
1370
1869
|
});
|
|
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
|
-
|
|
1870
|
+
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(
|
|
1871
|
+
"--framework <framework>",
|
|
1872
|
+
"node-app only: express | fastify | koa | nest | none (default: none)"
|
|
1873
|
+
).option(
|
|
1874
|
+
"--lib <name>",
|
|
1875
|
+
"python-vendor only: the internal Python library (libs/<name>) to vendor into <name>"
|
|
1876
|
+
).action(
|
|
1877
|
+
async (kind, name, options) => {
|
|
1878
|
+
await runAdd(kind, name, options);
|
|
1879
|
+
}
|
|
1880
|
+
);
|
|
1374
1881
|
program.action(async () => {
|
|
1375
1882
|
await runInteractive();
|
|
1376
1883
|
});
|
|
@@ -1378,7 +1885,15 @@ function buildProgram() {
|
|
|
1378
1885
|
}
|
|
1379
1886
|
async function main() {
|
|
1380
1887
|
try {
|
|
1381
|
-
|
|
1888
|
+
const cliVersion = readCliVersion(__dirname);
|
|
1889
|
+
const program = buildProgram(cliVersion);
|
|
1890
|
+
checkForUpdate(cliVersion);
|
|
1891
|
+
const arguments_ = new Set(process.argv.slice(2));
|
|
1892
|
+
const requestedVersionFlag = arguments_.has("-v") || arguments_.has("--version");
|
|
1893
|
+
if (process.stdout.isTTY && !requestedVersionFlag) {
|
|
1894
|
+
logger.info(`mnci v${cliVersion}`);
|
|
1895
|
+
}
|
|
1896
|
+
await program.parseAsync(process.argv);
|
|
1382
1897
|
} catch (error) {
|
|
1383
1898
|
logger.error(error instanceof Error ? error.message : String(error));
|
|
1384
1899
|
process.exitCode = 1;
|