build-release.sh 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. #!/usr/bin/env bash
  2. #
  3. # A build script for the release version of the Threema Android app.
  4. set -euo pipefail
  5. DOCKERIMAGE=threema/android-compile
  6. GREEN="\033[0;32m"
  7. RED="\033[0;31m"
  8. RESET="\033[0m"
  9. function print_usage() {
  10. echo "Usage: $0 -v <variants> [-b] [-k <dir>] [-o <dir>] [--no-image-export] --i-accept-the-android-sdk-license"
  11. echo ""
  12. echo "Options:"
  13. echo " -v <variants> Comma-separated variants to build: googleplay_private, googleplay_work, googleplay_onprem, hms_private, hms_work, threemashop_private, libre_private"
  14. echo " -b,--build (Re)build the Docker image"
  15. echo " --no-cache Clear Docker build cache"
  16. echo " -k,--keystore <dir> Path to the keystore directory"
  17. echo " -o,--outdir <dir> Path to the release output directory, will be created if it doesn't exist"
  18. echo " --no-image-export Skip the docker image export step"
  19. echo " -h,--help Print this help and exit"
  20. }
  21. function log() {
  22. echo -en "$1"
  23. echo -n "$2 $3"
  24. echo -e "$RESET"
  25. }
  26. function log_major() { log "$GREEN" "==>" "$1"; }
  27. function log_minor() { log "$GREEN" "--> " "$1"; }
  28. function log_error() { log "$RED" "!!!" "Error: $1"; }
  29. function fail() {
  30. log_error "$1"
  31. exit 1
  32. }
  33. # Re-implementation of realpath function (hello macOS)
  34. function realpath() {
  35. OURPWD=$PWD
  36. cd "$(dirname "$1")"
  37. LINK=$(readlink "$(basename "$1")")
  38. while [ "$LINK" ]; do
  39. cd "$(dirname "$LINK")"
  40. LINK=$(readlink "$(basename "$1")")
  41. done
  42. REALPATH="$PWD/$(basename "$1")"
  43. cd "$OURPWD"
  44. echo "$REALPATH"
  45. }
  46. # Determine script directory
  47. DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
  48. # If no arguments are passed, print usage
  49. if [ "$#" -lt 1 ]; then print_usage; exit 1; fi
  50. # Parse arguments
  51. license=""
  52. variants=""
  53. build=0
  54. no_cache=""
  55. keystore=""
  56. export_image=1
  57. releasedir="$DIR/../release"
  58. while [[ "$#" -gt 0 ]]; do
  59. case $1 in
  60. -v) variants="$2"; shift ;;
  61. -n) echo "Note: The -n parameter is deprecated and not needed anymore"; shift ;;
  62. -b|--build) build=1 ;;
  63. --no-cache) no_cache="--no-cache" ;;
  64. -k|--keystore) keystore="$2"; shift ;;
  65. -o|--outdir) releasedir="$2"; shift ;;
  66. --i-accept-the-android-sdk-license) license="accepted" ;;
  67. --no-image-export) export_image=0 ;;
  68. -h|--help) print_usage; exit 0 ;;
  69. *) echo "Unknown parameter passed: $1"; print_usage; exit 1 ;;
  70. esac
  71. shift
  72. done
  73. releasedir=$(realpath "$releasedir")
  74. # Process arguments
  75. IFS=', ' read -r -a variant_array <<< "$variants"
  76. for variant in "${variant_array[@]}"; do
  77. case $variant in
  78. googleplay_private | googleplay_work | googleplay_onprem | hms_private | hms_work | threemashop_private | libre_private)
  79. # Valid
  80. ;;
  81. *)
  82. fail "Invalid build variant: $variant"
  83. ;;
  84. esac
  85. done
  86. if [ "$license" != "accepted" ]; then
  87. fail 'Please accept the license with "--i-accept-the-android-sdk-license"'
  88. fi
  89. # Determine build version and full name in the form of: "1.1[-beta1]-1000"
  90. APP_BUILD_GRADLE_FILE="$DIR/../app/build.gradle.kts"
  91. APP_VERSION_CODE=$(grep "^\s*val defaultVersionCode = \d*" "$APP_BUILD_GRADLE_FILE" | sed 's/[^0-9]*//g')
  92. APP_VERSION_NAME_MAIN=$(grep '^val appVersion = "' "$APP_BUILD_GRADLE_FILE" | sed 's/^val appVersion = "\([^"]*\)".*/\1/')
  93. APP_VERSION_NAME_SUFFIX=$(grep '^val betaSuffix = "' "$APP_BUILD_GRADLE_FILE" | sed 's/^val betaSuffix = "\([^"]*\)".*/\1/')
  94. FULL_APP_VERSION_NAME="${APP_VERSION_NAME_MAIN}${APP_VERSION_NAME_SUFFIX}-${APP_VERSION_CODE}"
  95. sdk_version=$(grep "^\s*compileSdk = [0-9]\+" "$APP_BUILD_GRADLE_FILE" | sed 's/[^0-9]*//g')
  96. build_tools_version=$(grep "^\s*buildToolsVersion = \"\([0-9]\+\.\?\)\+\"" "$APP_BUILD_GRADLE_FILE" | sed 's/[^0-9\.]*//g')
  97. rust_version=$(grep channel "$DIR/../domain/libthreema/rust-toolchain.toml" | cut -d'"' -f2)
  98. # Validate target directory
  99. mkdir -p "$releasedir"
  100. name=${FULL_APP_VERSION_NAME//[^0-9\.a-zA-Z\-_]/}
  101. if [[ "$name" == "" || "$name" == .* ]]; then
  102. fail "Could not process app version name ($FULL_APP_VERSION_NAME)"
  103. fi
  104. targetdir="$releasedir/$name"
  105. log_major "Creating target directory $targetdir"
  106. if [[ -d "$targetdir" ]]; then
  107. fail "Output directory $targetdir already exists. Please remove it first."
  108. fi
  109. mkdir "$targetdir"
  110. # Build Docker image
  111. if [ $build -eq 1 ]; then
  112. log_major "Building Docker image with args:"
  113. log_minor "app_version_code=$APP_VERSION_CODE"
  114. log_minor "app_version_name=$FULL_APP_VERSION_NAME"
  115. log_minor "sdk_version=$sdk_version"
  116. log_minor "build_tools_version=$build_tools_version"
  117. log_minor "rust_version=$rust_version"
  118. docker build $no_cache "$DIR/../scripts/" \
  119. --build-arg SDK_VERSION="$sdk_version" \
  120. --build-arg BUILD_TOOLS_VERSION="$build_tools_version" \
  121. --build-arg RUST_VERSION="$rust_version" \
  122. -t "$DOCKERIMAGE:latest" \
  123. -t "$DOCKERIMAGE:$APP_VERSION_CODE"
  124. fi
  125. # Build app variant(s)
  126. for variant in "${variant_array[@]}"; do
  127. # Determine target and path
  128. case $variant in
  129. googleplay_private)
  130. target=assembleStore_googleRelease
  131. variant_dir="store_google"
  132. ;;
  133. googleplay_work)
  134. target=assembleStore_google_workRelease
  135. variant_dir="store_google_work"
  136. ;;
  137. googleplay_onprem)
  138. target=assembleOnpremRelease
  139. variant_dir="onprem"
  140. ;;
  141. hms_private)
  142. target=assembleHmsRelease
  143. variant_dir="hms"
  144. ;;
  145. hms_work)
  146. target=assembleHms_workRelease
  147. variant_dir="hms_work"
  148. ;;
  149. threemashop_private)
  150. target=assembleStore_threemaRelease
  151. variant_dir="store_threema"
  152. ;;
  153. libre_private)
  154. target=assembleLibreRelease
  155. variant_dir="libre"
  156. ;;
  157. *)
  158. fail "Invalid build variant: $variant"
  159. ;;
  160. esac
  161. # Compile
  162. log_major "Building gradle target $target"
  163. run_command="docker run --rm"
  164. if [ "${CI:-}" = "true" ]; then
  165. log_minor "CI detected, running Docker commands non-interactively"
  166. else
  167. run_command+=" -ti"
  168. fi
  169. run_command+=" -u \"$(id -u):$(id -g)\""
  170. run_command+=" -v \"$DIR/..\":/code"
  171. run_command+=" -v /dev/null:/code/local.properties" # Mask local.properties file
  172. run_command+=" -v /code/build/" # Mask root build directory
  173. run_command+=" -v /code/app/.cxx/" # Mask ndk build directory
  174. if [ "$keystore" != "" ]; then
  175. log_minor "Using keystore at $keystore"
  176. keystore_realpath=$(realpath "$keystore")
  177. run_command+=" -v \"$keystore_realpath:/keystore\""
  178. fi
  179. run_command+=" \"$DOCKERIMAGE:$APP_VERSION_CODE\""
  180. run_command+=" /bin/bash -c \"cd /code && ./gradlew clean -PbuildUniversalApk $target\""
  181. eval "$run_command"
  182. # Copy files
  183. log_major "Copying generated files for variant $variant"
  184. mkdir -p "$targetdir/$variant/"{logs,mapping}/
  185. for f in "$DIR"/../app/build/outputs/apk/"$variant_dir"/release/*; do
  186. log_minor "$(basename "$f")"
  187. cp -r "$f" "$targetdir/$variant/"
  188. done
  189. for f in "$DIR"/../app/build/outputs/logs/*"$variant_dir"*; do
  190. log_minor "$(basename "$f")"
  191. cp "$f" "$targetdir/$variant/logs/"
  192. done
  193. for f in "$DIR"/../app/build/outputs/mapping/"$variant_dir"Release/*; do
  194. log_minor "$(basename "$f")"
  195. cp "$f" "$targetdir/$variant/mapping/"
  196. done
  197. for f in "$DIR"/../app/build/outputs/native-debug-symbols/"$variant_dir"Release/native-debug-symbols.zip; do
  198. log_minor "$(basename "$f")"
  199. cp "$f" "$targetdir/$variant/mapping/"
  200. done
  201. for f in "$DIR"/../app/build/outputs/sdk-dependencies/"$variant_dir"Release/sdkDependencies.txt; do
  202. log_minor "$(basename "$f")"
  203. cp "$f" "$targetdir/$variant/"
  204. done
  205. done
  206. # Export image
  207. if [ $export_image -eq 1 ]; then
  208. log_major "Exporting docker image"
  209. docker image save -o "$targetdir/docker-image.tar" "$DOCKERIMAGE:$APP_VERSION_CODE"
  210. log_minor "Compressing docker image"
  211. gzip "${targetdir}/docker-image.tar"
  212. chmod 644 "${targetdir}/docker-image.tar.gz"
  213. fi
  214. log_major "Done! You can find the resulting files in the '$releasedir' directory."