From f0fea368f25e15d7246f5309c07a00345ef81aa5 Mon Sep 17 00:00:00 2001 From: Grzegorz Ziolkowski Date: Mon, 7 Mar 2022 16:58:56 +0100 Subject: [PATCH 1/4] Packages: Apply changes to npm publishing workflow --- bin/plugin/commands/packages.js | 9 ++++++--- docs/contributors/code/release.md | 32 +++++++++++++------------------ 2 files changed, 19 insertions(+), 22 deletions(-) diff --git a/bin/plugin/commands/packages.js b/bin/plugin/commands/packages.js index 34f0c9ef864a45..d90a0d66e06f37 100644 --- a/bin/plugin/commands/packages.js +++ b/bin/plugin/commands/packages.js @@ -466,9 +466,12 @@ async function runPackagesRelease( config, customMessages ) { let pluginReleaseBranch; if ( [ 'latest', 'next' ].includes( config.releaseType ) ) { - pluginReleaseBranch = await findPluginReleaseBranchName( - config.gitWorkingDirectoryPath - ); + pluginReleaseBranch = + config.releaseType === 'next' + ? 'trunk' + : await findPluginReleaseBranchName( + config.gitWorkingDirectoryPath + ); await runNpmReleaseBranchSyncStep( pluginReleaseBranch, config ); } else { await checkoutNpmReleaseBranch( config ); diff --git a/docs/contributors/code/release.md b/docs/contributors/code/release.md index 53f83b40c84409..f78ed28465553d 100644 --- a/docs/contributors/code/release.md +++ b/docs/contributors/code/release.md @@ -192,27 +192,21 @@ The Gutenberg repository mirrors the [WordPress SVN repository](https://make.wor - The `wp/trunk` branch contains the same version of packages published to npm with the `latest` distribution tag. The WordPress core consumes those packages directly in the `trunk` branch and uses them for public releases. - The `wp/next` branch contains the same version of packages published to npm with the `next` distribution tag. Projects should use those packages for development purposes only. -- A Gutenberg branch targeting a specific WordPress major release (including its further minor increments) is created (example `wp/5.2`) based on the `wp/trunk` Gutenberg branch when the corresponding WordPress release branch is created. (This usually happens when the first `RC` of the next WordPress major version is released). +- A Gutenberg branch targeting a specific WordPress major release (including its further minor increments) is created (example `wp/5.2`) based on the `wp/trunk` Gutenberg branch when the corresponding WordPress release branch is created. (This usually happens when the `beta` or `RC` of the next WordPress major version is released). Release types and their schedule: -- [Synchronizing WordPress Trunk](#synchronizing-wordpress-trunk) (`latest` dist tag) – when there is no "feature-freeze" mode in WordPress Core, publishing happens every two weeks based on the newly created RC1 version of the Gutenberg plugin. Otherwise, only bug fixes get manually included and published to npm before every next beta and RC version of the following WordPress release. -- [Minor WordPress Releases](#minor-wordpress-releases) (`patch` dist tag) – only when bug fixes or security releases need to be backported into WordPress Core. -- [Development Releases](#development-releases) (`next` dist tag) – when there is a "feature-freeze" mode in WordPress Core, publishing can still happen every two weeks based on the new RC1 version of the Gutenberg plugin. It is also possible to perform development releases at any time when there is a need to test the upcoming changes. +- [Synchronizing Gutenberg Plugin](#synchronizing-gutenberg-plugin) (`latest` dist tag) – publishing happens every two weeks based on the newly created RC1 version of the Gutenberg plugin. +- [WordPress Releases](#wordpress-releases) (`patch` dist tag) – only when bug or security fixes need to be backported into WordPress Core. +- [Development Releases](#development-releases) (`next` dist tag) – it is also possible to perform development releases at any time when there is a need to test the upcoming changes. -There is also an option to perform [Standalone Bugfix Package Releases](#standalone-bugfix-package-releases) at will. It should be reserved only for critical bug fixes or security releases that must be published to _npm_ outside of a regular WordPress release cycle. +There is also an option to perform [Standalone Bugfix Package Releases](#standalone-bugfix-package-releases) at will. It should be reserved only for critical bug fixes or security releases that must be published to _npm_ outside of regular cycles. -### Synchronizing WordPress Trunk +### Synchronizing Gutenberg Plugin -For each Gutenberg plugin release, WordPress trunk should be synchronized. +For each Gutenberg plugin RC1 release npm packages should be synchronized. This is automated with the [Release Tool](#release-tool) that handles releases for the Gutenberg plugin. -Note that the WordPress `trunk` branch can be closed or in "feature-freeze" mode. Usually, feature freeze in WordPress Core happens about 2 weeks before Beta 1 and remains in effect until RC1 when the `trunk` gets branched. During this period, the Gutenberg plugin releases should not be synchronized with WordPress Core. - -A different person usually synchronizes the WordPress `trunk` branch and publishes the npm packages. Therefore, you typically shouldn't need to worry about handling this for the normal plugin release process. However, if you are still unsure, ask in [the #core-editor Slack channel](https://wordpress.slack.com/archives/C02QB2JS7). - -The process has three steps: 1) update the `wp/trunk` branch within the Gutenberg repo 2) publish the new package versions to npm 3) update the WordPress `trunk` branch. - -All steps are automated via `./bin/plugin/cli.js npm-latest` command. You only have to run the command, but, for the record, the manual process would look very close to the following steps: +The process has three steps: 1) update the `wp/trunk` branch within the Gutenberg repo 2) publish the new package versions to npm 3) update the WordPress `trunk` branch. All steps are automated via `./bin/plugin/cli.js npm-latest` command. For the record, the manual process would look very close to the following steps: 1. Ensure the WordPress `trunk` branch is open for enhancements. 2. Get the last published Gutenberg release branch with `git fetch`. @@ -229,11 +223,11 @@ All steps are automated via `./bin/plugin/cli.js npm-latest` command. You only h - If the publishing process ends up incomplete (perhaps because it timed-out or an bad OTP was introduce) you can resume it via [`lerna publish from-package`](https://github.com/lerna/lerna/tree/HEAD/commands/publish#bump-from-package). 11. Finally, now that the npm packages are published, cherry-pick the commits created by lerna ("Publish" and the CHANGELOG update) into the `trunk` branch of Gutenberg. -### Minor WordPress Releases +### WordPress Releases -The following workflow is needed when bug fixes or security releases need to be backported into WordPress Core. This can happen in a few use-cases: +The following workflow is needed when bug or security fixes need to be backported into WordPress Core. This can happen in a few use-cases: -- During the `RC` period of the WordPress release cycle when `wp/X.Y` (example `wp/5.7`) branch for the release is already present. +- During the `beta` and `RC` periods of the WordPress release cycle when `wp/X.Y` (example `wp/5.7`) branch for the release is already present. - For WordPress minor releases and WordPress security releases (example `5.1.1`). 1. Check out the relevant WordPress major branch (If the minor release is 5.2.1, check out `wp/5.2`). @@ -256,7 +250,7 @@ Now, the npm packages should be ready and a patch can be created and committed i ### Standalone Bugfix Package Releases -The following workflow is needed when packages require bug fixes or security releases to be published to _npm_ outside of a regular WordPress release cycle. +The following workflow is needed when packages require bug fixes or security releases to be published to _npm_ outside of a regular release cycle. Note: Both the `trunk` and `wp/trunk` branches are restricted and can only be _pushed_ to by the Gutenberg Core team. @@ -313,7 +307,7 @@ The good news is that the rest of the process is automated with `./bin/plugin/cl ### Development Releases -As noted in the [Synchronizing WordPress Trunk](#synchronizing-wordpress-trunk) section, the WordPress trunk branch can be closed or in "feature-freeze" mode. Usually, this happens during the WordPress ongoing release cycle, which takes several weeks. It means that packages don't get any enhancements and new features during the ongoing WordPress major release process. Another type of release is available to address the limitation mentioned earlier and unblock ongoing development for projects that depend on WordPress packages. We are taking advantage of [package distribution tags](https://docs.npmjs.com/cli/v7/commands/npm-dist-tag) that make it possible to consume the future version of the codebase according to npm guidelines: +As noted in the [Synchronizing Gutenberg Plugin](#synchronizing-gutenberg-plugin) section, packages publishing happens every two weeks from the `wp/trunk` branch. It's also possible to use the development release to test the upcoming changes present in the `trunk` branch at any time. We are taking advantage of [package distribution tags](https://docs.npmjs.com/cli/v7/commands/npm-dist-tag) that make it possible to consume the future version of the codebase according to npm guidelines: > By default, the `latest` tag is used by npm to identify the current version of a package, and `npm install ` (without any `@` or `@` specifier) installs the `latest` tag. Typically, projects only use the `latest` tag for stable release versions, and use other tags for unstable versions such as prereleases. From f74fdf35567bd4e1b9ead1c8b25dea95397d96ec Mon Sep 17 00:00:00 2001 From: Grzegorz Ziolkowski Date: Tue, 8 Mar 2022 07:46:02 +0100 Subject: [PATCH 2/4] Extend plugin release workflow with npm publishing --- .github/workflows/static-checks.yml | 35 ++++++++++++++ .../upload-release-to-plugin-repo.yml | 31 +++++++++++++ bin/plugin/cli.js | 7 +++ bin/plugin/commands/packages.js | 46 +++++++++++++------ 4 files changed, 104 insertions(+), 15 deletions(-) diff --git a/.github/workflows/static-checks.yml b/.github/workflows/static-checks.yml index 299c3656a25ed0..6a43ff4e18b935 100644 --- a/.github/workflows/static-checks.yml +++ b/.github/workflows/static-checks.yml @@ -16,6 +16,41 @@ concurrency: cancel-in-progress: true jobs: + test-npm-publish: + name: Publish WordPress packages to npm (Work In Progress) + runs-on: ubuntu-latest + steps: + - name: Checkout (for CLI) + uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4 + with: + path: main + + - name: Checkout (for publishing) + uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4 + with: + path: publish + ref: trunk + token: ${{ secrets.GUTENBERG_TOKEN }} + + - name: Configure git user name and email (for publishing) + run: | + cd publish + git config user.name "Gutenberg Repository Automation" + git config user.email gutenberg@wordpress.org + + - name: Setup Node + uses: actions/setup-node@38d90ce44d5275ad62cc48384b3d8a58c500bb5f # v2.2.2 + with: + node-version: 14 + registry-url: 'https://registry.npmjs.org' + + - name: Publish packages to npm + run: | + cd main + npm ci + ./bin/plugin/cli.js npm-next --ci --repository-path ../publish + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} check: name: All runs-on: ubuntu-latest diff --git a/.github/workflows/upload-release-to-plugin-repo.yml b/.github/workflows/upload-release-to-plugin-repo.yml index 59cfed85c1b8f5..3248803a1cb4a9 100644 --- a/.github/workflows/upload-release-to-plugin-repo.yml +++ b/.github/workflows/upload-release-to-plugin-repo.yml @@ -94,6 +94,37 @@ jobs: name: changelog ${{ matrix.label }} path: ./changelog.txt + npm-publish: + name: Publish WordPress packages to npm (Work In Progress) + runs-on: ubuntu-latest + needs: update-changelog + if: ${{ github.event.release.prerelease && endsWith( github.event.release.tag_name, '-rc.1' ) && github.event.release.assets[0] }} + steps: + - name: Checkout + uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4 + with: + token: ${{ secrets.GUTENBERG_TOKEN }} + + - name: Configure git user name and email + run: | + git config user.name "Gutenberg Repository Automation" + git config user.email gutenberg@wordpress.org + + - name: Setup Node + uses: actions/setup-node@38d90ce44d5275ad62cc48384b3d8a58c500bb5f # v2.2.2 + with: + node-version: 14 + cache: npm + registry-url: 'https://registry.npmjs.org' + + - name: Install dependencies + run: npm ci + + - name: Publish packages to npm + run: npm whoami + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + upload: name: Upload Gutenberg Plugin runs-on: ubuntu-latest diff --git a/bin/plugin/cli.js b/bin/plugin/cli.js index 2227a507e45dca..5ca74a5d9b39da 100755 --- a/bin/plugin/cli.js +++ b/bin/plugin/cli.js @@ -29,12 +29,17 @@ const { runPerformanceTests } = require( './commands/performance' ); const semverOption = [ '--semver ', 'Semantic Versioning', 'patch' ]; const ciOption = [ '-c, --ci', 'Run in CI (non interactive)' ]; +const repositoryPathOption = [ + '--repository-path ', + 'Relative path to the git repository.', +]; program .command( 'publish-npm-packages-latest' ) .alias( 'npm-latest' ) .option( ...semverOption ) .option( ...ciOption ) + .option( ...repositoryPathOption ) .description( 'Publishes packages to npm (latest dist-tag, production version)' ) @@ -45,6 +50,7 @@ program .alias( 'npm-bugfix' ) .option( ...semverOption ) .option( ...ciOption ) + .option( ...repositoryPathOption ) .description( 'Publishes bugfixes for packages to npm (latest dist-tag, production version)' ) @@ -55,6 +61,7 @@ program .alias( 'npm-next' ) .option( ...semverOption ) .option( ...ciOption ) + .option( ...repositoryPathOption ) .description( 'Publishes packages to npm (next dist-tag, prerelease version)' ) diff --git a/bin/plugin/commands/packages.js b/bin/plugin/commands/packages.js index d90a0d66e06f37..1b070d4eb0ae1f 100644 --- a/bin/plugin/commands/packages.js +++ b/bin/plugin/commands/packages.js @@ -20,6 +20,7 @@ const { runCleanLocalFoldersStep, } = require( './common' ); const git = require( '../lib/git' ); +const { join } = require( 'path' ); /** * Release type names. @@ -36,8 +37,9 @@ const git = require( '../lib/git' ); /** * @typedef WPPackagesCommandOptions * - * @property {SemVer} [semver] The selected semantic versioning. Defaults to `patch`. - * @property {boolean} [ci] Disables interactive mode when executed in CI mode. + * @property {boolean} [ci] Disables interactive mode when executed in CI mode. + * @property {string} [repositoryPath] Relative path to the git repository. + * @property {SemVer} [semver] The selected semantic versioning. Defaults to `patch`. */ /** @@ -331,11 +333,18 @@ async function publishPackagesToNpm( { cwd: gitWorkingDirectoryPath, } ); + log( '>> Current npm user:' ); + await command( 'npm whoami', { + cwd: gitWorkingDirectoryPath, + stdio: 'inherit', + } ); + const beforeCommitHash = await git.getLastCommitHash( gitWorkingDirectoryPath ); const yesFlag = interactive ? '' : '--yes'; + const noVerifyAccessFlag = interactive ? '' : '--no-verify-access'; if ( releaseType === 'next' ) { log( '>> Bumping version of public packages changed since the last release.' @@ -351,7 +360,7 @@ async function publishPackagesToNpm( { log( '>> Publishing modified packages to npm.' ); await command( - `npx lerna publish from-package --dist-tag next ${ yesFlag }`, + `npx lerna publish from-package --dist-tag next ${ yesFlag } ${ noVerifyAccessFlag }`, { cwd: gitWorkingDirectoryPath, stdio: 'inherit', @@ -360,7 +369,7 @@ async function publishPackagesToNpm( { } else if ( releaseType === 'bugfix' ) { log( '>> Publishing modified packages to npm.' ); await command( - `npx lerna publish ${ minimumVersionBump } --no-private ${ yesFlag }`, + `npx lerna publish ${ minimumVersionBump } --no-private ${ yesFlag } ${ noVerifyAccessFlag }`, { cwd: gitWorkingDirectoryPath, stdio: 'inherit', @@ -379,10 +388,13 @@ async function publishPackagesToNpm( { ); log( '>> Publishing modified packages to npm.' ); - await command( `npx lerna publish from-package ${ yesFlag }`, { - cwd: gitWorkingDirectoryPath, - stdio: 'inherit', - } ); + await command( + `npx lerna publish from-package ${ yesFlag } ${ noVerifyAccessFlag }`, + { + cwd: gitWorkingDirectoryPath, + stdio: 'inherit', + } + ); } const afterCommitHash = await git.getLastCommitHash( @@ -458,11 +470,14 @@ async function runPackagesRelease( config, customMessages ) { await askForConfirmation( 'Ready to go?' ); } - // Cloning the Git repository. - config.gitWorkingDirectoryPath = await runGitRepositoryCloneStep( - config.abortMessage - ); - const temporaryFolders = [ config.gitWorkingDirectoryPath ]; + const temporaryFolders = []; + if ( ! config.gitWorkingDirectoryPath ) { + // Cloning the Git repository. + config.gitWorkingDirectoryPath = await runGitRepositoryCloneStep( + config.abortMessage + ); + temporaryFolders.push( config.gitWorkingDirectoryPath ); + } let pluginReleaseBranch; if ( [ 'latest', 'next' ].includes( config.releaseType ) ) { @@ -513,10 +528,11 @@ async function runPackagesRelease( config, customMessages ) { * * @return {WPPackagesConfig} The config object. */ -function getConfig( releaseType, { ci, semver } ) { +function getConfig( releaseType, { ci, repositoryPath, semver } ) { return { abortMessage: 'Aborting!', - gitWorkingDirectoryPath: process.cwd(), + gitWorkingDirectoryPath: + repositoryPath && join( process.cwd(), repositoryPath ), interactive: ! ci, minimumVersionBump: semver, npmReleaseBranch: releaseType === 'next' ? 'wp/next' : 'wp/trunk', From d7c77dc1409ce8c58325c831d38184e0685bb3c9 Mon Sep 17 00:00:00 2001 From: Grzegorz Ziolkowski Date: Wed, 9 Mar 2022 12:25:55 +0100 Subject: [PATCH 3/4] Set test npm publishing workflow to trigger when Gutenberg RC1 is out --- .github/workflows/static-checks.yml | 35 ------------------- .../upload-release-to-plugin-repo.yml | 26 +++++++++----- 2 files changed, 17 insertions(+), 44 deletions(-) diff --git a/.github/workflows/static-checks.yml b/.github/workflows/static-checks.yml index 6a43ff4e18b935..299c3656a25ed0 100644 --- a/.github/workflows/static-checks.yml +++ b/.github/workflows/static-checks.yml @@ -16,41 +16,6 @@ concurrency: cancel-in-progress: true jobs: - test-npm-publish: - name: Publish WordPress packages to npm (Work In Progress) - runs-on: ubuntu-latest - steps: - - name: Checkout (for CLI) - uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4 - with: - path: main - - - name: Checkout (for publishing) - uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4 - with: - path: publish - ref: trunk - token: ${{ secrets.GUTENBERG_TOKEN }} - - - name: Configure git user name and email (for publishing) - run: | - cd publish - git config user.name "Gutenberg Repository Automation" - git config user.email gutenberg@wordpress.org - - - name: Setup Node - uses: actions/setup-node@38d90ce44d5275ad62cc48384b3d8a58c500bb5f # v2.2.2 - with: - node-version: 14 - registry-url: 'https://registry.npmjs.org' - - - name: Publish packages to npm - run: | - cd main - npm ci - ./bin/plugin/cli.js npm-next --ci --repository-path ../publish - env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} check: name: All runs-on: ubuntu-latest diff --git a/.github/workflows/upload-release-to-plugin-repo.yml b/.github/workflows/upload-release-to-plugin-repo.yml index 3248803a1cb4a9..4b7863368eb9b8 100644 --- a/.github/workflows/upload-release-to-plugin-repo.yml +++ b/.github/workflows/upload-release-to-plugin-repo.yml @@ -95,18 +95,27 @@ jobs: path: ./changelog.txt npm-publish: - name: Publish WordPress packages to npm (Work In Progress) + name: Publish WordPress packages to npm runs-on: ubuntu-latest needs: update-changelog if: ${{ github.event.release.prerelease && endsWith( github.event.release.tag_name, '-rc.1' ) && github.event.release.assets[0] }} steps: - - name: Checkout + - name: Checkout (for CLI) uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4 with: + path: main + ref: trunk + + - name: Checkout (for publishing) + uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4 + with: + path: publish + ref: trunk token: ${{ secrets.GUTENBERG_TOKEN }} - - name: Configure git user name and email + - name: Configure git user name and email (for publishing) run: | + cd publish git config user.name "Gutenberg Repository Automation" git config user.email gutenberg@wordpress.org @@ -114,14 +123,13 @@ jobs: uses: actions/setup-node@38d90ce44d5275ad62cc48384b3d8a58c500bb5f # v2.2.2 with: node-version: 14 - cache: npm registry-url: 'https://registry.npmjs.org' - - name: Install dependencies - run: npm ci - - - name: Publish packages to npm - run: npm whoami + - name: Publish packages to npm ("next" dist-tag) + run: | + cd main + npm ci + ./bin/plugin/cli.js npm-next --semver minor --ci --repository-path ../publish env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} From c08c75480a201182da42509974e56b60db532124 Mon Sep 17 00:00:00 2001 From: Grzegorz Ziolkowski Date: Wed, 9 Mar 2022 15:19:36 +0100 Subject: [PATCH 4/4] Update documentation with the reasoning behind Gutenberg plugin sync --- docs/contributors/code/release.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/contributors/code/release.md b/docs/contributors/code/release.md index f78ed28465553d..1db37c7c6c23ba 100644 --- a/docs/contributors/code/release.md +++ b/docs/contributors/code/release.md @@ -204,9 +204,11 @@ There is also an option to perform [Standalone Bugfix Package Releases](#standal ### Synchronizing Gutenberg Plugin -For each Gutenberg plugin RC1 release npm packages should be synchronized. This is automated with the [Release Tool](#release-tool) that handles releases for the Gutenberg plugin. +For each Gutenberg plugin release, we also publish to npm an updated version of WordPress packages. This is automated with the [Release Tool](#release-tool) that handles releases for the Gutenberg plugin. -The process has three steps: 1) update the `wp/trunk` branch within the Gutenberg repo 2) publish the new package versions to npm 3) update the WordPress `trunk` branch. All steps are automated via `./bin/plugin/cli.js npm-latest` command. For the record, the manual process would look very close to the following steps: +We deliberately update the `wp/trunk` branch within the Gutenberg repo with the content from the Gutenberg release `release/*` (example `release/12.7`) branch at the time of the Gutenberg RC1 release. This is done to ensure that the `wp/trunk` branch is as close as possible to the latest version of the Gutenberg plugin. It also practically removes the chances of conflicts while backporting to `trunk` commits with updates applied during publishing to `package.json` and `CHANGELOG.md` files. In the past, we had many issues in that aspect when doing npm publishing after the regular Gutenberg release a week later. When publishing the new package versions to npm, we pick at least the `minor` version bump to account for future bugfix or security releases. + +All steps are automated via `./bin/plugin/cli.js npm-latest` command. For the record, the manual process would look very close to the following steps: 1. Ensure the WordPress `trunk` branch is open for enhancements. 2. Get the last published Gutenberg release branch with `git fetch`. @@ -214,7 +216,7 @@ The process has three steps: 1) update the `wp/trunk` branch within the Gutenber 4. Remove all files from the current branch: `git rm -r .`. 5. Check out all the files from the release branch: `git checkout release/x.x -- .`. 6. Commit all changes to the `wp/trunk` branch with `git commit -m "Merge changes published in the Gutenberg plugin vX.X release"` and push to the repository. -7. Update the `CHANGELOG.md` files of the packages with the new publish version calculated and commit to the `wp/trunk` branch. Assuming the package versions are written using this format `major.minor.patch`, make sure to bump at least the `minor` version number after every major WordPress release. For example, if the CHANGELOG of the package to be released indicates that the next unreleased version is `5.6.1`, choose `5.7.0` as a version in case of `minor` version. This is important as the patch version numbers should be reserved in case bug fixes are needed for a minor WordPress release (see below). +7. Update the `CHANGELOG.md` files of the packages with the new publish version calculated and commit to the `wp/trunk` branch. Assuming the package versions are written using this format `major.minor.patch`, make sure to bump at least the `minor` version bumps gets applied. For example, if the CHANGELOG of the package to be released indicates that the next unreleased version is `5.6.1`, choose `5.7.0` as a version in case of `minor` version. This is important as the patch version numbers should be reserved in case bug fixes are needed for a minor WordPress release (see below). 8. Log-in to npm via the console: `npm login`. Note that you should have 2FA enabled. 9. From the `wp/trunk` branch, install npm dependencies with `npm ci`. 10. Run the script `npm run publish:latest`.