@mnci/cli 4.5.0 → 4.6.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
@@ -313,8 +313,24 @@ function npmrcContent(registry, scope) {
313
313
  if (registry.kind === "npm") {
314
314
  return `; Publish authentication for the public npm registry.
315
315
  ;
316
- ; NODE_AUTH_TOKEN is exported by the generated CI's release step; an unset value
317
- ; is harmless locally, so no token is needed for day-to-day work.
316
+ ; NODE_AUTH_TOKEN is exported by the generated CI's release step. Nothing needs
317
+ ; it for day-to-day work here - installing, building and testing never
318
+ ; authenticate.
319
+ ;
320
+ ; It is NOT harmless the moment you DO want to authenticate from this directory,
321
+ ; which an earlier version of this comment claimed. A project .npmrc wins over
322
+ ; your user one, so with NODE_AUTH_TOKEN unset this line hands npm an empty
323
+ ; token and overrides 'npm login': inside this repo 'npm whoami' fails with a
324
+ ; 401 and any write is refused. The refusal is easy to misread, because for a
325
+ ; scoped package the registry answers a write you are not allowed to make with
326
+ ;
327
+ ; npm error 404 Not Found - PUT https://registry.npmjs.org/@scope%2fthing
328
+ ;
329
+ ; and everything above it is npm listing what it intended to send, so a long
330
+ ; healthy-looking run still changes nothing.
331
+ ;
332
+ ; To publish or deprecate by hand, run it from outside this directory, or export
333
+ ; a real token first.
318
334
  //registry.npmjs.org/:_authToken=\${NODE_AUTH_TOKEN}
319
335
 
320
336
  ; There is deliberately NO '${scope}:registry' line here. npmjs.org is already
@@ -873,6 +889,10 @@ var NPM_AUDIT_STEP = `node -e "const cp=require('node:child_process');const r=cp
873
889
  var VERIFY_TARGETS = "lint,typecheck,test,build";
874
890
  var AFFECTED_OR_ALL_GUARD = `node -e "const cp=require('node:child_process');const T='${VERIFY_TARGETS}';const all=()=>process.exit(cp.spawnSync('npx nx run-many -t '+T,{stdio:'inherit',shell:true}).status ?? 1);const ref=process.env.GITHUB_BASE_REF||process.env.SYSTEM_PULLREQUEST_TARGETBRANCH||'';if(!ref){console.log('Not a pull request - verifying EVERY project.');all()}const target=ref.replace('refs/heads/','');const mergeBase=r=>{const o=cp.spawnSync('git',['merge-base',r,'HEAD'],{encoding:'utf8'});return o.status===0?o.stdout.trim():''};let base=mergeBase('origin/'+target);if(!base){console.log('No origin/'+target+' ref - fetching it to resolve a merge-base.');cp.spawnSync('git',['fetch','--no-tags','origin',target],{stdio:'inherit'});base=mergeBase('FETCH_HEAD')}if(!base){console.log('Could not resolve a merge-base with '+target+' - verifying EVERY project.');all()}console.log('Pull request against '+target+' - verifying projects affected since '+base);process.exit(cp.spawnSync('npx nx affected -t '+T+' --base='+base,{stdio:'inherit',shell:true}).status ?? 1)"`;
875
891
  var PACK_APPS_GUARD = `node -e "const fs=require('node:fs');fs.mkdirSync('dist/drop',{recursive:true});const hasProjectJson=fs.globSync('apps/*/project.json').length>0;const hasInlineNx=fs.globSync('apps/*/package.json').some((f)=>{try{return Boolean(JSON.parse(fs.readFileSync(f,'utf8')).nx)}catch{return false}});const hasCsproj=fs.globSync('apps/*/*.csproj').length>0;if(!hasProjectJson&&!hasInlineNx&&!hasCsproj){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)"`;
892
+ var NPM_AUTH_PREFLIGHT = `node -e "const fs=require('node:fs'),cp=require('node:child_process');if(fs.globSync('packages/*/package.json').length===0){console.log('No npm packages to release - skipping.');process.exit(0)}if(!process.env.NODE_AUTH_TOKEN){console.error('NPM_TOKEN is empty or unset, so the publish would fail AFTER nx release has already tagged. Add it as a repository secret named exactly NPM_TOKEN - under Actions, not the Dependabot or Codespaces tab, and not an Environment secret. Use an npm Automation token - a Publish token is refused by 2FA in CI.');process.exit(1)}const r=cp.spawnSync('npm',['whoami','--registry=https://registry.npmjs.org/'],{encoding:'utf8',shell:process.platform==='win32'});if(r.status!==0){console.error('NPM_TOKEN is set but the registry rejected it - '+((r.stderr||'')+(r.stdout||'')).trim()+'. Check that it has not expired and that it grants publish rights on this scope.');process.exit(1)}console.log('npm auth OK as '+r.stdout.trim())"`;
893
+ function npmAuthPreflight(registryKind) {
894
+ return registryKind === "npm" ? NPM_AUTH_PREFLIGHT : "";
895
+ }
876
896
  var SHALLOW_CLONE_GUARD = `node -e "const r=require('node:child_process').spawnSync('git',['rev-parse','--is-shallow-repository'],{encoding:'utf8'});if(r.status!==0){console.error('Could not determine whether this checkout has full history (git rev-parse --is-shallow-repository failed) - refusing to release. '+(r.stderr||'').trim());process.exit(1)}if(r.stdout.trim()==='true'){console.error('This checkout is a shallow clone. nx release resolves the current version of each package from its git tag, and silently falls back to the permanently-stale on-disk version when a tag cannot be found - which can propose, and publish, a version DOWNGRADE. Fetch full history before releasing - set fetchDepth (Azure) or fetch-depth (GitHub) to 0.');process.exit(1)}"`;
877
897
  function releaseGuard(pythonPublishEnv, nugetPublishEnv) {
878
898
  return String.raw`node -e "const fs=require('node:fs'),cp=require('node:child_process');const npmCount=fs.globSync('packages/*/package.json').length;const csharpCount=fs.globSync('packages/*/*.csproj').length;const pythonCount=fs.globSync('python-packages/*/pyproject.toml').length;const dartCount=fs.globSync('packages/*/pubspec.yaml').length;const hasNpm=npmCount>0;const hasPython=pythonCount>0;const hasCsharp=csharpCount>0;const hasDart=dartCount>0;if(!hasNpm&&!hasPython&&!hasCsharp&&!hasDart){console.log('Nothing to release - skipping.');process.exit(0)}const specifier=process.env.RELEASE_SPECIFIER||'';let releaseCmd='npx nx release --yes';if(specifier){if(!/^(major|minor|patch|\d+\.\d+\.\d+(-[0-9A-Za-z.-]+)?)$/.test(specifier)){console.error('RELEASE_SPECIFIER value \''+specifier+'\' is invalid - use major, minor, patch, or an exact version like 1.2.3.');process.exit(1)}const releaseProjectCount=npmCount+csharpCount+pythonCount+dartCount;if(/^(major|minor|patch)$/.test(specifier)&&releaseProjectCount>1){console.error('RELEASE_SPECIFIER is a keyword (\''+specifier+'\') but this workspace has '+releaseProjectCount+' releasable packages - a keyword under-bumps interdependent packages, because nx computes the dependency-bump pass from a stale cached version. Set RELEASE_SPECIFIER to an exact version instead, or clear it.');process.exit(1)}releaseCmd='npx nx release '+specifier+' --yes'}const env={...process.env};${pythonPublishEnv}${nugetPublishEnv}process.exit(cp.spawnSync(releaseCmd,{stdio:'inherit',shell:true,env}).status ?? 1)"`;
@@ -1148,7 +1168,17 @@ steps:
1148
1168
  - script: ${SHALLOW_CLONE_GUARD}
1149
1169
  displayName: Verify this is a full checkout (nx release needs the real tag history)
1150
1170
  condition: ${onMain}
1151
-
1171
+ ${npmAuthPreflight(registryKind) ? `
1172
+ # Prove npm will accept us BEFORE anything irreversible happens. nx release
1173
+ # tags first and publishes last, so a rejected token leaves a version tagged
1174
+ # with nothing on the registry - and that number is then skipped forever,
1175
+ # because the next run resolves the current version from the newest tag.
1176
+ - script: ${npmAuthPreflight(registryKind)}
1177
+ displayName: Preflight \u2014 npm must accept the token before anything is tagged
1178
+ condition: ${onMain}
1179
+ env:
1180
+ NODE_AUTH_TOKEN: $(NPM_TOKEN)
1181
+ ` : ""}
1152
1182
  # Version + tag + publish, in one release, for npm (packages/*), Python
1153
1183
  # (python-packages/*) AND C# (packages/*/*.csproj) \u2014 conventional commits,
1154
1184
  # tag-only push. Portable guard: nx release errors on an empty scope, so
@@ -1374,7 +1404,18 @@ jobs:
1374
1404
  - run: ${SHALLOW_CLONE_GUARD}
1375
1405
  name: Verify this is a full checkout (nx release needs the real tag history)
1376
1406
  if: \${{ ${onMain} }}
1377
-
1407
+ ${npmAuthPreflight(registryKind) ? `
1408
+ # Prove npm will accept us BEFORE anything irreversible happens. nx
1409
+ # release tags first and publishes last, so a rejected token leaves a
1410
+ # version tagged with nothing on the registry - and that number is then
1411
+ # skipped forever, because the next run resolves the current version
1412
+ # from the newest tag.
1413
+ - run: ${npmAuthPreflight(registryKind)}
1414
+ name: Preflight \u2014 npm must accept the token before anything is tagged
1415
+ if: \${{ ${onMain} }}
1416
+ env:
1417
+ NODE_AUTH_TOKEN: \${{ secrets.NPM_TOKEN }}
1418
+ ` : ""}
1378
1419
  # Version + tag + publish, in one release, for npm (packages/*), Python
1379
1420
  # (python-packages/*) AND C# (packages/*/*.csproj) \u2014 conventional
1380
1421
  # commits, tag-only push. Portable guard: nx release errors on an empty