Skip to content

Commit 86063aa

Browse files
authored
feat: New labels API (#577)
* Add new labels API + docs * Add changelog
1 parent 5b93bca commit 86063aa

File tree

4 files changed

+146
-0
lines changed

4 files changed

+146
-0
lines changed

__tests__/unit/actions/labels.test.js

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,76 @@ const Labels = require('../../../lib/actions/labels')
22
const Helper = require('../../../__fixtures__/unit/helper')
33
const UnSupportedSettingError = require('../../../lib/errors/unSupportedSettingError')
44

5+
test('check that replace replaces existing labels', async () => {
6+
const labels = new Labels()
7+
const context = createMockContext(['drop_label'])
8+
9+
const settings = {
10+
replace: ['work in progress', 'development']
11+
}
12+
13+
await labels.afterValidate(context, settings)
14+
expect(context.octokit.issues.setLabels.mock.calls.length).toBe(1)
15+
expect(context.octokit.issues.setLabels.mock.calls[0][0].labels).toStrictEqual({ labels: settings.replace })
16+
})
17+
18+
test('check that add appends to existing labels', async () => {
19+
const labels = new Labels()
20+
const context = createMockContext(['another label', 'test label'])
21+
22+
const settings = {
23+
add: ['production', 'deploy']
24+
}
25+
26+
await labels.afterValidate(context, settings)
27+
expect(context.octokit.issues.setLabels.mock.calls.length).toBe(1)
28+
expect(context.octokit.issues.setLabels.mock.calls[0][0].labels).toStrictEqual({ labels: ['another label', 'test label', 'production', 'deploy'] })
29+
})
30+
31+
test('check that delete removes from existing labels', async () => {
32+
const labels = new Labels()
33+
const context = createMockContext(['another label', 'test label', 'delete me', 'delete this too'])
34+
35+
const settings = {
36+
delete: ['delete me', 'delete this too']
37+
}
38+
39+
await labels.afterValidate(context, settings)
40+
expect(context.octokit.issues.setLabels.mock.calls.length).toBe(1)
41+
expect(context.octokit.issues.setLabels.mock.calls[0][0].labels).toStrictEqual({ labels: ['another label', 'test label'] })
42+
})
43+
44+
test('check the order of replace, add, delete', async () => {
45+
const labels = new Labels()
46+
const context = createMockContext(['original label', 'another label'])
47+
48+
// order of operations is replace, then add, then delete
49+
const settings = {
50+
delete: ['not present', 'more adds'],
51+
add: ['addition', 'more adds'],
52+
replace: ['test present', 'not present']
53+
}
54+
55+
await labels.afterValidate(context, settings)
56+
expect(context.octokit.issues.setLabels.mock.calls.length).toBe(1)
57+
expect(context.octokit.issues.setLabels.mock.calls[0][0].labels).toStrictEqual({ labels: ['test present', 'addition'] })
58+
})
59+
60+
test('check that settings can be a single value', async () => {
61+
const labels = new Labels()
62+
const context = createMockContext(['original label', 'another label', 'delete me'])
63+
64+
const settings = {
65+
delete: 'deletion',
66+
add: 'addition',
67+
replace: 'replace'
68+
}
69+
70+
await labels.afterValidate(context, settings)
71+
expect(context.octokit.issues.setLabels.mock.calls.length).toBe(1)
72+
expect(context.octokit.issues.setLabels.mock.calls[0][0].labels).toStrictEqual({ labels: ['replace', 'addition'] })
73+
})
74+
575
test('check that unknown mode throw errors', async () => {
676
const labels = new Labels()
777
const context = Helper.mockContext()

docs/actions/labels.rst

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,54 @@
11
Labels
22
^^^^^^^^
33

4+
You can add, remove, and delete labels with one action.
5+
6+
.. warning::
7+
Using multiple ``do: labels`` in the same operation is not idempotent.
8+
Use only one ``labels`` action per block for accurate labeling.
9+
10+
You can add new labels, preserving existing ones
11+
12+
::
13+
14+
- do: labels
15+
add: 'Ready for Review'
16+
17+
You can delete existing labels
18+
19+
::
20+
21+
- do: labels
22+
delete: [ 'Ready for Review', 'Triage' ]
23+
24+
You can replace all current labels with new ones
25+
26+
::
27+
28+
- do: labels
29+
replace: [ 'Triage', 'Needs Deploy' ]
30+
31+
You can also use any combination of these options. They can be listed in any
32+
order, but the action is always evaluated in the order of ``replace`` → ``add``
33+
→ ``delete``.
34+
35+
::
36+
37+
- do: labels
38+
replace: [ 'New Task', 'Not Useful' ]
39+
add: [ 'Work in Progress', 'Needs Deploy' ]
40+
delete: 'Not Useful'
41+
42+
# result: [ 'New Task', 'Work in Progress', 'Needs Deploy' ]
43+
44+
45+
``labels`` and ``mode``
46+
"""""""""""""""""""""""
47+
48+
.. warning::
49+
Using ``labels`` with ``mode`` is deprecated and will be removed in v3.
50+
Use the ``add``, ``replace``, and ``delete`` options above for labeling.
51+
452
You can also add a set of the labels on an item
553

654
::

docs/changelog.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
CHANGELOG
22
=====================================
3+
| August 10, 2021 : feat: New labels API `#577 <https://github.com/mergeability/mergeable/pull/577>`_
34
| August 6, 2021 : feat: Add team assignment to request_review action `#574 <https://github.com/mergeability/mergeable/pull/574>`_
45
| August 6, 2021 : feat: Support must_include and must_exclude regex as an array `#575 <https://github.com/mergeability/mergeable/pull/575>`_
56
| July 19, 2021 : feat: Add ignore_drafts option to ignore drafts in stale validator `#565 <https://github.com/mergeability/mergeable/issues/565>`_

lib/actions/labels.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ const matchesPatterns = (label, patterns) => (
66
patterns.some((pattern) => minimatch(label, pattern))
77
)
88

9+
/**
10+
* @deprecated
11+
*/
912
const deleteLabels = async (context, issueNumber, labels, actionObj) => {
1013
const currentLabels = await actionObj.githubAPI.listLabelsOnIssue(context, issueNumber)
1114
// We get the current labels and filter out anything that matches
@@ -30,6 +33,30 @@ class Labels extends Action {
3033
async afterValidate (context, settings, name, results) {
3134
const items = this.getActionables(context, results)
3235

36+
if (settings.replace || settings.add || settings.delete) {
37+
return Promise.all(
38+
items.map(async issue => {
39+
let labels
40+
41+
if (settings.replace) {
42+
labels = [].concat(settings.replace)
43+
} else {
44+
labels = await this.githubAPI.listLabelsOnIssue(context, issue.number)
45+
}
46+
47+
if (settings.add) {
48+
labels = labels.concat(settings.add)
49+
}
50+
51+
if (settings.delete) {
52+
labels = labels.filter(label => !matchesPatterns(label, [].concat(settings.delete)))
53+
}
54+
55+
return this.githubAPI.setLabels(context, issue.number, labels)
56+
})
57+
)
58+
}
59+
3360
const actions = {
3461
add: this.githubAPI.addLabels,
3562
delete: deleteLabels,

0 commit comments

Comments
 (0)