@mnci/cli 4.43.0 → 4.45.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 +28 -0
- package/dist/cli.js +504 -226
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -8485,9 +8485,387 @@ function runDoctor(workspaceRoot, options = {}) {
|
|
|
8485
8485
|
process.exitCode = 1;
|
|
8486
8486
|
}
|
|
8487
8487
|
|
|
8488
|
-
// src/repository-adoption/
|
|
8488
|
+
// src/repository-adoption/adopt-overlay.use-case.ts
|
|
8489
|
+
var import_node_fs32 = require("fs");
|
|
8490
|
+
var import_node_path37 = require("path");
|
|
8491
|
+
|
|
8492
|
+
// src/workspace-upgrade/upgrade-workspace.use-case.ts
|
|
8489
8493
|
var import_node_fs31 = require("fs");
|
|
8490
8494
|
var import_node_path36 = require("path");
|
|
8495
|
+
function resolveRegistry(options, persisted) {
|
|
8496
|
+
if (options.registry === "azure-artifacts" || options.organization && options.artifactsFeed) {
|
|
8497
|
+
const persistedAzure = persisted?.kind === "azure-artifacts" ? persisted : void 0;
|
|
8498
|
+
const organization = options.organization ?? persistedAzure?.organization;
|
|
8499
|
+
const project = options.project ?? persistedAzure?.project;
|
|
8500
|
+
const artifactsFeed = options.artifactsFeed ?? persistedAzure?.artifactsFeed;
|
|
8501
|
+
if (!organization || !project || !artifactsFeed) {
|
|
8502
|
+
throw new Error(
|
|
8503
|
+
"Azure Artifacts registry needs --organization, --project and --artifacts-feed (none found in nx.json's persisted config either)."
|
|
8504
|
+
);
|
|
8505
|
+
}
|
|
8506
|
+
return { kind: "azure-artifacts", organization, project, artifactsFeed };
|
|
8507
|
+
}
|
|
8508
|
+
if (options.registry === "npm") {
|
|
8509
|
+
return { kind: "npm" };
|
|
8510
|
+
}
|
|
8511
|
+
if (persisted) {
|
|
8512
|
+
return persisted;
|
|
8513
|
+
}
|
|
8514
|
+
throw new Error(
|
|
8515
|
+
"No registry found in nx.json's persisted config. Pass --registry npm or --registry azure-artifacts (with --organization/--project/--artifacts-feed)."
|
|
8516
|
+
);
|
|
8517
|
+
}
|
|
8518
|
+
function resolveWorkspaceName(workspaceRoot, persisted) {
|
|
8519
|
+
if (persisted.workspaceName) {
|
|
8520
|
+
return persisted.workspaceName;
|
|
8521
|
+
}
|
|
8522
|
+
try {
|
|
8523
|
+
const existing = (0, import_node_fs31.readdirSync)(workspaceRoot).find(
|
|
8524
|
+
(file) => file.endsWith(".code-workspace") && file !== "undefined.code-workspace"
|
|
8525
|
+
);
|
|
8526
|
+
if (existing) {
|
|
8527
|
+
return existing.replace(/\.code-workspace$/, "");
|
|
8528
|
+
}
|
|
8529
|
+
} catch {
|
|
8530
|
+
}
|
|
8531
|
+
return (0, import_node_path36.basename)(workspaceRoot);
|
|
8532
|
+
}
|
|
8533
|
+
function detectNpmAuth(workspaceRoot) {
|
|
8534
|
+
try {
|
|
8535
|
+
return (0, import_node_fs31.readFileSync)((0, import_node_path36.join)(workspaceRoot, "azure-pipelines.yml"), "utf8").includes("npmAuthenticate@0") ? "build-identity" : void 0;
|
|
8536
|
+
} catch {
|
|
8537
|
+
return void 0;
|
|
8538
|
+
}
|
|
8539
|
+
}
|
|
8540
|
+
function resolveOverlayOptions(workspaceRoot, options, persisted) {
|
|
8541
|
+
const scope = options.scope ?? persisted.scope;
|
|
8542
|
+
if (!scope) {
|
|
8543
|
+
throw new Error("No npm scope found in nx.json's persisted config. Pass --scope explicitly.");
|
|
8544
|
+
}
|
|
8545
|
+
const registry = resolveRegistry(options, persisted.registry);
|
|
8546
|
+
const ci = options.ci ?? persisted.ci;
|
|
8547
|
+
if (!ci) {
|
|
8548
|
+
throw new Error(
|
|
8549
|
+
"No CI provider found in nx.json's persisted config. Pass --ci azure|github|both explicitly."
|
|
8550
|
+
);
|
|
8551
|
+
}
|
|
8552
|
+
const agent = options.agent ?? persisted.agent;
|
|
8553
|
+
if (!agent) {
|
|
8554
|
+
throw new Error(
|
|
8555
|
+
"No CI build agent found in nx.json's persisted config. Pass --agent explicitly."
|
|
8556
|
+
);
|
|
8557
|
+
}
|
|
8558
|
+
const variableGroup = options.variableGroup ?? persisted.variableGroup ?? "Build";
|
|
8559
|
+
const detected = ci === "azure" && persisted.npmAuth === void 0 && options.npmAuth === void 0 ? detectNpmAuth(workspaceRoot) : void 0;
|
|
8560
|
+
if (detected) {
|
|
8561
|
+
logger.detail("keeping build-identity npm auth: azure-pipelines.yml already runs npmAuthenticate@0");
|
|
8562
|
+
}
|
|
8563
|
+
const npmAuth = resolveNpmAuth(options.npmAuth, registry, ci, persisted.npmAuth ?? detected);
|
|
8564
|
+
const testRunner = options.testRunner ?? persisted.stack?.testRunner;
|
|
8565
|
+
if (!testRunner) {
|
|
8566
|
+
throw new Error(
|
|
8567
|
+
"No testRunner found in nx.json's persisted config. Pass --test-runner explicitly."
|
|
8568
|
+
);
|
|
8569
|
+
}
|
|
8570
|
+
return {
|
|
8571
|
+
workspaceName: resolveWorkspaceName(workspaceRoot, persisted),
|
|
8572
|
+
scope,
|
|
8573
|
+
registry,
|
|
8574
|
+
agent,
|
|
8575
|
+
variableGroup,
|
|
8576
|
+
ci,
|
|
8577
|
+
stack: { testRunner },
|
|
8578
|
+
...npmAuth && { npmAuth }
|
|
8579
|
+
};
|
|
8580
|
+
}
|
|
8581
|
+
function runUpgrade(workspaceRoot, options) {
|
|
8582
|
+
if (!fileExists((0, import_node_path36.join)(workspaceRoot, "nx.json"))) {
|
|
8583
|
+
throw new Error(
|
|
8584
|
+
`No nx.json found in ${workspaceRoot} \u2014 this does not look like an Nx workspace. Run 'mnci upgrade' from the workspace root.`
|
|
8585
|
+
);
|
|
8586
|
+
}
|
|
8587
|
+
const persisted = readMnciConfig(workspaceRoot);
|
|
8588
|
+
const resolved = resolveOverlayOptions(workspaceRoot, options, persisted);
|
|
8589
|
+
logger.step("Re-applying the MoNecromanCI overlay");
|
|
8590
|
+
applyOverlay(workspaceRoot, resolved, logger.detail);
|
|
8591
|
+
const repaired = repairRollupSourceMaps(workspaceRoot);
|
|
8592
|
+
if (repaired.length > 0) {
|
|
8593
|
+
logger.step("Enabling source maps in rollup configs (breakpoints in .ts files)");
|
|
8594
|
+
for (const path of repaired) {
|
|
8595
|
+
logger.detail(`updated ${path}`);
|
|
8596
|
+
}
|
|
8597
|
+
}
|
|
8598
|
+
const repairedManifests = repairPublishableManifests(workspaceRoot);
|
|
8599
|
+
if (repairedManifests.length > 0) {
|
|
8600
|
+
logger.step("Repointing `types` at the real declaration file");
|
|
8601
|
+
for (const path of repairedManifests) {
|
|
8602
|
+
logger.detail(`updated ${path}`);
|
|
8603
|
+
}
|
|
8604
|
+
}
|
|
8605
|
+
const lockfile = pruneStaleLocalRegistry(workspaceRoot);
|
|
8606
|
+
if (lockfile.stale) {
|
|
8607
|
+
logger.step("Dropping the unused local registry (verdaccio) from package-lock.json");
|
|
8608
|
+
if (lockfile.status !== 0) {
|
|
8609
|
+
logger.warn("npm could not re-resolve the lockfile; run `npm install --package-lock-only` once you are online.");
|
|
8610
|
+
}
|
|
8611
|
+
}
|
|
8612
|
+
const goApps = addGoPlatformTargets(workspaceRoot);
|
|
8613
|
+
if (goApps.length > 0) {
|
|
8614
|
+
logger.step("Adding the six-platform build to Go apps (build-all, package-all)");
|
|
8615
|
+
for (const path of goApps) {
|
|
8616
|
+
logger.detail(`updated ${path}`);
|
|
8617
|
+
}
|
|
8618
|
+
}
|
|
8619
|
+
const pinnedExtensions = pinVscodeExtensionProjectNames(workspaceRoot);
|
|
8620
|
+
if (pinnedExtensions.length > 0) {
|
|
8621
|
+
logger.step("Pinning the Nx project name of VS Code extensions (nx.name)");
|
|
8622
|
+
for (const path of pinnedExtensions) {
|
|
8623
|
+
logger.detail(`updated ${path}`);
|
|
8624
|
+
}
|
|
8625
|
+
}
|
|
8626
|
+
if (refreshVscodeExtensionScript(workspaceRoot)) {
|
|
8627
|
+
logger.step("Updating the VS Code extension packaging script");
|
|
8628
|
+
logger.detail(`updated ${VSCODE_EXTENSION_SCRIPT_PATH}`);
|
|
8629
|
+
}
|
|
8630
|
+
if (refreshGoReleaseScript(workspaceRoot)) {
|
|
8631
|
+
logger.step("Updating the releasable Go app script");
|
|
8632
|
+
logger.detail(`updated ${GO_RELEASE_SCRIPT_PATH}`);
|
|
8633
|
+
}
|
|
8634
|
+
if (hasGoProject(workspaceRoot) && registerNxGoPlugin(workspaceRoot)) {
|
|
8635
|
+
logger.step("Registering the Go plugin in nx.json, so Nx can see which Go project imports which");
|
|
8636
|
+
logger.detail("updated nx.json");
|
|
8637
|
+
}
|
|
8638
|
+
const addedDtsPlugins = addMissingDeclarationSpecifierPlugins(workspaceRoot);
|
|
8639
|
+
if (addedDtsPlugins.length > 0) {
|
|
8640
|
+
logger.step("Adding the declaration-specifier plugin to rollup configs that lacked it");
|
|
8641
|
+
for (const path of addedDtsPlugins) {
|
|
8642
|
+
logger.detail(`updated ${path}`);
|
|
8643
|
+
}
|
|
8644
|
+
}
|
|
8645
|
+
const upgradedDtsPlugins = upgradeDeclarationSpecifierPlugins(workspaceRoot);
|
|
8646
|
+
if (upgradedDtsPlugins.length > 0) {
|
|
8647
|
+
logger.step("Upgrading declaration specifiers to resolve under nodenext");
|
|
8648
|
+
for (const path of upgradedDtsPlugins) {
|
|
8649
|
+
logger.detail(`updated ${path}`);
|
|
8650
|
+
}
|
|
8651
|
+
}
|
|
8652
|
+
logger.step(
|
|
8653
|
+
"Formatting the workspace (eslint --fix) \u2014 the slowest step, minutes on a large workspace"
|
|
8654
|
+
);
|
|
8655
|
+
runFormatter(workspaceRoot);
|
|
8656
|
+
const nxJson = readJson((0, import_node_path36.join)(workspaceRoot, "nx.json"));
|
|
8657
|
+
const stranded = locateStrandedReleaseTags(workspaceRoot, nxJson.release?.releaseTag?.pattern);
|
|
8658
|
+
for (const entry of stranded) {
|
|
8659
|
+
logger.warn(`${entry.project} has release tags only under ${entry.oldTag}: nx release would restart it from its disk version. Run \`git tag ${entry.newTag} ${entry.oldTag}^{commit}\` before the next release (\`mnci doctor\` lists them all).`);
|
|
8660
|
+
}
|
|
8661
|
+
logger.success("Done. Review the changes with `git diff` before committing.");
|
|
8662
|
+
logger.info("Run `npm install` and commit package-lock.json too: the overlay may have added or moved a dependency, and CI installs with `npm ci`, which refuses a stale lockfile.");
|
|
8663
|
+
}
|
|
8664
|
+
|
|
8665
|
+
// src/repository-adoption/clean-working-tree.validator.ts
|
|
8666
|
+
function requireCleanWorkingTree(repositoryRoot, capture = runCapture) {
|
|
8667
|
+
if (capture("git", ["rev-parse", "--is-inside-work-tree"], repositoryRoot).status !== 0) {
|
|
8668
|
+
throw new Error("This directory is not a git repository. Every adoption step is one diff to review, so it needs git.");
|
|
8669
|
+
}
|
|
8670
|
+
if (capture("git", ["status", "--porcelain"], repositoryRoot).stdout.trim() !== "") {
|
|
8671
|
+
throw new Error("The working tree has uncommitted changes. Commit or stash them, so this step is one diff you can review or discard.");
|
|
8672
|
+
}
|
|
8673
|
+
}
|
|
8674
|
+
|
|
8675
|
+
// src/repository-adoption/adopt-overlay.use-case.ts
|
|
8676
|
+
function detectCiProvider(repositoryRoot) {
|
|
8677
|
+
const azure = (0, import_node_fs32.existsSync)((0, import_node_path37.join)(repositoryRoot, "azure-pipelines.yml"));
|
|
8678
|
+
const workflows = (0, import_node_path37.join)(repositoryRoot, ".github", "workflows");
|
|
8679
|
+
const github = (0, import_node_fs32.existsSync)(workflows) && (0, import_node_fs32.readdirSync)(workflows).some((file) => /\.ya?ml$/.test(file));
|
|
8680
|
+
if (azure && github) {
|
|
8681
|
+
return "both";
|
|
8682
|
+
}
|
|
8683
|
+
if (azure) {
|
|
8684
|
+
return "azure";
|
|
8685
|
+
}
|
|
8686
|
+
return github ? "github" : void 0;
|
|
8687
|
+
}
|
|
8688
|
+
function detectTestRunner(repositoryRoot) {
|
|
8689
|
+
const manifestPath = (0, import_node_path37.join)(repositoryRoot, "package.json");
|
|
8690
|
+
if (!fileExists(manifestPath)) {
|
|
8691
|
+
return void 0;
|
|
8692
|
+
}
|
|
8693
|
+
const manifest = readJson(manifestPath);
|
|
8694
|
+
const declared = new Set(Object.keys({ ...manifest.dependencies, ...manifest.devDependencies }));
|
|
8695
|
+
if (declared.has("vitest")) {
|
|
8696
|
+
return "vitest";
|
|
8697
|
+
}
|
|
8698
|
+
return declared.has("jest") ? "jest" : void 0;
|
|
8699
|
+
}
|
|
8700
|
+
function adoptOverlay(repositoryRoot, options, dependencies = {}) {
|
|
8701
|
+
requireCleanWorkingTree(repositoryRoot, dependencies.capture ?? runCapture);
|
|
8702
|
+
if (!fileExists((0, import_node_path37.join)(repositoryRoot, "nx.json"))) {
|
|
8703
|
+
throw new Error("There is no nx.json, so Nx is not set up here. Set it up first (`npx nx@latest init`); adopt does not install Nx yet.");
|
|
8704
|
+
}
|
|
8705
|
+
const persisted = readMnciConfig(repositoryRoot);
|
|
8706
|
+
const filled = {
|
|
8707
|
+
...options,
|
|
8708
|
+
ci: options.ci ?? (persisted.ci === void 0 ? detectCiProvider(repositoryRoot) : void 0),
|
|
8709
|
+
testRunner: options.testRunner ?? (persisted.stack?.testRunner === void 0 ? detectTestRunner(repositoryRoot) : void 0)
|
|
8710
|
+
};
|
|
8711
|
+
(dependencies.upgrade ?? runUpgrade)(repositoryRoot, filled);
|
|
8712
|
+
}
|
|
8713
|
+
|
|
8714
|
+
// src/repository-adoption/adopt-toolchain.use-case.ts
|
|
8715
|
+
var import_node_path39 = require("path");
|
|
8716
|
+
|
|
8717
|
+
// src/repository-adoption/align-nx-family.algorithm.ts
|
|
8718
|
+
var VERSION = /^[\^~]?(\d+)\.(\d+)\.(\d+)$/;
|
|
8719
|
+
function isNxFamily(name) {
|
|
8720
|
+
return name === "nx" || name.startsWith("@nx/");
|
|
8721
|
+
}
|
|
8722
|
+
function compare(left, right) {
|
|
8723
|
+
const a = left.split(".").map(Number);
|
|
8724
|
+
const b = right.split(".").map(Number);
|
|
8725
|
+
return a[0] - b[0] || a[1] - b[1] || a[2] - b[2] || 0;
|
|
8726
|
+
}
|
|
8727
|
+
function chooseNxVersion(declared, published, requested) {
|
|
8728
|
+
const current = Object.entries(declared).filter(([name]) => isNxFamily(name)).map(([, range]) => VERSION.exec(range)).filter((match) => match !== null).map((match) => `${match[1]}.${match[2]}.${match[3]}`);
|
|
8729
|
+
if (current.length === 0) {
|
|
8730
|
+
return void 0;
|
|
8731
|
+
}
|
|
8732
|
+
if (requested !== void 0) {
|
|
8733
|
+
return requested;
|
|
8734
|
+
}
|
|
8735
|
+
const newest = current.toSorted((a, b) => compare(a, b)).at(-1);
|
|
8736
|
+
const major = newest.split(".", 1)[0];
|
|
8737
|
+
const candidates = published.filter((version) => /^\d+\.\d+\.\d+$/.test(version) && version.split(".", 1)[0] === major);
|
|
8738
|
+
return candidates.toSorted((a, b) => compare(a, b)).at(-1);
|
|
8739
|
+
}
|
|
8740
|
+
function alignNxFamily(dependencies, version) {
|
|
8741
|
+
const changed = [];
|
|
8742
|
+
const aligned = Object.fromEntries(Object.entries(dependencies).map(([name, range]) => {
|
|
8743
|
+
if (range !== version && isNxFamily(name)) {
|
|
8744
|
+
changed.push(name);
|
|
8745
|
+
return [name, version];
|
|
8746
|
+
}
|
|
8747
|
+
return [name, range];
|
|
8748
|
+
}));
|
|
8749
|
+
return { dependencies: aligned, changed };
|
|
8750
|
+
}
|
|
8751
|
+
|
|
8752
|
+
// src/repository-adoption/retire-tooling.use-case.ts
|
|
8753
|
+
var import_node_fs33 = require("fs");
|
|
8754
|
+
var import_node_path38 = require("path");
|
|
8755
|
+
var RETIRED_COMMAND = /\b(?:prettier|oxfmt|oxlint)\b/;
|
|
8756
|
+
function retireTooling(repositoryRoot) {
|
|
8757
|
+
const removed = [];
|
|
8758
|
+
for (const file of RETIRED_FORMATTER_FILES) {
|
|
8759
|
+
if (!fileExists((0, import_node_path38.join)(repositoryRoot, file))) {
|
|
8760
|
+
continue;
|
|
8761
|
+
}
|
|
8762
|
+
(0, import_node_fs33.rmSync)((0, import_node_path38.join)(repositoryRoot, file), { force: true });
|
|
8763
|
+
removed.push(file);
|
|
8764
|
+
}
|
|
8765
|
+
const hadRegistry = fileExists((0, import_node_path38.join)(repositoryRoot, ".verdaccio"));
|
|
8766
|
+
removeLocalRegistryScaffolding(repositoryRoot);
|
|
8767
|
+
if (hadRegistry) {
|
|
8768
|
+
removed.push(".verdaccio, its dependency and the local-registry target");
|
|
8769
|
+
}
|
|
8770
|
+
const manifestPath = (0, import_node_path38.join)(repositoryRoot, "package.json");
|
|
8771
|
+
if (!fileExists(manifestPath)) {
|
|
8772
|
+
return removed;
|
|
8773
|
+
}
|
|
8774
|
+
const manifest = readJson(manifestPath);
|
|
8775
|
+
for (const key of ["dependencies", "devDependencies"]) {
|
|
8776
|
+
const before = manifest[key];
|
|
8777
|
+
if (before === void 0) {
|
|
8778
|
+
continue;
|
|
8779
|
+
}
|
|
8780
|
+
const after = withoutRetiredFormatterDependencies(before);
|
|
8781
|
+
const kept = new Set(Object.keys(after));
|
|
8782
|
+
const dropped = Object.keys(before).filter((name) => !kept.has(name));
|
|
8783
|
+
removed.push(...dropped.map((name) => `${key}: ${name}`));
|
|
8784
|
+
manifest[key] = after;
|
|
8785
|
+
}
|
|
8786
|
+
const scripts = Object.entries(manifest.scripts ?? {});
|
|
8787
|
+
for (const [name, command] of scripts) {
|
|
8788
|
+
if (!RETIRED_COMMAND.test(command)) {
|
|
8789
|
+
continue;
|
|
8790
|
+
}
|
|
8791
|
+
Reflect.deleteProperty(manifest.scripts, name);
|
|
8792
|
+
removed.push(`script: ${name} (${command})`);
|
|
8793
|
+
}
|
|
8794
|
+
writeFileEnsured(manifestPath, toJson(manifest));
|
|
8795
|
+
return removed;
|
|
8796
|
+
}
|
|
8797
|
+
|
|
8798
|
+
// src/repository-adoption/adopt-toolchain.use-case.ts
|
|
8799
|
+
var MAX_AUDIT_FIX_PASSES = 4;
|
|
8800
|
+
function publishedNxVersions(repositoryRoot, capture) {
|
|
8801
|
+
const answer = capture("npm", ["view", "nx", "versions", "--json"], repositoryRoot);
|
|
8802
|
+
try {
|
|
8803
|
+
const parsed = JSON.parse(answer.stdout);
|
|
8804
|
+
return Array.isArray(parsed) ? parsed.filter((version) => typeof version === "string") : [];
|
|
8805
|
+
} catch {
|
|
8806
|
+
return [];
|
|
8807
|
+
}
|
|
8808
|
+
}
|
|
8809
|
+
function adoptToolchain(repositoryRoot, options = {}, dependencies = {}) {
|
|
8810
|
+
const run = dependencies.run ?? runShell;
|
|
8811
|
+
const capture = dependencies.capture ?? runCapture;
|
|
8812
|
+
const audit = dependencies.audit ?? ((root) => runAudit(root));
|
|
8813
|
+
const log = dependencies.log ?? ((message) => {
|
|
8814
|
+
console.log(message);
|
|
8815
|
+
});
|
|
8816
|
+
requireCleanWorkingTree(repositoryRoot, capture);
|
|
8817
|
+
const manifestPath = (0, import_node_path39.join)(repositoryRoot, "package.json");
|
|
8818
|
+
if (!fileExists(manifestPath)) {
|
|
8819
|
+
throw new Error("No package.json at the repository root.");
|
|
8820
|
+
}
|
|
8821
|
+
log("Removing retired tooling");
|
|
8822
|
+
const removed = retireTooling(repositoryRoot);
|
|
8823
|
+
const manifest = readJson(manifestPath);
|
|
8824
|
+
const version = chooseNxVersion({ ...manifest.dependencies, ...manifest.devDependencies }, publishedNxVersions(repositoryRoot, capture), options.nxVersion);
|
|
8825
|
+
const names = [];
|
|
8826
|
+
if (version !== void 0) {
|
|
8827
|
+
for (const key of ["dependencies", "devDependencies"]) {
|
|
8828
|
+
if (manifest[key] === void 0) {
|
|
8829
|
+
continue;
|
|
8830
|
+
}
|
|
8831
|
+
const result = alignNxFamily(manifest[key], version);
|
|
8832
|
+
manifest[key] = result.dependencies;
|
|
8833
|
+
names.push(...result.changed);
|
|
8834
|
+
}
|
|
8835
|
+
writeFileEnsured(manifestPath, toJson(manifest));
|
|
8836
|
+
}
|
|
8837
|
+
log("Installing, so the lockfile follows the manifest");
|
|
8838
|
+
run("npm", ["install", "--ignore-scripts", "--no-audit", "--no-fund"], repositoryRoot);
|
|
8839
|
+
pruneStaleLocalRegistry(repositoryRoot);
|
|
8840
|
+
let auditFixes = 0;
|
|
8841
|
+
let passes = audit(repositoryRoot) === 0;
|
|
8842
|
+
while (!passes && auditFixes < MAX_AUDIT_FIX_PASSES) {
|
|
8843
|
+
auditFixes += 1;
|
|
8844
|
+
log(`npm audit fix, pass ${auditFixes}`);
|
|
8845
|
+
run("npm", ["audit", "fix", "--no-fund"], repositoryRoot);
|
|
8846
|
+
passes = audit(repositoryRoot) === 0;
|
|
8847
|
+
}
|
|
8848
|
+
return { removed, aligned: { names, version }, auditFixes, auditPasses: passes };
|
|
8849
|
+
}
|
|
8850
|
+
|
|
8851
|
+
// src/repository-adoption/create-baseline-tags.use-case.ts
|
|
8852
|
+
function createBaselineTags(repositoryRoot, stranded) {
|
|
8853
|
+
const result = { created: [], existing: [], failed: [] };
|
|
8854
|
+
for (const entry of stranded) {
|
|
8855
|
+
const present = runCapture("git", ["rev-parse", "--verify", "--quiet", `refs/tags/${entry.newTag}`], repositoryRoot);
|
|
8856
|
+
if (present.status === 0) {
|
|
8857
|
+
result.existing.push(entry.newTag);
|
|
8858
|
+
continue;
|
|
8859
|
+
}
|
|
8860
|
+
const made = runCapture("git", ["tag", entry.newTag, `${entry.oldTag}^{commit}`], repositoryRoot);
|
|
8861
|
+
(made.status === 0 ? result.created : result.failed).push(entry.newTag);
|
|
8862
|
+
}
|
|
8863
|
+
return result;
|
|
8864
|
+
}
|
|
8865
|
+
|
|
8866
|
+
// src/repository-adoption/inspect-repository.use-case.ts
|
|
8867
|
+
var import_node_fs34 = require("fs");
|
|
8868
|
+
var import_node_path40 = require("path");
|
|
8491
8869
|
var LOCKFILES = [
|
|
8492
8870
|
["package-lock.json", "npm"],
|
|
8493
8871
|
["pnpm-lock.yaml", "pnpm"],
|
|
@@ -8508,25 +8886,25 @@ function inspectRepository(repositoryRoot) {
|
|
|
8508
8886
|
const isGitRepo = inside.status === 0 && inside.stdout.trim() === "true";
|
|
8509
8887
|
const dirty = isGitRepo && runCapture("git", ["status", "--porcelain"], repositoryRoot).stdout.trim() !== "";
|
|
8510
8888
|
const tags = isGitRepo ? runCapture("git", ["tag", "--list"], repositoryRoot).stdout.split(/\r?\n/).filter(Boolean) : [];
|
|
8511
|
-
const manifestPath = (0,
|
|
8889
|
+
const manifestPath = (0, import_node_path40.join)(repositoryRoot, "package.json");
|
|
8512
8890
|
const manifest = fileExists(manifestPath) ? readJson(manifestPath) : {};
|
|
8513
|
-
const nxJsonPath = (0,
|
|
8891
|
+
const nxJsonPath = (0, import_node_path40.join)(repositoryRoot, "nx.json");
|
|
8514
8892
|
const nxJson = fileExists(nxJsonPath) ? readJson(nxJsonPath) : {};
|
|
8515
8893
|
const ci = [];
|
|
8516
8894
|
const pipelines = [];
|
|
8517
|
-
if ((0,
|
|
8895
|
+
if ((0, import_node_fs34.existsSync)((0, import_node_path40.join)(repositoryRoot, "azure-pipelines.yml"))) {
|
|
8518
8896
|
ci.push("azure");
|
|
8519
|
-
pipelines.push((0,
|
|
8897
|
+
pipelines.push((0, import_node_path40.join)(repositoryRoot, "azure-pipelines.yml"));
|
|
8520
8898
|
}
|
|
8521
|
-
const workflows = (0,
|
|
8522
|
-
if ((0,
|
|
8523
|
-
const files = (0,
|
|
8899
|
+
const workflows = (0, import_node_path40.join)(repositoryRoot, ".github", "workflows");
|
|
8900
|
+
if ((0, import_node_fs34.existsSync)(workflows)) {
|
|
8901
|
+
const files = (0, import_node_fs34.readdirSync)(workflows).filter((file) => /\.ya?ml$/.test(file));
|
|
8524
8902
|
if (files.length > 0) {
|
|
8525
8903
|
ci.push("github");
|
|
8526
|
-
pipelines.push(...files.map((file) => (0,
|
|
8904
|
+
pipelines.push(...files.map((file) => (0, import_node_path40.join)(workflows, file)));
|
|
8527
8905
|
}
|
|
8528
8906
|
}
|
|
8529
|
-
const rootFiles = (0,
|
|
8907
|
+
const rootFiles = (0, import_node_fs34.readdirSync)(repositoryRoot);
|
|
8530
8908
|
return {
|
|
8531
8909
|
isGitRepo,
|
|
8532
8910
|
dirty,
|
|
@@ -8537,7 +8915,7 @@ function inspectRepository(repositoryRoot) {
|
|
|
8537
8915
|
tagCount: tags.length,
|
|
8538
8916
|
strandedTags: isGitRepo ? locateStrandedReleaseTags(repositoryRoot, nxJson.release?.releaseTag?.pattern) : [],
|
|
8539
8917
|
ci,
|
|
8540
|
-
pipelineUsesMnci: pipelines.some((file) => /\bmnci ci\b/.test((0,
|
|
8918
|
+
pipelineUsesMnci: pipelines.some((file) => /\bmnci ci\b/.test((0, import_node_fs34.readFileSync)(file, "utf8"))),
|
|
8541
8919
|
retiredTooling: [.../* @__PURE__ */ new Set([...rootFiles.filter((file) => RETIRED_FILE_PATTERN.test(file) || file === ".verdaccio"), ...retiredNamesIn(manifest)])],
|
|
8542
8920
|
personalFiles: PERSONAL_FILES.filter((file) => rootFiles.includes(file))
|
|
8543
8921
|
};
|
|
@@ -8567,7 +8945,7 @@ function judgeAdoption(facts) {
|
|
|
8567
8945
|
warning("nx.json already carries an mnci block, so this is an mnci workspace", "use `mnci upgrade`; adopt is for repositories mnci has not touched");
|
|
8568
8946
|
}
|
|
8569
8947
|
if (facts.nxVersion === void 0) {
|
|
8570
|
-
warning("Nx is not installed", "
|
|
8948
|
+
warning("Nx is not installed", "set Nx up first (`npx nx@latest init`); adopt does not install it yet, and the toolchain and overlay steps need an nx.json");
|
|
8571
8949
|
}
|
|
8572
8950
|
if (facts.strandedTags.length > 0) {
|
|
8573
8951
|
warning(
|
|
@@ -8594,6 +8972,18 @@ function reportAdoption(repositoryRoot) {
|
|
|
8594
8972
|
}
|
|
8595
8973
|
function runAdopt(repositoryRoot, options = {}) {
|
|
8596
8974
|
const report = reportAdoption(repositoryRoot);
|
|
8975
|
+
if (options.overlay === true) {
|
|
8976
|
+
runOverlay(repositoryRoot, options);
|
|
8977
|
+
return;
|
|
8978
|
+
}
|
|
8979
|
+
if (options.toolchain === true) {
|
|
8980
|
+
runToolchain(repositoryRoot, options);
|
|
8981
|
+
return;
|
|
8982
|
+
}
|
|
8983
|
+
if (options.tags === true) {
|
|
8984
|
+
runBaselineTags(repositoryRoot, report);
|
|
8985
|
+
return;
|
|
8986
|
+
}
|
|
8597
8987
|
if (!report.ready) {
|
|
8598
8988
|
process.exitCode = 1;
|
|
8599
8989
|
}
|
|
@@ -8618,6 +9008,69 @@ function runAdopt(repositoryRoot, options = {}) {
|
|
|
8618
9008
|
logger.error("Adoption is blocked. Clear the blockers above and run the report again.");
|
|
8619
9009
|
}
|
|
8620
9010
|
}
|
|
9011
|
+
function runBaselineTags(repositoryRoot, report) {
|
|
9012
|
+
if (!report.facts.isGitRepo) {
|
|
9013
|
+
logger.error("This directory is not a git repository, so there are no tags to baseline.");
|
|
9014
|
+
process.exitCode = 1;
|
|
9015
|
+
return;
|
|
9016
|
+
}
|
|
9017
|
+
const { strandedTags } = report.facts;
|
|
9018
|
+
if (strandedTags.length === 0) {
|
|
9019
|
+
logger.success("No project has release tags stranded under an old name. Nothing to baseline.");
|
|
9020
|
+
return;
|
|
9021
|
+
}
|
|
9022
|
+
const result = createBaselineTags(repositoryRoot, strandedTags);
|
|
9023
|
+
for (const tag of result.created) {
|
|
9024
|
+
logger.success(`created ${tag}`);
|
|
9025
|
+
}
|
|
9026
|
+
for (const tag of result.existing) {
|
|
9027
|
+
logger.info(`${tag} already exists, left alone`);
|
|
9028
|
+
}
|
|
9029
|
+
for (const tag of result.failed) {
|
|
9030
|
+
logger.error(`could not create ${tag}: its old tag does not resolve to a commit`);
|
|
9031
|
+
}
|
|
9032
|
+
if (result.failed.length > 0) {
|
|
9033
|
+
process.exitCode = 1;
|
|
9034
|
+
}
|
|
9035
|
+
if (result.created.length > 0) {
|
|
9036
|
+
logger.info(`Local only. Publish them when you are ready: git push origin ${result.created.map((tag) => JSON.stringify(tag)).join(" ")}`);
|
|
9037
|
+
logger.info("A tag made under the new name at a LOWER version (for example @scope/x@0.0.5) stays; the baseline outranks it, and removing it from the remote is your call.");
|
|
9038
|
+
}
|
|
9039
|
+
}
|
|
9040
|
+
function runToolchain(repositoryRoot, options) {
|
|
9041
|
+
try {
|
|
9042
|
+
const result = adoptToolchain(repositoryRoot, { nxVersion: options.nx }, { log: (message) => {
|
|
9043
|
+
logger.step(message);
|
|
9044
|
+
} });
|
|
9045
|
+
for (const line of result.removed) {
|
|
9046
|
+
logger.info(`removed ${line}`);
|
|
9047
|
+
}
|
|
9048
|
+
if (result.aligned.version === void 0) {
|
|
9049
|
+
logger.info("Nx is not declared at the root, so there is no family to align.");
|
|
9050
|
+
} else {
|
|
9051
|
+
logger.info(`Nx family at ${result.aligned.version}${result.aligned.names.length > 0 ? ` (moved: ${result.aligned.names.join(", ")})` : " (already aligned)"}`);
|
|
9052
|
+
}
|
|
9053
|
+
if (result.auditPasses) {
|
|
9054
|
+
logger.success(`The audit gate passes${result.auditFixes > 0 ? ` after ${result.auditFixes} npm audit fix pass(es)` : ""}. Review with \`git diff\` and commit package.json and package-lock.json.`);
|
|
9055
|
+
} else {
|
|
9056
|
+
logger.error("The audit gate still fails after npm audit fix. Run `mnci ci audit` to see what is left; a targeted overrides entry is the usual answer.");
|
|
9057
|
+
process.exitCode = 1;
|
|
9058
|
+
}
|
|
9059
|
+
} catch (error) {
|
|
9060
|
+
logger.error(error instanceof Error ? error.message : String(error));
|
|
9061
|
+
process.exitCode = 1;
|
|
9062
|
+
}
|
|
9063
|
+
}
|
|
9064
|
+
function runOverlay(repositoryRoot, options) {
|
|
9065
|
+
try {
|
|
9066
|
+
adoptOverlay(repositoryRoot, options);
|
|
9067
|
+
logger.info("Steps of your old pipeline that mnci does not recognise are in its slots (# mnci:slot); anything it could not carry over was listed above.");
|
|
9068
|
+
logger.info("Run `mnci doctor` to confirm the verify phase is active, then review with `git diff`.");
|
|
9069
|
+
} catch (error) {
|
|
9070
|
+
logger.error(error instanceof Error ? error.message : String(error));
|
|
9071
|
+
process.exitCode = 1;
|
|
9072
|
+
}
|
|
9073
|
+
}
|
|
8621
9074
|
|
|
8622
9075
|
// src/command-catalog/command-groups.config.ts
|
|
8623
9076
|
var COMMAND_GROUPS = {
|
|
@@ -8690,11 +9143,11 @@ function runCommands(program, options) {
|
|
|
8690
9143
|
}
|
|
8691
9144
|
|
|
8692
9145
|
// src/workspace-info/list-projects.use-case.ts
|
|
8693
|
-
var
|
|
9146
|
+
var import_node_path41 = require("path");
|
|
8694
9147
|
var TYPE_TAG_PREFIX = "type:";
|
|
8695
9148
|
function listProjects(workspaceRoot) {
|
|
8696
9149
|
return locateProjects(workspaceRoot).map((location) => {
|
|
8697
|
-
const projectJsonPath = (0,
|
|
9150
|
+
const projectJsonPath = (0, import_node_path41.join)(workspaceRoot, location.dir, "project.json");
|
|
8698
9151
|
const project = fileExists(projectJsonPath) ? readJson(projectJsonPath) : {};
|
|
8699
9152
|
const typeTag = (project.tags ?? []).find((tag) => tag.startsWith(TYPE_TAG_PREFIX));
|
|
8700
9153
|
return {
|
|
@@ -8707,7 +9160,7 @@ function listProjects(workspaceRoot) {
|
|
|
8707
9160
|
}).toSorted((a, b) => a.dir.localeCompare(b.dir));
|
|
8708
9161
|
}
|
|
8709
9162
|
function runProjects(workspaceRoot, options) {
|
|
8710
|
-
if (!fileExists((0,
|
|
9163
|
+
if (!fileExists((0, import_node_path41.join)(workspaceRoot, "nx.json"))) {
|
|
8711
9164
|
throw new Error("No nx.json found here. Run `projects` from the workspace root.");
|
|
8712
9165
|
}
|
|
8713
9166
|
const projects = listProjects(workspaceRoot);
|
|
@@ -8721,11 +9174,11 @@ function runProjects(workspaceRoot, options) {
|
|
|
8721
9174
|
}
|
|
8722
9175
|
|
|
8723
9176
|
// src/workspace-info/read-workspace-info.use-case.ts
|
|
8724
|
-
var
|
|
9177
|
+
var import_node_path43 = require("path");
|
|
8725
9178
|
|
|
8726
9179
|
// src/cli-version/check-for-update.use-case.ts
|
|
8727
|
-
var
|
|
8728
|
-
var
|
|
9180
|
+
var import_node_fs35 = require("fs");
|
|
9181
|
+
var import_node_path42 = require("path");
|
|
8729
9182
|
var import_cross_spawn3 = __toESM(require("cross-spawn"));
|
|
8730
9183
|
var PACKAGE_NAME2 = "@mnci/cli";
|
|
8731
9184
|
var REGISTRY_TIMEOUT_MS = 2e3;
|
|
@@ -8784,8 +9237,8 @@ function releaseTriple(version) {
|
|
|
8784
9237
|
}
|
|
8785
9238
|
function readCliVersion(packageDirectory) {
|
|
8786
9239
|
try {
|
|
8787
|
-
const manifestPath = (0,
|
|
8788
|
-
const manifest = JSON.parse((0,
|
|
9240
|
+
const manifestPath = (0, import_node_path42.join)(packageDirectory, "..", "package.json");
|
|
9241
|
+
const manifest = JSON.parse((0, import_node_fs35.readFileSync)(manifestPath, "utf8"));
|
|
8789
9242
|
return manifest.version ?? "0.0.0";
|
|
8790
9243
|
} catch {
|
|
8791
9244
|
return "0.0.0";
|
|
@@ -8795,7 +9248,7 @@ function readCliVersion(packageDirectory) {
|
|
|
8795
9248
|
// src/workspace-info/read-workspace-info.use-case.ts
|
|
8796
9249
|
function readWorkspaceInfo(workspaceRoot, cliVersion) {
|
|
8797
9250
|
const latest = fetchLatestVersion() ?? null;
|
|
8798
|
-
const isWorkspace = fileExists((0,
|
|
9251
|
+
const isWorkspace = fileExists((0, import_node_path43.join)(workspaceRoot, "nx.json"));
|
|
8799
9252
|
return {
|
|
8800
9253
|
cli: {
|
|
8801
9254
|
version: cliVersion,
|
|
@@ -8816,8 +9269,8 @@ function runInfo(workspaceRoot, cliVersion, options) {
|
|
|
8816
9269
|
}
|
|
8817
9270
|
|
|
8818
9271
|
// src/workspace-creation/adopt-directory.use-case.ts
|
|
8819
|
-
var
|
|
8820
|
-
var
|
|
9272
|
+
var import_node_fs36 = require("fs");
|
|
9273
|
+
var import_node_path44 = require("path");
|
|
8821
9274
|
var NEVER_COPIED = /* @__PURE__ */ new Set([".git", "node_modules"]);
|
|
8822
9275
|
var AGENT_AND_CI_ENTRIES = [
|
|
8823
9276
|
"AGENTS.md",
|
|
@@ -8848,13 +9301,13 @@ var PREFLIGHT_BLOCKERS = [
|
|
|
8848
9301
|
"packages"
|
|
8849
9302
|
];
|
|
8850
9303
|
function assertAdoptableDirectory(target) {
|
|
8851
|
-
if (!(0,
|
|
9304
|
+
if (!(0, import_node_fs36.existsSync)(target)) {
|
|
8852
9305
|
throw new Error(`--into '${target}' does not exist. Create or clone it first.`);
|
|
8853
9306
|
}
|
|
8854
|
-
if (!(0,
|
|
9307
|
+
if (!(0, import_node_fs36.statSync)(target).isDirectory()) {
|
|
8855
9308
|
throw new Error(`--into '${target}' is not a directory.`);
|
|
8856
9309
|
}
|
|
8857
|
-
const blockers = PREFLIGHT_BLOCKERS.filter((entry) => (0,
|
|
9310
|
+
const blockers = PREFLIGHT_BLOCKERS.filter((entry) => (0, import_node_fs36.existsSync)((0, import_node_path44.join)(target, entry)));
|
|
8858
9311
|
if (blockers.length > 0) {
|
|
8859
9312
|
throw new Error(
|
|
8860
9313
|
`--into '${target}' already holds ${blockers.join(", ")}, so it is a project already. Use \`mnci upgrade\` to re-apply the overlay to an existing workspace, or point --into at an empty checkout.`
|
|
@@ -8862,15 +9315,15 @@ function assertAdoptableDirectory(target) {
|
|
|
8862
9315
|
}
|
|
8863
9316
|
}
|
|
8864
9317
|
function mergeGitignore(generatedFile, targetFile) {
|
|
8865
|
-
const existing = (0,
|
|
9318
|
+
const existing = (0, import_node_fs36.readFileSync)(targetFile, "utf8");
|
|
8866
9319
|
const present = new Set(existing.split("\n").map((line) => line.trim()));
|
|
8867
|
-
const additions = (0,
|
|
9320
|
+
const additions = (0, import_node_fs36.readFileSync)(generatedFile, "utf8").split("\n").map((line) => line.trimEnd()).filter((line) => {
|
|
8868
9321
|
const trimmed = line.trim();
|
|
8869
9322
|
return trimmed !== "" && !trimmed.startsWith("#") && !present.has(trimmed);
|
|
8870
9323
|
});
|
|
8871
9324
|
if (additions.length === 0) return false;
|
|
8872
9325
|
const separator = existing.endsWith("\n") ? "" : "\n";
|
|
8873
|
-
(0,
|
|
9326
|
+
(0, import_node_fs36.writeFileSync)(
|
|
8874
9327
|
targetFile,
|
|
8875
9328
|
`${existing}${separator}
|
|
8876
9329
|
# Added by mnci (Nx workspace)
|
|
@@ -8880,9 +9333,9 @@ ${additions.join("\n")}
|
|
|
8880
9333
|
return true;
|
|
8881
9334
|
}
|
|
8882
9335
|
function adoptGeneratedWorkspace(generatedRoot, target) {
|
|
8883
|
-
const entries = (0,
|
|
9336
|
+
const entries = (0, import_node_fs36.readdirSync)(generatedRoot).filter((entry) => !NEVER_COPIED.has(entry));
|
|
8884
9337
|
const collisions = entries.filter(
|
|
8885
|
-
(entry) => (0,
|
|
9338
|
+
(entry) => (0, import_node_fs36.existsSync)((0, import_node_path44.join)(target, entry)) && !EXISTING_WINS.has(entry) && entry !== ".gitignore"
|
|
8886
9339
|
);
|
|
8887
9340
|
if (collisions.length > 0) {
|
|
8888
9341
|
throw new Error(
|
|
@@ -8891,28 +9344,28 @@ function adoptGeneratedWorkspace(generatedRoot, target) {
|
|
|
8891
9344
|
}
|
|
8892
9345
|
const result = { copied: [], kept: [], mergedGitignore: false };
|
|
8893
9346
|
for (const entry of entries) {
|
|
8894
|
-
const from = (0,
|
|
8895
|
-
const to = (0,
|
|
8896
|
-
if (entry === ".gitignore" && (0,
|
|
9347
|
+
const from = (0, import_node_path44.join)(generatedRoot, entry);
|
|
9348
|
+
const to = (0, import_node_path44.join)(target, entry);
|
|
9349
|
+
if (entry === ".gitignore" && (0, import_node_fs36.existsSync)(to)) {
|
|
8897
9350
|
result.mergedGitignore = mergeGitignore(from, to);
|
|
8898
9351
|
result.kept.push(entry);
|
|
8899
9352
|
continue;
|
|
8900
9353
|
}
|
|
8901
|
-
if ((0,
|
|
9354
|
+
if ((0, import_node_fs36.existsSync)(to)) {
|
|
8902
9355
|
result.kept.push(entry);
|
|
8903
9356
|
continue;
|
|
8904
9357
|
}
|
|
8905
|
-
(0,
|
|
9358
|
+
(0, import_node_fs36.cpSync)(from, to, { recursive: true });
|
|
8906
9359
|
result.copied.push(entry);
|
|
8907
9360
|
}
|
|
8908
|
-
(0,
|
|
9361
|
+
(0, import_node_fs36.rmSync)(generatedRoot, { recursive: true, force: true });
|
|
8909
9362
|
return result;
|
|
8910
9363
|
}
|
|
8911
9364
|
|
|
8912
9365
|
// src/workspace-creation/create-workspace.use-case.ts
|
|
8913
|
-
var
|
|
9366
|
+
var import_node_fs37 = require("fs");
|
|
8914
9367
|
var import_node_os2 = require("os");
|
|
8915
|
-
var
|
|
9368
|
+
var import_node_path45 = require("path");
|
|
8916
9369
|
function nxCloudProviderValue(ci) {
|
|
8917
9370
|
return ci === "azure" ? "azure" : "github";
|
|
8918
9371
|
}
|
|
@@ -8937,7 +9390,7 @@ async function resolveCi(options) {
|
|
|
8937
9390
|
return await promptCi();
|
|
8938
9391
|
}
|
|
8939
9392
|
var REGISTRY_KINDS = /* @__PURE__ */ new Set(["azure-artifacts", "npm"]);
|
|
8940
|
-
async function
|
|
9393
|
+
async function resolveRegistry2(options) {
|
|
8941
9394
|
if (options.registry !== void 0 && !REGISTRY_KINDS.has(options.registry)) {
|
|
8942
9395
|
throw new Error(`Invalid --registry '${options.registry}'. Valid values: azure-artifacts, npm.`);
|
|
8943
9396
|
}
|
|
@@ -8955,12 +9408,12 @@ async function resolveRegistry(options) {
|
|
|
8955
9408
|
return await promptRegistry();
|
|
8956
9409
|
}
|
|
8957
9410
|
async function runNew(name, options) {
|
|
8958
|
-
const adoptTarget = options.into === void 0 ? void 0 : (0,
|
|
9411
|
+
const adoptTarget = options.into === void 0 ? void 0 : (0, import_node_path45.resolve)(process.cwd(), options.into);
|
|
8959
9412
|
if (adoptTarget !== void 0) assertAdoptableDirectory(adoptTarget);
|
|
8960
|
-
const workspaceName = name ?? (adoptTarget === void 0 ? await promptText("Workspace name") : (0,
|
|
9413
|
+
const workspaceName = name ?? (adoptTarget === void 0 ? await promptText("Workspace name") : (0, import_node_path45.basename)(adoptTarget));
|
|
8961
9414
|
assertValidProjectName(workspaceName, "Workspace name");
|
|
8962
9415
|
const scope = options.scope ?? (options.yes ? `@${workspaceName}` : await promptText("npm scope for publishable packages", `@${workspaceName}`));
|
|
8963
|
-
const registry = await
|
|
9416
|
+
const registry = await resolveRegistry2(options);
|
|
8964
9417
|
const ci = await resolveCi(options);
|
|
8965
9418
|
const agent = options.agent ?? (options.yes ? "ubuntu-latest" : await promptText(
|
|
8966
9419
|
"CI build agent/runner (vmImage, GitHub Actions runner label, or self-hosted pool name)",
|
|
@@ -8970,8 +9423,8 @@ async function runNew(name, options) {
|
|
|
8970
9423
|
const npmAuth = resolveNpmAuth(options.npmAuth, registry, ci);
|
|
8971
9424
|
const stack = await resolveStack(options);
|
|
8972
9425
|
const nxCloud = options.nxCloud ?? (options.yes ? false : await promptNxCloud());
|
|
8973
|
-
const stagingParent = adoptTarget === void 0 ? process.cwd() : (0,
|
|
8974
|
-
let workspaceRoot = (0,
|
|
9426
|
+
const stagingParent = adoptTarget === void 0 ? process.cwd() : (0, import_node_fs37.mkdtempSync)((0, import_node_path45.join)((0, import_node_os2.tmpdir)(), "mnci-new-"));
|
|
9427
|
+
let workspaceRoot = (0, import_node_path45.join)(stagingParent, workspaceName);
|
|
8975
9428
|
try {
|
|
8976
9429
|
logger.step(`Creating Nx workspace '${workspaceName}' (preset: ts)`);
|
|
8977
9430
|
runNpx(
|
|
@@ -8994,7 +9447,7 @@ async function runNew(name, options) {
|
|
|
8994
9447
|
workspaceRoot = adoptTarget;
|
|
8995
9448
|
}
|
|
8996
9449
|
} finally {
|
|
8997
|
-
if (adoptTarget !== void 0) (0,
|
|
9450
|
+
if (adoptTarget !== void 0) (0, import_node_fs37.rmSync)(stagingParent, { recursive: true, force: true });
|
|
8998
9451
|
}
|
|
8999
9452
|
logger.step(
|
|
9000
9453
|
"Applying MoNecromanCI overlay (VS Code workspace, release config, .npmrc, commitlint, pipeline, stack)"
|
|
@@ -9004,8 +9457,8 @@ async function runNew(name, options) {
|
|
|
9004
9457
|
{ workspaceName, scope, registry, agent, variableGroup, ci, stack, ...npmAuth && { npmAuth } },
|
|
9005
9458
|
logger.detail
|
|
9006
9459
|
);
|
|
9007
|
-
(0,
|
|
9008
|
-
(0,
|
|
9460
|
+
(0, import_node_fs37.rmSync)((0, import_node_path45.join)(workspaceRoot, "node_modules"), { recursive: true, force: true });
|
|
9461
|
+
(0, import_node_fs37.rmSync)((0, import_node_path45.join)(workspaceRoot, "package-lock.json"), { force: true });
|
|
9009
9462
|
logger.step("Installing the toolchain (commitlint + husky + linting/formatting)");
|
|
9010
9463
|
const installStatus = runShell(
|
|
9011
9464
|
"npm",
|
|
@@ -9043,185 +9496,10 @@ function projectKindHintLines(kinds = PROJECT_KINDS) {
|
|
|
9043
9496
|
}
|
|
9044
9497
|
|
|
9045
9498
|
// src/workspace-creation/interactive-wizard.use-case.ts
|
|
9046
|
-
var
|
|
9499
|
+
var import_node_path46 = require("path");
|
|
9047
9500
|
var import_prompts4 = require("@inquirer/prompts");
|
|
9048
|
-
|
|
9049
|
-
// src/workspace-upgrade/upgrade-workspace.use-case.ts
|
|
9050
|
-
var import_node_fs35 = require("fs");
|
|
9051
|
-
var import_node_path42 = require("path");
|
|
9052
|
-
function resolveRegistry2(options, persisted) {
|
|
9053
|
-
if (options.registry === "azure-artifacts" || options.organization && options.artifactsFeed) {
|
|
9054
|
-
const persistedAzure = persisted?.kind === "azure-artifacts" ? persisted : void 0;
|
|
9055
|
-
const organization = options.organization ?? persistedAzure?.organization;
|
|
9056
|
-
const project = options.project ?? persistedAzure?.project;
|
|
9057
|
-
const artifactsFeed = options.artifactsFeed ?? persistedAzure?.artifactsFeed;
|
|
9058
|
-
if (!organization || !project || !artifactsFeed) {
|
|
9059
|
-
throw new Error(
|
|
9060
|
-
"Azure Artifacts registry needs --organization, --project and --artifacts-feed (none found in nx.json's persisted config either)."
|
|
9061
|
-
);
|
|
9062
|
-
}
|
|
9063
|
-
return { kind: "azure-artifacts", organization, project, artifactsFeed };
|
|
9064
|
-
}
|
|
9065
|
-
if (options.registry === "npm") {
|
|
9066
|
-
return { kind: "npm" };
|
|
9067
|
-
}
|
|
9068
|
-
if (persisted) {
|
|
9069
|
-
return persisted;
|
|
9070
|
-
}
|
|
9071
|
-
throw new Error(
|
|
9072
|
-
"No registry found in nx.json's persisted config. Pass --registry npm or --registry azure-artifacts (with --organization/--project/--artifacts-feed)."
|
|
9073
|
-
);
|
|
9074
|
-
}
|
|
9075
|
-
function resolveWorkspaceName(workspaceRoot, persisted) {
|
|
9076
|
-
if (persisted.workspaceName) {
|
|
9077
|
-
return persisted.workspaceName;
|
|
9078
|
-
}
|
|
9079
|
-
try {
|
|
9080
|
-
const existing = (0, import_node_fs35.readdirSync)(workspaceRoot).find(
|
|
9081
|
-
(file) => file.endsWith(".code-workspace") && file !== "undefined.code-workspace"
|
|
9082
|
-
);
|
|
9083
|
-
if (existing) {
|
|
9084
|
-
return existing.replace(/\.code-workspace$/, "");
|
|
9085
|
-
}
|
|
9086
|
-
} catch {
|
|
9087
|
-
}
|
|
9088
|
-
return (0, import_node_path42.basename)(workspaceRoot);
|
|
9089
|
-
}
|
|
9090
|
-
function detectNpmAuth(workspaceRoot) {
|
|
9091
|
-
try {
|
|
9092
|
-
return (0, import_node_fs35.readFileSync)((0, import_node_path42.join)(workspaceRoot, "azure-pipelines.yml"), "utf8").includes("npmAuthenticate@0") ? "build-identity" : void 0;
|
|
9093
|
-
} catch {
|
|
9094
|
-
return void 0;
|
|
9095
|
-
}
|
|
9096
|
-
}
|
|
9097
|
-
function resolveOverlayOptions(workspaceRoot, options, persisted) {
|
|
9098
|
-
const scope = options.scope ?? persisted.scope;
|
|
9099
|
-
if (!scope) {
|
|
9100
|
-
throw new Error("No npm scope found in nx.json's persisted config. Pass --scope explicitly.");
|
|
9101
|
-
}
|
|
9102
|
-
const registry = resolveRegistry2(options, persisted.registry);
|
|
9103
|
-
const ci = options.ci ?? persisted.ci;
|
|
9104
|
-
if (!ci) {
|
|
9105
|
-
throw new Error(
|
|
9106
|
-
"No CI provider found in nx.json's persisted config. Pass --ci azure|github|both explicitly."
|
|
9107
|
-
);
|
|
9108
|
-
}
|
|
9109
|
-
const agent = options.agent ?? persisted.agent;
|
|
9110
|
-
if (!agent) {
|
|
9111
|
-
throw new Error(
|
|
9112
|
-
"No CI build agent found in nx.json's persisted config. Pass --agent explicitly."
|
|
9113
|
-
);
|
|
9114
|
-
}
|
|
9115
|
-
const variableGroup = options.variableGroup ?? persisted.variableGroup ?? "Build";
|
|
9116
|
-
const detected = ci === "azure" && persisted.npmAuth === void 0 && options.npmAuth === void 0 ? detectNpmAuth(workspaceRoot) : void 0;
|
|
9117
|
-
if (detected) {
|
|
9118
|
-
logger.detail("keeping build-identity npm auth: azure-pipelines.yml already runs npmAuthenticate@0");
|
|
9119
|
-
}
|
|
9120
|
-
const npmAuth = resolveNpmAuth(options.npmAuth, registry, ci, persisted.npmAuth ?? detected);
|
|
9121
|
-
const testRunner = options.testRunner ?? persisted.stack?.testRunner;
|
|
9122
|
-
if (!testRunner) {
|
|
9123
|
-
throw new Error(
|
|
9124
|
-
"No testRunner found in nx.json's persisted config. Pass --test-runner explicitly."
|
|
9125
|
-
);
|
|
9126
|
-
}
|
|
9127
|
-
return {
|
|
9128
|
-
workspaceName: resolveWorkspaceName(workspaceRoot, persisted),
|
|
9129
|
-
scope,
|
|
9130
|
-
registry,
|
|
9131
|
-
agent,
|
|
9132
|
-
variableGroup,
|
|
9133
|
-
ci,
|
|
9134
|
-
stack: { testRunner },
|
|
9135
|
-
...npmAuth && { npmAuth }
|
|
9136
|
-
};
|
|
9137
|
-
}
|
|
9138
|
-
function runUpgrade(workspaceRoot, options) {
|
|
9139
|
-
if (!fileExists((0, import_node_path42.join)(workspaceRoot, "nx.json"))) {
|
|
9140
|
-
throw new Error(
|
|
9141
|
-
`No nx.json found in ${workspaceRoot} \u2014 this does not look like an Nx workspace. Run 'mnci upgrade' from the workspace root.`
|
|
9142
|
-
);
|
|
9143
|
-
}
|
|
9144
|
-
const persisted = readMnciConfig(workspaceRoot);
|
|
9145
|
-
const resolved = resolveOverlayOptions(workspaceRoot, options, persisted);
|
|
9146
|
-
logger.step("Re-applying the MoNecromanCI overlay");
|
|
9147
|
-
applyOverlay(workspaceRoot, resolved, logger.detail);
|
|
9148
|
-
const repaired = repairRollupSourceMaps(workspaceRoot);
|
|
9149
|
-
if (repaired.length > 0) {
|
|
9150
|
-
logger.step("Enabling source maps in rollup configs (breakpoints in .ts files)");
|
|
9151
|
-
for (const path of repaired) {
|
|
9152
|
-
logger.detail(`updated ${path}`);
|
|
9153
|
-
}
|
|
9154
|
-
}
|
|
9155
|
-
const repairedManifests = repairPublishableManifests(workspaceRoot);
|
|
9156
|
-
if (repairedManifests.length > 0) {
|
|
9157
|
-
logger.step("Repointing `types` at the real declaration file");
|
|
9158
|
-
for (const path of repairedManifests) {
|
|
9159
|
-
logger.detail(`updated ${path}`);
|
|
9160
|
-
}
|
|
9161
|
-
}
|
|
9162
|
-
const lockfile = pruneStaleLocalRegistry(workspaceRoot);
|
|
9163
|
-
if (lockfile.stale) {
|
|
9164
|
-
logger.step("Dropping the unused local registry (verdaccio) from package-lock.json");
|
|
9165
|
-
if (lockfile.status !== 0) {
|
|
9166
|
-
logger.warn("npm could not re-resolve the lockfile; run `npm install --package-lock-only` once you are online.");
|
|
9167
|
-
}
|
|
9168
|
-
}
|
|
9169
|
-
const goApps = addGoPlatformTargets(workspaceRoot);
|
|
9170
|
-
if (goApps.length > 0) {
|
|
9171
|
-
logger.step("Adding the six-platform build to Go apps (build-all, package-all)");
|
|
9172
|
-
for (const path of goApps) {
|
|
9173
|
-
logger.detail(`updated ${path}`);
|
|
9174
|
-
}
|
|
9175
|
-
}
|
|
9176
|
-
const pinnedExtensions = pinVscodeExtensionProjectNames(workspaceRoot);
|
|
9177
|
-
if (pinnedExtensions.length > 0) {
|
|
9178
|
-
logger.step("Pinning the Nx project name of VS Code extensions (nx.name)");
|
|
9179
|
-
for (const path of pinnedExtensions) {
|
|
9180
|
-
logger.detail(`updated ${path}`);
|
|
9181
|
-
}
|
|
9182
|
-
}
|
|
9183
|
-
if (refreshVscodeExtensionScript(workspaceRoot)) {
|
|
9184
|
-
logger.step("Updating the VS Code extension packaging script");
|
|
9185
|
-
logger.detail(`updated ${VSCODE_EXTENSION_SCRIPT_PATH}`);
|
|
9186
|
-
}
|
|
9187
|
-
if (refreshGoReleaseScript(workspaceRoot)) {
|
|
9188
|
-
logger.step("Updating the releasable Go app script");
|
|
9189
|
-
logger.detail(`updated ${GO_RELEASE_SCRIPT_PATH}`);
|
|
9190
|
-
}
|
|
9191
|
-
if (hasGoProject(workspaceRoot) && registerNxGoPlugin(workspaceRoot)) {
|
|
9192
|
-
logger.step("Registering the Go plugin in nx.json, so Nx can see which Go project imports which");
|
|
9193
|
-
logger.detail("updated nx.json");
|
|
9194
|
-
}
|
|
9195
|
-
const addedDtsPlugins = addMissingDeclarationSpecifierPlugins(workspaceRoot);
|
|
9196
|
-
if (addedDtsPlugins.length > 0) {
|
|
9197
|
-
logger.step("Adding the declaration-specifier plugin to rollup configs that lacked it");
|
|
9198
|
-
for (const path of addedDtsPlugins) {
|
|
9199
|
-
logger.detail(`updated ${path}`);
|
|
9200
|
-
}
|
|
9201
|
-
}
|
|
9202
|
-
const upgradedDtsPlugins = upgradeDeclarationSpecifierPlugins(workspaceRoot);
|
|
9203
|
-
if (upgradedDtsPlugins.length > 0) {
|
|
9204
|
-
logger.step("Upgrading declaration specifiers to resolve under nodenext");
|
|
9205
|
-
for (const path of upgradedDtsPlugins) {
|
|
9206
|
-
logger.detail(`updated ${path}`);
|
|
9207
|
-
}
|
|
9208
|
-
}
|
|
9209
|
-
logger.step(
|
|
9210
|
-
"Formatting the workspace (eslint --fix) \u2014 the slowest step, minutes on a large workspace"
|
|
9211
|
-
);
|
|
9212
|
-
runFormatter(workspaceRoot);
|
|
9213
|
-
const nxJson = readJson((0, import_node_path42.join)(workspaceRoot, "nx.json"));
|
|
9214
|
-
const stranded = locateStrandedReleaseTags(workspaceRoot, nxJson.release?.releaseTag?.pattern);
|
|
9215
|
-
for (const entry of stranded) {
|
|
9216
|
-
logger.warn(`${entry.project} has release tags only under ${entry.oldTag}: nx release would restart it from its disk version. Run \`git tag ${entry.newTag} ${entry.oldTag}^{commit}\` before the next release (\`mnci doctor\` lists them all).`);
|
|
9217
|
-
}
|
|
9218
|
-
logger.success("Done. Review the changes with `git diff` before committing.");
|
|
9219
|
-
logger.info("Run `npm install` and commit package-lock.json too: the overlay may have added or moved a dependency, and CI installs with `npm ci`, which refuses a stale lockfile.");
|
|
9220
|
-
}
|
|
9221
|
-
|
|
9222
|
-
// src/workspace-creation/interactive-wizard.use-case.ts
|
|
9223
9501
|
async function runInteractive() {
|
|
9224
|
-
const inWorkspace = fileExists((0,
|
|
9502
|
+
const inWorkspace = fileExists((0, import_node_path46.join)(process.cwd(), "nx.json"));
|
|
9225
9503
|
const choiceNew = { name: "Create a new monorepo", value: "new" };
|
|
9226
9504
|
const choiceAdd = { name: "Add a project to this workspace", value: "add" };
|
|
9227
9505
|
const choiceUpgrade = {
|
|
@@ -9273,7 +9551,7 @@ function buildProgram(cliVersion) {
|
|
|
9273
9551
|
).option("--json", "print the findings as one JSON document, each with its remedy").action((options) => {
|
|
9274
9552
|
runDoctor(process.cwd(), options);
|
|
9275
9553
|
});
|
|
9276
|
-
program.command("adopt").description("Read an existing repository and report what bringing it under mnci would involve: blockers, warnings and the step that clears each. Read-only; exits non-zero when something blocks").option("--json", "print the report as one JSON document").action((options) => {
|
|
9554
|
+
program.command("adopt").description("Read an existing repository and report what bringing it under mnci would involve: blockers, warnings and the step that clears each. Read-only unless a step flag is given; exits non-zero when something blocks").option("--json", "print the report as one JSON document").option("--tags", "create, locally, the baseline tag each project whose release tags are stranded under an old name needs; nothing is pushed").option("--toolchain", "retire old formatter and registry tooling, align the Nx family to one version and run npm audit fix until the audit gate passes; needs a clean git tree and leaves the changes uncommitted").option("--nx <version>", "the Nx version --toolchain aligns to (default: the newest published in the major already in use)").option("--overlay", "apply the mnci overlay (release config, pipeline, npmrc, commitlint, scripts) with the flags mnci upgrade takes; the pipeline keeps the steps mnci does not recognise in its slots; needs a clean git tree and an nx.json").option("--scope <scope>", "npm scope for publishable packages (--overlay)").option("--registry <kind>", "azure-artifacts | npm (--overlay)").option("--organization <name>", "Azure DevOps organization (--overlay)").option("--project <name>", "Azure DevOps project (--overlay)").option("--artifacts-feed <name>", "Azure Artifacts feed (--overlay)").option("--agent <pool>", "CI build agent (--overlay)").option("--variable-group <name>", "Azure DevOps variable group holding the npm PAT (--overlay)").option("--npm-auth <mode>", "pat (default) | build-identity (--overlay, --ci azure only)").option("--ci <provider>", "azure | github | both (--overlay; default: what the repository already has)").option("--test-runner <runner>", "jest | vitest (--overlay; default: what the repository already declares)").action((options) => {
|
|
9277
9555
|
runAdopt(process.cwd(), options);
|
|
9278
9556
|
});
|
|
9279
9557
|
program.command("ci").addArgument(new import_commander.Argument("<phase>", "which phase of the pipeline to run").choices(CI_PHASES)).description(
|