forked from ampproject/amp-github-apps
-
Notifications
You must be signed in to change notification settings - Fork 0
/
owner.js
141 lines (130 loc) · 3.81 KB
/
owner.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
/**
* Copyright 2016 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS-IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const path = require('path');
/**
* @file Contains classes and functions in relation to "OWNER" files
* and theyre evaluation.
*/
/**
* Represents an OWNER file found in the repo.
*/
class Owner {
/**
* @param {!Array} config
* @param {string} pathToRepoDir
* @param {string} filePath
* @constructor
*/
constructor(config, pathToRepoDir, filePath) {
// We want it have the leading ./ to evaluate `.` later on
this.path = /^\./.test(filePath) ? filePath : `.${path.sep}${filePath}`;
this.dirname = path.dirname(this.path);
this.fullpath = path.join(pathToRepoDir, this.path);
this.score = (this.dirname.match(/\//g) || []).length;
this.dirOwners = [];
this.fileOwners = Object.create(null);
this.parseConfig_(config);
}
/**
* @param {!Array} config
*/
parseConfig_(config) {
config.forEach(entry => {
if (typeof entry === 'string') {
this.dirOwners.push(entry);
}
});
this.dirOwners.sort();
}
/**
* @param {!Git} git
* @param {!PullRequest} pr
* @return {object}
*/
static async getOwners(git, pr) {
// Update the local target repository of the latest from master
git.pullLatestForRepo(process.env.GITHUB_REPO_DIR, 'origin', 'master');
const promises = Promise.all([
pr.listFiles(),
git.getOwnersFilesForBranch(pr.author, process.env.GITHUB_REPO_DIR,
'master'),
]);
const res = await promises;
const [files, ownersMap] = res;
const owners = findOwners(files, ownersMap);
pr.context.log.debug('[getOwners]', owners);
return owners;
}
}
/**
* Returns a list of github usernames that can be "approvers" for the set
* of files. It first tries to find the interection across the files and if
* there are none it will return the union across usernames.
* @param {!Array} files
* @param {object} ownersMap
* @return {object}
*/
function findOwners(files, ownersMap) {
const fileOwners = Object.create(null);
files.forEach(file => {
const owner = findClosestOwnersFile(file, ownersMap);
if (!fileOwners[owner.dirname]) {
fileOwners[owner.dirname] = {
owner,
files: [file],
};
} else {
fileOwners[owner.dirname].files.push(file);
}
});
return fileOwners;
}
/**
* Using the `ownersMap` key which is the path to the actual OWNER file
* in the repo, we simulate a folder traversal by splitting the path and
* finding the closest OWNER file for a RepoFile.
*
* @param {!RepoFile} file
* @param {object} ownersMap
* @return {object}
*/
function findClosestOwnersFile(file, ownersMap) {
let dirname = file.dirname;
let owner = ownersMap[dirname];
const dirs = dirname.split(path.sep);
while (!owner && dirs.pop() && dirs.length) {
dirname = dirs.join(path.sep);
owner = ownersMap[dirname];
}
return owner;
}
/**
* @param {!Array} owners
* @return {object}
*/
function createOwnersMap(owners) {
return owners.reduce((ownersMap, owner) => {
// Handles empty OWNERS.yaml files.
if (!owner) {
return ownersMap;
}
if (owner.dirOwners.length) {
ownersMap[owner.dirname] = owner;
}
return ownersMap;
}, Object.create(null));
}
module.exports = {Owner, findOwners, findClosestOwnersFile, createOwnersMap};