-
Notifications
You must be signed in to change notification settings - Fork 117
/
node-repo.js
145 lines (117 loc) · 4.1 KB
/
node-repo.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
'use strict'
const LRU = require('lru-cache')
const githubClient = require('./github-client')
const resolveLabels = require('./node-labels').resolveLabels
const existingLabelsCache = new LRU({ max: 1, maxAge: 1000 * 60 * 60 })
function deferredResolveLabelsThenUpdatePr (options) {
const timeoutMillis = (options.timeoutInSec || 0) * 1000
setTimeout(resolveLabelsThenUpdatePr, timeoutMillis, options)
}
function resolveLabelsThenUpdatePr (options) {
options.logger.debug('Fetching PR files for labelling')
githubClient.pullRequests.getFiles({
user: options.owner,
repo: options.repo,
number: options.prId
}, (err, res) => {
if (err) {
return options.logger.error(err, 'Error retrieving files from GitHub')
}
const filepathsChanged = res.map((fileMeta) => fileMeta.filename)
const resolvedLabels = resolveLabels(filepathsChanged, options.baseBranch)
fetchExistingLabels(options, (err, existingLabels) => {
if (err) {
return options.logger.error(err, 'Error retrieving existing repo labels')
}
const labelsToAdd = stringsInCommon(existingLabels, resolvedLabels)
options.logger.debug('Resolved labels: ' + labelsToAdd)
updatePrWithLabels(options, labelsToAdd)
})
})
}
function updatePrWithLabels (options, labels) {
// no need to request github if we didn't resolve any labels
if (!labels.length) {
return
}
options.logger.debug('Trying to add labels: ' + labels)
githubClient.issues.addLabels({
user: options.owner,
repo: options.repo,
number: options.prId,
body: labels
}, (err) => {
if (err) {
return options.logger.error(err, 'Error while adding labels')
}
options.logger.info('Added labels: ' + labels)
})
}
function removeLabelFromPR (options, label) {
// no need to request github if we didn't resolve a label
if (!label) {
return
}
options.logger.debug('Trying to remove label: ' + label)
githubClient.issues.removeLabel({
user: options.owner,
repo: options.repo,
number: options.prId,
name: label
}, (err) => {
if (err) {
if (err.code === 404) return options.logger.info('Label to remove did not exist, bailing ' + label)
return options.logger.error(err, 'Error while removing a label')
}
options.logger.info('Removed a label ' + label)
})
}
function fetchExistingLabels (options, cb) {
const cacheKey = `${options.owner}:${options.repo}`
if (existingLabelsCache.has(cacheKey)) {
return cb(null, existingLabelsCache.get(cacheKey))
}
fetchLabelPages(options, 1, (err, existingLabels) => {
if (err) {
return cb(err)
}
const existingLabelNames = existingLabels.map((label) => label.name)
// cache labels so we don't have to fetch these *all the time*
existingLabelsCache.set(cacheKey, existingLabelNames)
options.logger.debug('Filled existing repo labels cache: ' + existingLabelNames)
cb(null, existingLabelNames)
})
}
function fetchLabelPages (options, startPageNum, cb) {
// the github client API is somewhat misleading,
// this fetches *all* repo labels not just for an issue
githubClient.issues.getLabels({
user: options.owner,
repo: options.repo,
page: startPageNum,
per_page: 100
}, (err, existingLabels) => {
if (err) {
return cb(err)
}
if (!githubClient.hasNextPage(existingLabels)) {
return cb(null, existingLabels)
}
fetchLabelPages(
options,
startPageNum + 1,
(err, remainingLabels) => err ? cb(err) : cb(null, existingLabels.concat(remainingLabels))
)
})
}
function stringsInCommon (arr1, arr2) {
const loweredArr2 = arr2.map((str) => str.toLowerCase())
// we want the original string cases in arr1, therefore we don't lowercase them
// before comparing them cause that would wrongly make "V8" -> "v8"
return arr1.filter((str) => loweredArr2.indexOf(str.toLowerCase()) !== -1)
}
exports.removeLabelFromPR = removeLabelFromPR
exports.updatePrWithLabels = updatePrWithLabels
exports.resolveLabelsThenUpdatePr = deferredResolveLabelsThenUpdatePr
// exposed for testability
exports._fetchExistingLabels = fetchExistingLabels