diff-pr.sh 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  1. #!/usr/bin/env bash
  2. set -Eeuo pipefail
  3. shopt -s dotglob
  4. # make sure we can GTFO
  5. trap 'echo >&2 Ctrl+C captured, exiting; exit 1' SIGINT
  6. # if bashbrew is missing, bail early with a sane error
  7. bashbrew --version > /dev/null
  8. usage() {
  9. cat <<-EOUSAGE
  10. usage: $0 [PR number] [repo[:tag]]
  11. ie: $0 1024
  12. $0 9001 debian php django
  13. EOUSAGE
  14. }
  15. # TODO flags parsing
  16. allFiles=
  17. listTarballContents=1
  18. findCopies='20%'
  19. uninterestingTarballContent=(
  20. # "config_diff_2017_01_07.log"
  21. 'var/log/YaST2/'
  22. # "ks-script-mqmz_080.log"
  23. # "ks-script-ycfq606i.log"
  24. 'var/log/anaconda/'
  25. # "2016-12-20/"
  26. 'var/lib/yum/history/'
  27. 'var/lib/dnf/history/'
  28. # "a/f8c032d2be757e1a70f00336b55c434219fee230-acl-2.2.51-12.el7-x86_64/var_uuid"
  29. 'var/lib/yum/yumdb/'
  30. 'var/lib/dnf/yumdb/'
  31. # "b42ff584.0"
  32. 'etc/pki/tls/rootcerts/'
  33. # "09/401f736622f2c9258d14388ebd47900bbab126"
  34. 'usr/lib/.build-id/'
  35. )
  36. # prints "$2$1$3$1...$N"
  37. join() {
  38. local sep="$1"; shift
  39. local out; printf -v out "${sep//%/%%}%s" "$@"
  40. echo "${out#$sep}"
  41. }
  42. uninterestingTarballGrep="^([.]?/)?($(join '|' "${uninterestingTarballContent[@]}"))"
  43. if [ "$#" -eq 0 ]; then
  44. usage >&2
  45. exit 1
  46. fi
  47. pull="$1" # PR number
  48. shift
  49. diffDir="$(readlink -f "$BASH_SOURCE")"
  50. diffDir="$(dirname "$diffDir")"
  51. tempDir="$(mktemp -d)"
  52. trap "rm -rf '$tempDir'" EXIT
  53. cd "$tempDir"
  54. git clone --quiet \
  55. https://github.com/docker-library/official-images.git \
  56. oi
  57. if [ "$pull" != '0' ]; then
  58. git -C oi fetch --quiet \
  59. origin "pull/$pull/merge":refs/heads/pull
  60. else
  61. git -C oi fetch --quiet --update-shallow \
  62. "$diffDir" HEAD:refs/heads/pull
  63. fi
  64. if [ "$#" -eq 0 ]; then
  65. images="$(git -C oi/library diff --name-only HEAD...pull -- .)"
  66. [ -n "$images" ] || exit 0
  67. images="$(xargs -n1 basename <<<"$images")"
  68. set -- $images
  69. fi
  70. export BASHBREW_CACHE="${BASHBREW_CACHE:-${XDG_CACHE_HOME:-$HOME/.cache}/bashbrew}"
  71. export BASHBREW_LIBRARY="$PWD/oi/library"
  72. : "${BASHBREW_ARCH:=amd64}" # TODO something smarter with arches
  73. export BASHBREW_ARCH
  74. # TODO something less hacky than "git archive" hackery, like a "bashbrew archive" or "bashbrew context" or something
  75. template='
  76. tempDir="$(mktemp -d)"
  77. {{- "\n" -}}
  78. {{- range $.Entries -}}
  79. {{- $arch := .HasArchitecture arch | ternary arch (.Architectures | first) -}}
  80. {{- $froms := $.ArchDockerFroms $arch . -}}
  81. {{- $outDir := join "_" $.RepoName (.Tags | last) -}}
  82. git -C "$BASHBREW_CACHE/git" archive --format=tar
  83. {{- " " -}}
  84. {{- "--prefix=" -}}
  85. {{- $outDir -}}
  86. {{- "/" -}}
  87. {{- " " -}}
  88. {{- .ArchGitCommit $arch -}}
  89. {{- ":" -}}
  90. {{- $dir := .ArchDirectory $arch -}}
  91. {{- (eq $dir ".") | ternary "" $dir -}}
  92. {{- "\n" -}}
  93. mkdir -p "$tempDir/{{- $outDir -}}" && echo "{{- .ArchFile $arch -}}" > "$tempDir/{{- $outDir -}}/.bashbrew-dockerfile-name"
  94. {{- "\n" -}}
  95. {{- end -}}
  96. tar -cC "$tempDir" . && rm -rf "$tempDir"
  97. '
  98. copy-tar() {
  99. local src="$1"; shift
  100. local dst="$1"; shift
  101. if [ -n "$allFiles" ]; then
  102. mkdir -p "$dst"
  103. cp -al "$src"/*/ "$dst/"
  104. return
  105. fi
  106. local d dockerfiles=()
  107. for d in "$src"/*/.bashbrew-dockerfile-name; do
  108. [ -f "$d" ] || continue
  109. local bf; bf="$(< "$d")"
  110. local dDir; dDir="$(dirname "$d")"
  111. dockerfiles+=( "$dDir/$bf" )
  112. if [ "$bf" = 'Dockerfile' ]; then
  113. # if "Dockerfile.builder" exists, let's check that too (busybox, hello-world)
  114. if [ -f "$dDir/$bf.builder" ]; then
  115. dockerfiles+=( "$dDir/$bf.builder" )
  116. fi
  117. fi
  118. rm "$d" # remove the ".bashbrew-dockerfile-name" file we created
  119. done
  120. for d in "${dockerfiles[@]}"; do
  121. local dDir; dDir="$(dirname "$d")"
  122. local dDirName; dDirName="$(basename "$dDir")"
  123. local IFS=$'\n'
  124. local dBase; dBase="$(basename "$d")"
  125. local copyAddContext; copyAddContext="$(awk '
  126. toupper($1) == "COPY" || toupper($1) == "ADD" {
  127. for (i = 2; i < NF; i++) {
  128. if ($i ~ /^--from=/) {
  129. next
  130. }
  131. if ($i !~ /^--chown=/) {
  132. print $i
  133. }
  134. }
  135. }
  136. ' "$d")"
  137. local files=(
  138. "$dBase"
  139. $copyAddContext
  140. # some extra files which are likely interesting if they exist, but no big loss if they do not
  141. ' .dockerignore' # will be used automatically by "docker build"
  142. ' *.manifest' # debian/ubuntu "package versions" list
  143. ' *.ks' # fedora "kickstart" (rootfs build script)
  144. ' build*.txt' # ubuntu "build-info.txt", debian "build-command.txt"
  145. # usefulness yet to be proven:
  146. #' *.log'
  147. #' {MD5,SHA1,SHA256}SUMS'
  148. #' *.{md5,sha1,sha256}'
  149. # (the space prefix is removed below and is used to ignore non-matching globs so that bad "Dockerfile" entries appropriately lead to failure)
  150. )
  151. unset IFS
  152. mkdir -p "$dst/$dDirName"
  153. local f origF failureMatters
  154. for origF in "${files[@]}"; do
  155. f="${origF# }" # trim off leading space (indicates we don't care about failure)
  156. [ "$f" = "$origF" ] && failureMatters=1 || failureMatters=
  157. local globbed
  158. # "find: warning: -path ./xxx/ will not match anything because it ends with /."
  159. local findGlobbedPath="${f%/}"
  160. findGlobbedPath="${findGlobbedPath#./}"
  161. local globbedStr; globbedStr="$(cd "$dDir" && find -path "./$findGlobbedPath")"
  162. local -a globbed=( $globbedStr )
  163. if [ "${#globbed[@]}" -eq 0 ]; then
  164. globbed=( "$f" )
  165. fi
  166. local g
  167. for g in "${globbed[@]}"; do
  168. if [ -z "$failureMatters" ] && [ ! -e "$dDir/$g" ]; then
  169. continue
  170. fi
  171. local gDir; gDir="$(dirname "$dst/$dDirName/$g")"
  172. mkdir -p "$gDir"
  173. cp -alT "$dDir/$g" "$dst/$dDirName/$g"
  174. if [ -n "$listTarballContents" ]; then
  175. case "$g" in
  176. *.tar.* | *.tgz)
  177. if [ -s "$dst/$dDirName/$g" ]; then
  178. tar -tf "$dst/$dDirName/$g" \
  179. | grep -vE "$uninterestingTarballGrep" \
  180. | sed -e 's!^[.]/!!' \
  181. | sort \
  182. > "$dst/$dDirName/$g 'tar -t'"
  183. fi
  184. ;;
  185. esac
  186. fi
  187. done
  188. done
  189. done
  190. }
  191. mkdir temp
  192. git -C temp init --quiet
  193. git -C temp config user.name 'Bogus'
  194. git -C temp config user.email 'bogus@bogus'
  195. # handle "new-image" PRs gracefully
  196. for img; do touch "$BASHBREW_LIBRARY/$img"; [ -s "$BASHBREW_LIBRARY/$img" ] || echo 'Maintainers: New Image! :D (@docker-library-bot)' > "$BASHBREW_LIBRARY/$img"; done
  197. bashbrew list "$@" 2>>temp/_bashbrew.err | sort -uV > temp/_bashbrew-list || :
  198. "$diffDir/_bashbrew-cat-sorted.sh" "$@" 2>>temp/_bashbrew.err > temp/_bashbrew-cat || :
  199. for image; do
  200. if script="$(bashbrew cat --format "$template" "$image")"; then
  201. mkdir tar
  202. ( eval "$script" | tar -xiC tar )
  203. copy-tar tar temp
  204. rm -rf tar
  205. fi
  206. done
  207. git -C temp add . || :
  208. git -C temp commit --quiet --allow-empty -m 'initial' || :
  209. git -C oi clean --quiet --force
  210. git -C oi checkout --quiet pull
  211. # handle "deleted-image" PRs gracefully :(
  212. for img; do touch "$BASHBREW_LIBRARY/$img"; [ -s "$BASHBREW_LIBRARY/$img" ] || echo 'Maintainers: Deleted Image D: (@docker-library-bot)' > "$BASHBREW_LIBRARY/$img"; done
  213. git -C temp rm --quiet -rf . || :
  214. bashbrew list "$@" 2>>temp/_bashbrew.err | sort -uV > temp/_bashbrew-list || :
  215. "$diffDir/_bashbrew-cat-sorted.sh" "$@" 2>>temp/_bashbrew.err > temp/_bashbrew-cat || :
  216. script="$(bashbrew cat --format "$template" "$@")"
  217. mkdir tar
  218. ( eval "$script" | tar -xiC tar )
  219. copy-tar tar temp
  220. rm -rf tar
  221. git -C temp add .
  222. git -C temp diff \
  223. --find-copies-harder \
  224. --find-copies="$findCopies" \
  225. --find-renames="$findCopies" \
  226. --ignore-blank-lines \
  227. --ignore-space-at-eol \
  228. --ignore-space-change \
  229. --irreversible-delete \
  230. --minimal \
  231. --staged