diff --git a/.github/workflows/pr-review.yml b/.github/workflows/pr-review.yml new file mode 100644 index 0000000000..0293ce197c --- /dev/null +++ b/.github/workflows/pr-review.yml @@ -0,0 +1,22 @@ +name: PR review +# This workflow is triggered when a PR review is submitted. It checks if the review is approved and runs a script to add labels to the PR based on the review. +on: + pull_request_review: + types: + - submitted + +jobs: + add-owner-approved-label: + if: github.event.review.state == 'approved' + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Run add-labels-to-reviewed-pr.sh + run: ./.github/workflows/scripts/add-labels-to-reviewed-pr.sh + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + REPO: ${{ github.repository }} + PR: ${{ github.event.number }} + \ No newline at end of file diff --git a/.github/workflows/scripts/add-labels-to-reviewed-pr.sh b/.github/workflows/scripts/add-labels-to-reviewed-pr.sh new file mode 100644 index 0000000000..c06607fe6f --- /dev/null +++ b/.github/workflows/scripts/add-labels-to-reviewed-pr.sh @@ -0,0 +1,75 @@ +#!/usr/bin/env bash +# +# Copyright The OpenTelemetry 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 +# +# https://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. +# +# Adds a "has:owner-approval" label to a PR if a reviewer who approved it is a component owner. + +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") + + # The latestReviews key returns the latest review for each reviewer cutting out any other reviews. + JSON=$(gh pr view "${PR}" --json "files,author,latestReviews" | tr -dc '[:print:]' | sed -E 's/\\[a-z]//g') + FILES=$(echo -n "${JSON}"| jq -r '.files[].path') + LATEST_REVIEWS=$(echo -n "${JSON}" | jq -c '.latestReviews') + + # Fetch components + COMPONENTS=$(bash "${CUR_DIRECTORY}/get-components.sh" | tac) # Reversed so we visit subdirectories first + + declare -A PROCESSED_COMPONENTS + + for COMPONENT in ${COMPONENTS}; do + COMPONENT_OWNERS=$(COMPONENT="${COMPONENT}" bash "${CUR_DIRECTORY}/get-codeowners.sh") + + 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, skip further processing for this file + if [[ -v PROCESSED_COMPONENTS["${COMPONENT}"] ]]; then + continue + fi + PROCESSED_COMPONENTS["${COMPONENT}"]=true + + # Check if updated file is owned by one of the reviewers" + echo "${LATEST_REVIEWS}" | jq -c '.[]' | while IFS= read -r REVIEW; do + REVIEW_AUTHOR=$(echo -n "${REVIEW}"| jq -r '.author.login') + REVIEW_STATE=$(echo -n "${REVIEW}"| jq -r '.state') + if [[ "${REVIEW_STATE}" == "APPROVED" ]]; then + # Review is approved. Checking if reviewer is a component owner + for OWNER in ${COMPONENT_OWNERS}; do + if [[ "${REVIEW_AUTHOR}" == "${OWNER}" ]]; then + echo "Reviewer $REVIEW_AUTHOR is a component owner. Adding 'has:owner-approval' label." + gh pr edit "${PR}" --repo "${REPO}" --add-label "has:owner-approval" + exit 0 + fi + done + fi + done + done + done +} + +# Ensure the script does not block a PR even if it fails +main || echo "Failed to run $0" diff --git a/.github/workflows/scripts/get-codeowners.sh b/.github/workflows/scripts/get-codeowners.sh new file mode 100644 index 0000000000..03ce00bc91 --- /dev/null +++ b/.github/workflows/scripts/get-codeowners.sh @@ -0,0 +1,50 @@ +#!/usr/bin/env bash +# +# Copyright The OpenTelemetry 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 +# +# https://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. +# +# Gets the owners for a given component from the component_owners.yml file. + +# Define the file path +YML_FILE=".github/component_owners.yml" + +if [[ -z "${COMPONENT:-}" ]]; then + echo "COMPONENT has not been set, please ensure it is set." + exit 1 +fi + +FOUND=0 + +# Parse the YAML file and extract owners for the given component +while IFS= read -r line; do + # Check if the line matches the given component + if [[ "$line" =~ ^[[:space:]]*${COMPONENT}:[[:space:]]*$ ]]; then + FOUND=1 + continue + fi + + # If the component is found, extract owners + if [[ $FOUND -eq 1 ]]; then + # Stop if we encounter another component or an empty line + if [[ "$line" =~ ^[[:space:]]*[^#]+: || -z "$line" ]]; then + break + fi + + # Extract the owner (remove leading spaces and '- ') + if [[ "$line" =~ ^[[:space:]]*-[[:space:]]*[^#]+ ]]; then + OWNER=$(echo "$line" | sed -E 's/^[[:space:]]*-[[:space:]]*([^#]+).*/\1/') + echo "$OWNER" + fi + fi +done < "$YML_FILE" diff --git a/.github/workflows/scripts/get-components.sh b/.github/workflows/scripts/get-components.sh new file mode 100644 index 0000000000..57eda9eb0b --- /dev/null +++ b/.github/workflows/scripts/get-components.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash +# +# Copyright The OpenTelemetry 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 +# +# https://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. +# +# Gets the components from the component_owners.yml file. + + +# Define the file path +YML_FILE=".github/component_owners.yml" + +# Parse the YAML file and extract components and their owners +while IFS= read -r line; do + # Check if the line contains a component (ends with ':') + if [[ "$line" =~ ^[[:space:]]*[^#]+: ]]; then + # Extract the component name (remove leading spaces and trailing ':') + COMPONENT=$(echo "$line" | sed -E 's/^[[:space:]]*([^:]+):.*/\1/') + echo "$COMPONENT" + fi +done < "$YML_FILE"