diff-pr.sh 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. #!/bin/bash
  2. set -eo pipefail
  3. shopt -s dotglob
  4. # make sure we can GTFO
  5. trap 'echo >&2 Ctrl+C captured, exiting; exit 1' SIGINT
  6. usage() {
  7. cat <<-EOUSAGE
  8. usage: $0 [PR number] [repo[:tag]]
  9. ie: $0 1024
  10. $0 9001 debian php django
  11. EOUSAGE
  12. }
  13. # TODO flags parsing
  14. allFiles=
  15. listTarballContents=1
  16. findCopies='20%'
  17. uninterestingTarballContent=(
  18. # "config_diff_2017_01_07.log"
  19. 'var/log/YaST2/'
  20. # "ks-script-mqmz_080.log"
  21. # "ks-script-ycfq606i.log"
  22. 'var/log/anaconda/'
  23. # "2016-12-20/"
  24. 'var/lib/yum/history/'
  25. 'var/lib/dnf/history/'
  26. # "a/f8c032d2be757e1a70f00336b55c434219fee230-acl-2.2.51-12.el7-x86_64/var_uuid"
  27. 'var/lib/yum/yumdb/'
  28. 'var/lib/dnf/yumdb/'
  29. # "b42ff584.0"
  30. 'etc/pki/tls/rootcerts/'
  31. )
  32. # prints "$2$1$3$1...$N"
  33. join() {
  34. local sep="$1"; shift
  35. local out; printf -v out "${sep//%/%%}%s" "$@"
  36. echo "${out#$sep}"
  37. }
  38. uninterestingTarballGrep="^([.]?/)?($(join '|' "${uninterestingTarballContent[@]}"))"
  39. if [ "$#" -eq 0 ]; then
  40. usage >&2
  41. exit 1
  42. fi
  43. pull="$1" # PR number
  44. shift
  45. #dir="$(dirname "$(readlink -f "$BASH_SOURCE")")"
  46. tempDir="$(mktemp -d)"
  47. trap "rm -rf '$tempDir'" EXIT
  48. cd "$tempDir"
  49. git clone --quiet \
  50. https://github.com/docker-library/official-images.git \
  51. oi
  52. git -C oi fetch --quiet \
  53. origin "pull/$pull/merge":pull
  54. images=( "$@" )
  55. if [ "${#images[@]}" -eq 0 ]; then
  56. images=( $(git -C oi/library diff --name-only master...pull -- . | xargs -n1 basename) )
  57. fi
  58. export BASHBREW_CACHE="${BASHBREW_CACHE:-${XDG_CACHE_HOME:-$HOME/.cache}/bashbrew}"
  59. export BASHBREW_LIBRARY="$PWD/oi/library"
  60. # TODO something less hacky than "git archive" hackery, like a "bashbrew archive" or "bashbrew context" or something
  61. template='
  62. {{- range $.Entries -}}
  63. {{- $from := $.DockerFrom . -}}
  64. git -C "$BASHBREW_CACHE/git" archive --format=tar
  65. {{- " " -}}
  66. {{- "--prefix=" -}}
  67. {{- $.RepoName -}}
  68. _
  69. {{- .Tags | last -}}
  70. {{- "/" -}}
  71. {{- " " -}}
  72. {{- .GitCommit -}}
  73. {{- ":" -}}
  74. {{- (eq .Directory ".") | ternary "" .Directory -}}
  75. {{- "\n" -}}
  76. {{- end -}}
  77. '
  78. copy-tar() {
  79. local src="$1"; shift
  80. local dst="$1"; shift
  81. if [ "$allFiles" ]; then
  82. mkdir -p "$dst"
  83. cp -al "$src"/*/ "$dst/"
  84. return
  85. fi
  86. # "Dockerfile*" at the end here ensures we capture "Dockerfile.builder" style repos in a useful way too (busybox, hello-world)
  87. for d in "$src"/*/Dockerfile*; do
  88. dDir="$(dirname "$d")"
  89. dDirName="$(basename "$dDir")"
  90. IFS=$'\n'
  91. files=(
  92. "$(basename "$d")"
  93. $(awk '
  94. toupper($1) == "COPY" || toupper($1) == "ADD" {
  95. for (i = 2; i < NF; i++) {
  96. print $i
  97. }
  98. }
  99. ' "$d")
  100. # some extra files which are likely interesting if they exist, but no big loss if they do not
  101. ' *.manifest' # debian/ubuntu "package versions" list
  102. ' *.ks' # fedora "kickstart" (rootfs build script)
  103. ' build*.txt' # ubuntu "build-info.txt", debian "build-command.txt"
  104. # usefulness yet to be proven:
  105. #' *.log'
  106. #' {MD5,SHA1,SHA256}SUMS'
  107. #' *.{md5,sha1,sha256}'
  108. # (the space prefix is removed below and is used to ignore non-matching globs so that bad "Dockerfile" entries appropriately lead to failure)
  109. )
  110. unset IFS
  111. mkdir -p "$dst/$dDirName"
  112. for origF in "${files[@]}"; do
  113. f="${origF# }" # trim off leading space (indicates we don't care about failure)
  114. [ "$f" = "$origF" ] && failureMatters=1 || failureMatters=
  115. globbed=( $(cd "$dDir" && eval "echo $f") )
  116. for g in "${globbed[@]}"; do
  117. if [ -z "$failureMatters" ] && [ ! -e "$dDir/$g" ]; then
  118. continue
  119. fi
  120. mkdir -p "$(dirname "$dst/$dDirName/$g")"
  121. cp -alT "$dDir/$g" "$dst/$dDirName/$g"
  122. if [ "$listTarballContents" ]; then
  123. case "$g" in
  124. *.tar.*|*.tgz)
  125. tar -tf "$dst/$dDirName/$g" \
  126. | grep -vE "$uninterestingTarballGrep" \
  127. | sort \
  128. > "$dst/$dDirName/$g 'tar -t'"
  129. ;;
  130. esac
  131. fi
  132. done
  133. done
  134. done
  135. }
  136. mkdir temp
  137. git -C temp init --quiet
  138. bashbrew list "${images[@]}" | sort -V > temp/_bashbrew-list || :
  139. for image in "${images[@]}"; do
  140. if script="$(bashbrew cat -f "$template" "$image")"; then
  141. mkdir tar
  142. ( eval "$script" | tar -xiC tar )
  143. copy-tar tar temp
  144. rm -rf tar
  145. fi
  146. done
  147. git -C temp add . || :
  148. git -C temp commit --quiet --allow-empty -m 'initial' || :
  149. git -C oi checkout --quiet pull
  150. git -C temp rm --quiet -rf . || :
  151. bashbrew list "${images[@]}" | sort -V > temp/_bashbrew-list || :
  152. script="$(bashbrew cat -f "$template" "${images[@]}")"
  153. mkdir tar
  154. ( eval "$script" | tar -xiC tar )
  155. copy-tar tar temp
  156. rm -rf tar
  157. git -C temp add .
  158. git -C temp diff --minimal --find-copies="$findCopies" --find-copies-harder --irreversible-delete --staged