naughty-commits.sh 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. #!/usr/bin/env bash
  2. set -Eeuo pipefail
  3. fileSizeThresholdMB='2'
  4. export BASHBREW_ARCH=
  5. gitCache="$(bashbrew cat --format '{{ gitCache }}' <(echo 'Maintainers: empty hack (@example)'))"
  6. _git() {
  7. git -C "$gitCache" "$@"
  8. }
  9. if [ "$#" -eq 0 ]; then
  10. set -- '--all'
  11. fi
  12. imgs="$(bashbrew list --repos "$@" | sort -u)"
  13. for img in $imgs; do
  14. bashbrew fetch "$img" # force `git fetch`
  15. commits="$(
  16. bashbrew cat --format '
  17. {{- range $e := .Entries -}}
  18. {{- range $a := .Architectures -}}
  19. {
  20. {{- json "GitRepo" }}:{{ json ($e.ArchGitRepo $a) -}},
  21. {{- json "GitFetch" }}:{{ json ($e.ArchGitFetch $a) -}},
  22. {{- json "GitCommit" }}:{{ json ($e.ArchGitCommit $a) -}}
  23. }
  24. {{- "\n" -}}
  25. {{- end -}}
  26. {{- end -}}
  27. ' "$img" | jq -s 'unique'
  28. )"
  29. declare -A naughtyCommits=() naughtyTopCommits=() seenCommits=()
  30. length="$(jq -r 'length' <<<"$commits")"
  31. for (( i = 0; i < length; i++ )); do
  32. topCommit="$(jq -r ".[$i].GitCommit" <<<"$commits")"
  33. gitRepo="$(jq -r ".[$i].GitRepo" <<<"$commits")"
  34. gitFetch="$(jq -r ".[$i].GitFetch" <<<"$commits")"
  35. if ! _git fetch --quiet "$gitRepo" "$gitFetch:" ; then
  36. naughtyCommits[$topCommit]="unable to to fetch specified GitFetch: $gitFetch"
  37. naughtyTopCommits[$topCommit]="$topCommit"
  38. elif ! _git merge-base --is-ancestor "$topCommit" 'FETCH_HEAD'; then
  39. # check that the commit is in the GitFetch branch specified
  40. naughtyCommits[$topCommit]="is not in the specified ref GitFetch: $gitFetch"
  41. naughtyTopCommits[$topCommit]="$topCommit"
  42. fi
  43. IFS=$'\n'
  44. potentiallyNaughtyGlobs=( '**.tar**' )
  45. potentiallyNaughtyCommits=( $(_git log --diff-filter=DMT --format='format:%H' "$topCommit" -- "${potentiallyNaughtyGlobs[@]}") )
  46. unset IFS
  47. # bash 4.3 sucks (https://stackoverflow.com/a/7577209/433558)
  48. [ "${#potentiallyNaughtyCommits[@]}" -gt 0 ] || continue
  49. for commit in "${potentiallyNaughtyCommits[@]}"; do
  50. [ -z "${seenCommits[$commit]:-}" ] || break
  51. seenCommits[$commit]=1
  52. IFS=$'\n'
  53. binaryFiles=( $(
  54. _git diff-tree --no-commit-id -r --numstat --diff-filter=DMT "$commit" -- "${potentiallyNaughtyGlobs[@]}" \
  55. | grep '^-' \
  56. | cut -d$'\t' -f3- \
  57. || :
  58. ) )
  59. unset IFS
  60. # bash 4.3 sucks (https://stackoverflow.com/a/7577209/433558)
  61. [ "${#binaryFiles[@]}" -gt 0 ] || continue
  62. naughtyReasons=()
  63. for file in "${binaryFiles[@]}"; do
  64. fileSize="$(_git ls-tree -r --long "$commit" -- "$file" | awk '{ print $4 }')"
  65. fileSizeMB="$(( fileSize / 1024 / 1024 ))"
  66. if [ "$fileSizeMB" -gt "$fileSizeThresholdMB" ]; then
  67. naughtyReasons+=( "modified binary file (larger than ${fileSizeThresholdMB}MB): $file (${fileSizeMB}MB)" )
  68. fi
  69. done
  70. if [ "${#naughtyReasons[@]}" -gt 0 ]; then
  71. : "${naughtyCommits[$commit]:=}"
  72. if [ -n "${naughtyCommits[$commit]}" ]; then
  73. naughtyCommits[$commit]+=$'\n'
  74. fi
  75. IFS=$'\n'
  76. naughtyCommits[$commit]+="${naughtyReasons[*]}"
  77. unset IFS
  78. naughtyTopCommits[$commit]="$topCommit"
  79. fi
  80. done
  81. done
  82. if [ "${#naughtyCommits[@]}" -gt 0 ]; then
  83. echo " - $img:"
  84. for naughtyCommit in "${!naughtyCommits[@]}"; do
  85. naughtyReasons="${naughtyCommits[$naughtyCommit]}"
  86. naughtyTopCommit="${naughtyTopCommits[$naughtyCommit]}"
  87. if [ "$naughtyTopCommit" != "$naughtyCommit" ]; then
  88. #commitsBetween="$(_git rev-list --count "$naughtyCommit...$naughtyTopCommit")"
  89. naughtyCommit+=" (in history of $naughtyTopCommit)"
  90. fi
  91. echo " - commit $naughtyCommit:"
  92. sed -e 's/^/ - /' <<<"$naughtyReasons"
  93. done
  94. echo
  95. fi
  96. done