Skip to content

Commit a342c32

Browse files
Merge pull request #31 from dawnlabs/commit-body
WIP react to new commit bodies
2 parents bf4e69b + fbe4f7e commit a342c32

File tree

5 files changed

+116
-3
lines changed

5 files changed

+116
-3
lines changed

index.js

+3
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ const threadLabeled = require('./src/thread/labeled')
77
const issueLabeled = require('./src/issue/labeled')
88
const pullLabeled = require('./src/pull/labeled')
99
const pullMerged = require('./src/pull/merged')
10+
const pullSynchronized = require('./src/pull/synchronized')
1011
const threadClosed = require('./src/thread/closed')
1112
const commentDeleted = require('./src/comment/deleted')
1213
const commentCreated = require('./src/comment/created')
@@ -86,6 +87,8 @@ module.exports = async robot => {
8687

8788
robot.on('pull_request.closed', wrapPaymentCheck(pullMerged()))
8889

90+
robot.on('pull_request.synchronize', pullSynchronized())
91+
8992
// Kill job when issue/pull is closed
9093
robot.on(['issues.closed', 'pull_request.closed'], threadClosed(queue))
9194

src/config.js

+9-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
const ms = require('ms')
22

3-
const { CLOSE, MERGE } = require('./constants')
3+
const { CLOSE, MERGE, LABEL } = require('./constants')
44

55
const TIME = process.env.NODE_ENV === 'production' ? '7 days' : '10s'
66

@@ -19,7 +19,14 @@ const defaultConfig = {
1919
invalid: CLOSE,
2020
'merge when passing': MERGE
2121
},
22-
comments: []
22+
comments: [],
23+
commits: [
24+
{
25+
action: LABEL,
26+
pattern: '/merge when passing/i',
27+
labels: ['merge when passing']
28+
}
29+
]
2330
}
2431

2532
exports.CONFIG_FILE = CONFIG_FILE

src/pull/synchronized.js

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
const { LABEL, MAINTAINERS } = require('../constants')
2+
const getConfig = require('../config')
3+
const { addLabels } = require('../api')
4+
5+
function isMaintainer(association) {
6+
return MAINTAINERS.includes(association)
7+
}
8+
9+
function parseRegex(string) {
10+
// https://stackoverflow.com/questions/874709/converting-user-input-string-to-regular-expression
11+
const match = string.match(new RegExp('^/(.*?)/([gimy]*)$'))
12+
13+
if (match && match[1] && match[2]) {
14+
return new RegExp(match[1], match[2])
15+
}
16+
17+
// matches nothing
18+
return /$^/
19+
}
20+
21+
module.exports = () => async context => {
22+
const config = await getConfig(context)
23+
24+
const {
25+
head: { sha },
26+
author_association
27+
} = context.payload.pull_request
28+
29+
if (!isMaintainer(author_association)) return
30+
31+
const {
32+
data: {
33+
commit: { message: body }
34+
}
35+
} = await context.github.repos.getCommit(context.repo({ sha }))
36+
37+
// TODO confirm this API before releasing in docs
38+
const rules = config.commits
39+
40+
if (!Array.isArray(rules)) return
41+
42+
await Promise.all(
43+
rules.map(async ({ action, pattern, labels } = {}) => {
44+
if (typeof action !== 'string' || action.trim().toLowerCase() !== LABEL) return
45+
if (!body.includes(pattern) && !parseRegex(pattern).test(body)) return
46+
if (!labels) return
47+
48+
return addLabels(context.github, context.issue({ labels }))
49+
})
50+
)
51+
}

test/fixtures/synchronized.js

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
module.exports = ({ author_association = 'COLLABORATOR' } = {}) => ({
2+
name: 'pull_request',
3+
payload: {
4+
action: 'synchronize',
5+
pull_request: {
6+
number: 4,
7+
head: {
8+
sha: 'e829d1a59ce17e24fee7b8ffa42774005acd0240'
9+
},
10+
author_association
11+
},
12+
repository: {
13+
id: 130609983,
14+
name: 'ranger-test',
15+
full_name: 'ranger/ranger-test',
16+
owner: {
17+
login: 'ranger'
18+
},
19+
private: false
20+
}
21+
}
22+
})

test/index.test.js

+31-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ const payload = require('./fixtures/labeled')
66
const commentPayload = require('./fixtures/comment')
77
const addedPayload = require('./fixtures/added')
88
const installedPayload = require('./fixtures/installed')
9+
const synchronizedPayload = require('./fixtures/synchronized')
910

1011
const { MAINTAINERS } = require('../src/constants')
1112

@@ -131,6 +132,12 @@ comments:
131132
merges:
132133
- action: delete_branch
133134
135+
commits:
136+
- action: label
137+
pattern: /merge when passing/i
138+
labels:
139+
- merge when passing
140+
134141
default:
135142
close:
136143
delay: 1ms
@@ -167,7 +174,10 @@ describe('Bot', () => {
167174
},
168175
repos: {
169176
getContent: jest.fn(() => ({ data: { content: Buffer.from(config).toString('base64') } })),
170-
getContents: jest.fn(() => ({ data: { content: Buffer.from(config).toString('base64') } }))
177+
getContents: jest.fn(() => ({ data: { content: Buffer.from(config).toString('base64') } })),
178+
getCommit: jest
179+
.fn()
180+
.mockResolvedValue({ data: { commit: { message: 'merge when passing' } } })
171181
},
172182
apps: {
173183
listRepos: jest.fn().mockResolvedValue({
@@ -479,6 +489,26 @@ describe('Bot', () => {
479489
repo: 'Hello-World'
480490
})
481491
})
492+
493+
test('Will take action on a maintainer commit message', async () => {
494+
await robot.receive(synchronizedPayload())
495+
496+
expect(github.issues.addLabels).toHaveBeenCalledWith({
497+
number: 4,
498+
labels: ['merge when passing'],
499+
owner: 'ranger',
500+
repo: 'ranger-test',
501+
headers: {
502+
Accept: 'application/vnd.github.symmetra-preview+json'
503+
}
504+
})
505+
})
506+
507+
test('Will not take action on a non-maintainer commit message', async () => {
508+
await robot.receive(synchronizedPayload({ author_association: 'CONTRIBUTOR' }))
509+
510+
expect(github.issues.addLabels).not.toHaveBeenCalled()
511+
})
482512
})
483513

484514
describe('comment', () => {

0 commit comments

Comments
 (0)