build-release.sh 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. #!/usr/bin/env bash
  2. #
  3. # A build script for the release version of the Threema Android app.
  4. # _____ _
  5. # |_ _| |_ _ _ ___ ___ _ __ __ _
  6. # | | | ' \| '_/ -_) -_) ' \/ _` |_
  7. # |_| |_||_|_| \___\___|_|_|_\__,_(_)
  8. #
  9. # Threema for Android
  10. # Copyright (c) 2020 Threema GmbH
  11. #
  12. # This program is free software: you can redistribute it and/or modify
  13. # it under the terms of the GNU Affero General Public License, version 3,
  14. # as published by the Free Software Foundation.
  15. #
  16. # This program is distributed in the hope that it will be useful,
  17. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  19. # GNU Affero General Public License for more details.
  20. #
  21. # You should have received a copy of the GNU Affero General Public License
  22. # along with this program. If not, see <https://www.gnu.org/licenses/>.
  23. set -euo pipefail
  24. DOCKERIMAGE=threema/android-compile
  25. GREEN="\033[0;32m"
  26. RED="\033[0;31m"
  27. RESET="\033[0m"
  28. function print_usage() {
  29. echo "Usage: $0 -v <variants> -n <version-name> [-b] [-k <dir>] [-o <dir>] [--no-image-export] --i-accept-the-android-sdk-license"
  30. echo ""
  31. echo "Options:"
  32. echo " -v <variants> Comma-separated variants to build: googleplay, threemashop, work, hms, hmswork"
  33. echo " -n <version-name> The version name. Example: '4.53'"
  34. echo " -b,--build (Re)build the Docker image"
  35. echo " -k,--keystore <dir> Path to the keystore directory"
  36. echo " -o,--outdir <dir> Path to the release output directory, will be created if it doesn't exist"
  37. echo " --no-image-export Skip the docker image export step"
  38. echo " -h,--help Print this help and exit"
  39. }
  40. function log() {
  41. echo -en "$1"
  42. echo -n "$2 $3"
  43. echo -e "$RESET"
  44. }
  45. function log_major() { log "$GREEN" "==>" "$1"; }
  46. function log_minor() { log "$GREEN" "--> " "$1"; }
  47. function log_error() { log "$RED" "!!!" "Error: $1"; }
  48. function fail() {
  49. log_error "$1"
  50. exit 1
  51. }
  52. # Re-implementation of realpath function (hello macOS)
  53. function realpath() {
  54. OURPWD=$PWD
  55. cd "$(dirname "$1")"
  56. LINK=$(readlink "$(basename "$1")")
  57. while [ "$LINK" ]; do
  58. cd "$(dirname "$LINK")"
  59. LINK=$(readlink "$(basename "$1")")
  60. done
  61. REALPATH="$PWD/$(basename "$1")"
  62. cd "$OURPWD"
  63. echo "$REALPATH"
  64. }
  65. # Determine script directory
  66. DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
  67. # If no arguments are passed, print usage
  68. if [ "$#" -lt 1 ]; then print_usage; exit 1; fi
  69. # Parse arguments
  70. license=""
  71. variants=""
  72. name=""
  73. build=0
  74. keystore=""
  75. export_image=1
  76. releasedir="$DIR/../release"
  77. while [[ "$#" -gt 0 ]]; do
  78. case $1 in
  79. -v) variants="$2"; shift ;;
  80. -n) name="$2"; shift ;;
  81. -b|--build) build=1 ;;
  82. -k|--keystore) keystore="$2"; shift ;;
  83. -o|--outdir) releasedir="$2"; shift ;;
  84. --i-accept-the-android-sdk-license) license="accepted" ;;
  85. --no-image-export) export_image=0 ;;
  86. -h|--help) print_usage; exit 0 ;;
  87. *) echo "Unknown parameter passed: $1"; print_usage; exit 1 ;;
  88. esac
  89. shift
  90. done
  91. releasedir=$(realpath $releasedir)
  92. # Process arguments
  93. IFS=', ' read -r -a variant_array <<< "$variants"
  94. for variant in "${variant_array[@]}"; do
  95. case $variant in
  96. googleplay | threemashop| work | hms | hmswork)
  97. # Valid
  98. ;;
  99. *)
  100. fail "Invalid build variant: $variant"
  101. ;;
  102. esac
  103. done
  104. if [ "$license" != "accepted" ]; then
  105. fail 'Please accept the license with "--i-accept-the-android-sdk-license"'
  106. fi
  107. name=${name//[^0-9\.a-zA-Z\-]/}
  108. if [[ "$name" == "" || "$name" == .* ]]; then
  109. log_error 'Please set a valid version name with "-n <name>".'
  110. fail 'Example: "-n 4.43"'
  111. fi
  112. # Determine build version
  113. app_version=$(grep "^\s*versionCode \d*" "$DIR/../app/build.gradle" | sed 's/[^0-9]*//g')
  114. sdk_version=$(grep "^\s*compileSdkVersion \d*" "$DIR/../app/build.gradle" | sed 's/[^0-9]*//g')
  115. build_tools_version=$(grep "^\s*buildToolsVersion \d*" "$DIR/../app/build.gradle" | sed 's/[^0-9\.]*//g')
  116. # Validate target directory
  117. mkdir -p "$releasedir"
  118. targetdir="$releasedir/$name"
  119. log_major "Creating target directory $targetdir"
  120. if [[ -d "$targetdir" ]]; then
  121. fail "Output directory $targetdir already exists. Please remove it first."
  122. fi
  123. mkdir "$targetdir"
  124. # Build Docker image
  125. if [ $build -eq 1 ]; then
  126. log_major "Building Docker image with args:"
  127. log_minor "app_version=$app_version"
  128. log_minor "sdk_version=$sdk_version"
  129. log_minor "build_tools_version=$build_tools_version"
  130. docker build "$DIR/../scripts/" \
  131. --build-arg SDK_VERSION="$sdk_version" \
  132. --build-arg BUILD_TOOLS_VERSION="$build_tools_version" \
  133. -t "$DOCKERIMAGE:latest" \
  134. -t "$DOCKERIMAGE:$app_version"
  135. fi
  136. # Build app variant(s)
  137. for variant in "${variant_array[@]}"; do
  138. # Determine target and path
  139. case $variant in
  140. googleplay)
  141. target=assembleStore_googleRelease
  142. variant_dir="store_google"
  143. ;;
  144. threemashop)
  145. target=assembleStore_threemaRelease
  146. variant_dir="store_threema"
  147. ;;
  148. work)
  149. target=assembleStore_google_workRelease
  150. variant_dir="store_google_work"
  151. ;;
  152. hms)
  153. target=assembleHmsRelease
  154. variant_dir="hms"
  155. ;;
  156. hmswork)
  157. target=assembleHms_workRelease
  158. variant_dir="hms_work"
  159. ;;
  160. *)
  161. fail "Invalid build variant: $variant"
  162. ;;
  163. esac
  164. # Compile
  165. log_major "Building gradle target $target"
  166. run_command="docker run --rm -ti"
  167. run_command+=" -u \"$(id -u):$(id -g)\""
  168. run_command+=" -v \"$DIR/..\":/code"
  169. run_command+=" -v /dev/null:/code/local.properties" # Mask local.properties file
  170. run_command+=" -v /code/build/" # Mask root build directory
  171. run_command+=" -v /code/app/.cxx/" # Mask ndk build directory
  172. if [ "$keystore" != "" ]; then
  173. log_minor "Using keystore at $keystore"
  174. keystore_realpath=$(realpath "$keystore")
  175. run_command+=" -v \"$keystore_realpath:/keystore\""
  176. fi
  177. run_command+=" \"$DOCKERIMAGE:$app_version\""
  178. run_command+=" /bin/bash -c \"cd /code && ./gradlew clean $target\""
  179. eval "$run_command"
  180. # Copy files
  181. log_major "Copying generated files for variant $variant"
  182. mkdir -p "$targetdir/$variant/"{logs,mapping}/
  183. for f in "$DIR"/../app/build/outputs/apk/"$variant_dir"/release/*; do
  184. log_minor "$(basename "$f")"
  185. cp "$f" "$targetdir/$variant/"
  186. done
  187. for f in "$DIR"/../app/build/outputs/logs/*"$variant_dir"*; do
  188. log_minor "$(basename "$f")"
  189. cp "$f" "$targetdir/$variant/logs/"
  190. done
  191. for f in "$DIR"/../app/build/outputs/mapping/"$variant_dir"Release/*; do
  192. log_minor "$(basename "$f")"
  193. cp "$f" "$targetdir/$variant/mapping/"
  194. done
  195. for f in "$DIR"/../app/build/outputs/sdk-dependencies/"$variant_dir"Release/sdkDependencies.txt; do
  196. log_minor "$(basename "$f")"
  197. cp "$f" "$targetdir/$variant/"
  198. done
  199. done
  200. # Export image
  201. if [ $export_image -eq 1 ]; then
  202. log_major "Exporting docker image"
  203. docker image save -o "$targetdir/docker-image.tar" "$DOCKERIMAGE:$app_version"
  204. log_minor "Compressing docker image"
  205. gzip "$targetdir/docker-image.tar"
  206. fi
  207. # Generate checksums
  208. log_major "Generate APK checksums"
  209. for variant in "${variant_array[@]}"; do
  210. log_minor "$variant"
  211. (cd "$targetdir/$variant/" && shasum -a 256 ./*.apk > apk-checksums-sha256.txt)
  212. done
  213. log_major "Done! You can find the resulting files in the '$releasedir' directory."