Skip to content

Commit d76e553

Browse files
authored
200 - Bundle format and multiple secrets support (#198)
* Add bundle options * Make script to setup and unpack secrets * Add more guidance in README and bundle unbranded for testing * Upload bundles * Add .pepk generation * Add more guides about how to publish. * Remove `branded` target that cannot be used when having multiple signing keys and actually was never used * Add signing config to the alerte_niger flavor * Fix Alerte Niger icon
1 parent c164f16 commit d76e553

File tree

14 files changed

+478
-103
lines changed

14 files changed

+478
-103
lines changed

.github/workflows/build.yml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,7 @@ jobs:
4545
env:
4646
ANDROID_SECRETS_KEY: ${{ secrets.ANDROID_SECRETS_KEY }}
4747
ANDROID_SECRETS_IV: ${{ secrets.ANDROID_SECRETS_IV }}
48-
run: |
49-
openssl aes-256-cbc -K $ANDROID_SECRETS_KEY -iv $ANDROID_SECRETS_IV -in secrets.tar.gz.enc -out ./secrets.tar.gz -d
50-
tar -xf ./secrets.tar.gz
48+
run: make org=medic keydec
5149

5250
- name: Assemble unbranded
5351
if: ${{ env.ANDROID_SECRETS_KEY }}

.github/workflows/publish.yml

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,19 +36,24 @@ jobs:
3636
- name: Set up Fastlane
3737
run: gem install fastlane --no-document --quiet
3838

39-
- name: Unpack secrets
39+
- name: Unpack secrets medic
4040
env:
4141
ANDROID_SECRETS_KEY: ${{ secrets.ANDROID_SECRETS_KEY }}
4242
ANDROID_SECRETS_IV: ${{ secrets.ANDROID_SECRETS_IV }}
43-
run: |
44-
openssl aes-256-cbc -K $ANDROID_SECRETS_KEY -iv $ANDROID_SECRETS_IV -in secrets.tar.gz.enc -out ./secrets.tar.gz -d
45-
tar -xf ./secrets.tar.gz
43+
run: make org=medic keydec
44+
45+
- name: Unpack secrets alerte_niger
46+
env:
47+
ANDROID_SECRETS_KEY: ${{ secrets.ANDROID_SECRETS_KEY_ALERTE_NIGER }}
48+
ANDROID_SECRETS_IV: ${{ secrets.ANDROID_SECRETS_IV_ALERTE_NIGER }}
49+
run: make org=alerte_niger keydec
4650

4751
- name: Assemble unbranded
4852
uses: maierj/fastlane-action@v1.4.0
4953
with:
5054
lane: build
5155
options: '{ "flavor": "unbranded" }'
56+
5257
- name: Assemble gamma
5358
uses: maierj/fastlane-action@v1.4.0
5459
with:
@@ -215,6 +220,19 @@ jobs:
215220
with:
216221
lane: build
217222
options: '{ "flavor": "alerte_niger" }'
223+
env:
224+
ANDROID_KEYSTORE_PATH: alerte_niger.keystore
225+
ANDROID_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD_ALERTE_NIGER }}
226+
ANDROID_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD_ALERTE_NIGER }}
227+
- name: Bundle alerte_niger
228+
uses: maierj/fastlane-action@v1.4.0
229+
with:
230+
lane: bundle
231+
options: '{ "flavor": "alerte_niger" }'
232+
env:
233+
ANDROID_KEYSTORE_PATH: alerte_niger.keystore
234+
ANDROID_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD_ALERTE_NIGER }}
235+
ANDROID_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD_ALERTE_NIGER }}
218236

219237
- name: GitHub release
220238
uses: softprops/action-gh-release@v1
@@ -226,5 +244,6 @@ jobs:
226244
build/outputs/apk/**/*-xwalk-arm64-v8a-release.apk
227245
build/outputs/apk/**/*-xwalk-armeabi-v7a-release.apk
228246
build/outputs/apk/**/*-webview-arm64-v8a-release.apk
247+
build/outputs/bundle/**/*-release.aab
229248
env:
230249
GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}

