Skip to content
Merged
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
28 changes: 21 additions & 7 deletions .github/workflows/release-pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ jobs:
contains(github.event.pull_request.labels.*.name, format('release-pr{0} next-drupal', ':')) &&
github.event.pull_request.head.repo.full_name == github.event.pull_request.base.repo.full_name
environment: Preview
permissions:
contents: read
pull-requests: write
id-token: write # Required for trusted publishing
steps:
- name: Init
uses: actions/checkout@v4
Expand All @@ -28,21 +32,31 @@ jobs:
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 20
registry-url: https://registry.npmjs.org/
node-version: 24
# Trusted publishing needs npm 11.5.1 or later, which every current Node
# 24 release ships. This asserts it rather than installing a newer npm,
# so nothing unpinned is fetched into a job that can publish.
- name: Verify npm supports trusted publishing
run: |
version="$(npm --version)"
echo "npm $version"
if [ "$(printf '11.5.1\n%s\n' "$version" | sort -V | head -1)" != "11.5.1" ]; then
echo "::error::npm $version is older than the 11.5.1 that trusted publishing requires"
exit 1
fi
- name: Install dependencies
run: yarn install --frozen-lockfile
- name: Determine version
uses: ./.github/version-pr
id: determine-version
env:
PR_NUMBER: ${{ github.event.number }}
# Publishes with an OIDC credential minted for this workflow. npm is
# configured to trust this filename, so renaming this file breaks
# publishing until the trusted publisher entry is updated to match.
- name: Publish to npm
run: |
cd packages/next-drupal
yarn publish --no-git-checks --access public --tag experimental
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
run: npm publish --tag experimental
working-directory: packages/next-drupal
- name: Comment version on PR
env:
VERSION: ${{ steps.determine-version.outputs.version }}
Expand Down
97 changes: 97 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
name: release

on:
push:
branches:
- main

# Default to read-only. Each job widens only what it needs.
permissions:
contents: read

# Never let two releases run at once, and never cancel one partway.
concurrency:
group: release
cancel-in-progress: false

jobs:
# Keeps a release pull request open with the version bump and changelog,
# built from the conventional commits since the last release. Merging that
# pull request is what triggers an actual release: this job then tags it and
# sets releases_created, which gates the publish job below.
release-please:
runs-on: ubuntu-latest
timeout-minutes: 10
if: github.repository == 'chapter-three/next-drupal'
# No permissions block: both steps authenticate with the app token, so the
# job's own GITHUB_TOKEN stays at the workflow default of contents: read.
# The app's permissions are set on the app itself, not here.
outputs:
releases_created: ${{ steps.release.outputs.releases_created }}
steps:
# The default GITHUB_TOKEN cannot open a pull request unless the
# repository allows Actions to create and approve them, and that setting
# is a single switch covering both. Granting approval to every workflow
# would undercut the review requirement on `main`, so this uses an app
# installation token scoped to this repository instead. It also means CI
# runs on the release pull request without someone first releasing the
# held runs a GITHUB_TOKEN-authored pull request produces.
- name: Mint app token
id: app-token
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
with:
app-id: ${{ secrets.RELEASE_APP_ID }}
private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }}

- name: Run release-please
id: release
uses: googleapis/release-please-action@45996ed1f6d02564a971a2fa1b5860e934307cf7 # v5.0.0
with:
token: ${{ steps.app-token.outputs.token }}
config-file: release-please-config.json
manifest-file: .release-please-manifest.json

# Publishes to npm with trusted publishing. There is no npm token: the
# id-token permission lets the runner mint a short-lived OIDC credential
# that npm accepts only from this workflow in this repository. Because the
# repository and the package are both public, npm also records provenance
# automatically, so no --provenance flag is needed.
publish:
needs: release-please
if: needs.release-please.outputs.releases_created == 'true'
runs-on: ubuntu-latest
timeout-minutes: 15
permissions:
contents: read
id-token: write
steps:
- name: Init
uses: actions/checkout@v4
with:
persist-credentials: false

- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 24

# Trusted publishing needs npm 11.5.1 or later, which every current Node
# 24 release ships. This asserts it rather than installing a newer npm,
# so nothing unpinned is fetched into a job that can publish.
- name: Verify npm supports trusted publishing
run: |
version="$(npm --version)"
echo "npm $version"
if [ "$(printf '11.5.1\n%s\n' "$version" | sort -V | head -1)" != "11.5.1" ]; then
echo "::error::npm $version is older than the 11.5.1 that trusted publishing requires"
exit 1
fi

- name: Install dependencies
run: yarn install --frozen-lockfile

