@anolilab/semantic-release-pnpm 3.1.0 → 3.2.1

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/CHANGELOG.md CHANGED
@@ -1,3 +1,24 @@
1
+ ## @anolilab/semantic-release-pnpm [3.2.1](https://github.com/anolilab/semantic-release/compare/@anolilab/semantic-release-pnpm@3.2.0...@anolilab/semantic-release-pnpm@3.2.1) (2025-12-05)
2
+
3
+ ### Bug Fixes
4
+
5
+ * implement caching for whoami results in verifyAuth ([e8a482f](https://github.com/anolilab/semantic-release/commit/e8a482fec317dc829f9a373804b99110abafd1b2))
6
+
7
+ ## @anolilab/semantic-release-pnpm [3.2.0](https://github.com/anolilab/semantic-release/compare/@anolilab/semantic-release-pnpm@3.1.0...@anolilab/semantic-release-pnpm@3.2.0) (2025-12-04)
8
+
9
+ ### Bug Fixes
10
+
11
+ * enhanced OIDC context handling, added better debug logs ([#239](https://github.com/anolilab/semantic-release/issues/239)) ([2ae7ea5](https://github.com/anolilab/semantic-release/commit/2ae7ea5282d20254b96bbf768c2b1af2e868b6ae))
12
+
13
+ ### Miscellaneous Chores
14
+
15
+ * enhance package.json keywords across multiple packages ([842cd32](https://github.com/anolilab/semantic-release/commit/842cd32acf48c34d3226b1b2d63e7cdecdeff6b7))
16
+
17
+
18
+ ### Dependencies
19
+
20
+ * **@anolilab/rc:** upgraded to 3.2.0
21
+
1
22
  ## @anolilab/semantic-release-pnpm [3.1.0](https://github.com/anolilab/semantic-release/compare/@anolilab/semantic-release-pnpm@3.0.0...@anolilab/semantic-release-pnpm@3.1.0) (2025-11-25)
2
23
 
3
24
 
package/README.md CHANGED
@@ -74,6 +74,14 @@ When publishing to the [official registry](https://registry.npmjs.org/), it is r
74
74
  > [!NOTE]
75
75
  > When using trusted publishing, provenance attestations are automatically generated for your packages without requiring provenance to be explicitly enabled.
76
76
 
77
+ > [!IMPORTANT]
78
+ > **First-time releases with OIDC**: npm requires a package to exist before you can configure OIDC trusted publishing. If you're releasing a package for the first time with OIDC, you have two options:
79
+ >
80
+ > 1. Publish a dummy version manually first (e.g., `pnpm publish --tag dummy`), then configure OIDC trusted publishing, and then use semantic-release for subsequent releases.
81
+ > 2. Use the [`setup-npm-trusted-publish`](https://github.com/azu/setup-npm-trusted-publish) tool to automatically create and publish a placeholder package for OIDC setup purposes.
82
+ >
83
+ > After the initial package exists, you can configure OIDC trusted publishing at `https://www.npmjs.com/package/<package-name>/access` and then use semantic-release for all future releases.
84
+
77
85
  ##### Trusted publishing from GitHub Actions
78
86
 
79
87
  To leverage trusted publishing and publish with provenance from GitHub Actions, the `id-token: write` permission is required to be enabled on the job:
package/dist/index.js CHANGED
@@ -17,6 +17,7 @@ const __cjs_getBuiltinModule = (module) => {
17
17
  return __cjs_require(module);
18
18
  };
19
19
 
20
+ import dbg from 'debug';
20
21
  import { execa } from 'execa';
21
22
  import { validRange, gte } from 'semver';
22
23
  import { isAccessibleSync, ensureFileSync, move, writeFile } from '@visulima/fs';
@@ -25,10 +26,10 @@ import { rc } from '@anolilab/rc';
25
26
  import normalizeUrl from 'normalize-url';
26
27
  import { findPackageJson, getPackageManagerVersion } from '@visulima/package';
27
28
  import SemanticReleaseError from '@semantic-release/error';
29
+ import getAuthToken from 'registry-auth-token';
28
30
  import { getIDToken } from '@actions/core';
29
31
  import envCi from 'env-ci';
30
32
  import { stringify } from 'ini';
31
- import getAuthToken from 'registry-auth-token';
32
33
  const {
33
34
  URL
34
35
  } = __cjs_getBuiltinModule("node:url");
@@ -59,6 +60,7 @@ const OFFICIAL_REGISTRY = "https://registry.npmjs.org/";
59
60
  const GITHUB_ACTIONS_PROVIDER_NAME = "GitHub Actions";
60
61
  const GITLAB_PIPELINES_PROVIDER_NAME = "GitLab CI/CD";
61
62
 
63
+ const debug$9 = dbg("semantic-release-pnpm:registry");
62
64
  const getRegistryUrl = (scope, npmrc) => {
63
65
  let url = OFFICIAL_REGISTRY;
64
66
  if (npmrc) {
@@ -69,14 +71,35 @@ const getRegistryUrl = (scope, npmrc) => {
69
71
  }
70
72
  return url.endsWith("/") ? url : `${url}/`;
71
73
  };
72
- const getRegistry = ({ name, publishConfig: { registry } = {} }, { cwd, env }) => registry ?? env.NPM_CONFIG_REGISTRY ?? getRegistryUrl(
73
- name.split("/")[0],
74
- rc("npm", {
75
- config: env.NPM_CONFIG_USERCONFIG ?? resolve(cwd, ".npmrc"),
76
- cwd,
77
- defaults: { registry: OFFICIAL_REGISTRY }
78
- }).config
79
- );
74
+ const getRegistry = ({ name, publishConfig = {} }, { cwd, env }) => {
75
+ let resolvedRegistry;
76
+ let source;
77
+ const scope = name.split("/")[0];
78
+ const publishRegistry = publishConfig[`${scope}:registry`] ?? publishConfig.registry;
79
+ if (publishRegistry) {
80
+ resolvedRegistry = publishRegistry;
81
+ source = "package.json#publishConfig.registry";
82
+ } else if (env.NPM_CONFIG_REGISTRY) {
83
+ resolvedRegistry = env.NPM_CONFIG_REGISTRY;
84
+ source = "NPM_CONFIG_REGISTRY environment variable";
85
+ } else {
86
+ const npmrcConfig = rc("npm", {
87
+ config: env.NPM_CONFIG_USERCONFIG ?? resolve(cwd, ".npmrc"),
88
+ cwd,
89
+ defaults: { registry: OFFICIAL_REGISTRY }
90
+ });
91
+ const npmrc = npmrcConfig.config;
92
+ resolvedRegistry = getRegistryUrl(scope, npmrc);
93
+ if (npmrc && (npmrc[`${scope}:registry`] ?? npmrc.registry)) {
94
+ source = `.npmrc file (${npmrc[`${scope}:registry`] ? `scoped registry for ${scope}` : "default registry"})`;
95
+ } else {
96
+ source = "default registry (OFFICIAL_REGISTRY)";
97
+ }
98
+ }
99
+ const finalRegistry = resolvedRegistry.endsWith("/") ? resolvedRegistry : `${resolvedRegistry}/`;
100
+ debug$9(`Resolved registry "${finalRegistry}" from ${source}`);
101
+ return finalRegistry;
102
+ };
80
103
 
81
104
  const getReleaseInfo = ({ name }, { env: { DEFAULT_NPM_REGISTRY = OFFICIAL_REGISTRY }, nextRelease: { version } }, distributionTag, registry) => {
82
105
  return {
@@ -97,6 +120,7 @@ const reasonToNotPublish = (pluginConfig, package_) => {
97
120
  };
98
121
  const shouldPublish = (pluginConfig, package_) => reasonToNotPublish(pluginConfig, package_) === null;
99
122
 
123
+ const debug$8 = dbg("semantic-release-pnpm:add-channel");
100
124
  const addChannel$1 = async (pluginConfig, packageJson, context) => {
101
125
  const {
102
126
  cwd,
@@ -111,7 +135,9 @@ const addChannel$1 = async (pluginConfig, packageJson, context) => {
111
135
  const distributionTag = getChannel(channel);
112
136
  logger.log(`Adding version ${version} to npm registry on dist-tag ${distributionTag}`);
113
137
  const npmrc = getNpmrcPath(cwd, env);
114
- const result = execa("pnpm", ["dist-tag", "add", `${packageJson.name}@${version}`, distributionTag, "--registry", registry], {
138
+ const distTagArguments = ["dist-tag", "add", `${packageJson.name}@${version}`, distributionTag, "--registry", registry];
139
+ debug$8(`Executing: pnpm ${distTagArguments.join(" ")}`);
140
+ const result = execa("pnpm", distTagArguments, {
115
141
  cwd,
116
142
  env: {
117
143
  ...env,
@@ -129,6 +155,7 @@ const addChannel$1 = async (pluginConfig, packageJson, context) => {
129
155
  return false;
130
156
  };
131
157
 
158
+ const debug$7 = dbg("semantic-release-pnpm:prepare");
132
159
  const prepare$1 = async ({ pkgRoot, tarballDir }, { cwd, env, logger, nextRelease: { version }, stderr, stdout }) => {
133
160
  const basePath = pkgRoot ? resolve(cwd, pkgRoot) : cwd;
134
161
  logger.log("Write version %s to package.json in %s", version, basePath);
@@ -148,12 +175,17 @@ const prepare$1 = async ({ pkgRoot, tarballDir }, { cwd, env, logger, nextReleas
148
175
  const tarball = (await packResult).stdout.split("\n").pop();
149
176
  const tarballSource = resolve(cwd, tarball);
150
177
  const tarballDestination = resolve(cwd, tarballDir.trim(), tarball);
151
- if (tarballSource !== tarballDestination) {
178
+ debug$7(`Created tarball: ${tarball}`);
179
+ if (tarballSource === tarballDestination) {
180
+ debug$7(`Tarball already at destination: ${tarballDestination}`);
181
+ } else {
182
+ debug$7(`Moving tarball from ${tarballSource} to ${tarballDestination}`);
152
183
  await move(tarballSource, tarballDestination);
153
184
  }
154
185
  }
155
186
  };
156
187
 
188
+ const debug$6 = dbg("semantic-release-pnpm:publish");
157
189
  const publish$1 = async (pluginConfig, packageJson, context) => {
158
190
  const {
159
191
  cwd,
@@ -181,6 +213,7 @@ const publish$1 = async (pluginConfig, packageJson, context) => {
181
213
  if (pluginConfig.disableScripts) {
182
214
  pnpmArguments.push("--ignore-scripts");
183
215
  }
216
+ debug$6(`Executing: pnpm ${pnpmArguments.join(" ")}`);
184
217
  const result = execa("pnpm", pnpmArguments, {
185
218
  cwd,
186
219
  env,
@@ -329,72 +362,96 @@ const getPackage = async ({ pkgRoot }, { cwd }) => {
329
362
  }
330
363
  };
331
364
 
332
- const exchangeIdToken = async (idToken, packageName, logger) => {
365
+ const debug$5 = dbg("semantic-release-pnpm:token-exchange");
366
+ const exchangeIdToken = async (idToken, packageName, context) => {
333
367
  const response = await fetch(`${OFFICIAL_REGISTRY}-/npm/v1/oidc/token/exchange/package/${encodeURIComponent(packageName)}`, {
334
368
  headers: { Authorization: `Bearer ${idToken}` },
335
369
  method: "POST"
336
370
  });
337
371
  const responseBody = await response.json();
338
372
  if (response.ok) {
339
- logger.log("OIDC token exchange with the npm registry succeeded");
373
+ debug$5("OIDC token exchange with the npm registry succeeded");
340
374
  return responseBody.token;
341
375
  }
342
- logger.log(`OIDC token exchange with the npm registry failed: ${response.status} ${responseBody.message}`);
376
+ debug$5(`OIDC token exchange with the npm registry failed: ${response.status} ${responseBody.message}`);
377
+ if (response.status === 404 || responseBody.message?.toLowerCase().includes("not found")) {
378
+ const warningMessage = `Package "${packageName}" does not exist on npm. npm requires a package to exist before you can configure OIDC trusted publishing. You can either publish a dummy version manually first (e.g., \`pnpm publish --tag dummy\`) or use the \`setup-npm-trusted-publish\` tool (https://github.com/azu/setup-npm-trusted-publish) to create a placeholder package. After the package exists, configure OIDC trusted publishing at https://www.npmjs.com/package/${encodeURIComponent(packageName)}/access`;
379
+ context?.logger?.error(warningMessage);
380
+ debug$5(warningMessage);
381
+ }
343
382
  return void 0;
344
383
  };
345
- const exchangeGithubActionsToken = async (packageName, logger) => {
384
+ const exchangeGithubActionsToken = async (packageName, context) => {
346
385
  let idToken;
347
- logger.log("Verifying OIDC context for publishing from GitHub Actions");
386
+ debug$5("Verifying OIDC context for publishing from GitHub Actions");
348
387
  try {
349
388
  idToken = await getIDToken("npm:registry.npmjs.org");
350
389
  } catch (error) {
351
- logger.log(`Retrieval of GitHub Actions OIDC token failed: ${error.message}`);
352
- logger.log("Have you granted the `id-token: write` permission to this workflow?");
390
+ debug$5(`Retrieval of GitHub Actions OIDC token failed: ${error.message}`);
391
+ debug$5("Have you granted the `id-token: write` permission to this workflow?");
353
392
  return void 0;
354
393
  }
355
394
  if (!idToken) {
356
- logger.log("NPM_ID_TOKEN environment variable is not set");
357
- logger.log("Configure trusted publishing in your GitLab project settings and set the NPM_ID_TOKEN variable");
395
+ debug$5("GitHub Actions OIDC token is not available");
396
+ debug$5("Have you granted the `id-token: write` permission to this workflow?");
358
397
  return void 0;
359
398
  }
360
- return exchangeIdToken(idToken, packageName, logger);
399
+ return exchangeIdToken(idToken, packageName, context);
361
400
  };
362
- const exchangeGitlabPipelinesToken = async (packageName, logger) => {
401
+ const exchangeGitlabPipelinesToken = async (packageName, context) => {
363
402
  const idToken = process.env.NPM_ID_TOKEN;
364
- logger.log("Verifying OIDC context for publishing from GitLab Pipelines");
403
+ debug$5("Verifying OIDC context for publishing from GitLab Pipelines");
365
404
  if (!idToken) {
405
+ debug$5("NPM_ID_TOKEN environment variable is not set");
406
+ debug$5("Configure trusted publishing in your GitLab project settings and set the NPM_ID_TOKEN variable");
366
407
  return void 0;
367
408
  }
368
- return exchangeIdToken(idToken, packageName, logger);
409
+ return exchangeIdToken(idToken, packageName, context);
369
410
  };
370
- const tokenExchange = (pkg, { logger }) => {
411
+ const tokenExchange = (pkg, context) => {
371
412
  if (!pkg.name || typeof pkg.name !== "string" || pkg.name.trim() === "") {
372
- logger.log("Invalid package name provided for OIDC token exchange");
413
+ context.logger.error("Invalid package name provided for OIDC token exchange");
373
414
  return Promise.resolve(void 0);
374
415
  }
375
416
  const ciEnv = envCi();
376
417
  const ciProviderName = typeof ciEnv === "object" && ciEnv !== null && typeof ciEnv.name === "string" ? ciEnv.name : void 0;
377
418
  if (!ciProviderName) {
378
- logger.log("Unable to detect CI provider for OIDC token exchange");
419
+ debug$5("Unable to detect CI provider for OIDC token exchange");
420
+ debug$5("Supported CI providers for OIDC trusted publishing: GitHub Actions, GitLab CI/CD");
379
421
  return Promise.resolve(void 0);
380
422
  }
423
+ debug$5(`Detected CI provider: ${ciProviderName}`);
381
424
  if (GITHUB_ACTIONS_PROVIDER_NAME === ciProviderName) {
382
- return exchangeGithubActionsToken(pkg.name, logger);
425
+ return exchangeGithubActionsToken(pkg.name, context);
383
426
  }
384
427
  if (GITLAB_PIPELINES_PROVIDER_NAME === ciProviderName) {
385
- return exchangeGitlabPipelinesToken(pkg.name, logger);
428
+ return exchangeGitlabPipelinesToken(pkg.name, context);
386
429
  }
430
+ debug$5(`CI provider "${ciProviderName}" is not supported for OIDC trusted publishing`);
431
+ debug$5("Supported CI providers: GitHub Actions, GitLab CI/CD");
387
432
  return Promise.resolve(void 0);
388
433
  };
389
434
 
435
+ const debug$4 = dbg("semantic-release-pnpm:oidc-context");
390
436
  const oidcContext = async (registry, pkg, context) => {
391
- if (OFFICIAL_REGISTRY !== registry) {
437
+ const normalizedRegistry = normalizeUrl(registry);
438
+ const normalizedOfficialRegistry = normalizeUrl(OFFICIAL_REGISTRY);
439
+ if (normalizedRegistry !== normalizedOfficialRegistry) {
440
+ debug$4(
441
+ `Registry "${registry}" (normalized: "${normalizedRegistry}") does not match official registry "${OFFICIAL_REGISTRY}" (normalized: "${normalizedOfficialRegistry}"), skipping OIDC check`
442
+ );
392
443
  return false;
393
444
  }
445
+ debug$4(`Registry matches official npm registry, attempting OIDC token exchange for package "${pkg.name ?? "unknown"}"`);
394
446
  try {
395
447
  const token = await tokenExchange({ name: pkg.name ?? "" }, context);
448
+ if (!token) {
449
+ debug$4("OIDC token exchange did not succeed, falling back to NPM_TOKEN authentication");
450
+ }
396
451
  return !!token;
397
- } catch {
452
+ } catch (error) {
453
+ debug$4(`OIDC context check failed: ${error instanceof Error ? error.message : String(error)}`);
454
+ debug$4("Falling back to NPM_TOKEN authentication");
398
455
  return false;
399
456
  }
400
457
  };
@@ -406,6 +463,7 @@ const nerfDart = (url) => {
406
463
  return `//${real.host}${real.pathname}`;
407
464
  };
408
465
 
466
+ const debug$3 = dbg("semantic-release-pnpm:auth");
409
467
  const setNpmrcAuth = async (npmrc, registry, { cwd, env: { NPM_CONFIG_USERCONFIG, NPM_EMAIL, NPM_PASSWORD, NPM_TOKEN, NPM_USERNAME }, logger }) => {
410
468
  logger.log("Verify authentication for registry %s", registry);
411
469
  const { config, files } = rc("npm", {
@@ -416,11 +474,14 @@ const setNpmrcAuth = async (npmrc, registry, { cwd, env: { NPM_CONFIG_USERCONFIG
416
474
  if (Array.isArray(files)) {
417
475
  logger.log("Reading npm config from %s", files.join(", "));
418
476
  }
419
- if (getAuthToken(registry, { npmrc: config })) {
477
+ const existingToken = getAuthToken(registry, { npmrc: config });
478
+ if (existingToken) {
479
+ debug$3(`Using existing authentication token from npmrc files for registry "${registry}"`);
420
480
  await writeFile(npmrc, stringify(config));
421
481
  return;
422
482
  }
423
483
  if (NPM_USERNAME && NPM_PASSWORD && NPM_EMAIL) {
484
+ debug$3(`Using username/password/email authentication strategy for registry "${registry}"`);
424
485
  await writeFile(
425
486
  npmrc,
426
487
  `${Object.keys(config).length > 0 ? `${stringify(config)}
@@ -429,15 +490,46 @@ email = \${NPM_EMAIL}`
429
490
  );
430
491
  logger.log(`Wrote NPM_USERNAME, NPM_PASSWORD, and NPM_EMAIL to ${npmrc}`);
431
492
  } else if (NPM_TOKEN) {
493
+ debug$3(`Using NPM_TOKEN authentication strategy for registry "${registry}"`);
432
494
  await writeFile(npmrc, `${Object.keys(config).length > 0 ? `${stringify(config)}
433
495
  ` : ""}${nerfDart(registry)}:_authToken = \${NPM_TOKEN}`);
434
496
  logger.log(`Wrote NPM_TOKEN to ${npmrc}`);
435
497
  } else {
498
+ debug$3(`No authentication credentials found for registry "${registry}"`);
436
499
  const semanticError = getError("ENONPMTOKEN", { registry });
437
500
  throw new AggregateError([semanticError], semanticError.message);
438
501
  }
439
502
  };
440
503
 
504
+ const debug$2 = dbg("semantic-release-pnpm:verify-auth");
505
+ const whoamiCache = /* @__PURE__ */ new Map();
506
+ const getCacheKey = (registry, context) => {
507
+ const normalizedRegistry = normalizeUrl(registry);
508
+ const {
509
+ env: { NPM_PASSWORD, NPM_TOKEN, NPM_USERNAME }
510
+ } = context;
511
+ if (NPM_TOKEN) {
512
+ const tokenId = NPM_TOKEN.length > 12 ? `${NPM_TOKEN.slice(0, 8)}...${NPM_TOKEN.slice(-4)}` : NPM_TOKEN.slice(0, 8);
513
+ return `${normalizedRegistry}:token:${tokenId}`;
514
+ }
515
+ if (NPM_USERNAME && NPM_PASSWORD) {
516
+ return `${normalizedRegistry}:user:${NPM_USERNAME}`;
517
+ }
518
+ try {
519
+ const { config } = rc("npm", {
520
+ config: context.env.NPM_CONFIG_USERCONFIG ?? resolve(context.cwd, ".npmrc"),
521
+ cwd: context.cwd,
522
+ defaults: { registry: OFFICIAL_REGISTRY }
523
+ });
524
+ const token = getAuthToken(registry, { npmrc: config });
525
+ if (token) {
526
+ const tokenId = token.length > 12 ? `${token.slice(0, 8)}...${token.slice(-4)}` : token.slice(0, 8);
527
+ return `${normalizedRegistry}:token:${tokenId}`;
528
+ }
529
+ } catch {
530
+ }
531
+ return `${normalizedRegistry}:default`;
532
+ };
441
533
  const isConnectionError = (error) => {
442
534
  const errorMessage = error instanceof Error ? error.message : String(error);
443
535
  const errorCode = error?.code || "";
@@ -445,32 +537,55 @@ const isConnectionError = (error) => {
445
537
  return isTimedOut || errorCode === "ECONNREFUSED" || errorCode === "ETIMEDOUT" || errorMessage.includes("ECONNREFUSED") || errorMessage.includes("ETIMEDOUT") || errorMessage.includes("getaddrinfo ENOTFOUND") || errorMessage.includes("timed out");
446
538
  };
447
539
  const verifyAuthContextAgainstRegistry = async (npmrc, registry, context) => {
448
- const { cwd, env, logger, stderr, stdout } = context;
449
- try {
450
- logger.log(`Running "pnpm whoami" to verify authentication on registry "${registry}"`);
451
- const whoamiResult = await execa("pnpm", ["whoami", "--registry", registry], {
452
- cwd,
453
- env: {
454
- ...env,
455
- NPM_CONFIG_USERCONFIG: npmrc
456
- },
457
- preferLocal: true,
458
- timeout: 5e3
459
- // 5 second timeout to prevent hanging when registry is unavailable
460
- });
461
- if (whoamiResult.stdout) {
462
- stdout.write(whoamiResult.stdout);
540
+ const cacheKey = getCacheKey(registry, context);
541
+ if (whoamiCache.has(cacheKey)) {
542
+ debug$2(`Using cached whoami result for registry "${registry}"`);
543
+ const cachedResult = whoamiCache.get(cacheKey);
544
+ try {
545
+ await cachedResult;
546
+ return;
547
+ } catch {
548
+ debug$2(`Cached whoami result failed, retrying for registry "${registry}"`);
549
+ whoamiCache.delete(cacheKey);
463
550
  }
464
- if (whoamiResult.stderr) {
465
- stderr.write(whoamiResult.stderr);
551
+ }
552
+ const verificationPromise = (async () => {
553
+ const { cwd, env, logger, stderr, stdout } = context;
554
+ try {
555
+ logger.log(`Running "pnpm whoami" to verify authentication on registry "${registry}"`);
556
+ const whoamiResult = await execa("pnpm", ["whoami", "--registry", registry], {
557
+ cwd,
558
+ env: {
559
+ ...env,
560
+ NPM_CONFIG_USERCONFIG: npmrc
561
+ },
562
+ preferLocal: true,
563
+ timeout: 5e3
564
+ // 5 second timeout to prevent hanging when registry is unavailable
565
+ });
566
+ if (whoamiResult.stdout) {
567
+ stdout.write(whoamiResult.stdout);
568
+ }
569
+ if (whoamiResult.stderr) {
570
+ stderr.write(whoamiResult.stderr);
571
+ }
572
+ } catch (error) {
573
+ if (isConnectionError(error)) {
574
+ const semanticError2 = getError("EINVALIDNPMAUTH", { registry });
575
+ throw new AggregateError([semanticError2], semanticError2.message);
576
+ }
577
+ const semanticError = getError("EINVALIDNPMTOKEN", { registry });
578
+ throw new AggregateError([semanticError], semanticError.message);
466
579
  }
580
+ })();
581
+ whoamiCache.set(cacheKey, verificationPromise);
582
+ try {
583
+ await verificationPromise;
467
584
  } catch (error) {
468
585
  if (isConnectionError(error)) {
469
- const semanticError2 = getError("EINVALIDNPMAUTH", { registry });
470
- throw new AggregateError([semanticError2], semanticError2.message);
586
+ whoamiCache.delete(cacheKey);
471
587
  }
472
- const semanticError = getError("EINVALIDNPMTOKEN", { registry });
473
- throw new AggregateError([semanticError], semanticError.message);
588
+ throw error;
474
589
  }
475
590
  };
476
591
  const isAuthErrorMessage = (message) => message.includes("requires you to be logged in") || message.includes("authentication") || message.includes("Unauthorized") || message.includes("401") || message.includes("403");
@@ -526,8 +641,15 @@ const verifyAuthContextAgainstCustomRegistry = async (npmrc, registry, context,
526
641
  };
527
642
  const verifyAuth = async (npmrc, package_, context, pkgRoot) => {
528
643
  const registry = getRegistry(package_, context);
529
- if (package_.name && await oidcContext(registry, package_, context)) {
530
- return;
644
+ if (package_.name) {
645
+ debug$2(`Checking OIDC trusted publishing for package "${package_.name}" on registry "${registry}"`);
646
+ if (await oidcContext(registry, package_, context)) {
647
+ debug$2("OIDC trusted publishing verified successfully, skipping NPM_TOKEN check");
648
+ return;
649
+ }
650
+ debug$2("OIDC trusted publishing not available, falling back to NPM_TOKEN authentication");
651
+ } else {
652
+ debug$2("Package name not found, skipping OIDC check and using NPM_TOKEN authentication");
531
653
  }
532
654
  await setNpmrcAuth(npmrc, registry, context);
533
655
  const normalizedRegistry = normalizeUrl(registry);
@@ -567,14 +689,18 @@ const verifyConfig = (config) => (
567
689
  }, [])
568
690
  );
569
691
 
692
+ const debug$1 = dbg("semantic-release-pnpm:verify-pnpm");
570
693
  const MIN_PNPM_VERSION = "8.0.0";
571
694
  const verifyPnpm = async ({ logger }) => {
572
695
  logger.log(`Verify pnpm version is >= ${MIN_PNPM_VERSION}`);
573
696
  const version = getPackageManagerVersion("pnpm");
697
+ debug$1(`Detected pnpm version: ${String(version)}`);
574
698
  if (gte(MIN_PNPM_VERSION, version)) {
699
+ debug$1(`pnpm version ${String(version)} is below minimum required version ${MIN_PNPM_VERSION}`);
575
700
  const semanticError = getError("EINVALIDPNPM", { version: String(version) });
576
701
  throw new AggregateError([semanticError], semanticError.message);
577
702
  }
703
+ debug$1(`pnpm version ${String(version)} meets minimum requirement (>= ${MIN_PNPM_VERSION})`);
578
704
  };
579
705
 
580
706
  const verify = async (pluginConfig, context) => {
@@ -590,8 +716,11 @@ const verify = async (pluginConfig, context) => {
590
716
  try {
591
717
  const packageJson = await getPackage(pluginConfig, context);
592
718
  if (shouldPublish(pluginConfig, packageJson)) {
719
+ context.logger.log(`Verifying authentication for package "${packageJson.name ?? "unknown"}"`);
593
720
  const npmrc = getNpmrcPath(context.cwd, context.env);
594
721
  await verifyAuth(npmrc, packageJson, context, pluginConfig.pkgRoot);
722
+ } else {
723
+ context.logger.log(`Skipping authentication verification for package "${packageJson.name ?? "unknown"}" (publishing disabled)`);
595
724
  }
596
725
  } catch (error) {
597
726
  const typedError = error;
@@ -603,6 +732,7 @@ const verify = async (pluginConfig, context) => {
603
732
  }
604
733
  };
605
734
 
735
+ const debug = dbg("semantic-release-pnpm:index");
606
736
  const PLUGIN_NAME = "semantic-release-pnpm";
607
737
  let verified;
608
738
  let prepared;
@@ -620,7 +750,10 @@ const verifyConditions = async (pluginConfig, context) => {
620
750
  verified = true;
621
751
  };
622
752
  const prepare = async (pluginConfig, context) => {
623
- if (!verified) {
753
+ if (verified) {
754
+ debug("Skipping verifyConditions (already verified)");
755
+ } else {
756
+ debug("Verification not cached, running verifyConditions");
624
757
  await verify(pluginConfig, context);
625
758
  }
626
759
  await prepare$1(pluginConfig, context);
@@ -628,16 +761,25 @@ const prepare = async (pluginConfig, context) => {
628
761
  };
629
762
  const publish = async (pluginConfig, context) => {
630
763
  const packageJson = await getPackage(pluginConfig, context);
631
- if (!verified) {
764
+ if (verified) {
765
+ debug("Skipping verifyConditions (already verified)");
766
+ } else {
767
+ debug("Verification not cached, running verifyConditions");
632
768
  await verify(pluginConfig, context);
633
769
  }
634
- if (!prepared) {
770
+ if (prepared) {
771
+ debug("Skipping prepare (already prepared)");
772
+ } else {
773
+ debug("Preparation not cached, running prepare");
635
774
  await prepare$1(pluginConfig, context);
636
775
  }
637
776
  return await publish$1(pluginConfig, packageJson, context);
638
777
  };
639
778
  const addChannel = async (pluginConfig, context) => {
640
- if (!verified) {
779
+ if (verified) {
780
+ debug("Skipping verifyConditions (already verified)");
781
+ } else {
782
+ debug("Verification not cached, running verifyConditions");
641
783
  await verify(pluginConfig, context);
642
784
  }
643
785
  const packageJson = await getPackage(pluginConfig, context);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@anolilab/semantic-release-pnpm",
3
- "version": "3.1.0",
3
+ "version": "3.2.1",
4
4
  "description": "Semantic-release plugin to publish a npm package with pnpm.",
5
5
  "keywords": [
6
6
  "anolilab",
@@ -10,7 +10,21 @@
10
10
  "monorepo",
11
11
  "semantic-release",
12
12
  "semantic-release-plugin",
13
- "semantic-release-pnpm"
13
+ "semantic-release-pnpm",
14
+ "registry",
15
+ "npmjs",
16
+ "npm-registry",
17
+ "package-manager",
18
+ "workspace",
19
+ "workspaces",
20
+ "trusted-publishing",
21
+ "otp",
22
+ "authentication",
23
+ "npm-publish",
24
+ "package-publish",
25
+ "release-plugin",
26
+ "ci-cd",
27
+ "automation"
14
28
  ],
15
29
  "homepage": "https://github.com/anolilab/semantic-release/tree/main/packages/semantic-release-pnpm",
16
30
  "bugs": {
@@ -47,13 +61,14 @@
47
61
  ],
48
62
  "dependencies": {
49
63
  "@actions/core": "^1.11.1",
50
- "@anolilab/rc": "3.1.0",
64
+ "@anolilab/rc": "3.2.0",
51
65
  "@semantic-release/error": "^4.0.0",
52
66
  "@visulima/fs": "^4.1.0",
53
67
  "@visulima/package": "^4.1.7",
54
68
  "@visulima/path": "^2.0.5",
69
+ "debug": "^4.4.3",
55
70
  "env-ci": "^11.2.0",
56
- "execa": "^9.6.0",
71
+ "execa": "^9.6.1",
57
72
  "ini": "^6.0.0",
58
73
  "normalize-url": "^8.1.0",
59
74
  "registry-auth-token": "^5.1.0",