-
Notifications
You must be signed in to change notification settings - Fork 576
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Automatically request reviews from codeowners of components (#6282)
We have more and more component owners who are not approvers/maintainers of the Go SIG, and are therefore not auto-assigned for review on opened PRs. This GH Action is heavily inspired from the collector-contrib one: * https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/.github/workflows/add-codeowners-to-pr.yml * https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/.github/workflows/scripts/add-codeowners-to-pr.sh * https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/.github/workflows/scripts/get-components.sh * https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/.github/workflows/scripts/get-codeowners.sh For every open PR, it will loop through every code owner of the components that were updated in that PR, and request a review from them, unless they were the ones opening the PR or they already reviewed the PR. Co-authored-by: Robert Pająk <[email protected]>
- Loading branch information
Showing
4 changed files
with
190 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
name: 'Request reviews from code owners of a PR' | ||
on: | ||
pull_request_target: | ||
types: [opened, synchronize] | ||
|
||
jobs: | ||
request_codeowners_review: | ||
runs-on: ubuntu-latest | ||
permissions: | ||
pull-requests: write | ||
if: ${{ github.repository_owner == 'open-telemetry' }} | ||
steps: | ||
- uses: actions/checkout@v4 | ||
|
||
- name: Run request_codeowners_review.sh | ||
run: ./tools/request_codeowners_review.sh | ||
env: | ||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
REPO: ${{ github.repository }} | ||
PR: ${{ github.event.number }} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
#!/usr/bin/env bash | ||
# | ||
# Copyright The OpenTelemetry Authors | ||
# SPDX-License-Identifier: Apache-2.0 | ||
# | ||
# This script checks the GitHub CODEOWNERS file for any code owners | ||
# of contrib components and returns a string of the code owners if it | ||
# finds them. | ||
|
||
set -euo pipefail | ||
|
||
get_component_type() { | ||
echo "${COMPONENT}" | cut -f 1 -d '/' | ||
} | ||
|
||
get_codeowners() { | ||
# grep arguments explained: | ||
# -m 1: Match the first occurrence | ||
# ^: Match from the beginning of the line | ||
# ${1}: Insert first argument given to this function | ||
# [\/]\?: Match 0 or 1 instances of a forward slash | ||
# \s: Match any whitespace character | ||
(grep -m 1 "^${1}[\/]\?\s" CODEOWNERS || true) | \ | ||
sed 's/ */ /g' | \ | ||
cut -f3- -d ' ' | ||
} | ||
|
||
if [[ -z "${COMPONENT:-}" ]]; then | ||
echo "COMPONENT has not been set, please ensure it is set." | ||
exit 1 | ||
fi | ||
|
||
OWNERS="$(get_codeowners "${COMPONENT}")" | ||
|
||
if [[ -z "${OWNERS:-}" ]]; then | ||
COMPONENT_TYPE=$(get_component_type "${COMPONENT}") | ||
OWNERS="$(get_codeowners "${COMPONENT}${COMPONENT_TYPE}")" | ||
fi | ||
|
||
echo "${OWNERS}" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
#!/usr/bin/env sh | ||
# | ||
# Copyright The OpenTelemetry Authors | ||
# SPDX-License-Identifier: Apache-2.0 | ||
# | ||
# Get a list of components within the repository that have some form of ownership | ||
# ascribed to them. | ||
|
||
grep -E '^[A-Za-z0-9/]' CODEOWNERS | \ | ||
awk '{ print $1 }' | \ | ||
sed -E 's%(.+)/$%\1%' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
#!/usr/bin/env bash | ||
# | ||
# Copyright The OpenTelemetry Authors | ||
# SPDX-License-Identifier: Apache-2.0 | ||
# | ||
# Adds code owners without write access as reviewers on a PR. Note that | ||
# the code owners must still be a member of the `open-telemetry` | ||
# organization. | ||
# | ||
# Note that since this script is considered a requirement for PRs, | ||
# it should never fail. | ||
|
||
set -euo pipefail | ||
|
||
if [[ -z "${REPO:-}" || -z "${PR:-}" ]]; then | ||
echo "One or more of REPO and PR have not been set, please ensure each is set." | ||
exit 0 | ||
fi | ||
|
||
main () { | ||
CUR_DIRECTORY=$(dirname "$0") | ||
|
||
# Reviews may have comments that need to be cleaned up for jq, | ||
# so restrict output to only printable characters and ensure escape | ||
# sequences are removed. | ||
# The latestReviews key only returns the latest review for each reviewer, | ||
# cutting out any other reviews. We use that instead of requestedReviews | ||
# since we need to get the list of users eligible for requesting another | ||
# review. The GitHub CLI does not offer a list of all reviewers, which | ||
# is only available through the API. To cut down on API calls to GitHub, | ||
# we use the latest reviews to determine which users to filter out. | ||
JSON=$(gh pr view "${PR}" --json "files,author,latestReviews" | tr -dc '[:print:]' | sed -E 's/\\[a-z]//g') | ||
AUTHOR=$(echo -n "${JSON}"| jq -r '.author.login') | ||
FILES=$(echo -n "${JSON}"| jq -r '.files[].path') | ||
REVIEW_LOGINS=$(echo -n "${JSON}"| jq -r '.latestReviews[].author.login') | ||
COMPONENTS=$(bash "${CUR_DIRECTORY}/get-components.sh") | ||
REVIEWERS="" | ||
declare -A PROCESSED_COMPONENTS | ||
declare -A REVIEWED | ||
|
||
for REVIEWER in ${REVIEW_LOGINS}; do | ||
# GitHub adds "app/" in front of user logins. The API docs don't make | ||
# it clear what this means or whether it will always be present. The | ||
# '/' character isn't a valid character for usernames, so this won't | ||
# replace characters within a username. | ||
REVIEWED["@${REVIEWER//app\//}"]=true | ||
done | ||
|
||
if [[ -v REVIEWED[@] ]]; then | ||
echo "Users that have already reviewed this PR and will not have another review requested:" "${!REVIEWED[@]}" | ||
else | ||
echo "This PR has not yet been reviewed, all code owners are eligible for a review request" | ||
fi | ||
|
||
for COMPONENT in ${COMPONENTS}; do | ||
# Files will be in alphabetical order and there are many files to | ||
# a component, so loop through files in an inner loop. This allows | ||
# us to remove all files for a component from the list so they | ||
# won't be checked against the remaining components in the components | ||
# list. This provides a meaningful speedup in practice. | ||
for FILE in ${FILES}; do | ||
MATCH=$(echo -n "${FILE}" | grep -E "^${COMPONENT}" || true) | ||
|
||
if [[ -z "${MATCH}" ]]; then | ||
continue | ||
fi | ||
|
||
# If we match a file with a component we don't need to process the file again. | ||
FILES=$(echo -n "${FILES}" | grep -v "${FILE}") | ||
|
||
if [[ -v PROCESSED_COMPONENTS["${COMPONENT}"] ]]; then | ||
continue | ||
fi | ||
|
||
PROCESSED_COMPONENTS["${COMPONENT}"]=true | ||
|
||
OWNERS=$(COMPONENT="${COMPONENT}" bash "${CUR_DIRECTORY}/get-codeowners.sh") | ||
|
||
for OWNER in ${OWNERS}; do | ||
# Users that leave reviews are removed from the "requested reviewers" | ||
# list and are eligible to have another review requested. We only want | ||
# to request a review once, so remove them from the list. | ||
if [[ -v REVIEWED["${OWNER}"] || "${OWNER}" = "@${AUTHOR}" ]]; then | ||
continue | ||
fi | ||
|
||
if [[ -n "${REVIEWERS}" ]]; then | ||
REVIEWERS+="," | ||
fi | ||
REVIEWERS+=$(echo -n "${OWNER}" | sed -E 's/@(.+)/"\1"/') | ||
done | ||
done | ||
done | ||
|
||
# We have to use the GitHub API directly due to an issue with how the CLI | ||
# handles PR updates that causes it require access to organization teams, | ||
# and the GitHub token doesn't provide that permission. | ||
# For more: https://github.com/cli/cli/issues/4844 | ||
# | ||
# The GitHub API validates that authors are not requested to review, but | ||
# accepts duplicate logins and logins that are already reviewers. | ||
if [[ -n "${REVIEWERS}" ]]; then | ||
echo "Requesting review from ${REVIEWERS}" | ||
curl \ | ||
-X POST \ | ||
-H "Accept: application/vnd.github+json" \ | ||
-H "Authorization: Bearer ${GITHUB_TOKEN}" \ | ||
"https://api.github.com/repos/${REPO}/pulls/${PR}/requested_reviewers" \ | ||
-d "{\"reviewers\":[${REVIEWERS}]}" \ | ||
| jq ".message" \ | ||
|| echo "jq was unable to parse GitHub's response" | ||
else | ||
echo "No code owners found" | ||
fi | ||
} | ||
|
||
# We don't want this workflow to ever fail and block a PR, | ||
# so ensure all errors are caught. | ||
main || echo "Failed to run $0" |