# `prepare` (tsup) runs as part of publish, so the tarball is built from
# this checkout rather than from whatever happens to be in dist/.
- name: Publish to npm
run: npm publish
working-directory: packages/next-drupal
3 changes: 3 additions & 0 deletions .release-please-manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"packages/next-drupal": "2.0.1"
}
149 changes: 99 additions & 50 deletions MAINTAINING.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ This document is for maintainers to explain the various procedures for all the p

### `next` (Drupal Module)

While maintaining releases for packages, starters and examples is done with Lerna, releases for Drupal modules are controlled by drupal.org’s infrastructure, so these steps don’t involve Lerna.
The `next-drupal` package is released automatically, and starters and examples are tagged by hand. Releases for Drupal modules are different again: they are controlled by drupal.org’s infrastructure, so none of that applies here.

1. Optionally, create a new branch on drupal.org.

Expand Down Expand Up @@ -34,85 +34,134 @@ While maintaining releases for packages, starters and examples is done with Lern

### `next-drupal` (NPM package)

Since we are using semantic commits, Lerna is able to read the git commits since the last release to auto-generate a CHANGELOG and to determine if the next release should be a:
Releases are automated. You do not run `npm publish` or `lerna publish`, and you
do not need an npm token on your machine.

- major version bump (e.g. 1.0.0 to 2.0.0). This will happen when Lerna finds a `BREAKING CHANGE` commit message.
- minor version bump (e.g. 1.0.0 to 1.1.0). This will happen when Lerna finds a `feat` commit message.
- patch version bump (e.g. 1.0.0 to 1.0.1). This is the default version bump for bug fixes, etc.
- prerelease version bump (e.g. 1.0.0-alpha.0 to 1.0.0-alpha.1)
#### How it works

