Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 72 additions & 17 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,33 +1,88 @@
name: release

on:
workflow_dispatch:
workflow_call:
inputs:
semver:
description: 'The semver to use'
description: 'Release bump type: patch, minor or major.'
required: true
default: 'patch'
pull_request:
types: [closed]
type: string
node-version:
description: 'The Node.js version used to build and publish the package.'
required: false
default: 'lts/*'
type: string
runs-on:
description: 'The runner used to publish the package.'
required: false
default: 'ubuntu-latest'
type: string
environment:
description: 'The deployment environment that gates the release. Configure it with the required reviewers that are allowed to release.'
required: false
default: 'release'
type: string

permissions:
contents: read
permissions: {}

jobs:
release:
runs-on: ubuntu-latest
name: Release
runs-on: ${{ inputs.runs-on }}
environment: ${{ inputs.environment }}
permissions:
contents: write
issues: write
pull-requests: write
id-token: write # required for npm provenance via OIDC
contents: write # required to push the release commit and the tag
env:
BUMP_TYPE: ${{ inputs.semver }}
steps:
- name: Use Node.js
- name: Validate the semver input
run: |
case "$BUMP_TYPE" in
patch|minor|major) ;;
*) echo "::error::Invalid semver input '$BUMP_TYPE': expected patch, minor or major." && exit 1 ;;
esac

- name: Check out repo
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
persist-credentials: true # explicit: the release commit and tag are pushed below

- name: Setup Node ${{ inputs.node-version }}
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
check-latest: true
node-version: lts/*
- uses: nearform-actions/optic-release-automation-action@08642f7889f3bb519fb69ce2c3bf58e4f900c018 # v4.12.4
node-version: ${{ inputs.node-version }}
registry-url: 'https://registry.npmjs.org'
package-manager-cache: false # never use caching in release builds

- name: Bump package version
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
NEW_VERSION=$(npm version "$BUMP_TYPE" --no-git-tag-version)
git add -u # stages package.json and the lockfile, whichever the repo tracks
git commit -m "Bumped v${NEW_VERSION#v}"
echo "NEW_VERSION=${NEW_VERSION#v}" >> "$GITHUB_ENV"
Comment on lines +61 to +67

- name: Install dependencies
run: npm install --ignore-scripts --no-audit --no-fund

- name: Build the release artifacts
run: npm run --if-present release:build

- name: Publish to npm
run: npm publish --provenance --access public

- name: Push release commit
run: git push origin HEAD

- name: Create GitHub release
uses: softprops/action-gh-release@efb35369e0ad2afab669f228072c1b0d510eae64 # v3.0.3
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
semver: ${{ github.event.inputs.semver }}
sync-semver-tags: true
tag_name: v${{ env.NEW_VERSION }}
target_commitish: ${{ github.ref_name }}
name: v${{ env.NEW_VERSION }}
generate_release_notes: true # "What's Changed" from merged PRs + contributors + Full Changelog link
make_latest: true
94 changes: 94 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,100 @@ jobs:
| `lint` | false | boolean | `false` | Set to `true` to run the `lint` script in a repository's `package.json`. |
| `node-versions` | false | string | `'["24", "26"]'` | Provide A JSON array that specifies the Node.js versions on which the job should run. |

## Release workflow

`release.yml` is a reusable workflow that bumps the package version, publishes it
to npm with [provenance](https://docs.npmjs.com/generating-provenance-statements)
and creates the matching GitHub release.

It authenticates to npm through OIDC, so the consuming package **must** be
configured as a [trusted publisher](https://docs.npmjs.com/trusted-publishers) on
npm. No `NPM_TOKEN` secret is needed.

If the repository defines a `release:build` script in its `package.json`, it is
executed after the dependencies are installed and before `npm publish`. Note that
this workflow does **not** run the test suite: the CI workflow is expected to
have already validated the commit being released.

### Usage

Add a `.github/workflows/release.yml` file to your repository:

```yml
name: release

on:
workflow_dispatch:
inputs:
semver:
description: 'Release bump type'
required: true
type: choice
options:
- patch
- minor
- major

permissions: {}

jobs:
release:
permissions:
id-token: write
contents: write
uses: fastify/workflows/.github/workflows/release.yml@v7
with:
semver: ${{ inputs.semver }}
```

Then run it from the *Actions* tab, choosing the bump type.

See [Restricting who can release](#restricting-who-can-release) to limit the
maintainers that are allowed to approve a release.

### Inputs

| Input Name | Required | Type | Default | Description |
| -------------- | -------- | ------ | -------------- | -------------------------------------------------------------------- |
| `semver` | true | string | | The release bump type: `patch`, `minor` or `major`. |
| `node-version` | false | string | `lts/*` | The Node.js version used to build and publish the package. |
| `runs-on` | false | string | `ubuntu-latest`| The runner used to publish the package. |
| `environment` | false | string | `release` | The deployment environment that gates the release. |

### Required permissions

The calling job must grant `id-token: write` (npm provenance via OIDC) and
`contents: write` (to push the release commit and the tag).

### Restricting who can release

GitHub Actions has no per-workflow access control: anyone with write access to a
repository can start a `workflow_dispatch` run. To restrict releases to a
specific set of maintainers, the job runs inside a **deployment environment**
(`release` by default, configurable through the `environment` input).

In the consuming repository, go to *Settings -> Environments*, create the
`release` environment and:

- add the team that is allowed to release (for example `fastify/release`) as a
**required reviewer**, so every run pauses until one of them approves it;
- enable **Prevent self-review**, so the person who started the run cannot
approve their own release;
- optionally limit the **deployment branches** to `main`.

Until the environment is approved no step of the job runs, so an unauthorised
dispatch cannot bump the version nor publish anything.

> [!IMPORTANT]
> An environment that is referenced but never configured is created
> automatically **without any protection rule**. Creating the environment and
> adding the reviewers is a manual, per-repository step.

The same environment name can also be set as the *Environment* field of the npm
[trusted publisher](https://docs.npmjs.com/trusted-publishers) configuration, so
that npm itself rejects any publish that does not come from it.


## Acknowledgments

Past sponsors:
Expand Down