@mnci/cli 4.7.0 → 4.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -1637,6 +1637,38 @@ function ensureEslintCacheIgnored(workspaceRoot) {
1637
1637
  `
1638
1638
  );
1639
1639
  }
1640
+ var PYTHON_ARTEFACT_IGNORES = ["__pycache__/", "*.py[cod]"];
1641
+ function ensurePythonArtefactsIgnored(workspaceRoot) {
1642
+ const gitignorePath = (0, import_node_path2.join)(workspaceRoot, ".gitignore");
1643
+ if (!fileExists(gitignorePath)) {
1644
+ return;
1645
+ }
1646
+ const hasPython = (0, import_node_fs2.globSync)(
1647
+ ["python-packages/*/pyproject.toml", "apps/*/pyproject.toml", "libs/*/pyproject.toml"],
1648
+ { cwd: workspaceRoot }
1649
+ ).length > 0;
1650
+ if (!hasPython) {
1651
+ return;
1652
+ }
1653
+ const current = (0, import_node_fs2.readFileSync)(gitignorePath, "utf8");
1654
+ const present = new Set(current.split("\n").map((line) => line.trim()));
1655
+ const missing = PYTHON_ARTEFACT_IGNORES.filter((line) => !present.has(line));
1656
+ if (missing.length === 0) {
1657
+ return;
1658
+ }
1659
+ const withoutTrailingBlankLines = current.replace(/\n+$/u, "");
1660
+ const separator = withoutTrailingBlankLines.length > 0 ? "\n\n" : "";
1661
+ const heading = [
1662
+ "# Added by MoNecromanCI: Python bytecode. The tool caches (.mypy_cache,",
1663
+ "# .pytest_cache, .ruff_cache) write their own .gitignore; CPython does not."
1664
+ ].join("\n");
1665
+ writeFileEnsured(
1666
+ gitignorePath,
1667
+ `${withoutTrailingBlankLines}${separator}${heading}
1668
+ ${missing.join("\n")}
1669
+ `
1670
+ );
1671
+ }
1640
1672
  function removeProjectEslintConfigs(workspaceRoot) {
1641
1673
  const matches = (0, import_node_fs2.globSync)("{apps,libs,packages}/*/eslint.config.{js,mjs,cjs,ts,mts,cts}", {
1642
1674
  cwd: workspaceRoot
@@ -1728,6 +1760,7 @@ function applyOverlay(workspaceRoot, options, onProgress = () => {
1728
1760
  writeFileEnsured((0, import_node_path2.join)(workspaceRoot, ESLINT_MNCI_FILENAME), ESLINT_MNCI_CONFIG);
1729
1761
  writeEslintEntryPoint(workspaceRoot, onProgress);
1730
1762
  ensureEslintCacheIgnored(workspaceRoot);
1763
+ ensurePythonArtefactsIgnored(workspaceRoot);
1731
1764
  for (const retired of RETIRED_FORMATTER_FILES) {
1732
1765
  removeIfPresent((0, import_node_path2.join)(workspaceRoot, retired));
1733
1766
  }
@@ -4685,6 +4718,53 @@ function checkNpmrc(workspaceRoot, registry, scope) {
4685
4718
  remedy: "run `mnci upgrade`"
4686
4719
  };
4687
4720
  }
4721
+ function checkNpmrcCredentialResolves(workspaceRoot, environment = process.env) {
4722
+ const npmrcPath = (0, import_node_path19.join)(workspaceRoot, ".npmrc");
4723
+ if (!fileExists(npmrcPath)) {
4724
+ return void 0;
4725
+ }
4726
+ const npmrc = (0, import_node_fs12.readFileSync)(npmrcPath, "utf8");
4727
+ const credentials = npmrc.split("\n").filter((line) => !line.trimStart().startsWith(";") && !line.trimStart().startsWith("#")).filter((line) => /(?:_authToken|_password|_auth)\s*=/u.test(line));
4728
+ const referenced = [
4729
+ ...new Set(
4730
+ credentials.flatMap(
4731
+ (line) => Array.from(line.matchAll(/\$\{(\w+)\}/gu), (match) => match[1])
4732
+ )
4733
+ )
4734
+ ];
4735
+ if (referenced.length === 0) {
4736
+ return void 0;
4737
+ }
4738
+ const unset = referenced.filter((name) => (environment[name] ?? "") === "");
4739
+ if (unset.length === 0) {
4740
+ return npmjsCredentialShape(credentials, environment);
4741
+ }
4742
+ return {
4743
+ check: "npm credentials in this workspace resolve to something",
4744
+ ok: false,
4745
+ detail: `.npmrc authenticates with \${${unset.join("}, ${")}}, which ${unset.length > 1 ? "are" : "is"} unset here \u2014 and a project .npmrc beats your user one, so npm will send an EMPTY token from this directory rather than the one \`npm login\` wrote. Writes are then refused as a 404 on the PUT, which reads like a missing package`,
4746
+ remedy: "nothing, for installing or building \u2014 this only affects authenticated commands. To publish or deprecate by hand, run it from outside this workspace, or export a real token first. If it still fails there, `npm whoami` \u2014 an expired token gives the same 404"
4747
+ };
4748
+ }
4749
+ var NPM_TOKEN_PREFIX = "npm_";
4750
+ var LEGACY_NPM_TOKEN = /^[\da-f]{8}-[\da-f]{4}-[\da-f]{4}-[\da-f]{4}-[\da-f]{12}$/iu;
4751
+ function npmjsCredentialShape(credentials, environment) {
4752
+ const line = credentials.find((entry) => entry.includes("//registry.npmjs.org/:_authToken="));
4753
+ const variable = line === void 0 ? void 0 : /\$\{(\w+)\}/u.exec(line)?.[1];
4754
+ if (variable === void 0) {
4755
+ return void 0;
4756
+ }
4757
+ const value = environment[variable] ?? "";
4758
+ if (value.startsWith(NPM_TOKEN_PREFIX) || LEGACY_NPM_TOKEN.test(value)) {
4759
+ return void 0;
4760
+ }
4761
+ return {
4762
+ check: "the credential bound for npmjs.org looks like an npm token",
4763
+ ok: false,
4764
+ detail: `\${${variable}} is set (${value.length} characters) but does not start with '${NPM_TOKEN_PREFIX}' and is not a legacy UUID token, so it is probably a credential for somewhere else \u2014 '${variable}' is a generic name that other tooling also sets, and inside this workspace it is sent to the PUBLIC registry. A write then fails as a 404 on the PUT, which reads like a missing package`,
4765
+ remedy: `\`npm whoami\` to confirm \u2014 it is the only thing that separates a wrong token from an expired one. Then unset \`${variable}\` for local work, or set it to a real npm token`
4766
+ };
4767
+ }
4688
4768
  function checkVersionActions(workspaceRoot) {
4689
4769
  const candidates = [
4690
4770
  ...(0, import_node_fs12.globSync)("packages/*/pubspec.yaml", { cwd: workspaceRoot }),
@@ -4869,6 +4949,7 @@ function collectFindings(workspaceRoot) {
4869
4949
  checkEslintPlugin(nxJson),
4870
4950
  checkResolvedEslint(workspaceRoot),
4871
4951
  checkNpmrc(workspaceRoot, nxJson.mnci?.registry, nxJson.mnci?.scope),
4952
+ checkNpmrcCredentialResolves(workspaceRoot),
4872
4953
  checkNoRootRuntimeDependencies(workspaceRoot),
4873
4954
  ...checkRollupSourceMaps(workspaceRoot),
4874
4955
  ...checkDeclarationSpecifiers(workspaceRoot),