@mnci/cli 4.42.0 → 4.44.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 +24 -0
- package/dist/cli.js +229 -43
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -505,6 +505,30 @@ from File`), and the curated root scripts.
|
|
|
505
505
|
4. Installs the chosen **stack** (see below), `husky` + `@commitlint/*` for
|
|
506
506
|
real, so versions resolve at generation time.
|
|
507
507
|
|
|
508
|
+
## `mnci adopt`: reading an existing repository before changing it
|
|
509
|
+
|
|
510
|
+
`mnci adopt` is the start of bringing a repository that already has history, tooling and published
|
|
511
|
+
packages under mnci (the plan is #383). So far it is **report mode only and changes nothing**:
|
|
512
|
+
|
|
513
|
+
```bash
|
|
514
|
+
mnci adopt # what was found, what blocks, what clears each
|
|
515
|
+
mnci adopt --json # the same, as one document
|
|
516
|
+
mnci adopt --tags # step (#379): create the baseline release tags, locally
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
It reads git and the filesystem (no Nx, no install, no network) and reports the package manager, the
|
|
520
|
+
`nx` version, every project manifest, the tags and any release tags stranded under an old project name,
|
|
521
|
+
the CI pipelines and whether they already call `mnci ci`, retired tooling (Prettier, oxfmt, verdaccio)
|
|
522
|
+
and the root files that are yours (`CLAUDE.md`, `AGENTS.md`). A **blocker** (not a git repository,
|
|
523
|
+
uncommitted changes, a package manager other than npm, no projects) makes it exit non-zero; a **warning**
|
|
524
|
+
names the later adoption step that clears it. Each later step is its own issue under #383.
|
|
525
|
+
|
|
526
|
+
**`--tags`** keeps a release from restarting below what was already published. When a project's name changed
|
|
527
|
+
(for example `mysql` became `@auto/mysql`), `nx release` finds no tag under the new name and versions from the
|
|
528
|
+
disk version. `mnci adopt --tags` creates, for each such project, a lightweight tag under the new name on the
|
|
529
|
+
commit of the old one (`@auto/mysql@1.12.11` on `mysql@1.12.11`). It is local and idempotent, never moves a tag,
|
|
530
|
+
and prints the `git push` for you to run: publishing tags changes the remote, so that stays your decision.
|
|
531
|
+
|
|
508
532
|
## `mnci upgrade`: re-applying the overlay to an existing workspace
|
|
509
533
|
|
|
510
534
|
Every fix to `workspace-overlay/overlay.use-case.ts` — a release-config correction, a CI guard rewritten,
|
package/dist/cli.js
CHANGED
|
@@ -8485,11 +8485,194 @@ function runDoctor(workspaceRoot, options = {}) {
|
|
|
8485
8485
|
process.exitCode = 1;
|
|
8486
8486
|
}
|
|
8487
8487
|
|
|
8488
|
+
// src/repository-adoption/create-baseline-tags.use-case.ts
|
|
8489
|
+
function createBaselineTags(repositoryRoot, stranded) {
|
|
8490
|
+
const result = { created: [], existing: [], failed: [] };
|
|
8491
|
+
for (const entry of stranded) {
|
|
8492
|
+
const present = runCapture("git", ["rev-parse", "--verify", "--quiet", `refs/tags/${entry.newTag}`], repositoryRoot);
|
|
8493
|
+
if (present.status === 0) {
|
|
8494
|
+
result.existing.push(entry.newTag);
|
|
8495
|
+
continue;
|
|
8496
|
+
}
|
|
8497
|
+
const made = runCapture("git", ["tag", entry.newTag, `${entry.oldTag}^{commit}`], repositoryRoot);
|
|
8498
|
+
(made.status === 0 ? result.created : result.failed).push(entry.newTag);
|
|
8499
|
+
}
|
|
8500
|
+
return result;
|
|
8501
|
+
}
|
|
8502
|
+
|
|
8503
|
+
// src/repository-adoption/inspect-repository.use-case.ts
|
|
8504
|
+
var import_node_fs31 = require("fs");
|
|
8505
|
+
var import_node_path36 = require("path");
|
|
8506
|
+
var LOCKFILES = [
|
|
8507
|
+
["package-lock.json", "npm"],
|
|
8508
|
+
["pnpm-lock.yaml", "pnpm"],
|
|
8509
|
+
["yarn.lock", "yarn"],
|
|
8510
|
+
["bun.lock", "bun"],
|
|
8511
|
+
["bun.lockb", "bun"]
|
|
8512
|
+
];
|
|
8513
|
+
var PERSONAL_FILES = ["CLAUDE.md", "AGENTS.md"];
|
|
8514
|
+
var RETIRED_FILE_PATTERN = /^(?:\.prettierrc.*|\.prettierignore|\.oxfmtrc\.json|oxlint\.config\.ts)$/;
|
|
8515
|
+
var RETIRED_NAMES = ["prettier", "oxfmt", "oxlint", "verdaccio"];
|
|
8516
|
+
function retiredNamesIn(manifest) {
|
|
8517
|
+
const declared = Object.keys({ ...manifest.dependencies, ...manifest.devDependencies });
|
|
8518
|
+
const scripts = Object.values(manifest.scripts ?? {}).join(" ");
|
|
8519
|
+
return RETIRED_NAMES.filter((name) => declared.includes(name) || new RegExp(String.raw`\b${name}\b`).test(scripts));
|
|
8520
|
+
}
|
|
8521
|
+
function inspectRepository(repositoryRoot) {
|
|
8522
|
+
const inside = runCapture("git", ["rev-parse", "--is-inside-work-tree"], repositoryRoot);
|
|
8523
|
+
const isGitRepo = inside.status === 0 && inside.stdout.trim() === "true";
|
|
8524
|
+
const dirty = isGitRepo && runCapture("git", ["status", "--porcelain"], repositoryRoot).stdout.trim() !== "";
|
|
8525
|
+
const tags = isGitRepo ? runCapture("git", ["tag", "--list"], repositoryRoot).stdout.split(/\r?\n/).filter(Boolean) : [];
|
|
8526
|
+
const manifestPath = (0, import_node_path36.join)(repositoryRoot, "package.json");
|
|
8527
|
+
const manifest = fileExists(manifestPath) ? readJson(manifestPath) : {};
|
|
8528
|
+
const nxJsonPath = (0, import_node_path36.join)(repositoryRoot, "nx.json");
|
|
8529
|
+
const nxJson = fileExists(nxJsonPath) ? readJson(nxJsonPath) : {};
|
|
8530
|
+
const ci = [];
|
|
8531
|
+
const pipelines = [];
|
|
8532
|
+
if ((0, import_node_fs31.existsSync)((0, import_node_path36.join)(repositoryRoot, "azure-pipelines.yml"))) {
|
|
8533
|
+
ci.push("azure");
|
|
8534
|
+
pipelines.push((0, import_node_path36.join)(repositoryRoot, "azure-pipelines.yml"));
|
|
8535
|
+
}
|
|
8536
|
+
const workflows = (0, import_node_path36.join)(repositoryRoot, ".github", "workflows");
|
|
8537
|
+
if ((0, import_node_fs31.existsSync)(workflows)) {
|
|
8538
|
+
const files = (0, import_node_fs31.readdirSync)(workflows).filter((file) => /\.ya?ml$/.test(file));
|
|
8539
|
+
if (files.length > 0) {
|
|
8540
|
+
ci.push("github");
|
|
8541
|
+
pipelines.push(...files.map((file) => (0, import_node_path36.join)(workflows, file)));
|
|
8542
|
+
}
|
|
8543
|
+
}
|
|
8544
|
+
const rootFiles = (0, import_node_fs31.readdirSync)(repositoryRoot);
|
|
8545
|
+
return {
|
|
8546
|
+
isGitRepo,
|
|
8547
|
+
dirty,
|
|
8548
|
+
packageManager: LOCKFILES.find(([file]) => rootFiles.includes(file))?.[1],
|
|
8549
|
+
nxVersion: manifest.devDependencies?.nx ?? manifest.dependencies?.nx,
|
|
8550
|
+
alreadyMnci: nxJson.mnci !== void 0,
|
|
8551
|
+
projects: locateProjects(repositoryRoot).map(({ name, dir, ecosystem }) => ({ name, dir, ecosystem })),
|
|
8552
|
+
tagCount: tags.length,
|
|
8553
|
+
strandedTags: isGitRepo ? locateStrandedReleaseTags(repositoryRoot, nxJson.release?.releaseTag?.pattern) : [],
|
|
8554
|
+
ci,
|
|
8555
|
+
pipelineUsesMnci: pipelines.some((file) => /\bmnci ci\b/.test((0, import_node_fs31.readFileSync)(file, "utf8"))),
|
|
8556
|
+
retiredTooling: [.../* @__PURE__ */ new Set([...rootFiles.filter((file) => RETIRED_FILE_PATTERN.test(file) || file === ".verdaccio"), ...retiredNamesIn(manifest)])],
|
|
8557
|
+
personalFiles: PERSONAL_FILES.filter((file) => rootFiles.includes(file))
|
|
8558
|
+
};
|
|
8559
|
+
}
|
|
8560
|
+
|
|
8561
|
+
// src/repository-adoption/judge-adoption.policy.ts
|
|
8562
|
+
function judgeAdoption(facts) {
|
|
8563
|
+
const findings = [];
|
|
8564
|
+
const blocker = (detail, step) => {
|
|
8565
|
+
findings.push({ severity: "blocker", detail, step });
|
|
8566
|
+
};
|
|
8567
|
+
const warning = (detail, step) => {
|
|
8568
|
+
findings.push({ severity: "warning", detail, step });
|
|
8569
|
+
};
|
|
8570
|
+
if (!facts.isGitRepo) {
|
|
8571
|
+
blocker("this directory is not a git repository", "run `git init` (or clone the repository) so every step can be its own commit");
|
|
8572
|
+
} else if (facts.dirty) {
|
|
8573
|
+
blocker("the working tree has uncommitted changes", "commit or stash them, so adoption starts from a state you can return to");
|
|
8574
|
+
}
|
|
8575
|
+
if (facts.packageManager !== void 0 && facts.packageManager !== "npm") {
|
|
8576
|
+
blocker(`the repository uses ${facts.packageManager}; mnci workspaces are npm workspaces`, "convert to npm first (out of scope for adopt for now)");
|
|
8577
|
+
}
|
|
8578
|
+
if (facts.projects.length === 0) {
|
|
8579
|
+
blocker("no project manifest was found under the repository", "run mnci adopt from the repository root, or `mnci new` for an empty one");
|
|
8580
|
+
}
|
|
8581
|
+
if (facts.alreadyMnci) {
|
|
8582
|
+
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");
|
|
8583
|
+
}
|
|
8584
|
+
if (facts.nxVersion === void 0) {
|
|
8585
|
+
warning("Nx is not installed", "the toolchain step installs the Nx family at the version mnci supports (#380)");
|
|
8586
|
+
}
|
|
8587
|
+
if (facts.strandedTags.length > 0) {
|
|
8588
|
+
warning(
|
|
8589
|
+
`${facts.strandedTags.length} project(s) have release tags only under an old name (${facts.strandedTags.map((entry) => entry.project).join(", ")})`,
|
|
8590
|
+
"the tags step creates the baseline tags before the first release (#379)"
|
|
8591
|
+
);
|
|
8592
|
+
}
|
|
8593
|
+
if (facts.retiredTooling.length > 0) {
|
|
8594
|
+
warning(`retired tooling is present: ${facts.retiredTooling.join(", ")}`, "the toolchain step removes it (#380)");
|
|
8595
|
+
}
|
|
8596
|
+
if (facts.ci.length > 0 && !facts.pipelineUsesMnci) {
|
|
8597
|
+
warning(`an existing ${facts.ci.join(" and ")} pipeline does not call mnci`, "the pipeline step moves its custom steps into slots (#381)");
|
|
8598
|
+
}
|
|
8599
|
+
if (facts.personalFiles.length > 0) {
|
|
8600
|
+
warning(`${facts.personalFiles.join(", ")} exist and are yours`, "adoption leaves them alone; only a block marked by Nx is ever removed");
|
|
8601
|
+
}
|
|
8602
|
+
const ordered = [...findings.filter((finding) => finding.severity === "blocker"), ...findings.filter((finding) => finding.severity === "warning")];
|
|
8603
|
+
return { ready: ordered.every((finding) => finding.severity !== "blocker"), facts, findings: ordered };
|
|
8604
|
+
}
|
|
8605
|
+
|
|
8606
|
+
// src/repository-adoption/run-adopt.use-case.ts
|
|
8607
|
+
function reportAdoption(repositoryRoot) {
|
|
8608
|
+
return judgeAdoption(inspectRepository(repositoryRoot));
|
|
8609
|
+
}
|
|
8610
|
+
function runAdopt(repositoryRoot, options = {}) {
|
|
8611
|
+
const report = reportAdoption(repositoryRoot);
|
|
8612
|
+
if (options.tags === true) {
|
|
8613
|
+
runBaselineTags(repositoryRoot, report);
|
|
8614
|
+
return;
|
|
8615
|
+
}
|
|
8616
|
+
if (!report.ready) {
|
|
8617
|
+
process.exitCode = 1;
|
|
8618
|
+
}
|
|
8619
|
+
if (options.json === true) {
|
|
8620
|
+
printJson(report);
|
|
8621
|
+
return;
|
|
8622
|
+
}
|
|
8623
|
+
const { facts } = report;
|
|
8624
|
+
logger.info(`${facts.projects.length} project(s), ${facts.tagCount} tag(s), ${facts.packageManager ?? "no lockfile"}, nx ${facts.nxVersion ?? "not installed"}`);
|
|
8625
|
+
for (const finding of report.findings) {
|
|
8626
|
+
const line = `${finding.severity === "blocker" ? "BLOCKER" : "warning"}: ${finding.detail}
|
|
8627
|
+
next: ${finding.step}`;
|
|
8628
|
+
if (finding.severity === "blocker") {
|
|
8629
|
+
logger.error(line);
|
|
8630
|
+
} else {
|
|
8631
|
+
logger.warn(line);
|
|
8632
|
+
}
|
|
8633
|
+
}
|
|
8634
|
+
if (report.ready) {
|
|
8635
|
+
logger.success("Nothing blocks adoption. This was a read-only report; nothing was changed.");
|
|
8636
|
+
} else {
|
|
8637
|
+
logger.error("Adoption is blocked. Clear the blockers above and run the report again.");
|
|
8638
|
+
}
|
|
8639
|
+
}
|
|
8640
|
+
function runBaselineTags(repositoryRoot, report) {
|
|
8641
|
+
if (!report.facts.isGitRepo) {
|
|
8642
|
+
logger.error("This directory is not a git repository, so there are no tags to baseline.");
|
|
8643
|
+
process.exitCode = 1;
|
|
8644
|
+
return;
|
|
8645
|
+
}
|
|
8646
|
+
const { strandedTags } = report.facts;
|
|
8647
|
+
if (strandedTags.length === 0) {
|
|
8648
|
+
logger.success("No project has release tags stranded under an old name. Nothing to baseline.");
|
|
8649
|
+
return;
|
|
8650
|
+
}
|
|
8651
|
+
const result = createBaselineTags(repositoryRoot, strandedTags);
|
|
8652
|
+
for (const tag of result.created) {
|
|
8653
|
+
logger.success(`created ${tag}`);
|
|
8654
|
+
}
|
|
8655
|
+
for (const tag of result.existing) {
|
|
8656
|
+
logger.info(`${tag} already exists, left alone`);
|
|
8657
|
+
}
|
|
8658
|
+
for (const tag of result.failed) {
|
|
8659
|
+
logger.error(`could not create ${tag}: its old tag does not resolve to a commit`);
|
|
8660
|
+
}
|
|
8661
|
+
if (result.failed.length > 0) {
|
|
8662
|
+
process.exitCode = 1;
|
|
8663
|
+
}
|
|
8664
|
+
if (result.created.length > 0) {
|
|
8665
|
+
logger.info(`Local only. Publish them when you are ready: git push origin ${result.created.map((tag) => JSON.stringify(tag)).join(" ")}`);
|
|
8666
|
+
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.");
|
|
8667
|
+
}
|
|
8668
|
+
}
|
|
8669
|
+
|
|
8488
8670
|
// src/command-catalog/command-groups.config.ts
|
|
8489
8671
|
var COMMAND_GROUPS = {
|
|
8490
8672
|
new: "workspace",
|
|
8491
8673
|
upgrade: "workspace",
|
|
8492
8674
|
doctor: "workspace",
|
|
8675
|
+
adopt: "inspect",
|
|
8493
8676
|
add: "projects",
|
|
8494
8677
|
install: "dependencies",
|
|
8495
8678
|
sync: "dependencies",
|
|
@@ -8555,11 +8738,11 @@ function runCommands(program, options) {
|
|
|
8555
8738
|
}
|
|
8556
8739
|
|
|
8557
8740
|
// src/workspace-info/list-projects.use-case.ts
|
|
8558
|
-
var
|
|
8741
|
+
var import_node_path37 = require("path");
|
|
8559
8742
|
var TYPE_TAG_PREFIX = "type:";
|
|
8560
8743
|
function listProjects(workspaceRoot) {
|
|
8561
8744
|
return locateProjects(workspaceRoot).map((location) => {
|
|
8562
|
-
const projectJsonPath = (0,
|
|
8745
|
+
const projectJsonPath = (0, import_node_path37.join)(workspaceRoot, location.dir, "project.json");
|
|
8563
8746
|
const project = fileExists(projectJsonPath) ? readJson(projectJsonPath) : {};
|
|
8564
8747
|
const typeTag = (project.tags ?? []).find((tag) => tag.startsWith(TYPE_TAG_PREFIX));
|
|
8565
8748
|
return {
|
|
@@ -8572,7 +8755,7 @@ function listProjects(workspaceRoot) {
|
|
|
8572
8755
|
}).toSorted((a, b) => a.dir.localeCompare(b.dir));
|
|
8573
8756
|
}
|
|
8574
8757
|
function runProjects(workspaceRoot, options) {
|
|
8575
|
-
if (!fileExists((0,
|
|
8758
|
+
if (!fileExists((0, import_node_path37.join)(workspaceRoot, "nx.json"))) {
|
|
8576
8759
|
throw new Error("No nx.json found here. Run `projects` from the workspace root.");
|
|
8577
8760
|
}
|
|
8578
8761
|
const projects = listProjects(workspaceRoot);
|
|
@@ -8586,11 +8769,11 @@ function runProjects(workspaceRoot, options) {
|
|
|
8586
8769
|
}
|
|
8587
8770
|
|
|
8588
8771
|
// src/workspace-info/read-workspace-info.use-case.ts
|
|
8589
|
-
var
|
|
8772
|
+
var import_node_path39 = require("path");
|
|
8590
8773
|
|
|
8591
8774
|
// src/cli-version/check-for-update.use-case.ts
|
|
8592
|
-
var
|
|
8593
|
-
var
|
|
8775
|
+
var import_node_fs32 = require("fs");
|
|
8776
|
+
var import_node_path38 = require("path");
|
|
8594
8777
|
var import_cross_spawn3 = __toESM(require("cross-spawn"));
|
|
8595
8778
|
var PACKAGE_NAME2 = "@mnci/cli";
|
|
8596
8779
|
var REGISTRY_TIMEOUT_MS = 2e3;
|
|
@@ -8649,8 +8832,8 @@ function releaseTriple(version) {
|
|
|
8649
8832
|
}
|
|
8650
8833
|
function readCliVersion(packageDirectory) {
|
|
8651
8834
|
try {
|
|
8652
|
-
const manifestPath = (0,
|
|
8653
|
-
const manifest = JSON.parse((0,
|
|
8835
|
+
const manifestPath = (0, import_node_path38.join)(packageDirectory, "..", "package.json");
|
|
8836
|
+
const manifest = JSON.parse((0, import_node_fs32.readFileSync)(manifestPath, "utf8"));
|
|
8654
8837
|
return manifest.version ?? "0.0.0";
|
|
8655
8838
|
} catch {
|
|
8656
8839
|
return "0.0.0";
|
|
@@ -8660,7 +8843,7 @@ function readCliVersion(packageDirectory) {
|
|
|
8660
8843
|
// src/workspace-info/read-workspace-info.use-case.ts
|
|
8661
8844
|
function readWorkspaceInfo(workspaceRoot, cliVersion) {
|
|
8662
8845
|
const latest = fetchLatestVersion() ?? null;
|
|
8663
|
-
const isWorkspace = fileExists((0,
|
|
8846
|
+
const isWorkspace = fileExists((0, import_node_path39.join)(workspaceRoot, "nx.json"));
|
|
8664
8847
|
return {
|
|
8665
8848
|
cli: {
|
|
8666
8849
|
version: cliVersion,
|
|
@@ -8681,8 +8864,8 @@ function runInfo(workspaceRoot, cliVersion, options) {
|
|
|
8681
8864
|
}
|
|
8682
8865
|
|
|
8683
8866
|
// src/workspace-creation/adopt-directory.use-case.ts
|
|
8684
|
-
var
|
|
8685
|
-
var
|
|
8867
|
+
var import_node_fs33 = require("fs");
|
|
8868
|
+
var import_node_path40 = require("path");
|
|
8686
8869
|
var NEVER_COPIED = /* @__PURE__ */ new Set([".git", "node_modules"]);
|
|
8687
8870
|
var AGENT_AND_CI_ENTRIES = [
|
|
8688
8871
|
"AGENTS.md",
|
|
@@ -8713,13 +8896,13 @@ var PREFLIGHT_BLOCKERS = [
|
|
|
8713
8896
|
"packages"
|
|
8714
8897
|
];
|
|
8715
8898
|
function assertAdoptableDirectory(target) {
|
|
8716
|
-
if (!(0,
|
|
8899
|
+
if (!(0, import_node_fs33.existsSync)(target)) {
|
|
8717
8900
|
throw new Error(`--into '${target}' does not exist. Create or clone it first.`);
|
|
8718
8901
|
}
|
|
8719
|
-
if (!(0,
|
|
8902
|
+
if (!(0, import_node_fs33.statSync)(target).isDirectory()) {
|
|
8720
8903
|
throw new Error(`--into '${target}' is not a directory.`);
|
|
8721
8904
|
}
|
|
8722
|
-
const blockers = PREFLIGHT_BLOCKERS.filter((entry) => (0,
|
|
8905
|
+
const blockers = PREFLIGHT_BLOCKERS.filter((entry) => (0, import_node_fs33.existsSync)((0, import_node_path40.join)(target, entry)));
|
|
8723
8906
|
if (blockers.length > 0) {
|
|
8724
8907
|
throw new Error(
|
|
8725
8908
|
`--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.`
|
|
@@ -8727,15 +8910,15 @@ function assertAdoptableDirectory(target) {
|
|
|
8727
8910
|
}
|
|
8728
8911
|
}
|
|
8729
8912
|
function mergeGitignore(generatedFile, targetFile) {
|
|
8730
|
-
const existing = (0,
|
|
8913
|
+
const existing = (0, import_node_fs33.readFileSync)(targetFile, "utf8");
|
|
8731
8914
|
const present = new Set(existing.split("\n").map((line) => line.trim()));
|
|
8732
|
-
const additions = (0,
|
|
8915
|
+
const additions = (0, import_node_fs33.readFileSync)(generatedFile, "utf8").split("\n").map((line) => line.trimEnd()).filter((line) => {
|
|
8733
8916
|
const trimmed = line.trim();
|
|
8734
8917
|
return trimmed !== "" && !trimmed.startsWith("#") && !present.has(trimmed);
|
|
8735
8918
|
});
|
|
8736
8919
|
if (additions.length === 0) return false;
|
|
8737
8920
|
const separator = existing.endsWith("\n") ? "" : "\n";
|
|
8738
|
-
(0,
|
|
8921
|
+
(0, import_node_fs33.writeFileSync)(
|
|
8739
8922
|
targetFile,
|
|
8740
8923
|
`${existing}${separator}
|
|
8741
8924
|
# Added by mnci (Nx workspace)
|
|
@@ -8745,9 +8928,9 @@ ${additions.join("\n")}
|
|
|
8745
8928
|
return true;
|
|
8746
8929
|
}
|
|
8747
8930
|
function adoptGeneratedWorkspace(generatedRoot, target) {
|
|
8748
|
-
const entries = (0,
|
|
8931
|
+
const entries = (0, import_node_fs33.readdirSync)(generatedRoot).filter((entry) => !NEVER_COPIED.has(entry));
|
|
8749
8932
|
const collisions = entries.filter(
|
|
8750
|
-
(entry) => (0,
|
|
8933
|
+
(entry) => (0, import_node_fs33.existsSync)((0, import_node_path40.join)(target, entry)) && !EXISTING_WINS.has(entry) && entry !== ".gitignore"
|
|
8751
8934
|
);
|
|
8752
8935
|
if (collisions.length > 0) {
|
|
8753
8936
|
throw new Error(
|
|
@@ -8756,28 +8939,28 @@ function adoptGeneratedWorkspace(generatedRoot, target) {
|
|
|
8756
8939
|
}
|
|
8757
8940
|
const result = { copied: [], kept: [], mergedGitignore: false };
|
|
8758
8941
|
for (const entry of entries) {
|
|
8759
|
-
const from = (0,
|
|
8760
|
-
const to = (0,
|
|
8761
|
-
if (entry === ".gitignore" && (0,
|
|
8942
|
+
const from = (0, import_node_path40.join)(generatedRoot, entry);
|
|
8943
|
+
const to = (0, import_node_path40.join)(target, entry);
|
|
8944
|
+
if (entry === ".gitignore" && (0, import_node_fs33.existsSync)(to)) {
|
|
8762
8945
|
result.mergedGitignore = mergeGitignore(from, to);
|
|
8763
8946
|
result.kept.push(entry);
|
|
8764
8947
|
continue;
|
|
8765
8948
|
}
|
|
8766
|
-
if ((0,
|
|
8949
|
+
if ((0, import_node_fs33.existsSync)(to)) {
|
|
8767
8950
|
result.kept.push(entry);
|
|
8768
8951
|
continue;
|
|
8769
8952
|
}
|
|
8770
|
-
(0,
|
|
8953
|
+
(0, import_node_fs33.cpSync)(from, to, { recursive: true });
|
|
8771
8954
|
result.copied.push(entry);
|
|
8772
8955
|
}
|
|
8773
|
-
(0,
|
|
8956
|
+
(0, import_node_fs33.rmSync)(generatedRoot, { recursive: true, force: true });
|
|
8774
8957
|
return result;
|
|
8775
8958
|
}
|
|
8776
8959
|
|
|
8777
8960
|
// src/workspace-creation/create-workspace.use-case.ts
|
|
8778
|
-
var
|
|
8961
|
+
var import_node_fs34 = require("fs");
|
|
8779
8962
|
var import_node_os2 = require("os");
|
|
8780
|
-
var
|
|
8963
|
+
var import_node_path41 = require("path");
|
|
8781
8964
|
function nxCloudProviderValue(ci) {
|
|
8782
8965
|
return ci === "azure" ? "azure" : "github";
|
|
8783
8966
|
}
|
|
@@ -8820,9 +9003,9 @@ async function resolveRegistry(options) {
|
|
|
8820
9003
|
return await promptRegistry();
|
|
8821
9004
|
}
|
|
8822
9005
|
async function runNew(name, options) {
|
|
8823
|
-
const adoptTarget = options.into === void 0 ? void 0 : (0,
|
|
9006
|
+
const adoptTarget = options.into === void 0 ? void 0 : (0, import_node_path41.resolve)(process.cwd(), options.into);
|
|
8824
9007
|
if (adoptTarget !== void 0) assertAdoptableDirectory(adoptTarget);
|
|
8825
|
-
const workspaceName = name ?? (adoptTarget === void 0 ? await promptText("Workspace name") : (0,
|
|
9008
|
+
const workspaceName = name ?? (adoptTarget === void 0 ? await promptText("Workspace name") : (0, import_node_path41.basename)(adoptTarget));
|
|
8826
9009
|
assertValidProjectName(workspaceName, "Workspace name");
|
|
8827
9010
|
const scope = options.scope ?? (options.yes ? `@${workspaceName}` : await promptText("npm scope for publishable packages", `@${workspaceName}`));
|
|
8828
9011
|
const registry = await resolveRegistry(options);
|
|
@@ -8835,8 +9018,8 @@ async function runNew(name, options) {
|
|
|
8835
9018
|
const npmAuth = resolveNpmAuth(options.npmAuth, registry, ci);
|
|
8836
9019
|
const stack = await resolveStack(options);
|
|
8837
9020
|
const nxCloud = options.nxCloud ?? (options.yes ? false : await promptNxCloud());
|
|
8838
|
-
const stagingParent = adoptTarget === void 0 ? process.cwd() : (0,
|
|
8839
|
-
let workspaceRoot = (0,
|
|
9021
|
+
const stagingParent = adoptTarget === void 0 ? process.cwd() : (0, import_node_fs34.mkdtempSync)((0, import_node_path41.join)((0, import_node_os2.tmpdir)(), "mnci-new-"));
|
|
9022
|
+
let workspaceRoot = (0, import_node_path41.join)(stagingParent, workspaceName);
|
|
8840
9023
|
try {
|
|
8841
9024
|
logger.step(`Creating Nx workspace '${workspaceName}' (preset: ts)`);
|
|
8842
9025
|
runNpx(
|
|
@@ -8859,7 +9042,7 @@ async function runNew(name, options) {
|
|
|
8859
9042
|
workspaceRoot = adoptTarget;
|
|
8860
9043
|
}
|
|
8861
9044
|
} finally {
|
|
8862
|
-
if (adoptTarget !== void 0) (0,
|
|
9045
|
+
if (adoptTarget !== void 0) (0, import_node_fs34.rmSync)(stagingParent, { recursive: true, force: true });
|
|
8863
9046
|
}
|
|
8864
9047
|
logger.step(
|
|
8865
9048
|
"Applying MoNecromanCI overlay (VS Code workspace, release config, .npmrc, commitlint, pipeline, stack)"
|
|
@@ -8869,8 +9052,8 @@ async function runNew(name, options) {
|
|
|
8869
9052
|
{ workspaceName, scope, registry, agent, variableGroup, ci, stack, ...npmAuth && { npmAuth } },
|
|
8870
9053
|
logger.detail
|
|
8871
9054
|
);
|
|
8872
|
-
(0,
|
|
8873
|
-
(0,
|
|
9055
|
+
(0, import_node_fs34.rmSync)((0, import_node_path41.join)(workspaceRoot, "node_modules"), { recursive: true, force: true });
|
|
9056
|
+
(0, import_node_fs34.rmSync)((0, import_node_path41.join)(workspaceRoot, "package-lock.json"), { force: true });
|
|
8874
9057
|
logger.step("Installing the toolchain (commitlint + husky + linting/formatting)");
|
|
8875
9058
|
const installStatus = runShell(
|
|
8876
9059
|
"npm",
|
|
@@ -8908,12 +9091,12 @@ function projectKindHintLines(kinds = PROJECT_KINDS) {
|
|
|
8908
9091
|
}
|
|
8909
9092
|
|
|
8910
9093
|
// src/workspace-creation/interactive-wizard.use-case.ts
|
|
8911
|
-
var
|
|
9094
|
+
var import_node_path43 = require("path");
|
|
8912
9095
|
var import_prompts4 = require("@inquirer/prompts");
|
|
8913
9096
|
|
|
8914
9097
|
// src/workspace-upgrade/upgrade-workspace.use-case.ts
|
|
8915
|
-
var
|
|
8916
|
-
var
|
|
9098
|
+
var import_node_fs35 = require("fs");
|
|
9099
|
+
var import_node_path42 = require("path");
|
|
8917
9100
|
function resolveRegistry2(options, persisted) {
|
|
8918
9101
|
if (options.registry === "azure-artifacts" || options.organization && options.artifactsFeed) {
|
|
8919
9102
|
const persistedAzure = persisted?.kind === "azure-artifacts" ? persisted : void 0;
|
|
@@ -8942,7 +9125,7 @@ function resolveWorkspaceName(workspaceRoot, persisted) {
|
|
|
8942
9125
|
return persisted.workspaceName;
|
|
8943
9126
|
}
|
|
8944
9127
|
try {
|
|
8945
|
-
const existing = (0,
|
|
9128
|
+
const existing = (0, import_node_fs35.readdirSync)(workspaceRoot).find(
|
|
8946
9129
|
(file) => file.endsWith(".code-workspace") && file !== "undefined.code-workspace"
|
|
8947
9130
|
);
|
|
8948
9131
|
if (existing) {
|
|
@@ -8950,11 +9133,11 @@ function resolveWorkspaceName(workspaceRoot, persisted) {
|
|
|
8950
9133
|
}
|
|
8951
9134
|
} catch {
|
|
8952
9135
|
}
|
|
8953
|
-
return (0,
|
|
9136
|
+
return (0, import_node_path42.basename)(workspaceRoot);
|
|
8954
9137
|
}
|
|
8955
9138
|
function detectNpmAuth(workspaceRoot) {
|
|
8956
9139
|
try {
|
|
8957
|
-
return (0,
|
|
9140
|
+
return (0, import_node_fs35.readFileSync)((0, import_node_path42.join)(workspaceRoot, "azure-pipelines.yml"), "utf8").includes("npmAuthenticate@0") ? "build-identity" : void 0;
|
|
8958
9141
|
} catch {
|
|
8959
9142
|
return void 0;
|
|
8960
9143
|
}
|
|
@@ -9001,7 +9184,7 @@ function resolveOverlayOptions(workspaceRoot, options, persisted) {
|
|
|
9001
9184
|
};
|
|
9002
9185
|
}
|
|
9003
9186
|
function runUpgrade(workspaceRoot, options) {
|
|
9004
|
-
if (!fileExists((0,
|
|
9187
|
+
if (!fileExists((0, import_node_path42.join)(workspaceRoot, "nx.json"))) {
|
|
9005
9188
|
throw new Error(
|
|
9006
9189
|
`No nx.json found in ${workspaceRoot} \u2014 this does not look like an Nx workspace. Run 'mnci upgrade' from the workspace root.`
|
|
9007
9190
|
);
|
|
@@ -9075,7 +9258,7 @@ function runUpgrade(workspaceRoot, options) {
|
|
|
9075
9258
|
"Formatting the workspace (eslint --fix) \u2014 the slowest step, minutes on a large workspace"
|
|
9076
9259
|
);
|
|
9077
9260
|
runFormatter(workspaceRoot);
|
|
9078
|
-
const nxJson = readJson((0,
|
|
9261
|
+
const nxJson = readJson((0, import_node_path42.join)(workspaceRoot, "nx.json"));
|
|
9079
9262
|
const stranded = locateStrandedReleaseTags(workspaceRoot, nxJson.release?.releaseTag?.pattern);
|
|
9080
9263
|
for (const entry of stranded) {
|
|
9081
9264
|
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).`);
|
|
@@ -9086,7 +9269,7 @@ function runUpgrade(workspaceRoot, options) {
|
|
|
9086
9269
|
|
|
9087
9270
|
// src/workspace-creation/interactive-wizard.use-case.ts
|
|
9088
9271
|
async function runInteractive() {
|
|
9089
|
-
const inWorkspace = fileExists((0,
|
|
9272
|
+
const inWorkspace = fileExists((0, import_node_path43.join)(process.cwd(), "nx.json"));
|
|
9090
9273
|
const choiceNew = { name: "Create a new monorepo", value: "new" };
|
|
9091
9274
|
const choiceAdd = { name: "Add a project to this workspace", value: "add" };
|
|
9092
9275
|
const choiceUpgrade = {
|
|
@@ -9138,6 +9321,9 @@ function buildProgram(cliVersion) {
|
|
|
9138
9321
|
).option("--json", "print the findings as one JSON document, each with its remedy").action((options) => {
|
|
9139
9322
|
runDoctor(process.cwd(), options);
|
|
9140
9323
|
});
|
|
9324
|
+
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").action((options) => {
|
|
9325
|
+
runAdopt(process.cwd(), options);
|
|
9326
|
+
});
|
|
9141
9327
|
program.command("ci").addArgument(new import_commander.Argument("<phase>", "which phase of the pipeline to run").choices(CI_PHASES)).description(
|
|
9142
9328
|
"Run a phase of the CI pipeline here, the same way CI runs it: `verify` is the sync check and then every project (or, in a pull request, the affected ones) through lint, typecheck, test and build. `setup` installs the Python, Go and Flutter toolchains the workspace needs, `audit` fails on a known advisory that has a fix, `pack` builds the per-app artifacts, `release` versions, tags and publishes, and `native` builds the apps that need a C toolchain on this OS. Exits non-zero when it fails"
|
|
9143
9329
|
).action(async (phase) => {
|