We’ll be using Lerna’s `--no-push` flag so that Lerna does not push git tags and commits automatically. This allows us to delete any commits and tags locally if we make a mistake.
[release-please](https://github.com/googleapis/release-please) reads the
conventional commits merged to `main` and keeps a pull request open titled
something like `chore(main): release next-drupal 2.1.0`. That pull request holds
the version bump and the generated `CHANGELOG.md`. It updates itself as more
commits land.

1. **Tag a new release**
The commit type decides the version:

- **to make the next logical semantic version**, run:
- `feat` gives a minor bump (2.0.1 to 2.1.0).
- `fix`, `perf` and similar give a patch bump (2.0.1 to 2.0.2).
- A `BREAKING CHANGE:` footer gives a major bump (2.0.1 to 3.0.0). Add this only
when you mean it.
- Other types (`chore`, `docs`, `ci`, `test`) do not trigger a release on their
own.

```
npx lerna version --no-push
```
Merging the release pull request tags the release and publishes to npm.

- **to make a new alpha prerelease version:**
#### Making a release

If the current version is not a prerelease version, you’ll need to explicitly tell Lerna that the next version should be an alpha release with:
1. **Check the release pull request.** Confirm the version is what you expect
and the changelog reads well. Edit the changelog in the pull request if a
commit message produced an unhelpful entry.

```
npx lerna version --conventional-prerelease --no-push
```
2. **Merge it.** Squash and merge, like any other pull request. `main` requires
a review, and the release pull request is authored by a bot, so you can
approve it yourself.

When creating a new prerelease version of `next-drupal`, Lerna will automatically determine if it needs to be a `premajor` (2.0.0-alpha.0), `preminor` (1.1.0-alpha.0), or `prepatch` (1.0.1-alpha.0) version.
3. **Watch the `release` workflow.** Merging tags the release and runs the
publish job.

- **to make a new beta prerelease version:**
4. **Confirm.** Check the "Current Tags" section of
[next-drupal's npm page](https://www.npmjs.com/package/next-drupal?activeTab=versions)
and confirm `latest` points at the new version.

If the current version is not a beta version, you’ll need to explicitly tell Lerna that the next version should be a beta release with:
That is the whole process. There is no local step.

```
npx lerna version --conventional-prerelease --preid beta --no-push
```
#### Prereleases

- **to make a new regular version from a prerelease version:**
To cut a prerelease, add a `Release-As:` footer naming the exact version to a
commit on `main`:

If the current version is a prerelease version, you’ll need to explicitly tell Lerna that the next version should no longer be a prerelease version with:
```
Release-As: 2.2.0-alpha.0
```

```
npx lerna version --conventional-graduate --no-push
```
release-please then proposes that version instead of the one it calculated. The
starters and the Docs sections below still refer to prereleases and to a
`canary` branch; those steps are about their own git repositories and release
notes, not about publishing this package.

**Confirm changes**
#### Publishing credentials

When Lerna asks “Are you sure you want to create these versions?”, carefully check if the versions listed are the ones you expect.
Publishing uses [npm trusted publishing](https://docs.npmjs.com/trusted-publishers/).
The runner mints a short-lived OIDC credential that npm accepts only from
`.github/workflows/release.yml` in this repository. No npm token is stored, so
there is nothing to rotate and nothing to leak.

2. **Push git changes** with:
Two consequences worth knowing:

```
git push
git push --tags
```
- **Renaming `release.yml` breaks publishing.** npm matches on the workflow
filename. If you rename or move it, update the trusted publisher entry in the
package settings on npmjs.com first.
- **Publishing from a laptop is not supported.** The package rejects token
authentication, so release through the pull request.

3. **Publish the release**
Because both the repository and the package are public, npm records
[provenance](https://docs.npmjs.com/generating-provenance-statements)
automatically. No flag is needed.

Ensure you have authenticated with npmjs.com using `npm login`.
#### First-time setup

Then, while your local git working area is clean of changes and `HEAD` is pointing to the commit created in step 1, have Lerna build, prepare, package and publish your release.
Only needed when wiring this up from scratch, or when rebuilding it after the
app or the npm settings are lost. Everything here is configured outside the
repository, which is why none of it is visible in the codebase.

For a new prerelease version, specify the `canary` dist-tag with:
On npmjs.com, under the package's Settings, add two trusted publisher entries
for the `chapter-three/next-drupal` repository:

```
npx lerna publish --dist-tag canary from-git
```
| Workflow filename | Environment |
| ----------------- | ----------- |
| `release.yml` | none |
| `release-pr.yml` | `Preview` |

Otherwise, use:
The environment field must match the workflow exactly, so `release.yml` is left
blank and `release-pr.yml` is set to `Preview`. Also enable "Require two-factor
authentication and disallow tokens" under Publishing access, which is what makes
the workflows the only way to publish.

```
npx lerna publish from-git
```
On GitHub, create an app owned by the `chapter-three` organization and install
it on this repository alone. It needs three repository permissions: **Contents**
read and write, **Issues** read and write, and **Pull requests** read and write.
Issues is not optional: release-please applies its `autorelease` labels through
the issues API, so without it a release tags and then fails.

Add the app's ID and private key as the repository secrets `RELEASE_APP_ID` and
`RELEASE_APP_PRIVATE_KEY`. Add app managers so more than one person can
administer it.

Leave "Allow GitHub Actions to create and approve pull requests" disabled. The
app exists so that this setting can stay off: it is a single switch that also
grants approval, which would let automation satisfy the review requirement on
`main`.

#### Experimental releases from a pull request

Maintainers will need permission to publish to `next-drupal` on npmjs.com. http://npmjs.com/package/next-drupal
To publish a throwaway version from an open pull request, for example so another
project can test a fix before it is released, add the label
`release-pr: next-drupal` to the pull request. The `release-pr` workflow
publishes under the `experimental` dist-tag and comments the install command on
the pull request.

4. **Confirm the release**
This only works for branches in this repository, not forks.

Look at the “Current Tags” section of [next-drupal’s npmjs page](https://www.npmjs.com/package/next-drupal?activeTab=versions) and confirm that the newest release is listed and that the `latest` tag and the `canary` tag point at the expected versions.
#### If the release does not happen

For more information, see Lerna’s [version docs](https://github.com/lerna/lerna/tree/main/libs/commands/version) and [publish docs](https://github.com/lerna/lerna/tree/main/libs/commands/publish).
- **No release pull request appeared.** Nothing since the last release changed a
releasable file, or every commit was a type that does not trigger a release.
Check that the commit touched `packages/next-drupal`.
- **The publish job failed.** Read the job log rather than guessing. If npm
rejects the credential, the likely cause is that `release.yml` was renamed, or
the trusted publisher entry on npmjs.com was deleted or recreated.
- **The version is wrong.** The version comes from commit types. To force a
specific version, use the `Release-As:` footer described under "Prereleases".

### Examples

Expand Down
5 changes: 0 additions & 5 deletions lerna.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,6 @@
},
"run": {
"loglevel": "verbose"
},
"publish": {
"allowBranch": "main",
"conventionalCommits": true,
"message": "chore(release): publish"
}
}
}
12 changes: 12 additions & 0 deletions release-please-config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json",
"include-component-in-tag": true,
"include-v-in-tag": false,
"tag-separator": "@",
"packages": {
"packages/next-drupal": {
"release-type": "node",
"changelog-path": "CHANGELOG.md"
}
}
}
Loading