Skip to content

Commit

Permalink
[BEAM-13925] Add weekly automation to update our reviewer config (apa…
Browse files Browse the repository at this point in the history
  • Loading branch information
damccorm authored Mar 19, 2022
1 parent 1a4b707 commit 518c7ff
Show file tree
Hide file tree
Showing 6 changed files with 437 additions and 7 deletions.
18 changes: 13 additions & 5 deletions .github/REVIEWERS.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,17 @@
# See the License for the specific language governing permissions and
# limitations under the License.

# If you would like to be excluded from consideration for reviewing a certain label,
# add yourself to that label's exclusionList
# FallbackReviewers is for reviewers who can review any area of the code base that might
# not receive a label. These should generally be more experienced committers.
labels:
- name: "Go"
reviewers: ["damccorm", "lostluck", "jrmccluskey", "youngoli", "riteshghorse"]
exclusionList: [] # These users will never be suggested as reviewers
# I don't know the other areas well enough to assess who the normal committers/contributors who might want to be reviewers are
fallbackReviewers: [] # List of committers to use when no label matches
- name: Go
reviewers:
- damccorm
- lostluck
- jrmccluskey
- youngoli
- riteshghorse
exclusionList: []
fallbackReviewers: []
39 changes: 39 additions & 0 deletions .github/workflows/pr-bot-update-reviewers.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You 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.

name: pr-bot-update-reviewers

# Run every week on Sunday at 16:05 UTC
on:
schedule:
- cron: '5 16 * * 0'
workflow_dispatch:

jobs:
update-reviewers:
# Don't run on forks
if: github.repository == 'apache/beam'
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2
- run: npm install
working-directory: 'scripts/ci/pr-bot'

# Runs a set of commands using the runners shell
- run: npm run updateReviewers
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
working-directory: 'scripts/ci/pr-bot'
1 change: 1 addition & 0 deletions scripts/ci/pr-bot/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"processNewPrs": "npm run build && node lib/processNewPrs.js",
"processPrUpdate": "npm run build && node lib/processPrUpdate.js",
"gatherMetrics": "npm run build && node lib/gatherMetrics.js",
"updateReviewers": "npm run build && node lib/updateReviewers.js",
"findPrsNeedingAttention": "npm run build && node lib/findPrsNeedingAttention.js"
},
"dependencies": {
Expand Down
36 changes: 36 additions & 0 deletions scripts/ci/pr-bot/shared/commentStrings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,42 @@ export function noLegalReviewers(): string {
return "No reviewers could be found from any of the labels on the PR or in the fallback reviewers list. Check the config file to make sure reviewers are configured";
}

export function updateReviewerConfig(
reviewersAddedForLabels: { [reviewer: string]: string[] },
reviewersRemovedForLabels: { [reviewer: string]: string[] }
) {
let commentString = `Adds and/or removes reviewers based on activity in the repo.
If you have been added and would prefer not to be, you can avoid getting repeatedly suggested by adding yourself to that label's exclusionList.
`;

if (Object.keys(reviewersAddedForLabels).length > 0) {
commentString += `
The following users have been added as reviewers to the configuration.
If you choose to accept being added, you will be added to the rotation of users who are automatically added to pull requests for an initial review.
A committer will still have to approve after your review (if you are not a committer), but you will be the initial touchpoint for PRs to which you are assigned.
`;
for (const reviewer of Object.keys(reviewersAddedForLabels)) {
commentString += `${reviewer} added for label(s): ${reviewersAddedForLabels[
reviewer
].join(",")}\n`;
}
}

if (Object.keys(reviewersRemovedForLabels).length > 0) {
commentString += `
The following users have been removed as reviewers from the configuration.
Users are removed if they haven't reviewed or completed a PR in the last 3 months.
`;
for (const reviewer of Object.keys(reviewersRemovedForLabels)) {
commentString += `@${reviewer} removed for label(s): ${reviewersRemovedForLabels[
reviewer
].join(",")}\n`;
}
}

return commentString;
}

export function assignNewReviewer(labelToReviewerMapping: {
[label: string]: string;
}): string {
Expand Down
48 changes: 46 additions & 2 deletions scripts/ci/pr-bot/shared/reviewerConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,24 @@ const { NO_MATCHING_LABEL } = require("./constants");

export class ReviewerConfig {
private config: any;
private configPath: string;
constructor(pathToConfigFile) {
this.config = yaml.load(
fs.readFileSync(pathToConfigFile, { encoding: "utf-8" })
);
this.configPath = pathToConfigFile;
}

// Returns all possible reviewers for each label configured.
getReviewersForAllLabels(): { [key: string]: string[] } {
const labelObjects = this.config.labels;
let reviewersForLabels = {};
for (const labelObject of labelObjects) {
reviewersForLabels[labelObject.name.toLowerCase()] =
labelObject.reviewers;
}

return reviewersForLabels;
}

// Given a list of labels and an exclusion list of reviewers not to include (e.g. the author)
Expand Down Expand Up @@ -56,7 +70,7 @@ export class ReviewerConfig {

// Get possible reviewers excluding the author.
getReviewersForLabel(label: string, exclusionList: string[]): string[] {
var labelObjects = this.config.labels;
const labelObjects = this.config.labels;
const labelObject = labelObjects.find(
(labelObject) => labelObject.name.toLowerCase() === label.toLowerCase()
);
Expand All @@ -67,8 +81,38 @@ export class ReviewerConfig {
return this.excludeFromReviewers(labelObject.reviewers, exclusionList);
}

updateReviewerForLabel(label: string, reviewers: string[]) {
const labelIndex = this.config.labels.findIndex(
(labelObject) => labelObject.name.toLowerCase() === label.toLowerCase()
);
this.config.labels[labelIndex].reviewers = reviewers;

const contents = `# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You 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.
# If you would like to be excluded from consideration for reviewing a certain label,
# add yourself to that label's exclusionList
# FallbackReviewers is for reviewers who can review any area of the code base that might
# not receive a label. These should generally be more experienced committers.
${yaml.dump(this.config)}`;

fs.writeFileSync(this.configPath, contents, { encoding: "utf-8" });
}

getExclusionListForLabel(label: string): string[] {
var labelObjects = this.config.labels;
const labelObjects = this.config.labels;
const labelObject = labelObjects.find(
(labelObject) => labelObject.name.toLowerCase() === label.toLowerCase()
);
Expand Down
Loading

0 comments on commit 518c7ff

Please sign in to comment.