verify-build.sh 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. #!/usr/bin/env bash
  2. #
  3. # A script to verify that a locally compiled APK matches the released APK.
  4. #
  5. # Steps taken to achieve this:
  6. #
  7. # 1. Unpack both APK files
  8. # 2. Remove meta information (containing things like the signature)
  9. # 3. Recursively diff the two directories to ensure they match
  10. set -euo pipefail
  11. GREEN="\033[0;32m"
  12. RED="\033[0;31m"
  13. YELLOW="\033[1;33m"
  14. RESET="\033[0m"
  15. function print_usage() {
  16. echo "Usage: $0 -n <version-name> -v <variant> -p <published-apk> [-l <local-apk>]"
  17. echo ""
  18. echo "Options:"
  19. echo " -n <version-name> The version name. Example: '4.43k'"
  20. echo " -v <variant> Variant to verify: Either googleplay_private, threemashop_private, libre_private or hms_private"
  21. echo " -p <published-apk> Path to the APK file extracted from the phone"
  22. echo " -l <local-apk> Optional: Path to the locally built APK"
  23. echo " -h,--help Print this help and exit"
  24. }
  25. function log() {
  26. echo -en "$1"
  27. echo -n "$2 $3"
  28. echo -e "$RESET"
  29. }
  30. function log_major() { log "$GREEN" "==>" "$1"; }
  31. function log_minor() { log "$GREEN" "--> " "$1"; }
  32. function log_warning() { log "$YELLOW" "==>" "$1"; }
  33. function log_error() { log "$RED" "!!!" "Error: $1"; }
  34. function fail() {
  35. log_error "$1"
  36. exit 1
  37. }
  38. # Re-implementation of realpath function (hello macOS)
  39. function realpath() {
  40. OURPWD=$PWD
  41. cd "$(dirname "$1")"
  42. LINK=$(readlink "$(basename "$1")")
  43. while [ "$LINK" ]; do
  44. cd "$(dirname "$LINK")"
  45. LINK=$(readlink "$(basename "$1")")
  46. done
  47. REALPATH="$PWD/$(basename "$1")"
  48. cd "$OURPWD"
  49. echo "$REALPATH"
  50. }
  51. # Determine script directory
  52. DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
  53. # If no arguments are passed, print usage
  54. if [ "$#" -lt 1 ]; then print_usage; exit 1; fi
  55. # Parse arguments
  56. name=""
  57. variant=""
  58. published_apk=""
  59. local_apk=""
  60. while [[ "$#" -gt 0 ]]; do
  61. case $1 in
  62. -n) name="$2"; shift ;;
  63. -v) variant="$2"; shift ;;
  64. -p) published_apk="$2"; shift ;;
  65. -l) local_apk="$2"; shift ;;
  66. -h|--help) print_usage; exit 0 ;;
  67. *) echo "Unknown parameter passed: $1"; print_usage; exit 1 ;;
  68. esac
  69. shift
  70. done
  71. # Process arguments
  72. if [[ "$name" == "" || "$name" == .* ]]; then
  73. log_error 'Please set a valid version name with "-n <name>".'
  74. fail 'Example: "-n 4.43k"'
  75. fi
  76. if [[ "$published_apk" == "" ]]; then
  77. log_error 'Please set a valid published APK path with "-p <path>".'
  78. fail 'Example: "-p threema-extracted.apk"'
  79. fi
  80. published_apk=$(realpath "$published_apk")
  81. if [[ "$variant" == "" ]]; then
  82. log_error 'Please set a valid build variant with "-v <variant>".'
  83. fail 'Example: "-v googleplay_private" or "-v threemashop_private"'
  84. fi
  85. case "$variant" in
  86. googleplay_private) variant_name="store_google" ;;
  87. threemashop_private) variant_name="store_threema" ;;
  88. libre_private) variant_name="libre" ;;
  89. hms_private) variant_name="hms" ;;
  90. *) fail "Invalid build variant: $variant" ;;
  91. esac
  92. # Validate target directory
  93. targetdir=$(realpath "$DIR/../reproduce")
  94. if [[ -d "$targetdir" ]]; then
  95. fail "The directory $targetdir already exists. Please remove it first."
  96. fi
  97. mkdir -p "$targetdir"/{published,local}
  98. # Unpack published APK
  99. if [[ ! -f "$published_apk" ]]; then
  100. fail "The published APK $published_apk could not be found."
  101. fi
  102. log_major "Unpacking published APK"
  103. unzip -q -d "$targetdir/published/" "$published_apk"
  104. # Determine local APK path
  105. if [[ "$local_apk" == "" ]]; then
  106. log_major "Determine local APK path"
  107. lib_count="$(find "$targetdir/published/lib/" -mindepth 1 -maxdepth 1 -type d | wc -l | xargs)"
  108. log_minor "Found $lib_count libs"
  109. case "$lib_count" in
  110. 1) architecture="$(ls "$targetdir/published/lib/")" ;;
  111. 4) architecture="universal" ;;
  112. *) fail "Could not determine architecture of published APK"
  113. esac
  114. log_minor "Architecture: $architecture"
  115. if [ -f "$DIR/../release/$name/$variant/app-$variant_name-$architecture-release-unsigned.apk" ]; then
  116. local_apk="$(realpath "$DIR/../release/$name/$variant/app-$variant_name-$architecture-release-unsigned.apk")"
  117. else
  118. local_apk="$(realpath "$DIR/../release/$name/$variant/app-$variant_name-$architecture-release.apk")"
  119. fi
  120. fi
  121. if [[ ! -f "$local_apk" ]]; then
  122. fail "The local APK $local_apk could not be found."
  123. fi
  124. log_major "Comparing the following APKs:"
  125. log_minor "Published: $published_apk"
  126. log_minor "Local: $local_apk"
  127. # Unpack local APK
  128. log_major "Unpacking local APK"
  129. unzip -q -d "$targetdir/local/" "$local_apk"
  130. # Remove meta information (containing things like the signature)
  131. log_major "Removing meta information, containing things like the app signature:"
  132. for path in META-INF/ resources.arsc; do
  133. for target in local published; do
  134. log_minor "rm -r $target/$path"
  135. rm -r "${targetdir:?}/${target:?}/${path:?}"
  136. done
  137. done
  138. # Diff!
  139. log_major "Comparing releases"
  140. diff -r "$targetdir/local/" "$targetdir/published/" && success=1 || success=0
  141. if [ $success -eq 1 ]; then
  142. log_major "Success! The APKs match."
  143. else
  144. log_warning "APK could not be verified."
  145. log_warning "Don't panic! First, make sure that you have compiled the correct version!"
  146. log_warning "If you cannot figure out why the verification failed,"
  147. log_warning "send us an e-mail to opensource@threema.ch containing the log above."
  148. exit 2
  149. fi