From 11fe480932306e3bd702b19674f385f5e36398b7 Mon Sep 17 00:00:00 2001 From: Gaëtan Gilbert Date: Wed, 4 Apr 2018 15:23:42 +0200 Subject: Script to identify the code owner for given files Can be used with git diff --name-only to identify owners for changes in given commits. --- dev/tools/check-owners.sh | 79 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100755 dev/tools/check-owners.sh (limited to 'dev') diff --git a/dev/tools/check-owners.sh b/dev/tools/check-owners.sh new file mode 100755 index 0000000000..c3c545d6df --- /dev/null +++ b/dev/tools/check-owners.sh @@ -0,0 +1,79 @@ +#!/usr/bin/env bash + +# Determine CODEOWNERS of the files given in argument +# For a given commit range: +# git diff --name-only -z COMMIT1 COMMIT2 | xargs -0 dev/tools/check-owners.sh + +# NB: gitignore files will be messed up if you interrupt the script. +# You should be able to just move the .gitignore.bak files back manually. + +if [ $# = 0 ]; then + >&2 echo "usage: $0 [FILE]..." + exit 1 +fi + +if ! [ -e .github/CODEOWNERS ]; then + >&2 echo "No CODEOWNERS set up or calling from wrong directory." + exit 1 +fi + +# CODEOWNERS uses .gitignore patterns so we want to use git to parse it +# The only available tool for that is git check-ignore +# However it provides no way to use alternate .gitignore files +# so we rename them temporarily + +find . -name .gitignore -print0 | while IFS= read -r -d '' f; do + if [ -e "$f.bak" ]; then + >&2 echo "$f.bak exists!" + exit 1 + else + mv "$f" "$f.bak" + fi +done + +# CODEOWNERS is not quite .gitignore patterns: +# after the pattern is the owner (space separated) +# git would interpret that as a big pattern containing spaces +# so we create a valid .gitignore by removing all but the first field + +while read -r pat _; do + printf "%s\n" "$pat" >> .gitignore +done < .github/CODEOWNERS + +# associative array [file => owner] +declare -A owners + +for f in "$@"; do + data=$(git check-ignore --verbose --no-index "./$f") + code=$? + + if [[ "$code" = 1 ]] || ! [[ "$data" =~ .gitignore:.* ]] ; then + # no match, or match from non tracked gitignore (eg global gitignore) + owners[$f]="Nobody" + else + # data looks like [.gitignore:$line:$pattern $file] + # extract the line to look it up in CODEOWNERS + data=${data#'.gitignore:'} + data=${data%%:*} + + # NB: supports multiple owners + # Does not support secondary owners declared in comment + read -r _ fowners < <(sed "${data}q;d" .github/CODEOWNERS) + owners["$f"]="$fowners" + fi +done + +for f in "${!owners[@]}"; do + printf "%s: %s\n" "$f" "${owners[$f]}" +done | sort -k 2 -k 1 # group by owner + +# restort gitignore files +rm .gitignore +find . -name .gitignore.bak -print0 | while IFS= read -r -d '' f; do + base=${f%.bak} + if [ -e "$base" ]; then + >&2 echo "$base exists!" + else + mv "$f" "$base" + fi +done -- cgit v1.2.3 From ea1d8bbc3cddbd661439155240bd6f9ec477d84c Mon Sep 17 00:00:00 2001 From: Gaëtan Gilbert Date: Wed, 4 Apr 2018 16:28:36 +0200 Subject: check-owners.sh: add --show-patterns and --owner options ```bash $ dev/tools/check-owners.sh --show-patterns Makefile Makefile.build /Makefile*: @letouzey $ dev/tools/check-owners.sh --owner '@gares' stm/stm.ml interp/declare.ml stm/stm.ml: @gares $ dev/tools/check-owners.sh --show-patterns --owner '@gares' stm/*.ml interp/*.ml /stm/: @gares ``` --- dev/tools/check-owners.sh | 79 +++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 69 insertions(+), 10 deletions(-) (limited to 'dev') diff --git a/dev/tools/check-owners.sh b/dev/tools/check-owners.sh index c3c545d6df..c28ab5cb63 100755 --- a/dev/tools/check-owners.sh +++ b/dev/tools/check-owners.sh @@ -2,21 +2,57 @@ # Determine CODEOWNERS of the files given in argument # For a given commit range: -# git diff --name-only -z COMMIT1 COMMIT2 | xargs -0 dev/tools/check-owners.sh +# git diff --name-only -z COMMIT1 COMMIT2 | xargs -0 dev/tools/check-owners.sh [opts] # NB: gitignore files will be messed up if you interrupt the script. # You should be able to just move the .gitignore.bak files back manually. -if [ $# = 0 ]; then - >&2 echo "usage: $0 [FILE]..." - exit 1 -fi +usage() { + { echo "usage: $0 [--show-patterns] [--owner OWNER] [FILE]..." + echo " --show-patterns: instead of printing file names print the matching patterns (more compact)" + echo " --owner: show only files/patterns owned by OWNER (use Nobody to see only non-owned files)" + } >&2 +} + +case "$1" in + "--help"|"-h") + usage + if [ $# = 1 ]; then exit 0; else exit 1; fi +esac if ! [ -e .github/CODEOWNERS ]; then >&2 echo "No CODEOWNERS set up or calling from wrong directory." exit 1 fi +files=() +show_patterns=false + +target_owner="" + +while [[ "$#" -gt 0 ]]; do + case "$1" in + "--show-patterns") + show_patterns=true + shift;; + "--owner") + if [[ "$#" = 1 ]]; then + >&2 echo "Missing argument to --owner" + usage + exit 1 + elif [[ "$target_owner" != "" ]]; then + >&2 echo "Only one --owner allowed" + usage + exit 1 + fi + target_owner="$2" + shift 2;; + *) + files+=("$@") + break;; + esac +done + # CODEOWNERS uses .gitignore patterns so we want to use git to parse it # The only available tool for that is git check-ignore # However it provides no way to use alternate .gitignore files @@ -43,23 +79,46 @@ done < .github/CODEOWNERS # associative array [file => owner] declare -A owners -for f in "$@"; do +for f in "${files[@]}"; do data=$(git check-ignore --verbose --no-index "./$f") code=$? if [[ "$code" = 1 ]] || ! [[ "$data" =~ .gitignore:.* ]] ; then # no match, or match from non tracked gitignore (eg global gitignore) - owners[$f]="Nobody" + if [ "$target_owner" != "" ] && [ "$target_owner" != Nobody ] ; then + owner="" + else + owner="Nobody" + pat="$f" # no patterns for unowned files + fi else # data looks like [.gitignore:$line:$pattern $file] # extract the line to look it up in CODEOWNERS data=${data#'.gitignore:'} - data=${data%%:*} + line=${data%%:*} # NB: supports multiple owners # Does not support secondary owners declared in comment - read -r _ fowners < <(sed "${data}q;d" .github/CODEOWNERS) - owners["$f"]="$fowners" + read -r pat fowners < <(sed "${line}q;d" .github/CODEOWNERS) + + owner="" + if [ "$target_owner" != "" ]; then + for o in $fowners; do # do not quote: multiple owners possible + if [ "$o" = "$target_owner" ]; then + owner="$o" + fi + done + else + owner="$fowners" + fi + fi + + if [ "$owner" != "" ]; then + if $show_patterns; then + owners[$pat]="$owner" + else + owners[$f]="$owner" + fi fi done -- cgit v1.2.3 From aed2eec838151dfe13e9ad2697b7ccfe319778df Mon Sep 17 00:00:00 2001 From: Gaëtan Gilbert Date: Thu, 5 Apr 2018 11:03:58 +0200 Subject: Add check-owners-pr.sh wrapper around check-owners ``` $ dev/tools/check-owners-pr.sh 6809 --show-patterns --owner '@MSoegtropIMC' remote: Counting objects: 93, done. remote: Compressing objects: 100% (3/3), done. remote: Total 93 (delta 47), reused 50 (delta 47), pack-reused 43 Unpacking objects: 100% (93/93), done. From github.com:coq/coq * branch refs/pull/6809/head -> FETCH_HEAD * branch master -> FETCH_HEAD /dev/build/windows: @MSoegtropIMC ``` --- dev/tools/check-owners-pr.sh | 32 ++++++++++++++++++++++++++++++++ dev/tools/check-owners.sh | 6 +++--- 2 files changed, 35 insertions(+), 3 deletions(-) create mode 100755 dev/tools/check-owners-pr.sh (limited to 'dev') diff --git a/dev/tools/check-owners-pr.sh b/dev/tools/check-owners-pr.sh new file mode 100755 index 0000000000..d2910279b5 --- /dev/null +++ b/dev/tools/check-owners-pr.sh @@ -0,0 +1,32 @@ +#!/usr/bin/env sh + +usage() { + { echo "usage: $0 PR [ARGS]..." + echo "A wrapper around check-owners.sh to check owners for a PR." + echo "Assumes upstream is the canonical Coq repository." + echo "Assumes the PR is against master." + echo + echo " PR: PR number" + echo " ARGS: passed through to check-owners.sh" + } >&2 +} + +case "$1" in + "--help"|"-h") + usage + if [ $# = 1 ]; then exit 0; else exit 1; fi;; + "") + usage + exit 1;; +esac + +PR="$1" +shift + +# this puts both refs in the FETCH_HEAD file but git rev-parse will use the first +git fetch upstream "+refs/pull/$PR/head" master + +head=$(git rev-parse FETCH_HEAD) +base=$(git merge-base upstream/master "$head") + +git diff --name-only -z "$base" "$head" | xargs -0 dev/tools/check-owners.sh "$@" diff --git a/dev/tools/check-owners.sh b/dev/tools/check-owners.sh index c28ab5cb63..1a97508abb 100755 --- a/dev/tools/check-owners.sh +++ b/dev/tools/check-owners.sh @@ -73,7 +73,7 @@ done # so we create a valid .gitignore by removing all but the first field while read -r pat _; do - printf "%s\n" "$pat" >> .gitignore + printf '%s\n' "$pat" >> .gitignore done < .github/CODEOWNERS # associative array [file => owner] @@ -123,10 +123,10 @@ for f in "${files[@]}"; do done for f in "${!owners[@]}"; do - printf "%s: %s\n" "$f" "${owners[$f]}" + printf '%s: %s\n' "$f" "${owners[$f]}" done | sort -k 2 -k 1 # group by owner -# restort gitignore files +# restore gitignore files rm .gitignore find . -name .gitignore.bak -print0 | while IFS= read -r -d '' f; do base=${f%.bak} -- cgit v1.2.3