@anolilab/semantic-release-pnpm 2.0.5 → 3.0.0-alpha.2

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,13 +1,35 @@
1
- ## @anolilab/semantic-release-pnpm [2.0.5](https://github.com/anolilab/semantic-release/compare/@anolilab/semantic-release-pnpm@2.0.4...@anolilab/semantic-release-pnpm@2.0.5) (2025-11-01)
1
+ ## @anolilab/semantic-release-pnpm [3.0.0-alpha.2](https://github.com/anolilab/semantic-release/compare/@anolilab/semantic-release-pnpm@3.0.0-alpha.1...@anolilab/semantic-release-pnpm@3.0.0-alpha.2) (2025-10-21)
2
+
3
+ ### Features
4
+
5
+ * enhance npm authentication and trusted publishing support ([ccd056a](https://github.com/anolilab/semantic-release/commit/ccd056a02432b988fdcc79401d71182a433c4e97))
6
+ * update dependencies in pnpm-lock.yaml and pnpm-workspace.yaml ([001fd47](https://github.com/anolilab/semantic-release/commit/001fd4722fa6ba02c4d9479522ee2cb6f3d4067e))
7
+
8
+ ### Bug Fixes
9
+
10
+ * update package manager and dependencies across multiple configurations ([07a4e0b](https://github.com/anolilab/semantic-release/commit/07a4e0be0c8ba0b8fc79f5d922bb62f60db38350))
11
+
12
+
13
+ ### Dependencies
14
+
15
+ * **@anolilab/rc:** upgraded to 3.0.0-alpha.2
16
+
17
+ ## @anolilab/semantic-release-pnpm [3.0.0-alpha.1](https://github.com/anolilab/semantic-release/compare/@anolilab/semantic-release-pnpm@2.0.4...@anolilab/semantic-release-pnpm@3.0.0-alpha.1) (2025-10-14)
18
+
19
+ ### ⚠ BREAKING CHANGES
20
+
21
+ * - node-versions: the minimum node version for the v24 range is now v24.10.0
22
+ - deps: a minimum of node v22.14 is now required
2
23
 
3
24
  ### Bug Fixes
4
25
 
5
- * update package dependencies and configurations ([d94ebd8](https://github.com/anolilab/semantic-release/commit/d94ebd8869c34106aa0c11beb0f7410bbc86367f))
26
+ * update dependencies and configurations across multiple packages ([62c36cd](https://github.com/anolilab/semantic-release/commit/62c36cd7eb77324e2e60ba6d0f9137e4b9b2c86d))
27
+ * update dependencies and node version across multiple packages ([3cea078](https://github.com/anolilab/semantic-release/commit/3cea078f8aa27d0873e082bc54c4c5117e5105bf))
6
28
 
7
29
 
8
30
  ### Dependencies
9
31
 
10
- * **@anolilab/rc:** upgraded to 2.0.5
32
+ * **@anolilab/rc:** upgraded to 3.0.0-alpha.1
11
33
 
12
34
  ## @anolilab/semantic-release-pnpm [2.0.4](https://github.com/anolilab/semantic-release/compare/@anolilab/semantic-release-pnpm@2.0.3...@anolilab/semantic-release-pnpm@2.0.4) (2025-09-22)
13
35
 
package/README.md CHANGED
@@ -62,29 +62,28 @@ The plugin can be configured in the [**semantic-release** configuration file](ht
62
62
 
63
63
  ### npm registry authentication
64
64
 
65
- The npm [token](https://docs.npmjs.com/about-access-tokens) authentication configuration is **required** and can be set via [environment variables](#environment-variables).
65
+ #### Official Registry
66
66
 
67
- Automation tokens are recommended since they can be used for an automated workflow, even when your account is configured to use the [`auth-and-writes` level of 2FA](https://docs.npmjs.com/about-two-factor-authentication#authorization-and-writes).
67
+ When publishing to the [official registry](https://registry.npmjs.org/), it is recommended to publish with authentication intended for automation:
68
68
 
69
- ### npm provenance
70
-
71
- If you are publishing to the official registry and your pipeline is on a [provider that is supported by npm for provenance](https://docs.npmjs.com/generating-provenance-statements#provenance-limitations), npm can be configured to [publish with provenance](https://docs.npmjs.com/generating-provenance-statements).
69
+ - For improved security, and since access tokens have recently had their [maximum lifetimes restricted](https://github.blog/changelog/2025-09-29-strengthening-npm-security-important-changes-to-authentication-and-token-management/),
70
+ [trusted publishing](https://docs.npmjs.com/trusted-publishers) is recommended when publishing from a [supported CI provider](https://docs.npmjs.com/trusted-publishers#supported-cicd-providers)
71
+ - [Granular access tokens](https://docs.npmjs.com/creating-and-viewing-access-tokens#creating-granular-access-tokens-on-the-website) are recommended when publishing from a CI provider that is not supported by npm for trusted publishing, and can be set via [environment variables](#environment-variables).
72
+ Because these access tokens expire, rotation will need to be accounted for in this scenario.
72
73
 
73
- Since semantic-release wraps the npm publish command, configuring provenance is not exposed directly.
74
- Instead, provenance can be configured through the [other configuration options exposed by npm](https://docs.npmjs.com/generating-provenance-statements#using-third-party-package-publishing-tools).
75
- Provenance applies specifically to publishing, so our recommendation is to configure under `publishConfig` within the `package.json`.
74
+ > [!NOTE]
75
+ > When using trusted publishing, provenance attestations are automatically generated for your packages without requiring provenance to be explicitly enabled.
76
76
 
77
- #### npm provenance on GitHub Actions
77
+ ##### Trusted publishing from GitHub Actions
78
78
 
79
- For package provenance to be signed on the GitHub Actions CI the following permission is required
80
- to be enabled on the job:
79
+ To leverage trusted publishing and publish with provenance from GitHub Actions, the `id-token: write` permission is required to be enabled on the job:
81
80
 
82
81
  ```yaml
83
82
  permissions:
84
- id-token: write # to enable use of OIDC for npm provenance
83
+ id-token: write # to enable use of OIDC for trusted publishing and npm provenance
85
84
  ```
86
85
 
87
- It's worth noting that if you are using semantic-release to its fullest with a GitHub release, GitHub comments,
86
+ It's also worth noting that if you are using semantic-release to its fullest with a GitHub release, GitHub comments,
88
87
  and other features, then [more permissions are required](https://github.com/semantic-release/github#github-authentication) to be enabled on this job:
89
88
 
90
89
  ```yaml
@@ -92,11 +91,51 @@ permissions:
92
91
  contents: write # to be able to publish a GitHub release
93
92
  issues: write # to be able to comment on released issues
94
93
  pull-requests: write # to be able to comment on released pull requests
95
- id-token: write # to enable use of OIDC for npm provenance
94
+ id-token: write # to enable use of OIDC for trusted publishing and npm provenance
96
95
  ```
97
96
 
98
97
  Refer to the [GitHub Actions recipe for npm package provenance](https://semantic-release.gitbook.io/semantic-release/recipes/ci-configurations/github-actions#.github-workflows-release.yml-configuration-for-node-projects) for the full CI job's YAML code example.
99
98
 
99
+ ##### Trusted publishing for GitLab Pipelines
100
+
101
+ To leverage trusted publishing and publish with provenance from GitLab Pipelines, `NPM_ID_TOKEN` needs to be added as an entry under `id_tokens` in the job definition with an audience of `npm:registry.npmjs.org`:
102
+
103
+ ```yaml
104
+ id_tokens:
105
+ NPM_ID_TOKEN:
106
+ aud: "npm:registry.npmjs.org"
107
+ ```
108
+
109
+ See the [npm documentation for more details about configuring pipeline details](https://docs.npmjs.com/trusted-publishers#gitlab-cicd-configuration)
110
+
111
+ ##### Unsupported CI providers
112
+
113
+ Token authentication is **required** and can be set via [environment variables](#environment-variables).
114
+ [Granular access tokens](https://docs.npmjs.com/creating-and-viewing-access-tokens#creating-granular-access-tokens-on-the-website) are recommended in this scenario, since trusted publishing is not available from all CI providers.
115
+ Because these access tokens expire, rotation will need to be accounted for in your process.
116
+
117
+ #### Alternative Registries
118
+
119
+ Token authentication is **required** and can be set via [environment variables](#environment-variables).
120
+ See the documentation for your registry for details on how to create a token for automation.
121
+
122
+ ### npm provenance
123
+
124
+ When using trusted publishing to the official npm registry, provenance attestations are automatically generated for your packages without requiring provenance to be explicitly enabled.
125
+
126
+ For alternative registries or when using token-based authentication, provenance can be configured through the [other configuration options exposed by npm](https://docs.npmjs.com/generating-provenance-statements#using-third-party-package-publishing-tools).
127
+ Provenance applies specifically to publishing, so configure it under `publishConfig` within the `package.json`:
128
+
129
+ ```json
130
+ {
131
+ "publishConfig": {
132
+ "registry": "https://registry.npmjs.org/",
133
+ "tag": "latest",
134
+ "provenance": true
135
+ }
136
+ }
137
+ ```
138
+
100
139
  ### Environment variables
101
140
 
102
141
  | Variable | Description |
package/dist/index.js CHANGED
@@ -1,14 +1,40 @@
1
+ import { createRequire as __cjs_createRequire } from "node:module";
2
+ const __cjs_require = __cjs_createRequire(import.meta.url);
3
+
4
+ // Runtime capability helpers
5
+ const __cjs_getBuiltinModule = (module) => {
6
+ // Check if we're in Node.js and version supports getBuiltinModule
7
+ if ("object" !== "undefined" && process.versions?.node) {
8
+ const [major, minor] = process.versions.node.split(".").map(Number);
9
+ // Node.js 20.16.0+ and 22.3.0+
10
+ if (major > 22 || (major === 22 && minor >= 3) || (major === 20 && minor >= 16)) {
11
+ return process.getBuiltinModule(module);
12
+ }
13
+ }
14
+ // Fallback to createRequire
15
+ return __cjs_require(module);
16
+ };
17
+
18
+ const __cjs_getProcess = typeof globalThis !== "undefined" && typeof globalThis.process !== "undefined" ? globalThis.process : process;
1
19
  import { execa } from 'execa';
2
- import { validRange, gte } from 'semver';
20
+ const {
21
+ validRange,
22
+ gte
23
+ } = __cjs_require("semver");
3
24
  import { isAccessibleSync, ensureFileSync, move, writeFile } from '@visulima/fs';
4
25
  import { resolve } from '@visulima/path';
5
26
  import { rc } from '@anolilab/rc';
6
27
  import normalizeUrl from 'normalize-url';
7
28
  import { findPackageJson, getPackageManagerVersion } from '@visulima/package';
8
29
  import SemanticReleaseError from '@semantic-release/error';
9
- import { stringify } from 'ini';
10
- import getAuthToken from 'registry-auth-token';
11
- import { URL } from 'node:url';
30
+ import envCi from 'env-ci';
31
+ const {
32
+ stringify
33
+ } = __cjs_require("ini");
34
+ const getAuthToken = __cjs_require("registry-auth-token");
35
+ const {
36
+ URL
37
+ } = __cjs_getBuiltinModule("node:url");
12
38
 
13
39
  const getChannel = (channel) => {
14
40
  if (!channel) {
@@ -32,10 +58,12 @@ const getNpmrcPath = (cwd, environment) => {
32
58
  return npmrcPath;
33
59
  };
34
60
 
35
- const DEFAULT_NPM_REGISTRY = "https://registry.npmjs.org/";
61
+ const OFFICIAL_REGISTRY = "https://registry.npmjs.org/";
62
+ const GITHUB_ACTIONS_PROVIDER_NAME = "GitHub Actions";
63
+ const GITLAB_PIPELINES_PROVIDER_NAME = "GitLab CI/CD";
36
64
 
37
65
  const getRegistryUrl = (scope, npmrc) => {
38
- let url = DEFAULT_NPM_REGISTRY;
66
+ let url = OFFICIAL_REGISTRY;
39
67
  if (npmrc) {
40
68
  const registryUrl = npmrc[`${scope}:registry`] ?? npmrc.registry;
41
69
  if (registryUrl) {
@@ -49,11 +77,11 @@ const getRegistry = ({ name, publishConfig: { registry } = {} }, { cwd, env }) =
49
77
  rc("npm", {
50
78
  config: env.NPM_CONFIG_USERCONFIG ?? resolve(cwd, ".npmrc"),
51
79
  cwd,
52
- defaults: { registry: "https://registry.npmjs.org/" }
80
+ defaults: { registry: OFFICIAL_REGISTRY }
53
81
  }).config
54
82
  );
55
83
 
56
- const getReleaseInfo = ({ name }, { env: { DEFAULT_NPM_REGISTRY = "https://registry.npmjs.org/" }, nextRelease: { version } }, distributionTag, registry) => {
84
+ const getReleaseInfo = ({ name }, { env: { DEFAULT_NPM_REGISTRY = OFFICIAL_REGISTRY }, nextRelease: { version } }, distributionTag, registry) => {
57
85
  return {
58
86
  channel: distributionTag,
59
87
  name: `pnpm package (@${distributionTag} dist-tag)`,
@@ -186,6 +214,15 @@ Your configuration for the \`branches\` option is \`${branches.join(",")}\`.`,
186
214
  message: "Invalid `branches` option."
187
215
  };
188
216
  },
217
+ EINVALIDNPMAUTH: ({ registry }) => {
218
+ return {
219
+ details: `The [authentication required to publish](${linkify(
220
+ "README.md#npm-registry-authentication"
221
+ )}) configured in the \`NPM_TOKEN\` environment variable must be a valid [token](https://docs.npmjs.com/getting-started/working_with_tokens) allowed to publish to the registry \`${String(registry)}\`.
222
+ Please make sure to set the \`NPM_TOKEN\` environment variable in your CI with the exact value of the npm token.`,
223
+ message: "Invalid npm authentication."
224
+ };
225
+ },
189
226
  EINVALIDNPMPUBLISH: ({ npmPublish }) => {
190
227
  return {
191
228
  details: `The [npmPublish option](${linkify("README.md#npmpublish")}) option, if defined, must be a \`Boolean\`.
@@ -198,8 +235,7 @@ Your configuration for the \`npmPublish\` option is \`${String(npmPublish)}\`.`,
198
235
  details: `The [npm token](${linkify(
199
236
  "README.md#npm-registry-authentication"
200
237
  )}) configured in the \`NPM_TOKEN\` environment variable must be a valid [token](https://docs.npmjs.com/getting-started/working_with_tokens) allowing to publish to the registry \`${String(registry)}\`.
201
- If you are using Two Factor Authentication for your account, set its level to ["Authorization only"](https://docs.npmjs.com/getting-started/using-two-factor-authentication#levels-of-authentication) in your account settings. **semantic-release** cannot publish with the default "Authorization and writes" level.
202
- Please make sure to set the \`NPM_TOKEN\` environment variable in your CI with the exact value of the npm token.`,
238
+ Please verify your authentication configuration.`,
203
239
  message: "Invalid npm token."
204
240
  };
205
241
  },
@@ -236,10 +272,10 @@ Your configuration for the \`tarballDir\` option is \`${String(tarballDir)}\`.`,
236
272
  },
237
273
  ENONPMTOKEN: ({ registry }) => {
238
274
  return {
239
- details: `An [npm token](${linkify(
275
+ details: `When not publishing through [trusted publishing](https://docs.npmjs.com/trusted-publishers), an [npm token](${linkify(
240
276
  "README.md#npm-registry-authentication"
241
277
  )}) must be created and set in the \`NPM_TOKEN\` environment variable on your CI environment.
242
- Please make sure to create an [npm token](https://docs.npmjs.com/getting-started/working_with_tokens#how-to-create-new-tokens) and to set it in the \`NPM_TOKEN\` environment variable on your CI environment. The token must allow to publish to the registry \`${String(registry)}\`.`,
278
+ Please make sure to create an [npm token](https://docs.npmjs.com/getting-started/working_with_tokens#how-to-create-new-tokens) and set it in the \`NPM_TOKEN\` environment variable on your CI environment. The token must allow publishing to the registry \`${registry}\`.`,
243
279
  message: "No npm token specified."
244
280
  };
245
281
  },
@@ -293,6 +329,66 @@ const getPackage = async ({ pkgRoot }, { cwd }) => {
293
329
  }
294
330
  };
295
331
 
332
+ const exchangeIdToken = async (idToken, packageName, logger) => {
333
+ const response = await fetch(`${OFFICIAL_REGISTRY}-/npm/v1/oidc/token/exchange/package/${encodeURIComponent(packageName)}`, {
334
+ headers: { Authorization: `Bearer ${idToken}` },
335
+ method: "POST"
336
+ });
337
+ const responseBody = await response.json();
338
+ if (response.ok) {
339
+ logger.log("OIDC token exchange with the npm registry succeeded");
340
+ return responseBody.token;
341
+ }
342
+ logger.log(`OIDC token exchange with the npm registry failed: ${response.status} ${responseBody.message}`);
343
+ return void 0;
344
+ };
345
+ const exchangeGithubActionsToken = async (packageName, logger) => {
346
+ let idToken;
347
+ logger.log("Verifying OIDC context for publishing from GitHub Actions");
348
+ try {
349
+ const { getIDToken } = await import('@actions/core');
350
+ idToken = await getIDToken("npm:registry.npmjs.org");
351
+ } catch (error) {
352
+ logger.log(`Retrieval of GitHub Actions OIDC token failed: ${error.message}`);
353
+ logger.log("Have you granted the `id-token: write` permission to this workflow?");
354
+ return void 0;
355
+ }
356
+ if (!idToken) {
357
+ return void 0;
358
+ }
359
+ return exchangeIdToken(idToken, packageName, logger);
360
+ };
361
+ const exchangeGitlabPipelinesToken = async (packageName, logger) => {
362
+ const idToken = process.env.NPM_ID_TOKEN;
363
+ logger.log("Verifying OIDC context for publishing from GitLab Pipelines");
364
+ if (!idToken) {
365
+ return void 0;
366
+ }
367
+ return exchangeIdToken(idToken, packageName, logger);
368
+ };
369
+ const tokenExchange = (pkg, { logger }) => {
370
+ const { name: ciProviderName } = envCi();
371
+ if (GITHUB_ACTIONS_PROVIDER_NAME === ciProviderName) {
372
+ return exchangeGithubActionsToken(pkg.name, logger);
373
+ }
374
+ if (GITLAB_PIPELINES_PROVIDER_NAME === ciProviderName) {
375
+ return exchangeGitlabPipelinesToken(pkg.name, logger);
376
+ }
377
+ return Promise.resolve(void 0);
378
+ };
379
+
380
+ const oidcContext = async (registry, pkg, context) => {
381
+ if (OFFICIAL_REGISTRY !== registry) {
382
+ return false;
383
+ }
384
+ try {
385
+ const token = await tokenExchange({ name: pkg.name ?? "" }, context);
386
+ return !!token;
387
+ } catch {
388
+ return false;
389
+ }
390
+ };
391
+
296
392
  const nerfDart = (url) => {
297
393
  const parsed = new URL(url);
298
394
  const from = `${parsed.protocol}//${parsed.host}${parsed.pathname}`;
@@ -305,7 +401,7 @@ const setNpmrcAuth = async (npmrc, registry, { cwd, env: { NPM_CONFIG_USERCONFIG
305
401
  const { config, files } = rc("npm", {
306
402
  config: NPM_CONFIG_USERCONFIG ?? resolve(cwd, ".npmrc"),
307
403
  cwd,
308
- defaults: { registry: DEFAULT_NPM_REGISTRY }
404
+ defaults: { registry: OFFICIAL_REGISTRY }
309
405
  });
310
406
  if (Array.isArray(files)) {
311
407
  logger.log("Reading npm config from %s", files.join(", "));
@@ -329,33 +425,43 @@ email = \${NPM_EMAIL}`);
329
425
  }
330
426
  };
331
427
 
332
- const verifyAuth = async (npmrc, package_, context) => {
428
+ const verifyAuthContextAgainstRegistry = async (npmrc, registry, context) => {
333
429
  const {
334
430
  cwd,
335
- env: { DEFAULT_NPM_REGISTRY = "https://registry.npmjs.org/", ...environment },
431
+ env: { ...environment },
336
432
  logger,
337
433
  stderr,
338
434
  stdout
339
435
  } = context;
436
+ try {
437
+ logger.log(`Running "pnpm whoami" to verify authentication on registry "${registry}"`);
438
+ const whoamiResult = await execa("pnpm", ["whoami", "--userconfig", npmrc, "--registry", registry], {
439
+ cwd,
440
+ env: environment,
441
+ preferLocal: true
442
+ });
443
+ if (whoamiResult.stdout) {
444
+ stdout.write(whoamiResult.stdout);
445
+ }
446
+ if (whoamiResult.stderr) {
447
+ stderr.write(whoamiResult.stderr);
448
+ }
449
+ } catch {
450
+ const semanticError = getError("EINVALIDNPMTOKEN", { registry });
451
+ throw new AggregateError([semanticError], semanticError.message);
452
+ }
453
+ };
454
+ const verifyAuth = async (npmrc, package_, context, pkgRoot) => {
340
455
  const registry = getRegistry(package_, context);
456
+ if (package_.name && await oidcContext(registry, package_, context)) {
457
+ return;
458
+ }
341
459
  await setNpmrcAuth(npmrc, registry, context);
460
+ const {
461
+ env: { DEFAULT_NPM_REGISTRY = OFFICIAL_REGISTRY }
462
+ } = context;
342
463
  if (normalizeUrl(registry) === normalizeUrl(DEFAULT_NPM_REGISTRY)) {
343
- try {
344
- logger.log(`Running "pnpm whoami" to verify authentication on registry "${registry}"`);
345
- const whoamiResult = execa("pnpm", ["whoami", "--userconfig", npmrc, "--registry", registry], {
346
- cwd,
347
- env: environment,
348
- preferLocal: true
349
- });
350
- whoamiResult.stdout.pipe(stdout, { end: false });
351
- whoamiResult.stderr.pipe(stderr, { end: false });
352
- await whoamiResult;
353
- } catch {
354
- const semanticError = getError("EINVALIDNPMTOKEN", { registry });
355
- throw new AggregateError([semanticError], semanticError.message);
356
- }
357
- } else {
358
- logger.log(`Skipping authentication verification for non-default registry "${registry}"`);
464
+ await verifyAuthContextAgainstRegistry(npmrc, registry, context);
359
465
  }
360
466
  };
361
467
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@anolilab/semantic-release-pnpm",
3
- "version": "2.0.5",
3
+ "version": "3.0.0-alpha.2",
4
4
  "description": "Semantic-release plugin to publish a npm package with pnpm.",
5
5
  "keywords": [
6
6
  "anolilab",
@@ -46,19 +46,21 @@
46
46
  "dist"
47
47
  ],
48
48
  "dependencies": {
49
- "@anolilab/rc": "2.0.5",
49
+ "@actions/core": "^1.11.1",
50
+ "@anolilab/rc": "3.0.0-alpha.2",
50
51
  "@semantic-release/error": "^4.0.0",
51
52
  "@visulima/fs": "^4.0.2",
52
53
  "@visulima/package": "^4.1.2",
53
54
  "@visulima/path": "^2.0.1",
55
+ "env-ci": "^11.2.0",
54
56
  "execa": "^9.6.0",
55
- "ini": "^6.0.0",
57
+ "ini": "^5.0.0",
56
58
  "normalize-url": "^8.1.0",
57
59
  "registry-auth-token": "^5.1.0",
58
60
  "semver": "^7.7.3"
59
61
  },
60
62
  "engines": {
61
- "node": ">=20.8.1"
63
+ "node": "^22.14.0 || >=24.10.0"
62
64
  },
63
65
  "publishConfig": {
64
66
  "access": "public",