Merge branch 'master' into 'master'
[bitmask_android.git] / prepareForDistribution.sh
1 #!/bin/bash
2
3 # Copyright (c) 2019 LEAP Encryption Access Project and contributers
4 #
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation, either version 3 of the License, or
8 # (at your option) any later version.
9 #
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18
19 function quit {
20     echo -e "${RED}Task failed. Exit value: $?.${NC}"
21     cleanUp
22     exit 1
23 }
24
25 function cleanUp {
26     if [[ -f ${ALIGNED_UNSIGNED_APK} ]]
27     then
28         rm ${ALIGNED_UNSIGNED_APK}
29     fi
30     if [[ -f ${ALIGNED_SIGNED_APK} ]]
31     then
32         rm ${ALIGNED_SIGNED_APK}
33     fi
34 }
35
36 function sign {
37     #---- ALIGN AND JARSIGN APK  -----
38     if [[ -z $FILE_NAME_STRING ]]
39     then
40         FILE_NAME_STRING=$1
41         FILE_NAME=${FILE_NAME_STRING##*/} #remove everything till the last '/'
42         FILE_DIR=${FILE_NAME_STRING%/*} #remove everything after the last '/'
43     fi
44
45     FINAL_APK="${FILE_DIR}/${FILE_NAME}"
46     ALIGNED_UNSIGNED_APK="${FILE_DIR}/aligned-${FILE_NAME}"
47     ALIGNED_SIGNED_APK="${FILE_DIR}/aligned-signed-${FILE_NAME}"
48
49     echo -e "${GREEN} -> zip align ${ALIGNED_UNSIGNED_APK}${NC}"
50     ${ANDROID_BUILD_TOOLS}/zipalign -v -p 4 "${FINAL_APK}" ${ALIGNED_UNSIGNED_APK} > /dev/null && echo "zip alignment successful" || quit
51     echo -e "${GREEN} -> apksign ${ALIGNED_UNSIGNED_APK}${NC}"
52     ${ANDROID_BUILD_TOOLS}/apksigner sign --ks "${KEY_STORE_STRING}" --out ${ALIGNED_SIGNED_APK} ${ALIGNED_UNSIGNED_APK} || quit
53     rm ${ALIGNED_UNSIGNED_APK}
54     
55     FINGERPRINT=$(unzip -p ${ALIGNED_SIGNED_APK} META-INF/*.RSA | keytool -printcert | grep "SHA256" | tr -d '[:space:]') || quit
56     
57     if [[ ${FINGERPRINT} == ${EXPECTED_FINGERPRINT} ]] 
58     then
59         echo "Certificate fingerprint matches: ${FINGERPRINT}"
60     else 
61         echo -e "${RED}Certificate fingerprint \n${FINGERPRINT} \ndid not match expected fingerprint \n\t${EXPECTED_FINGERPRINT}${NC}"
62         quit
63     fi
64
65     echo -e "${GREEN} -> rename aligned signed apk to ${FINAL_APK}${NC}"
66     cp ${ALIGNED_SIGNED_APK} ${FINAL_APK} || quit
67     cleanUp
68     
69     #---- GPG SIGNING ----
70     if [[ -z ${GPG_KEY} && -z ${GPG_KEY_USER} ]] 
71     then
72         echo -e "${ORANGE}WARNING: Could not do gpg signing!${NC}"
73         exit
74     fi
75     
76     if [[ ${GPG_KEY} ]] 
77     then
78         echo -e "${GREEN} -> gpg sign using key ${GPG_KEY}${NC}"
79         gpg --default-key ${GPG_KEY} --armor --output "${FINAL_APK}.sig" --detach-sign ${FINAL_APK} || quit
80         #gpg -u ${GPG_KEY} -sab --output ${FINAL_APK} || quit
81     else
82         echo -e "${GREEN} -> gpg sign using pub key of user ${GPG_KEY_USER}${NC}"
83         GPG_KEY=$(gpg --list-keys $GPG_KEY_USER | grep pub | cut -d '/' -f 2 | cut -d ' ' -f 1) || quit
84         #gpg -u ${GPG_KEY} -sab --output ${FINAL_APK} || quit
85         gpg --default-key ${GPG_KEY} --armor --output "${FINAL_APK}.sig" --detach-sign ${FINAL_APK} || quit
86     fi
87
88     echo -e "${GREEN} -> gpg verify ${FINAL_APK}${NC}"
89     gpg --verify "${FINAL_APK}.sig" || quit
90 }
91
92 # ----Main-----
93
94 DO_BUILD=false
95 DO_SIGN=false
96 BETA=false
97 NO_TAG=false
98 FLAVOR="Normal"
99 FLAVOR_LOWERCASE="normal"
100 EXPECTED_FINGERPRINT="SHA256:9C:94:DB:F8:46:FD:95:97:47:57:17:2A:6A:8D:9A:9B:DF:8C:40:21:A6:6C:15:11:28:28:D1:72:39:1B:81:AA"
101 GREEN='\033[0;32m'
102 RED='\033[0;31m'
103 ORANGE='\033[0;33m'
104 NC='\033[0m'
105
106 export GREEN=${GREEN}
107 export RED=${RED}
108 export ORANGE=${ORANGE}
109 export EXPECTED_FINGERPRINT=${EXPECTED_FINGERPRINT}
110 export -f sign
111 export -f quit
112 export -f cleanUp
113
114
115 # init parameters
116 for ((i=1;i<=$#;i++)); 
117 do
118     if [[ ${!i} = "b" || ${!i} = "build" ]] 
119     then 
120         DO_BUILD=true
121         
122     elif [[ ${!i} = "s" || ${!i} = "sign" ]] 
123     then 
124         DO_SIGN=true
125         
126     elif [[ ${!i} = "-f" || ${!i} = "-file" ]] 
127     then 
128         ((i++)) 
129         FILE_NAME_STRING=${!i}
130         FILE_NAME=${FILE_NAME_STRING##*/} #remove everything till the last '/'
131         FILE_DIR=${FILE_NAME_STRING%/*} #remove everything after the last '/'
132
133     elif [[ ${!i} = "-d" || ${!i} = "-dir" ]]
134     then
135         ((i++))
136         FILE_DIR=${!i}
137         MULTIPLE_APKS=true
138     elif [[ ${!i} = "-ks" || ${!i} = "-keystore" ]] 
139     then 
140         ((i++)) 
141         KEY_STORE_STRING=${!i};
142         KEY_STORE_NAME=${KEY_STORE_STRING##*/}
143         KEY_STORE_DIR=${KEY_STORE_STRING%/*}
144         export KEY_STORE_STRING=${KEY_STORE_STRING}
145
146     elif [[ ${!i} = "-v" || ${!i} = "-version" ]] 
147     then 
148         ((i++)) 
149         VERSION_NAME=${!i};
150         if [[ -z $(git tag --list | grep -w ${VERSION_NAME}) ]] 
151         then
152             echo -e "${RED}ERROR: Version name has to be a git tag!${NC}"
153             exit
154         fi
155     elif [[ ${!i} = "-k" || ${!i} = "-key" ]];
156     then 
157         ((i++)) 
158         GPG_KEY=${!i}
159         export GPG_KEY=${GPG_KEY}
160     elif [[ ${!i} = "-u" || ${!i} = "-user" ]];
161     then 
162         ((i++)) 
163         GPG_KEY_USER=${!i}
164         export GPG_KEY_USER=${GPG_KEY_USER}
165     elif [[ ${!i} = "-b" || ${!i} = "-beta" ]];
166     then 
167         BETA=true
168     elif [[ ${!i} = "-no-tag" ]];
169     then 
170         NO_TAG=true
171     elif [[  ${!i} = "-c" || ${!i} = "-custom" ]]
172     then
173         ((i++))
174         FLAVOR="Custom"
175         FLAVOR_LOWERCASE="custom"
176     elif [[ ${!i} = "-h" || ${!i} = "-help" ]];
177     then 
178         echo -e "
179         sign [-ks -fp -f -b -u -k]            sign a given apk (both app signing and GPG signing)
180         -ks / -keystore [path] -------------- define path to keystore for signing (required)
181         -fp / -fingerprint [fingerprint] ---- define the fingerprint for the app (required for non-LEAP
182                                               signed apps)
183         -f / -file [inputfile] -------------- define path to apk going to be signed
184         -d / -dir [path] -------------------- define path to directory including apks to be signed
185         -u / -user [gpguser] ---------------- define the gpg user whose key will be used for GPG signing
186                                               (optional)
187         -k / -key [gpgkey] ------------------ define the key used for GPG signing. Using this option,
188                                               -u will be ignored (optional)                           
189         
190         
191         build [-v, -c, -b, -no-tag]
192         -v / -version [gittag] -------------- define the git version tag that needs to be checked out 
193                                               for building. It's also part of the resulting apk file 
194                                               name. (required if you don't use -no-tag)
195         -c / -custom ------------------------ build custom Bitmask client instead of main Bitmask client 
196                                               (optional)
197         -b / -beta -------------------------- build beta version with .beta appended to applicationId (optional)
198         -no-tag ----------------------------- force to build current checked out git commit instead of an
199                                               official release version
200
201         
202         -h / -help                            print out this help
203         
204         
205         example Usages:
206         ---------------
207         
208         * jarsign only:
209         ./prepareForDistribution.sh sign -f app/build/outputs/apk/app-production-beta.apk -ks ~/path/to/bitmask-android.keystore
210         
211         * jarsign and gpg sign only:
212         ./prepareForDistribution.sh sign -f app/build/outputs/apk/app-production-beta.apk -ks ~/path/to/bitmask-android.keystore -u GPG_USER
213
214         * jarsign and gpg sign all apks in directory:
215         ./prepareForDistribution.sh sign -d currentReleases/ -ks ~/path/to/bitmask-android.keystore -u GPG_USER
216
217         * build custom stable
218         ./prepareForDistribution.sh build -v 0.9.7 -c
219         
220         * build and sign custom stable:
221         ./prepareForDistribution.sh build sign -ks ~/path/to/bitmask-android.keystore -u GPG_USER -c -v 0.9.7
222         
223         * build and sign custom beta:
224         ./prepareForDistribution.sh build sign -ks ~/path/to/bitmask-android.keystore -u GPG_USER -c -b -v 0.9.7RC2
225
226         * build and sign stable:
227         ./prepareForDistribution.sh build sign -ks ~/path/to/bitmask-android.keystore -u GPG_USER -v 0.9.7
228         
229         * build and sign current git HEAD
230         ./prepareForDistribution.sh build sign -ks ~/path/to/bitmask-android.keystore -u GPG_USER -no-tag"
231         exit
232
233     else
234         echo -e "${RED}Invalid argument: ${!i}${NC}"
235         exit
236     fi
237
238 done;
239
240
241 # check what to do
242 if [[ ${DO_BUILD} == false && ${DO_SIGN} == false ]]
243 then
244     echo -e "${RED}ERROR: No action set. Please check  ./prepareForDistribution -help!${NC}"
245     exit
246 fi
247
248 BASE_FILE_DIR="$(pwd)/app/build/outputs/apk"
249 RELEASES_FILE_DIR="$(pwd)/currentReleases"
250
251 if [[ ${DO_BUILD} == true ]]
252 then
253     if [[ ${NO_TAG} == false && -z ${VERSION_NAME} ]]
254     then
255         echo -e "${RED}ERROR: You didn't enter the version (git tag) to be built. If you really want to force building the current checked out commit, use -no-tag.${NC}"
256         quit
257     fi
258     if [[ ${NO_TAG} == false ]] 
259     then
260         #---- COMPARE TAG COMMIT WITH CURRENT COMMIT AND CHECK OUT TAG COMMIT IF NECESSARY ----
261         TAG_COMMIT=$(git log -n 1 ${VERSION_NAME} --format="%H")
262         CURRENT_COMMIT=$(git log -n 1 --format="%H")
263         if [[ ${TAG_COMMIT} != ${CURRENT_COMMIT} ]]
264         then
265             echo "CHECKING OUT VERSION: ${VERSION_NAME} ..."
266             git checkout ${VERSION_NAME} || quit
267         fi
268     fi
269
270     ./cleanProject.sh || quit
271     ./build_deps.sh || quit
272     ./fix_gradle_lock.sh || quit
273
274     if [[ ! -d $RELEASES_FILE_DIR ]]
275     then
276         mkdir $RELEASES_FILE_DIR
277     fi
278     rm -rf $RELEASES_FILE_DIR/*
279
280     if [[ ${BETA} == true ]]
281     then
282         echo -e "${GREEN} -> build beta releases for flavor ${FLAVOR}${NC}"
283         ./gradlew clean assemble${FLAVOR}ProductionFatBeta --stacktrace || quit
284  #       echo "copy file:  $(ls $BASE_FILE_DIR/${FLAVOR_LOWERCASE}ProductionFat/beta/*.apk)"
285         cp $BASE_FILE_DIR/${FLAVOR_LOWERCASE}ProductionFat/beta/*.apk $RELEASES_FILE_DIR/.
286         
287         # custom builds might have disabled split apks -> check if build task exist
288         if [[ $(./gradlew tasks --console plain | grep ${FLAVOR}ProductionX86Beta) ]]; then
289             ./gradlew clean assemble${FLAVOR}ProductionX86Beta --stacktrace || quit
290             cp $BASE_FILE_DIR/${FLAVOR_LOWERCASE}ProductionX86/beta/*.apk $RELEASES_FILE_DIR/.
291         fi
292         if [[  $(./gradlew tasks --console plain | grep ${FLAVOR}ProductionX86_64Beta) ]]; then
293             ./gradlew clean assemble${FLAVOR}ProductionX86_64Beta --stacktrace || quit
294             cp $BASE_FILE_DIR/${FLAVOR_LOWERCASE}ProductionX86_64/beta/*.apk $RELEASES_FILE_DIR/.
295         fi
296         if [[ $(./gradlew tasks --console plain | grep ${FLAVOR}ProductionArmv7Beta) ]]; then
297             ./gradlew clean assemble${FLAVOR}ProductionArmv7Beta --stacktrace || quit
298             cp $BASE_FILE_DIR/${FLAVOR_LOWERCASE}ProductionArmv7/beta/*.apk $RELEASES_FILE_DIR/.
299         fi
300         if [[ $(./gradlew tasks --console plain | grep ${FLAVOR}ProductionArmv7Beta) ]]; then
301             ./gradlew clean assemble${FLAVOR}ProductionArm64Beta --stacktrace || quit
302             cp $BASE_FILE_DIR/${FLAVOR_LOWERCASE}ProductionArm64/beta/*.apk $RELEASES_FILE_DIR/.
303         fi
304     else
305             echo -e "${GREEN} -> build stable releases for flavor ${FLAVOR}${NC}"
306         ./gradlew clean assemble${FLAVOR}ProductionFatRelease --stacktrace || quit
307         cp $BASE_FILE_DIR/${FLAVOR_LOWERCASE}ProductionFat/release/*.apk $RELEASES_FILE_DIR/.
308         
309         # custom builds might have disabled split apks -> check if build task exist
310         if [[ $(./gradlew tasks --console plain | grep ${FLAVOR}ProductionX86Release) ]]; then
311             ./gradlew clean assemble${FLAVOR}ProductionX86Release --stacktrace || quit
312             cp $BASE_FILE_DIR/${FLAVOR_LOWERCASE}ProductionX86/release/*.apk $RELEASES_FILE_DIR/.
313         fi
314         if [[ $(./gradlew tasks --console plain | grep ${FLAVOR}ProductionX86_64Release) ]]; then
315             ./gradlew clean assemble${FLAVOR}ProductionX86_64Release --stacktrace || quit
316             cp $BASE_FILE_DIR/${FLAVOR_LOWERCASE}ProductionX86_64/release/*.apk $RELEASES_FILE_DIR/.
317         fi
318         if [[ $(./gradlew tasks --console plain | grep ${FLAVOR}ProductionArmv7Release) ]]; then
319             ./gradlew clean assemble${FLAVOR}ProductionArmv7Release --stacktrace || quit
320             cp $BASE_FILE_DIR/${FLAVOR_LOWERCASE}ProductionArmv7/release/*.apk $RELEASES_FILE_DIR/.
321         fi
322         if [[ $(./gradlew tasks --console plain | grep ${FLAVOR}ProductionArm64Release) ]]; then
323             ./gradlew clean assemble${FLAVOR}ProductionArm64Release --stacktrace || quit
324             cp $BASE_FILE_DIR/${FLAVOR_LOWERCASE}ProductionArm64/release/*.apk $RELEASES_FILE_DIR/.
325         fi
326     fi
327 fi
328
329 if [[ ${DO_SIGN} == true ]]
330 then
331     # check global vars
332     if [[ -z ${ANDROID_BUILD_TOOLS} ]] 
333     then
334         echo -e "${RED}ERROR: Environment variable ANDROID_BUILD_TOOLS not set! Please add it to your environment variables. Exiting.${NC}"
335         exit 
336     fi
337
338     if [[ -z ${FILE_NAME} && -z ${FILE_DIR} && ${DO_BUILD} == false ]]
339     then
340         echo -e "${RED}ERROR: Sign only needs a file name or a directory. Please check ./prepareForDistribution -help!${NC}"
341         exit
342     fi
343     if [[ -z ${KEY_STORE_NAME} ]] 
344     then
345         echo -e "${RED}ERROR: Key store not set. Please check ./prepareForDistribution -help${NC}"
346         exit
347     fi
348     if [[ -n ${FILE_NAME_STRING} && ${DO_BUILD} == true ]]
349     then
350         echo -e "${ORANGE}WARNING: Ignoring parameter -file. Built APK will be used instead.${NC}"
351     fi
352     
353     #---- OPT: SELECT APK FROM LAST BUILD ----
354     if [[ ${DO_BUILD} == true ]]
355     then
356         FILE_DIR=$RELEASES_FILE_DIR
357         echo -e "${GREEN} -> sign apks:${NC}"
358         ls -w 1 $FILE_DIR/*\.apk | xargs -I {} echo {}
359         xargs -I _ -ra <(ls -w 1 $FILE_DIR/*\.apk) bash -c 'sign _'
360     elif [[ ${MULTIPLE_APKS} == true ]]
361     then
362         echo -e "${GREEN} -> sign apks:${NC}"
363         ls -w 1 $FILE_DIR/*\.apk | xargs -I {} echo {}
364         xargs -I _ -ra <(ls -w 1 $FILE_DIR/*\.apk) bash -c 'sign _'
365     else
366         echo -e "${GREEN} -> sign apk: ${FILE_NAME_STRING}${NC}"
367         sign $FILE_NAME_STRING
368     fi
369 fi