21885379c1
Signed-off-by: Fredrik Adelöw <freben@gmail.com>
185 lines
5.5 KiB
JavaScript
185 lines
5.5 KiB
JavaScript
/*
|
|
* Copyright 2022 The Backstage Authors
|
|
*
|
|
* 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 Codeowners = require('codeowners');
|
|
|
|
const getRepoEvents = async ({ github, context, pull_number }) => {
|
|
const commits = await github.paginate(github.rest.pulls.listCommits, {
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
pull_number,
|
|
});
|
|
|
|
const reviews = await github.paginate(github.rest.pulls.listReviews, {
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
pull_number,
|
|
});
|
|
|
|
const pullComments = await github.paginate(
|
|
'GET /repos/{owner}/{repo}/pulls/{pull_number}/comments',
|
|
{
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
pull_number,
|
|
},
|
|
);
|
|
|
|
const comments = await github.paginate(github.rest.issues.listComments, {
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
issue_number: pull_number,
|
|
});
|
|
|
|
const events = [
|
|
...reviews.map(({ user, submitted_at }) => ({
|
|
user,
|
|
updated_at: submitted_at,
|
|
type: 'review',
|
|
})),
|
|
...commits.map(({ commit, author, committer }) => ({
|
|
user: author || committer,
|
|
updated_at: commit.author.date || commit.committer.date,
|
|
type: 'commit',
|
|
})),
|
|
...pullComments.map(({ user, updated_at }) => ({
|
|
user,
|
|
updated_at,
|
|
type: 'pr_comment',
|
|
})),
|
|
...comments.map(({ user, updated_at }) => ({
|
|
user,
|
|
updated_at,
|
|
type: 'pr_comment',
|
|
})),
|
|
];
|
|
|
|
return events
|
|
.sort((a, b) => new Date(a.updated_at) - new Date(b.updated_at))
|
|
.filter(({ user }) => (user ? !user.login.includes('[bot]') : false));
|
|
};
|
|
|
|
module.exports = async ({ github, context, core }) => {
|
|
// first get all open pull requests
|
|
const allPullRequests = await github.paginate(github.rest.pulls.list, {
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
state: 'open',
|
|
});
|
|
|
|
const { data: teams } = await github.request('GET /orgs/{org}/teams', {
|
|
org: context.repo.owner,
|
|
});
|
|
|
|
const groupMembers = await Promise.all(
|
|
teams.map(async team => {
|
|
const { data } = await github.rest.teams.listMembersInOrg({
|
|
org: context.repo.owner,
|
|
team_slug: team.slug,
|
|
});
|
|
|
|
return { team: `@backstage/${team.slug}`, data };
|
|
}),
|
|
);
|
|
|
|
const codeowners = new Codeowners();
|
|
|
|
for (const pullRequest of allPullRequests) {
|
|
// Go through each file changed and get the codeowners for that file.
|
|
// Find the group in the group list that matches the codeowner.
|
|
// If it does match push the owner to a list of reviewers
|
|
// check to see the reviews and if there is at least one matching reviewer from those group
|
|
|
|
const changedFiles = await github.paginate(github.rest.pulls.listFiles, {
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
pull_number: pullRequest.number,
|
|
});
|
|
|
|
const allReviews = await github.paginate(github.rest.pulls.listReviews, {
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
pull_number: pullRequest.number,
|
|
});
|
|
|
|
const expectedReviewers = new Set();
|
|
|
|
for (const file of changedFiles) {
|
|
expectedReviewers.add(...codeowners.getOwner(file.filename));
|
|
}
|
|
|
|
const hasReviewed = new Set();
|
|
|
|
// For each reviewer in the group, check to see if they have a review set
|
|
for (const reviewer of expectedReviewers) {
|
|
const members = groupMembers.find(member => member.team === reviewer);
|
|
if (members) {
|
|
// then we are dealing with a group
|
|
const hasMemberReview = allReviews.some(review =>
|
|
members.data.some(member => member.login === review.user.login),
|
|
);
|
|
if (hasMemberReview) {
|
|
hasReviewed.add(reviewer);
|
|
}
|
|
} else {
|
|
// reviewer is a person
|
|
const hasReview = allReviews.some(
|
|
review => reviewer === `@${review.user.login}`,
|
|
);
|
|
if (hasReview) {
|
|
hasReviewed.add(reviewer);
|
|
}
|
|
}
|
|
}
|
|
|
|
// if all required reviewers have reviewed
|
|
if (hasReviewed.size === expectedReviewers.size) {
|
|
const repoEvents = await getRepoEvents({
|
|
github,
|
|
context,
|
|
pull_number: pullRequest.number,
|
|
});
|
|
// if the last event for the issue is not by the author, remove the label
|
|
if (
|
|
repoEvents[repoEvents.length - 1].user.login !== pullRequest.user.login
|
|
) {
|
|
await github.rest.issues
|
|
.removeLabel({
|
|
issue_number: pullRequest.number,
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
name: 'awaiting-review',
|
|
})
|
|
.catch(e => {
|
|
console.error(e);
|
|
});
|
|
}
|
|
// add the awaiting-review label to tell us that the PR is waiting on reviews
|
|
} else {
|
|
await github.rest.issues
|
|
.addLabels({
|
|
issue_number: pullRequest.number,
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
labels: ['awaiting-review'],
|
|
})
|
|
.catch(e => {
|
|
console.error(e);
|
|
});
|
|
}
|
|
}
|
|
};
|