blob: d6a61c3dd452c82de1fb235cb8722ff6ecfcbeb3 [file] [log] [blame]
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
#
# Copyright 2024 Google LLC
#
# Author: Lee Jones <lee@kernel.org>
#
# Usage
# voting_results [ v6.7.1..v6.7.2 ]
#
# * Conducts voting system between the present reviewers
# set -x # Uncomment to enable debugging
# ------- ACTION REQUIRED -------
# Change these to suit your own setup
STABLEREMOTE=stable # Whatever you called your Stable remote
# Define primary reviewers - other reviewers are determined dynamically
declare -a PRIMARY_REVIEWERS=("greg" "lee" "sasha")
# Define colors if we're in a terminal
if [[ -t 1 ]]; then
RED="\e[1;31m"
BLUE="\e[1;34m"
RESET="\e[0m"
else
RED=""
BLUE=""
RESET=""
fi
print_red() { echo -e "${RED}$*${RESET}"; }
print_blue() { echo -e "${BLUE}$*${RESET}"; }
function usage() {
echo "Usage: $(basename $0) [RANGE]"
echo
echo "Conducts voting system between reviewers for the specified Git range"
echo
echo "Arguments:"
echo " RANGE Git range in the format 'v6.7.1..v6.7.2'"
echo " --help, -h Display this help message and exit"
echo
echo "Example:"
echo " $(basename $0) v6.7.1..v6.7.2"
exit 0
}
function print_annotations()
{
oneline="${1}"
sha="$(echo ${oneline} | cut -d' ' -f1)"
for f in ${ANNOTATEDFILES}; do
annotation=$(grep -A1 ${sha} ${PROPOSED}/${f} | tail -n1)
if [ "${annotation}" != "" ]; then
echo " ${annotation}"
fi
done
}
while [ $# -gt 0 ]; do
case $1 in
*..*)
RANGE=${1}
;;
--help|-h)
usage
;;
*)
print_red "Unrecognised argument: ${1}"
usage
;;
esac
shift
done
if [ "${RANGE}" == "" ]; then
print_red "Please supply a Git range (e.g v6.7.1..v6.7.2)"
usage
fi
if [ ! -e .git ] || [ ! -f MAINTAINERS ]; then
print_red "Not in a kernel directory"
exit 1
fi
print_blue "Fetching from ${STABLEREMOTE}"
git fetch ${STABLEREMOTE}
TOP=${RANGE#*..}
BOTTOM=${RANGE%..*}
SCRIPTDIR=$(dirname ${0})
PROPOSED=${SCRIPTDIR}/../cve/review/proposed
# Determine all reviewers dynamically by looking at review files
declare -a REVIEWERS=()
if [ -d "${PROPOSED}" ]; then
# Extract reviewer names from review filenames
for file in $(ls ${PROPOSED} | grep -E "${TOP}-[a-z]+$"); do
reviewer=${file#*-}
# Check if reviewer is not already in array
if [[ ! " ${REVIEWERS[*]} " =~ " ${reviewer} " ]]; then
REVIEWERS+=("$reviewer")
fi
done
else
print_red "Cannot find review directory: ${PROPOSED}"
exit 1
fi
# If no reviewers found, use primary reviewers as fallback
if [ ${#REVIEWERS[@]} -eq 0 ]; then
print_red "No reviewers found in review files, using only primary reviewers"
REVIEWERS=("${PRIMARY_REVIEWERS[@]}")
fi
# Create reviewer pattern for grep
REVIEWER_PATTERN=$(IFS="|"; echo "${REVIEWERS[*]}")
REVIEWFILES=$(ls ${PROPOSED} | grep -E "${TOP}-(${REVIEWER_PATTERN})$")
ANNOTATEDFILES=$(ls ${PROPOSED} | grep -E "${TOP}.*-annotated-(${REVIEWER_PATTERN})$")
# Identify guest reviewers - anyone who is not a primary reviewer
declare -a GUEST_REVIEWERS=()
for reviewer in "${REVIEWERS[@]}"; do
is_primary=0
for primary in "${PRIMARY_REVIEWERS[@]}"; do
if [ "$reviewer" = "$primary" ]; then
is_primary=1
break
fi
done
if [ $is_primary -eq 0 ]; then
GUEST_REVIEWERS+=("$reviewer")
fi
done
# Display found reviewers for debugging
print_blue "Primary reviewers: ${PRIMARY_REVIEWERS[*]}"
print_blue "All reviewers found: ${REVIEWERS[*]}"
if [ ${#GUEST_REVIEWERS[@]} -gt 0 ]; then
print_blue "Guest reviewers: ${GUEST_REVIEWERS[*]}"
else
print_blue "No guest reviewers found"
fi
# Initialize arrays for storing commits by vote pattern
declare -A commits_by_pattern
commits_by_pattern["cve"]=""
commits_by_pattern["all"]=""
# Initialize vote patterns
for reviewer in "${REVIEWERS[@]}"; do
commits_by_pattern["$reviewer"]=""
done
# Two-reviewer combinations for primary reviewers
for ((i=0; i<${#PRIMARY_REVIEWERS[@]}; i++)); do
for ((j=i+1; j<${#PRIMARY_REVIEWERS[@]}; j++)); do
pattern="${PRIMARY_REVIEWERS[i]},${PRIMARY_REVIEWERS[j]}"
commits_by_pattern["$pattern"]=""
done
done
# Combinations with guest reviewers
for reviewer in "${PRIMARY_REVIEWERS[@]}"; do
for guest in "${GUEST_REVIEWERS[@]}"; do
pattern="${reviewer},${guest}"
commits_by_pattern["$pattern"]=""
done
done
for stablesha in $(git log --format=%h ${BOTTOM}..${TOP}); do
mainlinelongsha=$(git --no-pager log -n1 ${stablesha} | grep -i upstream | grep -oE "[a-f0-9]{40,}") || true
# If the commit does not contain a Mainline SHA, we'll assume it's Stable only
if [ "${mainlinesha}" == "" ]; then
mainlinesha=${stablesha}
fi
oneline=$(git --no-pager log --format="%h %s" -n1 ${mainlinelongsha})
subject=$(echo ${oneline} | cut -d' ' -f 2-)
mainlinesha=$(echo ${oneline} | cut -d' ' -f 1)
# Track votes using associative array
declare -A reviewer_votes
total_votes=0
for reviewer in "${REVIEWERS[@]}"; do
reviewer_votes["$reviewer"]=0
done
# Count votes
for f in ${REVIEWFILES}; do
reviewer=${f#*-}
if grep -qF "${subject}" ${PROPOSED}/${f}; then
reviewer_votes["$reviewer"]=1
total_votes=$((total_votes + 1))
fi
done
# Skip if no votes
if [ ${total_votes} -eq 0 ]; then
continue
fi
# Check for CVE
found=$(${SCRIPTDIR}/cve_search ${mainlinesha})
found_result=$?
if [ "${found_result}" == "0" ]; then
commits_by_pattern["cve"]="${commits_by_pattern["cve"]}
${oneline}"
continue
fi
# Check for all reviewers agreeing
all_agreed=1
for reviewer in "${REVIEWERS[@]}"; do
if [ ${reviewer_votes["$reviewer"]} -eq 0 ]; then
all_agreed=0
break
fi
done
if [ ${all_agreed} -eq 1 ]; then
commits_by_pattern["all"]="${commits_by_pattern["all"]}
${oneline}"
continue
fi
# Generate vote pattern string and store commit
if [ ${total_votes} -eq 1 ]; then
# Single reviewer
for reviewer in "${REVIEWERS[@]}"; do
if [ ${reviewer_votes["$reviewer"]} -eq 1 ]; then
commits_by_pattern["$reviewer"]="${commits_by_pattern["$reviewer"]}
${oneline}"
fi
done
else
# Check two-reviewer combinations
for ((i=0; i<${#REVIEWERS[@]}; i++)); do
for ((j=i+1; j<${#REVIEWERS[@]}; j++)); do
if [ ${reviewer_votes[${REVIEWERS[i]}]} -eq 1 ] && [ ${reviewer_votes[${REVIEWERS[j]}]} -eq 1 ]; then
pattern="${REVIEWERS[i]},${REVIEWERS[j]}"
commits_by_pattern["$pattern"]="${commits_by_pattern["$pattern"]}
${oneline}"
fi
done
done
fi
echo "${oneline}"
echo -ne "\tCVE:\t${found_result}\t"
for reviewer in "${REVIEWERS[@]}"; do
echo -ne "${reviewer^}:\t${reviewer_votes[$reviewer]}\t"
done
echo
done
# Print results
print_blue "\nAlready assigned a CVE"
echo "${commits_by_pattern["cve"]}" | while read -r commit; do
if [ ! -z "$commit" ]; then
echo " $commit"
print_annotations "$commit"
fi
done
print_blue "\nEveryone agrees"
echo "${commits_by_pattern["all"]}" | while read -r commit; do
if [ ! -z "$commit" ]; then
echo " $commit"
print_annotations "$commit"
fi
done
# Print primary reviewer combinations
for ((i=0; i<${#PRIMARY_REVIEWERS[@]}; i++)); do
for ((j=i+1; j<${#PRIMARY_REVIEWERS[@]}; j++)); do
pattern="${PRIMARY_REVIEWERS[i]},${PRIMARY_REVIEWERS[j]}"
print_blue "\n${PRIMARY_REVIEWERS[i]^} and ${PRIMARY_REVIEWERS[j]^} agree"
echo "${commits_by_pattern["$pattern"]}" | while read -r commit; do
if [ ! -z "$commit" ]; then
echo " $commit"
print_annotations "$commit"
fi
done
done
done
# Print single primary reviewer results
for reviewer in "${PRIMARY_REVIEWERS[@]}"; do
print_blue "\n${reviewer^} only"
echo "${commits_by_pattern["$reviewer"]}" | while read -r commit; do
if [ ! -z "$commit" ]; then
echo " $commit"
print_annotations "$commit"
fi
done
done
print_blue "\n------------ GUEST RESULTS BELOW, use for re-review only at this time ----------------"
# Print combinations with guest reviewers
for reviewer in "${PRIMARY_REVIEWERS[@]}"; do
for guest in "${GUEST_REVIEWERS[@]}"; do
pattern="${reviewer},${guest}"
print_blue "\n${reviewer^} and guest (${guest^}) agree"
echo "${commits_by_pattern["$pattern"]}" | while read -r commit; do
if [ ! -z "$commit" ]; then
echo " $commit"
print_annotations "$commit"
fi
done
done
done
# Print guest-only results - consolidated into a single section
print_blue "\nGuest-only results"
for guest in "${GUEST_REVIEWERS[@]}"; do
print_blue " ${guest^}:"
echo "${commits_by_pattern["$guest"]}" | while read -r commit; do
if [ ! -z "$commit" ]; then
echo " $commit"
print_annotations "$commit"
fi
done
done