@anolilab/semantic-release-pnpm 3.0.0-alpha.1 → 3.0.0-alpha.3
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 +53 -0
- package/README.md +53 -14
- package/dist/index.js +206 -55
- package/package.json +8 -6
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,45 @@
|
|
|
1
|
+
## @anolilab/semantic-release-pnpm [3.0.0-alpha.3](https://github.com/anolilab/semantic-release/compare/@anolilab/semantic-release-pnpm@3.0.0-alpha.2...@anolilab/semantic-release-pnpm@3.0.0-alpha.3) (2025-11-20)
|
|
2
|
+
|
|
3
|
+
### Bug Fixes
|
|
4
|
+
|
|
5
|
+
* enhance integration and unit tests for npm authentication ([79ae50d](https://github.com/anolilab/semantic-release/commit/79ae50d47362159a426d31acc9432ba3ac31993e))
|
|
6
|
+
* update package dependencies and configurations ([fed735c](https://github.com/anolilab/semantic-release/commit/fed735c729647a595d587eaa162d2c44ff9948f1))
|
|
7
|
+
|
|
8
|
+
### Miscellaneous Chores
|
|
9
|
+
|
|
10
|
+
* add '@types/env-ci' dependency and refine type handling ([f897d6c](https://github.com/anolilab/semantic-release/commit/f897d6c26436790504bcb8ca98095e4ab3f2bd61))
|
|
11
|
+
* update '@actions/core' dependency in semantic-release-pnpm ([50cc6e0](https://github.com/anolilab/semantic-release/commit/50cc6e09e2d1959acdcfc6475c1ecfbcb0fdb406))
|
|
12
|
+
* update '@actions/core' dependency to production in pnpm configuration ([bfec287](https://github.com/anolilab/semantic-release/commit/bfec287c74f048f41bcb0517353d3d4dede451a0))
|
|
13
|
+
* update Node.js version and refine dependencies in pnpm-workspace ([cbd6172](https://github.com/anolilab/semantic-release/commit/cbd617234ac6e5f5a14b70fbf6d6aec3bcd38fe0))
|
|
14
|
+
|
|
15
|
+
### Code Refactoring
|
|
16
|
+
|
|
17
|
+
* clean up whitespace and comments in get-config.ts and verify-auth.ts ([26dc037](https://github.com/anolilab/semantic-release/commit/26dc037917f281f24d096d9790b671262f432b24))
|
|
18
|
+
* enhance inline plugin creator with consistent git root handling ([4ebaea4](https://github.com/anolilab/semantic-release/commit/4ebaea46d35bb89a0adcb4bf58c6df559e35f107))
|
|
19
|
+
* Ensure consistent cwd handling across all plugin hooks ([3028b71](https://github.com/anolilab/semantic-release/commit/3028b71f6f16244968bd6646b042ac893ffa5bb9))
|
|
20
|
+
* update test cases and improve type handling ([328c392](https://github.com/anolilab/semantic-release/commit/328c39278c609fd9169df79f3f48bbd8038d2e64))
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
### Dependencies
|
|
24
|
+
|
|
25
|
+
* **@anolilab/rc:** upgraded to 3.0.0-alpha.3
|
|
26
|
+
|
|
27
|
+
## @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)
|
|
28
|
+
|
|
29
|
+
### Features
|
|
30
|
+
|
|
31
|
+
* enhance npm authentication and trusted publishing support ([ccd056a](https://github.com/anolilab/semantic-release/commit/ccd056a02432b988fdcc79401d71182a433c4e97))
|
|
32
|
+
* update dependencies in pnpm-lock.yaml and pnpm-workspace.yaml ([001fd47](https://github.com/anolilab/semantic-release/commit/001fd4722fa6ba02c4d9479522ee2cb6f3d4067e))
|
|
33
|
+
|
|
34
|
+
### Bug Fixes
|
|
35
|
+
|
|
36
|
+
* update package manager and dependencies across multiple configurations ([07a4e0b](https://github.com/anolilab/semantic-release/commit/07a4e0be0c8ba0b8fc79f5d922bb62f60db38350))
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
### Dependencies
|
|
40
|
+
|
|
41
|
+
* **@anolilab/rc:** upgraded to 3.0.0-alpha.2
|
|
42
|
+
|
|
1
43
|
## @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)
|
|
2
44
|
|
|
3
45
|
### ⚠ BREAKING CHANGES
|
|
@@ -15,6 +57,17 @@
|
|
|
15
57
|
|
|
16
58
|
* **@anolilab/rc:** upgraded to 3.0.0-alpha.1
|
|
17
59
|
|
|
60
|
+
## @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)
|
|
61
|
+
|
|
62
|
+
### Bug Fixes
|
|
63
|
+
|
|
64
|
+
* update package dependencies and configurations ([d94ebd8](https://github.com/anolilab/semantic-release/commit/d94ebd8869c34106aa0c11beb0f7410bbc86367f))
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
### Dependencies
|
|
68
|
+
|
|
69
|
+
* **@anolilab/rc:** upgraded to 2.0.5
|
|
70
|
+
|
|
18
71
|
## @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)
|
|
19
72
|
|
|
20
73
|
### Bug Fixes
|
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
|
-
|
|
65
|
+
#### Official Registry
|
|
66
66
|
|
|
67
|
-
|
|
67
|
+
When publishing to the [official registry](https://registry.npmjs.org/), it is recommended to publish with authentication intended for automation:
|
|
68
68
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
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
|
-
|
|
74
|
-
|
|
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
|
-
|
|
77
|
+
##### Trusted publishing from GitHub Actions
|
|
78
78
|
|
|
79
|
-
|
|
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,36 +1,33 @@
|
|
|
1
1
|
import { createRequire as __cjs_createRequire } from "node:module";
|
|
2
|
+
|
|
2
3
|
const __cjs_require = __cjs_createRequire(import.meta.url);
|
|
3
4
|
|
|
4
|
-
|
|
5
|
+
const __cjs_getProcess = typeof globalThis !== "undefined" && typeof globalThis.process !== "undefined" ? globalThis.process : process;
|
|
6
|
+
|
|
5
7
|
const __cjs_getBuiltinModule = (module) => {
|
|
6
8
|
// Check if we're in Node.js and version supports getBuiltinModule
|
|
7
|
-
if (
|
|
8
|
-
const [major, minor] =
|
|
9
|
+
if (typeof __cjs_getProcess !== "undefined" && __cjs_getProcess.versions && __cjs_getProcess.versions.node) {
|
|
10
|
+
const [major, minor] = __cjs_getProcess.versions.node.split(".").map(Number);
|
|
9
11
|
// Node.js 20.16.0+ and 22.3.0+
|
|
10
12
|
if (major > 22 || (major === 22 && minor >= 3) || (major === 20 && minor >= 16)) {
|
|
11
|
-
return
|
|
13
|
+
return __cjs_getProcess.getBuiltinModule(module);
|
|
12
14
|
}
|
|
13
15
|
}
|
|
14
16
|
// Fallback to createRequire
|
|
15
17
|
return __cjs_require(module);
|
|
16
18
|
};
|
|
17
19
|
|
|
18
|
-
const __cjs_getProcess = typeof globalThis !== "undefined" && typeof globalThis.process !== "undefined" ? globalThis.process : process;
|
|
19
20
|
import { execa } from 'execa';
|
|
20
|
-
|
|
21
|
-
validRange,
|
|
22
|
-
gte
|
|
23
|
-
} = __cjs_require("semver");
|
|
21
|
+
import { validRange, gte } from 'semver';
|
|
24
22
|
import { isAccessibleSync, ensureFileSync, move, writeFile } from '@visulima/fs';
|
|
25
23
|
import { resolve } from '@visulima/path';
|
|
26
24
|
import { rc } from '@anolilab/rc';
|
|
27
25
|
import normalizeUrl from 'normalize-url';
|
|
28
26
|
import { findPackageJson, getPackageManagerVersion } from '@visulima/package';
|
|
29
27
|
import SemanticReleaseError from '@semantic-release/error';
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
const getAuthToken = __cjs_require("registry-auth-token");
|
|
28
|
+
import envCi from 'env-ci';
|
|
29
|
+
import { stringify } from 'ini';
|
|
30
|
+
import getAuthToken from 'registry-auth-token';
|
|
34
31
|
const {
|
|
35
32
|
URL
|
|
36
33
|
} = __cjs_getBuiltinModule("node:url");
|
|
@@ -57,10 +54,12 @@ const getNpmrcPath = (cwd, environment) => {
|
|
|
57
54
|
return npmrcPath;
|
|
58
55
|
};
|
|
59
56
|
|
|
60
|
-
const
|
|
57
|
+
const OFFICIAL_REGISTRY = "https://registry.npmjs.org/";
|
|
58
|
+
const GITHUB_ACTIONS_PROVIDER_NAME = "GitHub Actions";
|
|
59
|
+
const GITLAB_PIPELINES_PROVIDER_NAME = "GitLab CI/CD";
|
|
61
60
|
|
|
62
61
|
const getRegistryUrl = (scope, npmrc) => {
|
|
63
|
-
let url =
|
|
62
|
+
let url = OFFICIAL_REGISTRY;
|
|
64
63
|
if (npmrc) {
|
|
65
64
|
const registryUrl = npmrc[`${scope}:registry`] ?? npmrc.registry;
|
|
66
65
|
if (registryUrl) {
|
|
@@ -74,11 +73,11 @@ const getRegistry = ({ name, publishConfig: { registry } = {} }, { cwd, env }) =
|
|
|
74
73
|
rc("npm", {
|
|
75
74
|
config: env.NPM_CONFIG_USERCONFIG ?? resolve(cwd, ".npmrc"),
|
|
76
75
|
cwd,
|
|
77
|
-
defaults: { registry:
|
|
76
|
+
defaults: { registry: OFFICIAL_REGISTRY }
|
|
78
77
|
}).config
|
|
79
78
|
);
|
|
80
79
|
|
|
81
|
-
const getReleaseInfo = ({ name }, { env: { DEFAULT_NPM_REGISTRY =
|
|
80
|
+
const getReleaseInfo = ({ name }, { env: { DEFAULT_NPM_REGISTRY = OFFICIAL_REGISTRY }, nextRelease: { version } }, distributionTag, registry) => {
|
|
82
81
|
return {
|
|
83
82
|
channel: distributionTag,
|
|
84
83
|
name: `pnpm package (@${distributionTag} dist-tag)`,
|
|
@@ -111,9 +110,12 @@ const addChannel$1 = async (pluginConfig, packageJson, context) => {
|
|
|
111
110
|
const distributionTag = getChannel(channel);
|
|
112
111
|
logger.log(`Adding version ${version} to npm registry on dist-tag ${distributionTag}`);
|
|
113
112
|
const npmrc = getNpmrcPath(cwd, env);
|
|
114
|
-
const result = execa("pnpm", ["dist-tag", "add", `${packageJson.name}@${version}`, distributionTag, "--
|
|
113
|
+
const result = execa("pnpm", ["dist-tag", "add", `${packageJson.name}@${version}`, distributionTag, "--registry", registry], {
|
|
115
114
|
cwd,
|
|
116
|
-
env
|
|
115
|
+
env: {
|
|
116
|
+
...env,
|
|
117
|
+
NPM_CONFIG_USERCONFIG: npmrc
|
|
118
|
+
},
|
|
117
119
|
preferLocal: true
|
|
118
120
|
});
|
|
119
121
|
result.stdout.pipe(stdout, { end: false });
|
|
@@ -198,7 +200,7 @@ const publish$1 = async (pluginConfig, packageJson, context) => {
|
|
|
198
200
|
return false;
|
|
199
201
|
};
|
|
200
202
|
|
|
201
|
-
|
|
203
|
+
var homepage = "https://github.com/anolilab/semantic-release/tree/main/packages/semantic-release-pnpm";
|
|
202
204
|
const packageJson = {
|
|
203
205
|
homepage: homepage};
|
|
204
206
|
|
|
@@ -211,6 +213,15 @@ Your configuration for the \`branches\` option is \`${branches.join(",")}\`.`,
|
|
|
211
213
|
message: "Invalid `branches` option."
|
|
212
214
|
};
|
|
213
215
|
},
|
|
216
|
+
EINVALIDNPMAUTH: ({ registry }) => {
|
|
217
|
+
return {
|
|
218
|
+
details: `The [authentication required to publish](${linkify(
|
|
219
|
+
"README.md#npm-registry-authentication"
|
|
220
|
+
)}) 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)}\`.
|
|
221
|
+
Please make sure to set the \`NPM_TOKEN\` environment variable in your CI with the exact value of the npm token.`,
|
|
222
|
+
message: "Invalid npm authentication."
|
|
223
|
+
};
|
|
224
|
+
},
|
|
214
225
|
EINVALIDNPMPUBLISH: ({ npmPublish }) => {
|
|
215
226
|
return {
|
|
216
227
|
details: `The [npmPublish option](${linkify("README.md#npmpublish")}) option, if defined, must be a \`Boolean\`.
|
|
@@ -223,8 +234,7 @@ Your configuration for the \`npmPublish\` option is \`${String(npmPublish)}\`.`,
|
|
|
223
234
|
details: `The [npm token](${linkify(
|
|
224
235
|
"README.md#npm-registry-authentication"
|
|
225
236
|
)}) 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)}\`.
|
|
226
|
-
|
|
227
|
-
Please make sure to set the \`NPM_TOKEN\` environment variable in your CI with the exact value of the npm token.`,
|
|
237
|
+
Please verify your authentication configuration.`,
|
|
228
238
|
message: "Invalid npm token."
|
|
229
239
|
};
|
|
230
240
|
},
|
|
@@ -261,10 +271,10 @@ Your configuration for the \`tarballDir\` option is \`${String(tarballDir)}\`.`,
|
|
|
261
271
|
},
|
|
262
272
|
ENONPMTOKEN: ({ registry }) => {
|
|
263
273
|
return {
|
|
264
|
-
details: `
|
|
274
|
+
details: `When not publishing through [trusted publishing](https://docs.npmjs.com/trusted-publishers), an [npm token](${linkify(
|
|
265
275
|
"README.md#npm-registry-authentication"
|
|
266
276
|
)}) must be created and set in the \`NPM_TOKEN\` environment variable on your CI environment.
|
|
267
|
-
Please make sure to create an [npm token](https://docs.npmjs.com/getting-started/working_with_tokens#how-to-create-new-tokens) and
|
|
277
|
+
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}\`.`,
|
|
268
278
|
message: "No npm token specified."
|
|
269
279
|
};
|
|
270
280
|
},
|
|
@@ -318,6 +328,77 @@ const getPackage = async ({ pkgRoot }, { cwd }) => {
|
|
|
318
328
|
}
|
|
319
329
|
};
|
|
320
330
|
|
|
331
|
+
const exchangeIdToken = async (idToken, packageName, logger) => {
|
|
332
|
+
const response = await fetch(`${OFFICIAL_REGISTRY}-/npm/v1/oidc/token/exchange/package/${encodeURIComponent(packageName)}`, {
|
|
333
|
+
headers: { Authorization: `Bearer ${idToken}` },
|
|
334
|
+
method: "POST"
|
|
335
|
+
});
|
|
336
|
+
const responseBody = await response.json();
|
|
337
|
+
if (response.ok) {
|
|
338
|
+
logger.log("OIDC token exchange with the npm registry succeeded");
|
|
339
|
+
return responseBody.token;
|
|
340
|
+
}
|
|
341
|
+
logger.log(`OIDC token exchange with the npm registry failed: ${response.status} ${responseBody.message}`);
|
|
342
|
+
return void 0;
|
|
343
|
+
};
|
|
344
|
+
const exchangeGithubActionsToken = async (packageName, logger) => {
|
|
345
|
+
let idToken;
|
|
346
|
+
logger.log("Verifying OIDC context for publishing from GitHub Actions");
|
|
347
|
+
try {
|
|
348
|
+
const { getIDToken } = await import('@actions/core');
|
|
349
|
+
idToken = await getIDToken("npm:registry.npmjs.org");
|
|
350
|
+
} 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?");
|
|
353
|
+
return void 0;
|
|
354
|
+
}
|
|
355
|
+
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");
|
|
358
|
+
return void 0;
|
|
359
|
+
}
|
|
360
|
+
return exchangeIdToken(idToken, packageName, logger);
|
|
361
|
+
};
|
|
362
|
+
const exchangeGitlabPipelinesToken = async (packageName, logger) => {
|
|
363
|
+
const idToken = process.env.NPM_ID_TOKEN;
|
|
364
|
+
logger.log("Verifying OIDC context for publishing from GitLab Pipelines");
|
|
365
|
+
if (!idToken) {
|
|
366
|
+
return void 0;
|
|
367
|
+
}
|
|
368
|
+
return exchangeIdToken(idToken, packageName, logger);
|
|
369
|
+
};
|
|
370
|
+
const tokenExchange = (pkg, { logger }) => {
|
|
371
|
+
if (!pkg.name || typeof pkg.name !== "string" || pkg.name.trim() === "") {
|
|
372
|
+
logger.log("Invalid package name provided for OIDC token exchange");
|
|
373
|
+
return Promise.resolve(void 0);
|
|
374
|
+
}
|
|
375
|
+
const ciEnv = envCi();
|
|
376
|
+
const ciProviderName = typeof ciEnv === "object" && ciEnv !== null && typeof ciEnv.name === "string" ? ciEnv.name : void 0;
|
|
377
|
+
if (!ciProviderName) {
|
|
378
|
+
logger.log("Unable to detect CI provider for OIDC token exchange");
|
|
379
|
+
return Promise.resolve(void 0);
|
|
380
|
+
}
|
|
381
|
+
if (GITHUB_ACTIONS_PROVIDER_NAME === ciProviderName) {
|
|
382
|
+
return exchangeGithubActionsToken(pkg.name, logger);
|
|
383
|
+
}
|
|
384
|
+
if (GITLAB_PIPELINES_PROVIDER_NAME === ciProviderName) {
|
|
385
|
+
return exchangeGitlabPipelinesToken(pkg.name, logger);
|
|
386
|
+
}
|
|
387
|
+
return Promise.resolve(void 0);
|
|
388
|
+
};
|
|
389
|
+
|
|
390
|
+
const oidcContext = async (registry, pkg, context) => {
|
|
391
|
+
if (OFFICIAL_REGISTRY !== registry) {
|
|
392
|
+
return false;
|
|
393
|
+
}
|
|
394
|
+
try {
|
|
395
|
+
const token = await tokenExchange({ name: pkg.name ?? "" }, context);
|
|
396
|
+
return !!token;
|
|
397
|
+
} catch {
|
|
398
|
+
return false;
|
|
399
|
+
}
|
|
400
|
+
};
|
|
401
|
+
|
|
321
402
|
const nerfDart = (url) => {
|
|
322
403
|
const parsed = new URL(url);
|
|
323
404
|
const from = `${parsed.protocol}//${parsed.host}${parsed.pathname}`;
|
|
@@ -330,7 +411,7 @@ const setNpmrcAuth = async (npmrc, registry, { cwd, env: { NPM_CONFIG_USERCONFIG
|
|
|
330
411
|
const { config, files } = rc("npm", {
|
|
331
412
|
config: NPM_CONFIG_USERCONFIG ?? resolve(cwd, ".npmrc"),
|
|
332
413
|
cwd,
|
|
333
|
-
defaults: { registry:
|
|
414
|
+
defaults: { registry: OFFICIAL_REGISTRY }
|
|
334
415
|
});
|
|
335
416
|
if (Array.isArray(files)) {
|
|
336
417
|
logger.log("Reading npm config from %s", files.join(", "));
|
|
@@ -340,9 +421,12 @@ const setNpmrcAuth = async (npmrc, registry, { cwd, env: { NPM_CONFIG_USERCONFIG
|
|
|
340
421
|
return;
|
|
341
422
|
}
|
|
342
423
|
if (NPM_USERNAME && NPM_PASSWORD && NPM_EMAIL) {
|
|
343
|
-
await writeFile(
|
|
344
|
-
|
|
345
|
-
|
|
424
|
+
await writeFile(
|
|
425
|
+
npmrc,
|
|
426
|
+
`${Object.keys(config).length > 0 ? `${stringify(config)}
|
|
427
|
+
` : ""}${nerfDart(registry)}:_auth = \${LEGACY_TOKEN}
|
|
428
|
+
email = \${NPM_EMAIL}`
|
|
429
|
+
);
|
|
346
430
|
logger.log(`Wrote NPM_USERNAME, NPM_PASSWORD, and NPM_EMAIL to ${npmrc}`);
|
|
347
431
|
} else if (NPM_TOKEN) {
|
|
348
432
|
await writeFile(npmrc, `${Object.keys(config).length > 0 ? `${stringify(config)}
|
|
@@ -354,34 +438,101 @@ email = \${NPM_EMAIL}`);
|
|
|
354
438
|
}
|
|
355
439
|
};
|
|
356
440
|
|
|
357
|
-
const
|
|
358
|
-
const
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
} = context;
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
const semanticError = getError("EINVALIDNPMTOKEN", { registry });
|
|
380
|
-
throw new AggregateError([semanticError], semanticError.message);
|
|
441
|
+
const isConnectionError = (error) => {
|
|
442
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
443
|
+
const errorCode = error?.code || "";
|
|
444
|
+
const isTimedOut = error?.timedOut === true;
|
|
445
|
+
return isTimedOut || errorCode === "ECONNREFUSED" || errorCode === "ETIMEDOUT" || errorMessage.includes("ECONNREFUSED") || errorMessage.includes("ETIMEDOUT") || errorMessage.includes("getaddrinfo ENOTFOUND") || errorMessage.includes("timed out");
|
|
446
|
+
};
|
|
447
|
+
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);
|
|
381
463
|
}
|
|
382
|
-
|
|
383
|
-
|
|
464
|
+
if (whoamiResult.stderr) {
|
|
465
|
+
stderr.write(whoamiResult.stderr);
|
|
466
|
+
}
|
|
467
|
+
} catch (error) {
|
|
468
|
+
if (isConnectionError(error)) {
|
|
469
|
+
const semanticError2 = getError("EINVALIDNPMAUTH", { registry });
|
|
470
|
+
throw new AggregateError([semanticError2], semanticError2.message);
|
|
471
|
+
}
|
|
472
|
+
const semanticError = getError("EINVALIDNPMTOKEN", { registry });
|
|
473
|
+
throw new AggregateError([semanticError], semanticError.message);
|
|
474
|
+
}
|
|
475
|
+
};
|
|
476
|
+
const isAuthErrorMessage = (message) => message.includes("requires you to be logged in") || message.includes("authentication") || message.includes("Unauthorized") || message.includes("401") || message.includes("403");
|
|
477
|
+
const handlePublishError = (error, registry) => {
|
|
478
|
+
if (error instanceof AggregateError) {
|
|
479
|
+
throw error;
|
|
480
|
+
}
|
|
481
|
+
const errorStderrRaw = error?.stderr;
|
|
482
|
+
const errorStderr = Array.isArray(errorStderrRaw) ? errorStderrRaw.join("\n") : errorStderrRaw || "";
|
|
483
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
484
|
+
const combinedMessage = `${errorStderr} ${errorMessage}`;
|
|
485
|
+
if (isConnectionError(error)) {
|
|
486
|
+
const semanticError = getError("EINVALIDNPMAUTH", { registry });
|
|
487
|
+
throw new AggregateError([semanticError], semanticError.message);
|
|
488
|
+
}
|
|
489
|
+
if (isAuthErrorMessage(combinedMessage)) {
|
|
490
|
+
const semanticError = getError("EINVALIDNPMAUTH", { registry });
|
|
491
|
+
throw new AggregateError([semanticError], semanticError.message);
|
|
384
492
|
}
|
|
493
|
+
throw error;
|
|
494
|
+
};
|
|
495
|
+
const verifyAuthContextAgainstCustomRegistry = async (npmrc, registry, context, pkgRoot = ".") => {
|
|
496
|
+
const { cwd, env, logger, stderr, stdout } = context;
|
|
497
|
+
try {
|
|
498
|
+
logger.log(`Running "pnpm publish --dry-run" to verify authentication on registry "${registry}"`);
|
|
499
|
+
const publishArgs = ["publish", pkgRoot, "--dry-run", "--tag=semantic-release-auth-check", "--registry", registry];
|
|
500
|
+
const publishOptions = {
|
|
501
|
+
cwd,
|
|
502
|
+
env: {
|
|
503
|
+
...env,
|
|
504
|
+
NPM_CONFIG_USERCONFIG: npmrc
|
|
505
|
+
},
|
|
506
|
+
preferLocal: true,
|
|
507
|
+
timeout: 5e3
|
|
508
|
+
// 5 second timeout to prevent hanging when registry is unavailable
|
|
509
|
+
};
|
|
510
|
+
const publishResult = await execa("pnpm", publishArgs, publishOptions);
|
|
511
|
+
const stdoutString = Array.isArray(publishResult.stdout) ? publishResult.stdout.join("\n") : publishResult.stdout || "";
|
|
512
|
+
const stderrString = Array.isArray(publishResult.stderr) ? publishResult.stderr.join("\n") : publishResult.stderr || "";
|
|
513
|
+
if (stdoutString) {
|
|
514
|
+
stdout.write(stdoutString);
|
|
515
|
+
}
|
|
516
|
+
if (stderrString) {
|
|
517
|
+
stderr.write(stderrString);
|
|
518
|
+
if (isAuthErrorMessage(stderrString)) {
|
|
519
|
+
const semanticError = getError("EINVALIDNPMAUTH", { registry });
|
|
520
|
+
throw new AggregateError([semanticError], semanticError.message);
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
} catch (error) {
|
|
524
|
+
handlePublishError(error, registry);
|
|
525
|
+
}
|
|
526
|
+
};
|
|
527
|
+
const verifyAuth = async (npmrc, package_, context, pkgRoot) => {
|
|
528
|
+
const registry = getRegistry(package_, context);
|
|
529
|
+
if (package_.name && await oidcContext(registry, package_, context)) {
|
|
530
|
+
return;
|
|
531
|
+
}
|
|
532
|
+
await setNpmrcAuth(npmrc, registry, context);
|
|
533
|
+
const normalizedRegistry = normalizeUrl(registry);
|
|
534
|
+
const normalizedOfficialRegistry = normalizeUrl(OFFICIAL_REGISTRY);
|
|
535
|
+
await (normalizedRegistry === normalizedOfficialRegistry ? verifyAuthContextAgainstRegistry(npmrc, registry, context) : verifyAuthContextAgainstCustomRegistry(npmrc, registry, context, pkgRoot));
|
|
385
536
|
};
|
|
386
537
|
|
|
387
538
|
const isString = (value) => typeof value === "string";
|
|
@@ -440,7 +591,7 @@ const verify = async (pluginConfig, context) => {
|
|
|
440
591
|
const packageJson = await getPackage(pluginConfig, context);
|
|
441
592
|
if (shouldPublish(pluginConfig, packageJson)) {
|
|
442
593
|
const npmrc = getNpmrcPath(context.cwd, context.env);
|
|
443
|
-
await verifyAuth(npmrc, packageJson, context);
|
|
594
|
+
await verifyAuth(npmrc, packageJson, context, pluginConfig.pkgRoot);
|
|
444
595
|
}
|
|
445
596
|
} catch (error) {
|
|
446
597
|
const typedError = error;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@anolilab/semantic-release-pnpm",
|
|
3
|
-
"version": "3.0.0-alpha.
|
|
3
|
+
"version": "3.0.0-alpha.3",
|
|
4
4
|
"description": "Semantic-release plugin to publish a npm package with pnpm.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"anolilab",
|
|
@@ -46,13 +46,15 @@
|
|
|
46
46
|
"dist"
|
|
47
47
|
],
|
|
48
48
|
"dependencies": {
|
|
49
|
-
"@
|
|
49
|
+
"@actions/core": "^1.11.1",
|
|
50
|
+
"@anolilab/rc": "3.0.0-alpha.3",
|
|
50
51
|
"@semantic-release/error": "^4.0.0",
|
|
51
|
-
"@visulima/fs": "^
|
|
52
|
-
"@visulima/package": "^4.
|
|
53
|
-
"@visulima/path": "^
|
|
52
|
+
"@visulima/fs": "^4.0.4",
|
|
53
|
+
"@visulima/package": "^4.1.4",
|
|
54
|
+
"@visulima/path": "^2.0.3",
|
|
55
|
+
"env-ci": "^11.2.0",
|
|
54
56
|
"execa": "^9.6.0",
|
|
55
|
-
"ini": "^
|
|
57
|
+
"ini": "^6.0.0",
|
|
56
58
|
"normalize-url": "^8.1.0",
|
|
57
59
|
"registry-auth-token": "^5.1.0",
|
|
58
60
|
"semver": "^7.7.3"
|