-
Notifications
You must be signed in to change notification settings - Fork 2
/
index.js
148 lines (130 loc) · 5.95 KB
/
index.js
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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
const Slack = require('./lib/slack')
const GraphQL = require('./lib/graphql')
module.exports = app => {
app.on(`pull_request.labeled`, async context => {
const label = context.payload.label.name
const config = await context.config('reviewers.yml')
for (let i in config.labels) {
if (label === config.labels[i].label) {
await assignReviewers(context, config.labels[i], config.notifications, config.user_mappings)
}
}
})
app.on(`issue_comment.created`, async context => {
let commentRegex = /^\/reviewers unassign @(\S+)/gi
const body = context.payload.comment.body
let unassignment = commentRegex.exec(body)
if (unassignment !== null) {
let unassignedPerson = unassignment[1].toLowerCase()
let currentReviewers = await getCurrentReviewers(context)
let mappedCurrentReviewers = currentReviewers.data.users.map(x => x.login.toLowerCase()).filter(x => x)
if (mappedCurrentReviewers.includes(unassignedPerson)) {
let replacementReviewer = await getPossibleReviewer(context, [unassignedPerson.toLowerCase(), ...mappedCurrentReviewers].filter(x => x), unassignedPerson.toLowerCase())
if (replacementReviewer) {
await context.github.pullRequests.deleteReviewRequest(
context.issue({ reviewers: [unassignment[1]] })
)
await context.github.pullRequests.createReviewRequest(
context.issue(
{
reviewers: [replacementReviewer.toLowerCase(), ...mappedCurrentReviewers.filter(name => name !== unassignedPerson)],
team_reviewers: currentReviewers.data.teams.map(x => x.slug).filter(y => y)
}
)
)
}
}
}
})
async function getCurrentReviewers (context) {
return context.github.pullRequests.listReviewRequests(context.issue())
}
async function getPossibleReviewer (context, reviewersToExclude, unassignedPerson) {
const currentLabels = context.payload.issue.labels.map(x => x.name)
const uniqueReviewersToExclude = [...(new Set(reviewersToExclude))]
const owner = context.payload.issue.user.login.toLowerCase()
reviewersToExclude = [owner, ...uniqueReviewersToExclude].filter(x => x)
const config = await context.config('reviewers.yml')
for (let i in config.labels) {
if (currentLabels.includes(config.labels[i].label)) {
for (let g in config.labels[i].groups) {
let specificConfig = config.labels[i].groups[g]
let possibleReviewers = specificConfig.possible_reviewers.map(x => x.toLowerCase())
if (possibleReviewers && possibleReviewers.includes(unassignedPerson) && specificConfig.number_of_picks) {
for (let i = 0; i < reviewersToExclude.length; i++) {
let index = possibleReviewers.indexOf(reviewersToExclude[i])
if (index > -1) {
possibleReviewers.splice(index, 1)
}
}
let graphQl = new GraphQL(context)
possibleReviewers = await graphQl.filterAvailableUsers(possibleReviewers)
if (possibleReviewers.length > 0) {
return possibleReviewers[Math.floor(Math.random() * possibleReviewers.length)]
}
}
}
}
}
return null
}
async function assignReviewers (context, config, notificationsConfig, userMappings) {
const owner = context.payload.pull_request.user.login.toLowerCase()
const existingReviewers = await getCurrentReviewers(context)
let pickedReviewers = []
for (let i in config.groups) {
let group = config.groups[i]
if (group.possible_reviewers && group.number_of_picks) {
let possibleReviewers = group.possible_reviewers.map(x => x.toLowerCase())
let numberOfPicks = group.number_of_picks
// Remove PR owner from the array of possible reviewers
let index = possibleReviewers.indexOf(owner)
if (index > -1) {
possibleReviewers.splice(index, 1)
}
// Remove existing reviewers from the array of possible reviewers
for (let i = 0; i < existingReviewers.data.users.length; i++) {
index = possibleReviewers.indexOf(existingReviewers.data.users[i].login.toLowerCase())
if (index > -1) {
possibleReviewers.splice(index, 1)
}
}
// Remove reviewers already picked from the array of possible reviewers
for (let i = 0; i < pickedReviewers.length; i++) {
index = possibleReviewers.indexOf(pickedReviewers[i])
if (index > -1) {
possibleReviewers.splice(index, 1)
}
}
// Remove reviewers who are busy according to their user status
let graphQl = new GraphQL(context)
possibleReviewers = await graphQl.filterAvailableUsers(possibleReviewers)
// Pick reviewers at random until you have enough, or run out of possible reviewers
for (let i = 0; i < numberOfPicks && possibleReviewers.length > 0; i++) {
let pickedReviewer = possibleReviewers[Math.floor(Math.random() * possibleReviewers.length)]
index = possibleReviewers.indexOf(pickedReviewer)
possibleReviewers.splice(index, 1)
pickedReviewers.push(pickedReviewer)
}
}
}
if (pickedReviewers.length > 0) {
try {
await context.github.pullRequests.createReviewRequest(
context.issue(
{
reviewers: [...pickedReviewers, ...existingReviewers.data.users.map(x => x.login.toLowerCase())].filter(x => x),
team_reviewers: existingReviewers.data.teams.map(x => x.slug).filter(y => y)
}
)
)
if (!!notificationsConfig && notificationsConfig.hasOwnProperty('slack')) {
let slack = new Slack(context.payload.pull_request, notificationsConfig.slack, pickedReviewers, userMappings)
await slack.sendMessage()
}
} catch (error) {
context.log.error({ error: error.message })
}
}
}
}