-
-
Notifications
You must be signed in to change notification settings - Fork 457
114 lines (104 loc) · 4.2 KB
/
Copy pathpr-labels.yaml
File metadata and controls
114 lines (104 loc) · 4.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
---
name: PR Labels
# Each PR must carry exactly one of the labels the release-notes
# generator knows how to slot into a section. We derive that label
# from the "Types of changes" checkbox in the PR template and apply
# it automatically — most external contributors can't label their
# own PRs. The job fails when zero or more than one checkbox is
# ticked.
# yamllint disable-line rule:truthy
on:
pull_request_target:
types:
- opened
- edited
- synchronize
- labeled
- unlabeled
- reopened
- ready_for_review
branches:
- dev
permissions:
pull-requests: write
jobs:
apply_label:
name: Apply
runs-on: ubuntu-latest
# GitHub-typed bots (Dependabot, etc.) don't follow the PR
# template and apply their own labels — skip them outright.
# PAT-driven service accounts (e.g. music-assistant-machine)
# report user.type == 'User', so we additionally skip below
# whenever the PR already carries a known-set label.
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', 'refactor',
'new-feature', 'enhancement', 'new-provider',
'maintenance', 'ci', 'dependencies', 'documentation',
];
const { owner, repo } = context.repo;
const issue_number = context.payload.pull_request.number;
const current = (context.payload.pull_request.labels || [])
.map(l => l.name);
// If the PR already carries exactly one known-set label
// (e.g. applied by auto-update-frontend or backport-to-stable
// tooling that doesn't use the PR template), trust it and
// exit. Zero or 2+ known labels still fall through to the
// checkbox-driven path so a stray manual label can't hide
// a missing template tick.
const alreadyLabelled = current.filter(n => known.includes(n));
if (alreadyLabelled.length === 1) {
core.info(
`Skipping: PR already labelled '${alreadyLabelled[0]}'.`
);
return;
}
// Match a ticked checkbox bullet whose line ends with a
// backticked label, e.g. - [x] CI / workflow change — `ci`
// The PR template puts the canonical label name in
// backticks at the end of each "Types of changes" bullet.
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];
if (known.includes(name) && !ticked.includes(name)) {
ticked.push(name);
}
}
if (ticked.length === 0) {
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;
}
if (ticked.length > 1) {
core.setFailed(
`Multiple "Types of changes" checkboxes are ticked ` +
`(${ticked.join(', ')}). Tick exactly one.`
);
return;
}
const desired = ticked[0];
// Strip any stale labels from the known set so a
// contributor changing their mind in the description
// doesn't leave the previous label behind.
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],
});
}