Skip to content

Commit d321e46

Browse files
authored
Merge pull request #1017 from semantic-release/beta
2 parents 2d37cf7 + 316ce21 commit d321e46

20 files changed

+612
-79
lines changed

.github/workflows/release.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ name: Release
55
- master
66
- next
77
- beta
8+
- alpha
89
- "*.x"
910
permissions:
1011
contents: read

.github/workflows/test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ jobs:
2323
node-version:
2424
- 22.14.0
2525
- 22
26-
- 24
26+
# - 24
2727
os:
2828
- ubuntu-latest
2929
runs-on: "${{ matrix.os }}"

README.md

Lines changed: 39 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -38,41 +38,63 @@ The plugin can be configured in the [**semantic-release** configuration file](ht
3838

3939
### npm registry authentication
4040

41-
The npm [token](https://docs.npmjs.com/about-access-tokens) authentication configuration is **required** and can be set via [environment variables](#environment-variables).
41+
### Official Registry
4242

43-
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).
43+
When publishing to the [official registry](https://registry.npmjs.org/), it is recommended to publish with authentication intended for automation:
4444

45-
### npm provenance
45+
- 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/),
46+
[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)
47+
- [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).
48+
Because these access tokens expire, rotation will need to be accounted for in this scenario.
4649

47-
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).
50+
> [!NOTE]
51+
> When using trusted publishing, provenance attestations are automatically generated for your packages without requiring provenance to be explicitly enabled.
4852
49-
Since semantic-release wraps the npm publish command, configuring provenance is not exposed directly.
50-
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).
51-
Provenance applies specifically to publishing, so our recommendation is to configure under `publishConfig` within the `package.json`.
53+
#### Trusted publishing from GitHub Actions
5254

53-
#### npm provenance on GitHub Actions
54-
55-
For package provenance to be signed on the GitHub Actions CI the following permission is required
56-
to be enabled on the job:
55+
To leverage trusted publishing and publish with provenance from GitHub Actions, the `id-token: write` permission is required to be enabled on the job:
5756

5857
```yaml
5958
permissions:
60-
id-token: write # to enable use of OIDC for npm provenance
59+
id-token: write # to enable use of OIDC for trusted publishing and npm provenance
6160
```
6261
63-
It's worth noting that if you are using semantic-release to its fullest with a GitHub release, GitHub comments,
62+
It's also worth noting that if you are using semantic-release to its fullest with a GitHub release, GitHub comments,
6463
and other features, then [more permissions are required](https://github.com/semantic-release/github#github-authentication) to be enabled on this job:
6564
6665
```yaml
6766
permissions:
6867
contents: write # to be able to publish a GitHub release
6968
issues: write # to be able to comment on released issues
7069
pull-requests: write # to be able to comment on released pull requests
71-
id-token: write # to enable use of OIDC for npm provenance
70+
id-token: write # to enable use of OIDC for trusted publishing and npm provenance
7271
```
7372
7473
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.
7574
75+
#### Trusted publishing for GitLab Pipelines
76+
77+
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`:
78+
79+
```yaml
80+
id_tokens:
81+
NPM_ID_TOKEN:
82+
aud: "npm:registry.npmjs.org"
83+
```
84+
85+
See the [npm documentation for more details about configuring pipeline details](https://docs.npmjs.com/trusted-publishers#gitlab-cicd-configuration)
86+
87+
#### Unsupported CI providers
88+
89+
Token authentication is **required** and can be set via [environment variables](#environment-variables).
90+
[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.
91+
Because these access tokens expire, rotation will need to be accounted for in your process.
92+
93+
### Alternative Registries
94+
95+
Token authentication is **required** and can be set via [environment variables](#environment-variables).
96+
See the documentation for your registry for details on how to create a token for automation.
97+
7698
### Environment variables
7799

78100
| Variable | Description |
@@ -97,13 +119,14 @@ The plugin uses the [`npm` CLI](https://github.com/npm/cli) which will read the
97119

98120
The [`registry`](https://docs.npmjs.com/misc/registry) can be configured via the npm environment variable `NPM_CONFIG_REGISTRY` and will take precedence over the configuration in `.npmrc`.
99121

100-
The [`registry`](https://docs.npmjs.com/misc/registry) and [`dist-tag`](https://docs.npmjs.com/cli/dist-tag) can be configured under `publishConfig` in the `package.json`:
122+
The [`registry`](https://docs.npmjs.com/misc/registry), [`dist-tag`](https://docs.npmjs.com/cli/dist-tag), and [`provenance`](https://docs.npmjs.com/generating-provenance-statements#using-third-party-package-publishing-tools) can be configured under `publishConfig` in the `package.json`:
101123

102124
```json
103125
{
104126
"publishConfig": {
105127
"registry": "https://registry.npmjs.org/",
106-
"tag": "latest"
128+
"tag": "latest",
129+
"provenance": true
107130
}
108131
}
109132
```

index.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ export async function verifyConditions(pluginConfig, context) {
3030

3131
// Verify the npm authentication only if `npmPublish` is not false and `pkg.private` is not `true`
3232
if (pluginConfig.npmPublish !== false && pkg.private !== true) {
33-
await verifyNpmAuth(npmrc, pkg, context);
33+
await verifyNpmAuth(npmrc, pkg, pluginConfig, context);
3434
}
3535
} catch (error) {
3636
errors.push(...error.errors);
@@ -50,7 +50,7 @@ export async function prepare(pluginConfig, context) {
5050
// Reload package.json in case a previous external step updated it
5151
const pkg = await getPkg(pluginConfig, context);
5252
if (!verified && pluginConfig.npmPublish !== false && pkg.private !== true) {
53-
await verifyNpmAuth(npmrc, pkg, context);
53+
await verifyNpmAuth(npmrc, pkg, pluginConfig, context);
5454
}
5555
} catch (error) {
5656
errors.push(...error.errors);
@@ -72,7 +72,7 @@ export async function publish(pluginConfig, context) {
7272
// Reload package.json in case a previous external step updated it
7373
pkg = await getPkg(pluginConfig, context);
7474
if (!verified && pluginConfig.npmPublish !== false && pkg.private !== true) {
75-
await verifyNpmAuth(npmrc, pkg, context);
75+
await verifyNpmAuth(npmrc, pkg, pluginConfig, context);
7676
}
7777
} catch (error) {
7878
errors.push(...error.errors);
@@ -97,7 +97,7 @@ export async function addChannel(pluginConfig, context) {
9797
// Reload package.json in case a previous external step updated it
9898
pkg = await getPkg(pluginConfig, context);
9999
if (!verified && pluginConfig.npmPublish !== false && pkg.private !== true) {
100-
await verifyNpmAuth(npmrc, pkg, context);
100+
await verifyNpmAuth(npmrc, pkg, pluginConfig, context);
101101
}
102102
} catch (error) {
103103
errors.push(...error.errors);

lib/definitions/constants.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export const OFFICIAL_REGISTRY = "https://registry.npmjs.org/";
2+
3+
export const GITHUB_ACTIONS_PROVIDER_NAME = "GitHub Actions";
4+
export const GITLAB_PIPELINES_PROVIDER_NAME = "GitLab CI/CD";

lib/definitions/errors.js

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,22 @@ Your configuration for the \`pkgRoot\` option is \`${pkgRoot}\`.`,
3737
export function ENONPMTOKEN({ registry }) {
3838
return {
3939
message: "No npm token specified.",
40-
details: `An [npm token](${linkify(
40+
details: `When not publishing through [trusted publishing](https://docs.npmjs.com/trusted-publishers), an [npm token](${linkify(
4141
"README.md#npm-registry-authentication"
4242
)}) must be created and set in the \`NPM_TOKEN\` environment variable on your CI environment.
4343
44-
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 \`${registry}\`.`,
44+
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}\`.`,
45+
};
46+
}
47+
48+
export function EINVALIDNPMAUTH({ registry }) {
49+
return {
50+
message: "Invalid npm authentication.",
51+
details: `The [authentication required to publish](${linkify(
52+
"README.md#npm-registry-authentication"
53+
)}) 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 \`${registry}\`.
54+
55+
Please make sure to set the \`NPM_TOKEN\` environment variable in your CI with the exact value of the npm token.`,
4556
};
4657
}
4758

