# GitHub Actions

Use this recipe to run semantic-release on GitHub Actions with a secure default setup. It covers authentication options, trusted publishing with OIDC and npm provenance, a minimal release workflow for Node projects, and common pitfalls to avoid when configuring npm and GitHub tokens.

## Quick start

1. Create `.github/workflows/verify-and-release.yml` using the [verify-and-release workflow configuration](#githubworkflowsverify-and-releaseyml-configuration-for-node-projects).
2. [Choose a publishing path](#choose-a-publishing-path).
   - Use **trusted publishing** (recommended): follow [Path A](#path-a-recommended-trusted-publishing-oidc), then review [Trusted publishing and npm provenance](#trusted-publishing-and-npm-provenance).
   - If you cannot use trusted publishing, add `NPM_TOKEN` as a GitHub Actions secret as described in [Environment variables](#environment-variables) and follow [Path B](#path-b-fallback-npm_token).
3. Push a commit to `main`/`master` (or run `workflow_dispatch`) and verify with the [Release readiness checklist](#release-readiness-checklist). For manual runs, see [Trigger semantic-release on demand](#trigger-semantic-release-on-demand).

## Choose a publishing path

### Path A (recommended): Trusted publishing (OIDC)

- No long-lived npm token in GitHub secrets.
- npm provenance is generated automatically.
- Requires `id-token: write` permission in the job that runs semantic-release.
- Requires an npm [Trusted Publisher](https://docs.npmjs.com/trusted-publishers#for-github-actions) configured for the workflow that triggers the run.

### Path B (fallback): `NPM_TOKEN`

- Add `NPM_TOKEN` to repository or organization secrets.
- Keep `GITHUB_TOKEN` for GitHub release notes, issue comments, and pull request comments.
- Use this only when trusted publishing is not available for your setup.

## Node project configuration

[GitHub Actions](https://github.com/features/actions) supports [workflows](https://docs.github.com/en/actions/writing-workflows), allowing you to run tests on multiple Node versions and publish a release only when all tests pass.

### `.github/workflows/verify-and-release.yml` configuration for Node projects

Use this as a starting point for `.github/workflows/verify-and-release.yml`. This example runs verification first and only runs semantic-release if verification succeeds. Make sure to adjust the trigger and branch name as needed. The example below is configured for trusted publishing with OIDC, but it can be easily adapted to use `NPM_TOKEN` instead by removing the `id-token: write` permission and ensuring `NPM_TOKEN` is available in secrets.

:::note
The publish pipeline must run on a [Node version that meets our version requirement](/support/node-version), but can be different than the Node version used in your verification job(s).
:::

```yaml
name: Verify and Release
on:
  push:
    branches:
      - main # or master

permissions:
  contents: read # for checkout

jobs:
  verify:
    name: Verify
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version-file: .nvmrc
      - name: Install dependencies
        run: npm clean-install
      - name: Verify the integrity of provenance attestations and registry signatures for installed dependencies
        run: npm audit signatures
      - name: Run lint, tests, and other verification
        run: npm test

  release:
    name: Release
    runs-on: ubuntu-latest
    needs: verify
    permissions:
      contents: write # to be able to publish a GitHub release
      issues: write # to be able to comment on released issues
      pull-requests: write # to be able to comment on released pull requests
      id-token: write # to enable use of OIDC for trusted publishing and npm provenance
    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: "lts/*"
      - name: Install dependencies
        run: npm clean-install
      - name: Verify the integrity of provenance attestations and registry signatures for installed dependencies
        run: npm audit signatures
      - name: Release
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: npx semantic-release # <- The Run Commnand
```

The workflow above shows a verify-then-release setup with the default semantic-release flow. If your release process needs a different invocation style or non-default behavior (for example, plugins or shareable configurations), refer to [Running semantic-release](/usage/running).

## Environment variables

The [Authentication](/usage/ci-configuration#authentication) environment variables can be configured with [GitHub Actions secrets](https://docs.github.com/en/actions/security-for-github-actions/security-guides/using-secrets-in-github-actions).

If you are not using trusted publishing, an [`NPM_TOKEN`](https://docs.npmjs.com/creating-and-viewing-authentication-tokens) is required to publish a package to the npm registry. GitHub Actions [automatically populates](https://docs.github.com/en/actions/security-for-github-actions/security-guides/automatic-token-authentication) a [`GITHUB_TOKEN`](https://docs.github.com/en/actions/security-for-github-actions/security-guides/automatic-token-authentication) environment variable that can be used in workflows.

## Trusted publishing and npm provenance

For improved security and automation, it is recommended to leverage [trusted publishing](https://docs.npmjs.com/trusted-publishers) through [OpenID Connect (OIDC)](https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect) when publishing to npm from GitHub Actions. GitHub Actions is a [trusted identity provider](https://docs.npmjs.com/trusted-publishers#identity-providers) for npm, enabling configuration of a trust relationship between your GitHub repository and npm so that no long-lived secret (like an `NPM_TOKEN`) is required to publish packages to npm from GitHub Actions. The npm registry [recently increased restrictions for use of long-lived access tokens](https://github.blog/changelog/2025-09-29-strengthening-npm-security-important-changes-to-authentication-and-token-management/), further encouraging trusted publishing as the preferred approach for publishing to npm from GitHub Actions. Enabling trusted publishing requires granting the `id-token: write` permission to the job performing the publish step and [configuring a trust relationship](https://docs.npmjs.com/trusted-publishers#step-1-add-a-trusted-publisher-on-npmjscom) between your GitHub repository and npm.

:::note
When setting up a Trusted Publisher on npmjs for GitHub Actions, it's crucial to specify the workflow file that triggers the release process, not necessarily the one that contains the release logic itself.

If your release job is encapsulated in a [reusable workflow](https://docs.github.com/en/actions/how-tos/reuse-automations/reuse-workflows), the workflow file you must reference is the caller workflow—typically the one triggered by events like `push` or `workflow_dispatch` on your main branch.

This is because npm's Trusted Publisher mechanism authorizes the workflow that initiates the run, not any downstream workflows it invokes.
:::

[npm provenance](https://docs.npmjs.com/generating-provenance-statements) is valuable for increasing supply-chain security for your npm packages. Before trusted publishing was available, generating provenance attestations required configuring your project to enable publishing with provenance. With trusted publishing, npm provenance is automatically generated for packages published to npm from GitHub Actions without any additional configuration.

## Common pitfalls checklist

- Do not set `registry-url` in `actions/setup-node` when using semantic-release for npm publishing. It creates an `.npmrc` that can conflict with semantic-release auth and lead to `EINVALIDNPMTOKEN` errors.
- If you need a custom npm registry, set it in your project `.npmrc`.
- If using npm trusted publishing, keep `id-token: write` permission in the release job.
- If using `NPM_TOKEN`, ensure it is configured as a repository or organization secret.

```yaml
# ❌ Don't do this - can cause conflicts with semantic-release
- name: Setup Node.js
  uses: actions/setup-node@v4
  with:
    node-version: "lts/*"
    registry-url: "https://registry.npmjs.org"

# ✅ Do this instead - let semantic-release handle registry configuration through normal npm conventions
- name: Setup Node.js
  uses: actions/setup-node@v4
  with:
    node-version: "lts/*"
```

## Pushing `package.json` changes to your repository

If you choose to commit changes to your `package.json` [against our recommendation](/support/faq#making-commits-during-the-release-process-adds-significant-complexity), the [`@semantic-release/git`](https://github.com/semantic-release/git) plugin can be used.

:::note
The automatically populated `GITHUB_TOKEN` cannot be used if branch protection is enabled for the target branch. In that case, prefer [GitHub App authentication](https://github.com/actions/create-github-app-token) so the release job can create commits without relying on a long-lived secret.

Personal Access Tokens are not recommended here. They create a broader security risk because a secret exposed to any workflow run can be reused with elevated permissions, which makes branch protection much less effective. Only consider a PAT in tightly controlled environments where the security tradeoff is acceptable.
:::

Example release job using GitHub App authentication with `@semantic-release/git` (assuming a preceding `verify` job):

```yaml
# verify:
#   # ... your existing verify job

release:
  name: Release
  runs-on: ubuntu-latest
  needs: verify
  permissions:
    contents: write
    issues: write
    pull-requests: write
    id-token: write
  steps:
    - name: Create GitHub App token
      id: app-token
      uses: actions/create-github-app-token@v1
      with:
        app-id: ${{ vars.RELEASE_APP_ID }}
        private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }}
    - name: Checkout
      uses: actions/checkout@v4
      with:
        fetch-depth: 0
        token: ${{ steps.app-token.outputs.token }}
    - name: Setup Node.js
      uses: actions/setup-node@v4
      with:
        node-version: "lts/*"
    - name: Install dependencies
      run: npm clean-install
    - name: Release
      env:
        GITHUB_TOKEN: ${{ steps.app-token.outputs.token }}
      run: npx semantic-release
```

## Release readiness checklist

- Your workflow triggers on the branch you release from (`main` or `master`).
- `fetch-depth: 0` is enabled in checkout.
- `GITHUB_TOKEN` is available to semantic-release for the default release flow.
- If you commit files during release on a protected branch (for example with `@semantic-release/git`), GitHub App auth is configured and checkout uses the app token.
- You chose one npm auth path: trusted publishing (`id-token: write`) or `NPM_TOKEN` secret.
- `registry-url` is not set in `actions/setup-node`.
- Your runtime meets the [supported Node version policy](/support/node-version).

## Trigger semantic-release on demand

Use these options when you want to trigger a release outside the normal push-based workflow.

:::caution[Discouraged in most setups]
Manual or API-triggered releases are generally **not recommended** with semantic-release.

semantic-release is designed for continuous delivery: once changes are merged to your primary release branch, releases should happen automatically and predictably from CI. Adding ad-hoc triggers increases operational complexity, weakens release consistency, and can bypass the normal branch-based release flow your team relies on.

Use the options below only for exceptional operational scenarios.
:::

### Using GUI

You can use [`workflow_dispatch`](https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#onworkflow_dispatch) to run the release workflow manually from the GitHub UI.

### Using HTTP

Use the [`repository_dispatch`](https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#repository_dispatch) event to control when to generate a release by making an HTTP request, for example:

```yaml
name: Release
on:
  repository_dispatch:
    types: [semantic-release]
jobs:
# ...
```

To trigger a release, call the GitHub API with a [Personal Access Token](https://docs.github.com/en/rest/authentication/authenticating-to-the-rest-api) stored in the `GITHUB_TOKEN` environment variable:

```
$ curl -v -X POST \
  -H "Accept: application/vnd.github+json" \
  -H "Authorization: Bearer ${GITHUB_TOKEN}" \
  -H "X-GitHub-Api-Version: 2022-11-28" \
  https://api.github.com/repos/[org-name-or-username]/[repository]/dispatches \
  -d '{ "event_type": "semantic-release" }'
```