.gitignore

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,16 @@
88
/.idea/
99
*.iml
1010

11-
secrets.tar.gz
12-
medic-official.keystore
11+
secrets*.tar.gz
12+
*.keystore
13+
*_private_key.pepk
1314
playstore-secret.json
15+
pepk.jar
1416

1517
fastlane/*
1618
!fastlane/Fastfile
1719

1820
.DS_Store
1921
.classpath
2022
.project
21-
.settings
23+
.settings

CHANGELOG.md

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
# Release notes
2+
3+
## 0.9.0
4+
5+
### Changes
6+
7+
- [feature] [cht-core#6741](https://github.com/medic/cht-core/issues/6741) Add more info about the version of the Android app :bookmark: cht-core v3.12.
8+
- [improvement] [#195](https://github.com/medic/cht-android/pull/195) New branding icon, and switch from "Medic Mobile" to "Medic".
9+
- [improvement] [#164](https://github.com/medic/cht-android/issues/164) Support Android 11.
10+
- [feature] [#206](https://github.com/medic/cht-android/issues/206) New branded app _Alerte Niger_.
11+
- [deprecation] Disable the _Demo_ app in the releases and in the Play Store (demo.dev.medicmobile.org is unmaintained).
12+
13+
### Development changes
14+
15+
- Upgrade Java and build dependencies. Now the base version is Java 11.
16+
- Support new Android App Bundle (`.aab`) required by the Play Store for new apps (for now only the new app _Alerte Niger_ uses it).
17+
- Add tooling to simplify the creation of new private keys for the Play Store.
18+
- Improvements in the linters configuration for better code quality.
19+
- Skip upload of APKs for `webview-armeabi-v7a` arch not used in releases.
20+
- Improvements in the Make config and more options available for developers using the CLI instead of the Android Studio.
21+
Improve the development section in the README, and how to create new branded apps and private keys for signing.
22+
- Fix instrumentations tests (UI tests) to avoid random failures, and speed up executions in Github Actions using caches.
23+
- Fix Github Actions configuration to enable CI when the PR is created for an external contributor.
24+
25+
## 0.8.0
26+
27+
### Changes
28+
29+
- [improvement] [#163](https://github.com/medic/cht-android/issues/163) New connection errors UX:
30+
- The improvements only apply to _Webview_ flavors.
31+
- It also applies when the app migrates to Webview from a XWalk installation.
32+
- [improvement] [#134](https://github.com/medic/cht-android/issues/134) New UX of Crosswalk to Webview migration:
33+
- Add splash screen while the data is migrated.
34+
- Fix bug that caused redirect to the login page after migrate.
35+
- [improvement] Remove unused `READ_EXTERNAL_STORAGE` from the `cmmb_kenya` and `surveillance_covid19_kenya` flavors
36+
37+
You can see more about these changes in the release notes for [CHT Core v3.11.0](https://github.com/medic/cht-core/blob/master/release-notes/docs/3.11.0.md)
38+
39+
## 0.7.3
40+
41+
### Changes
42+
43+
- [improvement] [cht-android#148](https://github.com/medic/cht-android/issues/148): Remove storage access for most flavors that don't use the permission
44+
- [improvement] New flavors added, and "livinggoods_innovation_ke" removed
45+
- [improvement] Add new translations for the prominent disclosure for location access: Tagalog (tel), Illonggo (hil), and Bisaya (ceb), and fixed translation string for the disclosure in Nepal (ne)
46+
47+
### Notes
48+
49+
A new flavor `unbranded_test` was added with the storage permission removed for testing, while `unbranded` flavor keeps the permission from the global manifest.
50+
51+
## 0.7.0
52+
53+
### Changes
54+
55+
- [feature] [cht-android#136](https://github.com/medic/cht-android/issues/136): Add UI for prominent disclosure when requesting location permissions.
56+
57+
### Notes
58+
59+
The text used in the new location permission request is in the Android wrapper app itself (`cht-android`), and translated differently than [CHT Core labels](https://docs.communityhealthtoolkit.org/core/overview/translations/). Any additions or modifications to translations in `cht-android` are done in the `strings.xml` files according to the [Android localization framework](https://developer.android.com/guide/topics/resources/localization).
60+
61+
62+
## 0.6.0
63+
64+
### Upgrade notes
65+
66+
This release is largely intended to unblock the publishing of new apps onto the Google Play Store in a manner compatible with Android 10+. This release includes a difficult upgrade experience which may cause operational challenges in the field for apps with users on Android 10+. In particular:
67+
68+
1. When loading the application after upgrade you will need an internet connection in order to cache the application code.
69+
2. The migration can take a few minutes to complete so after upgrade the user may be shown a login screen. If this happens, restart the application periodically until the user is logged in and the app loads as per usual.
70+
71+
We are planning improvements to [make the migration more user friendly](https://github.com/medic/cht-android/issues/134) in a future release.
72+
73+
Users on Android 9 and below do not need to be migrated and will be unaffected by this change. Because of this it is recommended that projects upgrade to v0.6.0 version of their app before issuing Android 10+ devices, if possible.
74+
75+
Earlier releases are no longer accepted by the Google Play Store.
76+
77+
### Changes
78+
79+
- [improvement] [cht-android#106](https://github.com/medic/cht-android/issues/106): Update target SDK version to 29 for Play Store compliance.
80+
81+
## 0.5.0
82+
83+
### Upgrade notes
84+
85+
This release changes the way in which location data is collected to better align with Play Store policies. Now the information is gathered only when filling in a form as opposed to as soon as the app is loaded.
86+
87+
*Note*: This breaks backwards compatibility with older versions of the CHT Core Framework which may mean that location data is no longer collected at all. It is recommended you upgrade to CHT v3.9.2 or later before upgrading the android app.
88+
89+
### Changes
90+
91+
- [feature] [cht-core#6380](https://github.com/medic/cht-core/issues/6380): Adds intent so opening deployment URLs will prompt to load in app
92+
- [improvement] [cht-android#111](https://github.com/medic/cht-android/issues/111): Compliance with Play Store developer policies for PII collection disclosure
93+
- [bug] [cht-core#6648](https://github.com/medic/cht-core/issues/6648): Blank screen when launching external apps from CHT Android app

Makefile

Lines changed: 162 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,15 @@ GRADLE = ./gradlew
33
GRADLE_OPTS = --daemon --parallel
44
flavor = UnbrandedWebview
55
abi = x86
6+
KEYTOOL = keytool
7+
OPENSSL = openssl
8+
JAVA = java
9+
BASE16 = base16
10+
RM_KEY_OPTS = -i
11+
APKSIGNER = apksigner
12+
13+
# Public key from Google for signing .pepk files (is the same for all apps in the Play Store)
14+
GOOGLE_ENC_KEY = eb10fe8f7c7c9df715022017b00c6471f8ba8170b13049a11e6c09ffe3056a104a3bbe4ac5a955f4ba4fe93fc8cef27558a3eb9d2a529a2092761fb833b656cd48b9de6a
615

716
ifdef ComSpec # Windows
817
# Use `/` for all paths, except `.\`
@@ -11,8 +20,6 @@ ifdef ComSpec # Windows
1120
endif
1221

1322
default: deploy logs
14-
branded: clean-apks assemble-all deploy-all logs
15-
branded-debug: clean-apks assemble-all-debug deploy-all logs
1623
xwalk: deploy-xwalk logs
1724

1825
logs:
@@ -31,13 +38,26 @@ clean:
3138
clean-apks:
3239
rm -rf build/outputs/apk/
3340

34-
assemble:
35-
${GRADLE} ${GRADLE_OPTS} assemble${flavor}
36-
assemble-all:
37-
${GRADLE} ${GRADLE_OPTS} assembleRelease
41+
assemble: check-env
42+
ANDROID_KEYSTORE_PATH=${ANDROID_KEYSTORE_PATH} ANDROID_KEY_ALIAS=${ANDROID_KEY_ALIAS} \
43+
ANDROID_KEYSTORE_PASSWORD=${ANDROID_KEYSTORE_PASSWORD} ANDROID_KEY_PASSWORD=${ANDROID_KEY_PASSWORD} \
44+
${GRADLE} ${GRADLE_OPTS} assemble${flavor}
45+
assemble-all: check-env
46+
ANDROID_KEYSTORE_PATH=${ANDROID_KEYSTORE_PATH} ANDROID_KEY_ALIAS=${ANDROID_KEY_ALIAS} \
47+
ANDROID_KEYSTORE_PASSWORD=${ANDROID_KEYSTORE_PASSWORD} ANDROID_KEY_PASSWORD=${ANDROID_KEY_PASSWORD} \
48+
${GRADLE} ${GRADLE_OPTS} assembleRelease
3849
assemble-all-debug:
3950
${GRADLE} ${GRADLE_OPTS} assembleDebug
4051

52+
bundle: check-env
53+
ANDROID_KEYSTORE_PATH=${ANDROID_KEYSTORE_PATH} ANDROID_KEY_ALIAS=${ANDROID_KEY_ALIAS} \
54+
ANDROID_KEYSTORE_PASSWORD=${ANDROID_KEYSTORE_PASSWORD} ANDROID_KEY_PASSWORD=${ANDROID_KEY_PASSWORD} \
55+
${GRADLE} ${GRADLE_OPTS} bundle${flavor}Release
56+
bundle-all: check-env
57+
ANDROID_KEYSTORE_PATH=${ANDROID_KEYSTORE_PATH} ANDROID_KEY_ALIAS=${ANDROID_KEY_ALIAS} \
58+
ANDROID_KEYSTORE_PASSWORD=${ANDROID_KEYSTORE_PASSWORD} ANDROID_KEY_PASSWORD=${ANDROID_KEY_PASSWORD} \
59+
${GRADLE} ${GRADLE_OPTS} bundleRelease
60+
4161
uninstall-all:
4262
${GRADLE} uninstallAll
4363

@@ -54,4 +74,139 @@ test: lint
5474
test-ui:
5575
${GRADLE} connectedUnbrandedWebviewDebugAndroidTest -Pabi=${abi} --stacktrace
5676
test-ui-gamma:
57-
${GRADLE} connectedMedicmobilegammaWebviewDebugAndroidTest -Pabi=${abi} --stacktrace
77+
${GRADLE} connectedMedicmobilegammaWebviewDebugAndroidTest -Pabi=${abi} --
78+
79+
80+
#
81+
# "secrets" targets, to setup and unpack keystores
82+
#
83+
84+
# Generate keystore
85+
keystore: check-org ${org}.keystore
86+
87+
# Remove the keystore, the pepk file, and the compressed version
88+
keyrm: check-org
89+
rm ${RM_KEY_OPTS} ${org}.keystore ${org}_private_key.pepk secrets/secrets-${org}.tar.gz
90+
91+
# Remove all: the keystore, the encrypted version, the compressed version and the encrypted version
92+
keyrm-all: check-org
93+
rm ${RM_KEY_OPTS} secrets/secrets-${org}.tar.gz.enc
94+
${MAKE} keyrm
95+
96+
# Remove the keystore and the compressed version, leaving only the encrypted version
97+
keyclean: check-org
98+
rm ${RM_KEY_OPTS} ${org}.keystore secrets/secrets-${org}.tar.gz
99+
100+
# Print info about the keystore
101+
keyprint: check-org check-env
102+
${KEYTOOL} -list -v -storepass ${ANDROID_KEYSTORE_PASSWORD} -keystore ${org}.keystore
103+
104+
# Print info about the key signature used in the apk release file
105+
keyprint-apk:
106+
$(if $(shell which $(APKSIGNER)),,$(error "No command '$(APKSIGNER)' in $$PATH"))
107+
$(eval APK := $(shell find build/outputs/apk -name \*-release.apk | head -n1))
108+
${APKSIGNER} verify -v --print-certs ${APK}
109+
110+
# Print info about the key signature used in the apk release file
111+
keyprint-bundle:
112+
$(eval AAB := $(shell find build/outputs/bundle -name \*-release.aab | head -n1))
113+
${KEYTOOL} -printcert -jarfile ${AAB}
114+
115+
keygen: check-org keysetup secrets/secrets-${org}.tar.gz.enc
116+
117+
keydec: check-org keysetup check-env
118+
${OPENSSL} aes-256-cbc -iv ${ANDROID_SECRETS_IV} -K ${ANDROID_SECRETS_KEY} -in secrets/secrets-${org}.tar.gz.enc -out secrets/secrets-${org}.tar.gz -d
119+
chmod go-rw secrets/secrets-${org}.tar.gz
120+
${MAKE} keyunpack
121+
122+
keyunpack: check-org
123+
tar -xf secrets/secrets-${org}.tar.gz
124+
125+
keysetup:
126+
$(eval EXEC_CERT_REQUIRED = ${JAVA} ${KEYTOOL} ${OPENSSL})
127+
$(info Verifing the following executables are in the $$PATH: ${EXEC_CERT_REQUIRED} ...)
128+
$(foreach exec,$(EXEC_CERT_REQUIRED),\
129+
$(if $(shell which $(exec)),,$(error "No command '$(exec)' in $$PATH")))
130+
131+
#
132+
# Intermediate targets for "secrets", don't use them
133+
#
134+
135+
check-org:
136+
ifndef org
137+
second_argument := $(word 2, $(MAKECMDGOALS) )
138+
$(error "org" name not set. Try 'make org=name $(filter-out $@, $(MAKECMDGOALS))')
139+
endif
140+
ifeq ($(org),name)
141+
$(error "org" cannot be equal to "name", it was just an example :S)
142+
endif
143+
ifdef org
144+
$(eval ORG_UPPER := $(shell echo $(org) | tr [:lower:] [:upper:]))
145+
endif
146+
147+
check-env:
148+
ifdef org
149+
$(eval ORG_UPPER := $(shell echo $(org) | tr [:lower:] [:upper:]))
150+
endif
151+
ifndef ANDROID_SECRETS_IV
152+
$(eval VARNAME=ANDROID_SECRETS_IV_${ORG_UPPER})
153+
$(eval ANDROID_SECRETS_IV := $(shell echo ${${VARNAME}}))
154+
$(eval VARNAME=ANDROID_SECRETS_KEY_${ORG_UPPER})
155+
$(eval ANDROID_SECRETS_KEY := $(shell echo ${${VARNAME}}))
156+
$(eval VARNAME=ANDROID_KEYSTORE_PASSWORD_${ORG_UPPER})
157+
$(eval ANDROID_KEYSTORE_PASSWORD := $(shell echo ${${VARNAME}}))
158+
$(eval VARNAME=ANDROID_KEY_PASSWORD_${ORG_UPPER})
159+
$(eval ANDROID_KEY_PASSWORD := $(shell echo ${${VARNAME}}))
160+
$(eval VARNAME=ANDROID_KEY_ALIAS_${ORG_UPPER})
161+
$(eval ANDROID_KEY_ALIAS := $(shell echo ${${VARNAME}}))
162+
$(eval VARNAME=ANDROID_KEYSTORE_PATH_${ORG_UPPER})
163+
$(eval ANDROID_KEYSTORE_PATH := $(shell echo ${${VARNAME}}))
164+
endif
165+
166+
${org}.keystore: check-org
167+
$(if $(shell which $(BASE16)),,$(error "No command '$(BASE16)' in $$PATH"))
168+
$(eval ANDROID_KEYSTORE_PASSWORD := $(shell ${BASE16} /dev/urandom | head -n 1 -c 16))
169+
${KEYTOOL} -genkey -storepass ${ANDROID_KEYSTORE_PASSWORD} -v -keystore ${org}.keystore -alias medicmobile -keyalg RSA -keysize 2048 -validity 9125
170+
chmod go-rw ${org}.keystore
171+
172+
secrets/secrets-${org}.tar.gz: ${org}.keystore ${org}_private_key.pepk
173+
tar -czf secrets/secrets-${org}.tar.gz ${org}.keystore ${org}_private_key.pepk
174+
chmod go-rw secrets/secrets-${org}.tar.gz
175+
176+
${org}_private_key.pepk: check-org pepk.jar
177+
${JAVA} -jar pepk.jar --keystore=${org}.keystore --alias=medicmobile --keystore-pass=${ANDROID_KEYSTORE_PASSWORD} --output=${org}_private_key.pepk --include-cert --encryptionkey=${GOOGLE_ENC_KEY}
178+
chmod go-rw ${org}_private_key.pepk
179+
180+
pepk.jar:
181+
$(info Downloading pepk.jar ...)
182+
curl https://www.gstatic.com/play-apps-publisher-rapid/signing-tool/prod/pepk.jar -o pepk.jar
183+
184+
secrets/secrets-${org}.tar.gz.enc: secrets/secrets-${org}.tar.gz
185+
$(eval ANDROID_SECRETS_IV := $(shell ${BASE16} /dev/urandom | head -n 1 -c 32))
186+
$(eval ANDROID_SECRETS_KEY := $(shell ${BASE16} /dev/urandom | head -n 1 -c 64))
187+
$(eval ANDROID_KEYSTORE_PATH := $(org).keystore)
188+
$(eval ANDROID_KEY_ALIAS := medicmobile)
189+
${OPENSSL} aes-256-cbc -iv ${ANDROID_SECRETS_IV} -K ${ANDROID_SECRETS_KEY} -in secrets/secrets-${org}.tar.gz -out secrets/secrets-${org}.tar.gz.enc
190+
chmod go-rw secrets/secrets-${org}.tar.gz.enc
191+
$(info )
192+
$(info ########################################### Secrets! ###########################################)
193+
$(info # #)
194+
$(info # The following environment variables needs to be added to the CI environment #)
195+
$(info # (Github Actions), and to your local environment if you also want #)
196+
$(info # to sign APK or AAB files locally: #)
197+
$(info # #)
198+
$(info )
199+
$(info export ANDROID_KEYSTORE_PASSWORD_$(ORG_UPPER)=$(ANDROID_KEYSTORE_PASSWORD))
200+
$(info export ANDROID_KEY_PASSWORD_$(ORG_UPPER)=$(ANDROID_KEYSTORE_PASSWORD))
201+
$(info export ANDROID_SECRETS_IV_$(ORG_UPPER)=$(ANDROID_SECRETS_IV))
202+
$(info export ANDROID_SECRETS_KEY_$(ORG_UPPER)=$(ANDROID_SECRETS_KEY))
203+
$(info export ANDROID_KEYSTORE_PATH_$(ORG_UPPER)=$(ANDROID_KEYSTORE_PATH))
204+
$(info export ANDROID_KEY_ALIAS_$(ORG_UPPER)=$(ANDROID_KEY_ALIAS))
205+
$(info )
206+
$(info #)
207+
$(info # The file secrets/secrets-${org}.tar.gz.enc was created and has to be added to the git)
208+
$(info # repository (don't worry, it's encrypted with some of the keys above). #)
209+
$(info # NOTE: *keep the environment variables secret !!* #)
210+
$(info # #)
211+
$(info ########################################### End of Secrets ###########################################)
212+
$(info )

0 commit comments

Comments
 (0)