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
21 changes: 19 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
21 changes: 17 additions & 4 deletions index.js
Original file line number Diff line number Diff line change
@@ -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('|')})$`);
Expand Down
25 changes: 24 additions & 1 deletion index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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);
Expand Down
99 changes: 84 additions & 15 deletions release-please-config.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
".": {
Expand All @@ -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
}
]
}