diff-pr.sh 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  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. 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. # "09/401f736622f2c9258d14388ebd47900bbab126"
  32. 'usr/lib/.build-id/'
  33. )
  34. # prints "$2$1$3$1...$N"
  35. join() {
  36. local sep="$1"; shift
  37. local out; printf -v out "${sep//%/%%}%s" "$@"
  38. echo "${out#$sep}"
  39. }
  40. uninterestingTarballGrep="^([.]?/)?($(join '|' "${uninterestingTarballContent[@]}"))"
  41. if [ "$#" -eq 0 ]; then
  42. usage >&2
  43. exit 1
  44. fi
  45. pull="$1" # PR number
  46. shift
  47. #dir="$(dirname "$(readlink -f "$BASH_SOURCE")")"
  48. tempDir="$(mktemp -d)"
  49. trap "rm -rf '$tempDir'" EXIT
  50. cd "$tempDir"
  51. git clone --quiet \
  52. https://github.com/docker-library/official-images.git \
  53. oi
  54. git -C oi fetch --quiet \
  55. origin "pull/$pull/merge":pull
  56. images=( "$@" )
  57. if [ "${#images[@]}" -eq 0 ]; then
  58. images=( $(git -C oi/library diff --name-only HEAD...pull -- . | xargs -n1 basename) )
  59. fi
  60. export BASHBREW_CACHE="${BASHBREW_CACHE:-${XDG_CACHE_HOME:-$HOME/.cache}/bashbrew}"
  61. export BASHBREW_LIBRARY="$PWD/oi/library"
  62. : "${BASHBREW_ARCH:=amd64}" # TODO something smarter with arches
  63. export BASHBREW_ARCH
  64. # "bashbrew cat" template for duplicating something like "bashbrew list --uniq" but with architectures too
  65. archesListTemplate='
  66. {{- range $e := $.Entries -}}
  67. {{- range .Architectures -}}
  68. {{- $.RepoName -}}:{{- $e.Tags | last -}}
  69. {{- " @ " -}}
  70. {{- . -}}
  71. {{- "\n" -}}
  72. {{- end -}}
  73. {{- end -}}
  74. '
  75. # ... and SharedTags
  76. sharedTagsListTemplate='
  77. {{- range $group := .Manifest.GetSharedTagGroups -}}
  78. {{- range $tag := $group.SharedTags -}}
  79. {{- join ":" $.RepoName $tag -}}
  80. {{- " -- " -}}
  81. {{- range $i, $e := $group.Entries -}}
  82. {{- if gt $i 0 -}}
  83. {{- ", " -}}
  84. {{- end -}}
  85. {{- join ":" $.RepoName ($e.Tags | last) -}}
  86. {{- end -}}
  87. {{- "\n" -}}
  88. {{- end -}}
  89. {{- end -}}
  90. '
  91. # TODO something less hacky than "git archive" hackery, like a "bashbrew archive" or "bashbrew context" or something
  92. template='
  93. tempDir="$(mktemp -d)"
  94. {{- "\n" -}}
  95. {{- range $.Entries -}}
  96. {{- $arch := .HasArchitecture arch | ternary arch (.Architectures | first) -}}
  97. {{- $froms := $.ArchDockerFroms $arch . -}}
  98. {{- $outDir := join "_" $.RepoName (.Tags | last) -}}
  99. git -C "$BASHBREW_CACHE/git" archive --format=tar
  100. {{- " " -}}
  101. {{- "--prefix=" -}}
  102. {{- $outDir -}}
  103. {{- "/" -}}
  104. {{- " " -}}
  105. {{- .ArchGitCommit $arch -}}
  106. {{- ":" -}}
  107. {{- $dir := .ArchDirectory $arch -}}
  108. {{- (eq $dir ".") | ternary "" $dir -}}
  109. {{- "\n" -}}
  110. mkdir -p "$tempDir/{{- $outDir -}}" && echo "{{- .ArchFile $arch -}}" > "$tempDir/{{- $outDir -}}/.bashbrew-dockerfile-name"
  111. {{- "\n" -}}
  112. {{- end -}}
  113. tar -cC "$tempDir" . && rm -rf "$tempDir"
  114. '
  115. copy-tar() {
  116. local src="$1"; shift
  117. local dst="$1"; shift
  118. if [ "$allFiles" ]; then
  119. mkdir -p "$dst"
  120. cp -al "$src"/*/ "$dst/"
  121. return
  122. fi
  123. local d dockerfiles=()
  124. for d in "$src"/*/.bashbrew-dockerfile-name; do
  125. local bf="$(< "$d")" dDir="$(dirname "$d")"
  126. dockerfiles+=( "$dDir/$bf" )
  127. if [ "$bf" = 'Dockerfile' ]; then
  128. # if "Dockerfile.builder" exists, let's check that too (busybox, hello-world)
  129. if [ -f "$dDir/$bf.builder" ]; then
  130. dockerfiles+=( "$dDir/$bf.builder" )
  131. fi
  132. fi
  133. rm "$d" # remove the ".bashbrew-dockerfile-name" file we created
  134. done
  135. for d in "${dockerfiles[@]}"; do
  136. local dDir; dDir="$(dirname "$d")"
  137. local dDirName; dDirName="$(basename "$dDir")"
  138. local IFS=$'\n'
  139. local dBase; dBase="$(basename "$d")"
  140. local copyAddContext; copyAddContext="$(awk '
  141. toupper($1) == "COPY" || toupper($1) == "ADD" {
  142. for (i = 2; i < NF; i++) {
  143. if ($i ~ /^--from=/) {
  144. next
  145. }
  146. if ($i !~ /^--chown=/) {
  147. print $i
  148. }
  149. }
  150. }
  151. ' "$d")"
  152. local files=(
  153. "$dBase"
  154. $copyAddContext
  155. # some extra files which are likely interesting if they exist, but no big loss if they do not
  156. ' .dockerignore' # will be used automatically by "docker build"
  157. ' *.manifest' # debian/ubuntu "package versions" list
  158. ' *.ks' # fedora "kickstart" (rootfs build script)
  159. ' build*.txt' # ubuntu "build-info.txt", debian "build-command.txt"
  160. # usefulness yet to be proven:
  161. #' *.log'
  162. #' {MD5,SHA1,SHA256}SUMS'
  163. #' *.{md5,sha1,sha256}'
  164. # (the space prefix is removed below and is used to ignore non-matching globs so that bad "Dockerfile" entries appropriately lead to failure)
  165. )
  166. unset IFS
  167. mkdir -p "$dst/$dDirName"
  168. local f origF failureMatters
  169. for origF in "${files[@]}"; do
  170. f="${origF# }" # trim off leading space (indicates we don't care about failure)
  171. [ "$f" = "$origF" ] && failureMatters=1 || failureMatters=
  172. local globbed
  173. # "find: warning: -path ./xxx/ will not match anything because it ends with /."
  174. local findGlobbedPath="${f%/}"
  175. findGlobbedPath="${findGlobbedPath#./}"
  176. globbed=( $(cd "$dDir" && find -path "./$findGlobbedPath") )
  177. if [ "${#globbed[@]}" -eq 0 ]; then
  178. globbed=( "$f" )
  179. fi
  180. local g
  181. for g in "${globbed[@]}"; do
  182. if [ -z "$failureMatters" ] && [ ! -e "$dDir/$g" ]; then
  183. continue
  184. fi
  185. local gDir; gDir="$(dirname "$dst/$dDirName/$g")"
  186. mkdir -p "$gDir"
  187. cp -alT "$dDir/$g" "$dst/$dDirName/$g"
  188. if [ "$listTarballContents" ]; then
  189. case "$g" in
  190. *.tar.*|*.tgz)
  191. tar -tf "$dst/$dDirName/$g" \
  192. | grep -vE "$uninterestingTarballGrep" \
  193. | sed -e 's!^[.]/!!' \
  194. | sort \
  195. > "$dst/$dDirName/$g 'tar -t'"
  196. ;;
  197. esac
  198. fi
  199. done
  200. done
  201. done
  202. }
  203. mkdir temp
  204. git -C temp init --quiet
  205. git -C temp config user.name 'Bogus'
  206. git -C temp config user.email 'bogus@bogus'
  207. bashbrew list "${images[@]}" | sort -uV > temp/_bashbrew-list || :
  208. bashbrew cat --format "$archesListTemplate" "${images[@]}" | sort -V > temp/_bashbrew-arches || :
  209. bashbrew cat --format "$sharedTagsListTemplate" "${images[@]}" | grep -vE '^$' | sort -V > temp/_bashbrew-shared-tags || :
  210. for image in "${images[@]}"; do
  211. if script="$(bashbrew cat -f "$template" "$image")"; then
  212. mkdir tar
  213. ( eval "$script" | tar -xiC tar )
  214. copy-tar tar temp
  215. rm -rf tar
  216. fi
  217. done
  218. git -C temp add . || :
  219. git -C temp commit --quiet --allow-empty -m 'initial' || :
  220. git -C oi checkout --quiet pull
  221. git -C temp rm --quiet -rf . || :
  222. bashbrew list "${images[@]}" | sort -uV > temp/_bashbrew-list || :
  223. bashbrew cat --format "$archesListTemplate" "${images[@]}" | sort -V > temp/_bashbrew-arches || :
  224. bashbrew cat --format "$sharedTagsListTemplate" "${images[@]}" | grep -vE '^$' | sort -V > temp/_bashbrew-shared-tags || :
  225. script="$(bashbrew cat -f "$template" "${images[@]}")"
  226. mkdir tar
  227. ( eval "$script" | tar -xiC tar )
  228. copy-tar tar temp
  229. rm -rf tar
  230. git -C temp add .
  231. git -C temp diff \
  232. --find-copies-harder \
  233. --find-copies="$findCopies" \
  234. --find-renames="$findCopies" \
  235. --ignore-blank-lines \
  236. --ignore-space-at-eol \
  237. --ignore-space-change \
  238. --irreversible-delete \
  239. --minimal \
  240. --staged