diff --git a/README.md b/README.md index 500da84..6eb2dbf 100644 --- a/README.md +++ b/README.md @@ -87,15 +87,32 @@ build(composer): Update humbug/php-scoper to v0.18.19 chore(actions): Update actions/checkout to v7 ``` -Allowed: `deps`, `deps-dev`, `wp-plugin`, `wp-theme`, `npm`, `composer`, `actions`. These are emitted by [linchpin/renovatebot-config](https://github.com/linchpin/renovatebot-config); anything else must still be a task key, `NO-TASK`, or an issue number. +Allowed: `deps`, `deps-dev`, `wp-plugin`, `wp-theme`, `wporg`, `linchpin`, `npm`, `composer`, `actions`. These are emitted by [linchpin/renovatebot-config](https://github.com/linchpin/renovatebot-config); anything else must still be a task key, `NO-TASK`, or an issue number. `build` is the [Angular convention's type for external dependencies](https://www.conventionalcommits.org/en/v1.0.0-beta.4/), and `update` reads better for a WordPress plugin bump — both are valid here. +### WordPress types + +`wp-plugin` and `wp-theme` are types as well as scopes. [release-please](https://github.com/googleapis/release-please) groups changelog sections strictly by **type** — its schema has no scope key — so WordPress updates need their own types to get their own changelog sections. Under those types the scope names where the package came from: + +``` +wp-plugin(wporg): Update akismet to v5.3 +wp-plugin(linchpin): Update gravityforms to v3 +wp-theme(deps): Update ollie-pro to v2.6.1 +deps(npm): Update webpack to v5.94.0 +deps(composer): Update wp-cli to v2.11 +platform(deps): Update php to v8.3 +``` + +`wporg` is anything public — [wp-packages.org](https://wp-packages.org) and [wpackagist.org](https://wpackagist.org) are two routes to the same wordpress.org packages, so they share a scope. `linchpin` is our own [packagist.linchpin.com](https://packagist.linchpin.com). Themes are routed as one group upstream, so they carry the generic `deps` scope. + +The matching changelog sections live in [linchpin/release-please-config](https://github.com/linchpin/release-please-config), whose test suite asserts that its section list equals the type list here — so a type added in one place and not the other fails CI rather than silently dropping commits from a changelog. + ## Rules | Rule | Level | Description | | --- | --- | --- | -| `type-enum` | error | Type must be one of: `add`, `improve`, `build`, `chore`, `ci`, `docs`, `feat`, `feature`, `fix`, `perf`, `refactor`, `remove`, `revert`, `style`, `test`, `update` | +| `type-enum` | error | Type must be one of: `add`, `improve`, `build`, `chore`, `ci`, `deps`, `docs`, `feat`, `feature`, `fix`, `perf`, `platform`, `refactor`, `remove`, `revert`, `style`, `test`, `update`, `wp-plugin`, `wp-theme` | | `subject-case` | warning | Subject must be in sentence-case | The config also sets a custom `parserPreset.parserOpts.headerPattern` that enforces the scope format. diff --git a/index.js b/index.js index 332fff1..1be3f6b 100644 --- a/index.js +++ b/index.js @@ -1,12 +1,25 @@ 'use strict'; -const TYPES = ['add', 'improve', 'build', 'chore', 'ci', 'docs', 'feat', 'feature', 'fix', 'perf', 'refactor', 'remove', 'revert', 'style', 'test', 'update']; +// wp-plugin and wp-theme are types here, not only scopes, because release-please groups +// changelog sections strictly by type - there is no scope key in its schema - so +// `update(wp-plugin)` and `update(wp-theme)` would share the type `update` and collapse into +// one section. As types they get a section each. linchpin/renovatebot-config emits them. +// +// deps and platform are no longer emitted by renovatebot-config, which routes that traffic +// through build(npm) and build(composer). They stay allowed because the fleet's history +// contains them, and because either reads better than `build` for a hand-written dependency +// commit. @linchpinagency/release-please-config maps every type here to a section, and its +// tests assert the two lists are identical - so adding a type in one place without the other +// fails CI instead of silently dropping commits from a changelog. +const TYPES = ['add', 'improve', 'build', 'chore', 'ci', 'deps', 'docs', 'feat', 'feature', 'fix', 'perf', 'platform', 'refactor', 'remove', 'revert', 'style', 'test', 'update', 'wp-plugin', 'wp-theme']; // Dependency updates have no task behind them, so the scope slot carries the kind of // dependency instead - which is what the wider ecosystem does too (`build(deps)`, -// `chore(deps-dev)`). wp-plugin and wp-theme keep WordPress updates obvious at a glance -// in a log that is mostly automated. deps-dev precedes deps so the longer one wins. -const DEP_SCOPES = ['deps-dev', 'deps', 'wp-plugin', 'wp-theme', 'npm', 'composer', 'actions']; +// `chore(deps-dev)`). Under the wp-plugin and wp-theme types the scope names where the +// package came from: `wporg` for anything public (wp-packages.org or wpackagist.org, which +// are two routes to the same wordpress.org packages) and `linchpin` for our own +// packagist.linchpin.com. deps-dev precedes deps so the longer one wins. +const DEP_SCOPES = ['deps-dev', 'deps', 'wp-plugin', 'wp-theme', 'wporg', 'linchpin', 'npm', 'composer', 'actions']; // A ClickUp-style task key, NO-TASK, a GitHub issue number, or a dependency scope. const SCOPE = new RegExp(`^(?:[A-Z]+-\\d+|NO-TASK|#\\d+|${DEP_SCOPES.join('|')})$`); diff --git a/index.test.js b/index.test.js index 84b36e6..a780d89 100644 --- a/index.test.js +++ b/index.test.js @@ -18,10 +18,18 @@ describe('@linchpinagency/commitlint-config', () => { test('type-enum includes expected types', () => { const [, , types] = config.rules['type-enum']; - const expected = ['add', 'improve', 'build', 'chore', 'ci', 'docs', 'feat', 'feature', 'fix', 'perf', 'refactor', 'remove', 'revert', 'style', 'test', 'update']; + const expected = ['add', 'improve', 'build', 'chore', 'ci', 'deps', 'docs', 'feat', 'feature', 'fix', 'perf', 'platform', 'refactor', 'remove', 'revert', 'style', 'test', 'update', 'wp-plugin', 'wp-theme']; expect(types).toEqual(expect.arrayContaining(expected)); }); + // These four exist so release-please can give them their own changelog sections — it + // groups strictly by type, with no scope key in its schema. They are listed separately + // from the rest because dropping one here silently collapses a section over there. + test('includes the types release-please sections depend on', () => { + const [, , types] = config.rules['type-enum']; + expect(types).toEqual(expect.arrayContaining(['deps', 'platform', 'wp-plugin', 'wp-theme'])); + }); + // Every type in type-enum must also be reachable through headerPattern. The two are // separate lists, so a type added to one and not the other passes type-enum and is // then rejected by the parser — the failure mode this guards against. @@ -144,11 +152,26 @@ describe('@linchpinagency/commitlint-config', () => { 'build(composer): Update humbug/php-scoper to v0.18.19', 'build(npm): Update webpack to v5.94.0', 'chore(actions): Update actions/checkout to v7', + // The WordPress types, with the scope naming where the package came from. + 'wp-plugin(wporg): Update akismet to v5.3', + 'wp-plugin(linchpin): Update gravityforms to v3', + 'wp-theme(deps): Update ollie-pro to v2.6.1', + 'deps(composer): Update wp-cli to v2.11', + 'deps(npm): Update webpack to v5.94.0', + 'platform(deps): Update php to v8.3', ])('accepts %s', (header) => { expect(explain(header)).toBeNull(); expect(header).toMatch(pattern); }); + // wp-plugin and wp-theme are valid in both slots. A hyphenated type must not be + // truncated by the alternation the way deps-dev could be. + test('parses a hyphenated type as a whole type', () => { + const [, type, scope] = 'wp-plugin(wporg): Update akismet to v5'.match(pattern); + expect(type).toBe('wp-plugin'); + expect(scope).toBe('wporg'); + }); + // deps-dev must win over deps, or the scope parses as `deps` and the header breaks. test('parses deps-dev as a whole scope', () => { const [, , scope] = 'build(deps-dev): Update svgo to v3'.match(pattern); diff --git a/release-please-config.json b/release-please-config.json index 2b563cc..3495b3a 100644 --- a/release-please-config.json +++ b/release-please-config.json @@ -3,8 +3,8 @@ "release-type": "node", "bump-minor-pre-major": true, "bump-patch-for-minor-pre-major": true, - "include-v-in-tag": true, "include-component-in-tag": false, + "include-v-in-tag": true, "pull-request-header": ":gem: Automated Release", "packages": { ".": { @@ -13,19 +13,88 @@ } }, "changelog-sections": [ - { "type": "feat", "section": "Features ✨" }, - { "type": "feature", "section": "Features ✨" }, - { "type": "fix", "section": "Bug Fixes 🐛" }, - { "type": "improve", "section": "Improvements 🎨" }, - { "type": "update", "section": "Improvements 🎨" }, - { "type": "docs", "section": "Documentation 📚" }, - { "type": "refactor", "section": "Refactoring ♻️" }, - { "type": "perf", "section": "Performance ⚡️" }, - { "type": "style", "section": "Styles 💄" }, - { "type": "test", "section": "Tests 🧪" }, - { "type": "chore", "section": "Miscellaneous 🧹" }, - { "type": "revert", "section": "Reverts ⏪️" }, - { "type": "ci", "section": "Continuous Integration 👷", "hidden": true }, - { "type": "build", "section": "Build System 📦", "hidden": true } + { + "type": "feat", + "section": "Features ✨" + }, + { + "type": "feature", + "section": "Features ✨" + }, + { + "type": "add", + "section": "Features ✨" + }, + { + "type": "fix", + "section": "Bug Fixes 🐛" + }, + { + "type": "improve", + "section": "Changes to Existing Features 💅" + }, + { + "type": "refactor", + "section": "Changes to Existing Features 💅" + }, + { + "type": "update", + "section": "Changes to Existing Features 💅" + }, + { + "type": "perf", + "section": "Performance Improvements ⚡️" + }, + { + "type": "remove", + "section": "Removed 🗑️" + }, + { + "type": "wp-plugin", + "section": "WordPress Plugins 🔌" + }, + { + "type": "wp-theme", + "section": "WordPress Themes 🖌️" + }, + { + "type": "deps", + "section": "Dependencies 📦" + }, + { + "type": "docs", + "section": "Documentation 📚" + }, + { + "type": "style", + "section": "Styles 🎨" + }, + { + "type": "test", + "section": "Tests 🧪" + }, + { + "type": "revert", + "section": "Reverts ⏪️" + }, + { + "type": "chore", + "section": "Miscellaneous Chores 🧹" + }, + { + "type": "platform", + "section": "Platform 🏗️", + "hidden": true + }, + { + "type": "build", + "section": "Build System 🔧", + "hidden": true + }, + { + "type": "ci", + "section": "Continuous Integration 👷", + "hidden": true + } ] }