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
24 changes: 24 additions & 0 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# What does this implement/fix?

<!-- Quick description and explanation of changes. -->

**Related issue (if applicable):**

- <link>

## Types of changes

<!--
Tick exactly one box. CI (.github/workflows/pr-labels.yaml) applies the
matching label, and the release notes use it to sort this change.
-->

- [ ] Bugfix (non-breaking change which fixes an issue): `bugfix`
- [ ] New feature (non-breaking change which adds functionality): `new-feature`
- [ ] Enhancement to an existing feature: `enhancement`
- [ ] Breaking change (changes CLI options, settings, hooks or the protocol in a way that is not backwards compatible): `breaking-change`
- [ ] Refactor (no behavior change): `refactor`
- [ ] Documentation only: `documentation`
- [ ] Maintenance / chore: `maintenance`
- [ ] CI / workflow change: `ci`
- [ ] Dependencies bump: `dependencies`
7 changes: 4 additions & 3 deletions .github/release-drafter.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,20 @@ categories:

- title: "🚀 Features"
labels:
- "feature"
- "new-feature"
- "enhancement"

- title: "🧰 Maintenance"
labels:
- "refactor"
- "documentation"
- "ci"
- "maintenance"

- title: "⬆️ Dependencies and CI"
- title: "⬆️ Dependencies"
collapse-after: 1
labels:
- "dependencies"
- "ci"
template: |
## What’s Changed

Expand Down
95 changes: 95 additions & 0 deletions .github/workflows/pr-labels.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
---
name: PR Labels

# Each PR must carry exactly one of the labels the release notes sort by.
# The label comes from the ticked "Types of changes" checkbox in the PR
# template, since external contributors can't label their own PRs.

# yamllint disable-line rule:truthy
on:
pull_request_target:
types:
- opened
- edited
- synchronize
- labeled
- unlabeled
- reopened
- ready_for_review
branches:
- main

permissions:
pull-requests: write

jobs:
pr_labels:
name: Verify
runs-on: ubuntu-latest
# Bots like Dependabot don't use the template and label their own PRs.
if: github.event.pull_request.user.type != 'Bot'
steps:
- name: 🏷 Apply label from PR description checkbox
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
script: |
const known = [
'breaking-change', 'bugfix', 'new-feature', 'enhancement',
'refactor', 'documentation', 'maintenance', 'ci', 'dependencies',
];

const { owner, repo } = context.repo;
const issue_number = context.payload.pull_request.number;
const current = (context.payload.pull_request.labels || [])
.map(l => l.name);

// Match a ticked bullet whose first backticked word is a label,
// e.g. - [x] CI / workflow change: `ci`
const body = context.payload.pull_request.body || '';
const re = /^\s*-\s*\[x\][^`\n]*`([^`]+)`/gim;
const ticked = [];
for (const m of body.matchAll(re)) {
const name = m[1].trim();
if (known.includes(name) && !ticked.includes(name)) {
ticked.push(name);
}
}

if (ticked.length > 1) {
core.setFailed(
`Multiple "Types of changes" checkboxes are ticked ` +
`(${ticked.join(', ')}). Tick exactly one.`
);
return;
}

// A ticked box wins. Without one, accept a single known label
// already on the PR, for PRs opened without the template.
if (ticked.length === 0) {
const existing = current.filter(n => known.includes(n));
if (existing.length === 1) {
core.info(`Skipping: PR already labelled '${existing[0]}'.`);
return;
}
core.setFailed(
'No "Types of changes" checkbox is ticked in the PR ' +
'description. Edit the description and tick exactly ' +
'one box so this PR can be release-noted.'
);
return;
}

const desired = ticked[0];

for (const name of current) {
if (known.includes(name) && name !== desired) {
await github.rest.issues.removeLabel({
owner, repo, issue_number, name,
}).catch(() => {});
}
}
if (!current.includes(desired)) {
await github.rest.issues.addLabels({
owner, repo, issue_number, labels: [desired],
});
}
Loading