1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138
|
#!/bin/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 [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.
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
# 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 "${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)
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:'}
line=${data%%:*}
# NB: supports multiple owners
# Does not support secondary owners declared in comment
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
for f in "${!owners[@]}"; do
printf '%s: %s\n' "$f" "${owners[$f]}"
done | sort -k 2 -k 1 # group by owner
# restore 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
|