@@ -52,10 +63,7 @@ export function EINVALIDNPMTOKEN({ registry }) {
5263
"README.md#npm-registry-authentication"
5364
)}) 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 \`${registry}\`.
5465
55-
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 "
56-
Authorization and writes" level.
57-
58-
Please make sure to set the \`NPM_TOKEN\` environment variable in your CI with the exact value of the npm token.`,
66+
Please verify your authentication configuration.`,
5967
};
6068
}
6169

lib/get-registry.js

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,15 @@
11
import path from "path";
22
import rc from "rc";
33
import getRegistryUrl from "registry-auth-token/registry-url.js";
4+
import { OFFICIAL_REGISTRY } from "./definitions/constants.js";
45

56
export default function ({ publishConfig: { registry } = {}, name }, { cwd, env }) {
67
return (
78
registry ||
89
env.NPM_CONFIG_REGISTRY ||
910
getRegistryUrl(
1011
name.split("/")[0],
11-
rc(
12-
"npm",
13-
{ registry: "https://registry.npmjs.org/" },
14-
{ config: env.NPM_CONFIG_USERCONFIG || path.resolve(cwd, ".npmrc") }
15-
)
12+
rc("npm", { registry: OFFICIAL_REGISTRY }, { config: env.NPM_CONFIG_USERCONFIG || path.resolve(cwd, ".npmrc") })
1613
)
1714
);
1815
}

lib/get-release-info.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import normalizeUrl from "normalize-url";
2+
import { OFFICIAL_REGISTRY } from "./definitions/constants.js";
23

34
export default function (
45
{ name },
5-
{ env: { DEFAULT_NPM_REGISTRY = "https://registry.npmjs.org/" }, nextRelease: { version } },
6+
{ env: { DEFAULT_NPM_REGISTRY = OFFICIAL_REGISTRY }, nextRelease: { version } },
67
distTag,
78
registry
89
) {

lib/set-npmrc-auth.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,13 @@ import getAuthToken from "registry-auth-token";
55
import nerfDart from "nerf-dart";
66
import AggregateError from "aggregate-error";
77
import getError from "./get-error.js";
8+
import { OFFICIAL_REGISTRY } from "./definitions/constants.js";
89

910
export default async function (npmrc, registry, { cwd, env: { NPM_TOKEN, NPM_CONFIG_USERCONFIG }, logger }) {
1011
logger.log("Verify authentication for registry %s", registry);
11-
const { configs, ...rcConfig } = rc(
12+
const { configs, config, ...rcConfig } = rc(
1213
"npm",
13-
{ registry: "https://registry.npmjs.org/" },
14+
{ registry: OFFICIAL_REGISTRY },
1415
{ config: NPM_CONFIG_USERCONFIG || path.resolve(cwd, ".npmrc") }
1516
);
1617

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { OFFICIAL_REGISTRY } from "../definitions/constants.js";
2+
import exchangeToken from "./token-exchange.js";
3+
4+
export default async function oidcContextEstablished(registry, pkg, context) {
5+
return OFFICIAL_REGISTRY === registry && !!(await exchangeToken(pkg, context));
6+
}

0 commit comments

Comments
 (0)