From 437d32d4d496f3949ec343b020a041c09804f0cb Mon Sep 17 00:00:00 2001 From: Ash Davies <1892070+ashdavies@users.noreply.github.com> Date: Wed, 28 Aug 2024 21:24:54 +0200 Subject: [PATCH] Configure release signing for conference app (#1124) --- .github/workflows/android-release.yml | 86 ++++++++++++++++++ .../{pre-merge.yml => pull-request.yml} | 7 +- .gitignore | 11 ++- app-launcher/android/google-services.aes | Bin 1408 -> 0 bytes asg-service/src/.gitignore | 1 - conferences-app/build.gradle.kts | 51 +++++++++-- conferences-app/google-services.aes | Bin 1408 -> 0 bytes conferences-app/proguard-rules.pro | 1 + .../kotlin/io/ashdavies/party/MainActivity.kt | 4 +- .../src/main/kotlin/StringProperty.kt | 57 ++++++++---- keystore.properties | 0 11 files changed, 183 insertions(+), 35 deletions(-) create mode 100644 .github/workflows/android-release.yml rename .github/workflows/{pre-merge.yml => pull-request.yml} (94%) delete mode 100644 app-launcher/android/google-services.aes delete mode 100644 asg-service/src/.gitignore delete mode 100644 conferences-app/google-services.aes create mode 100644 conferences-app/proguard-rules.pro create mode 100644 keystore.properties diff --git a/.github/workflows/android-release.yml b/.github/workflows/android-release.yml new file mode 100644 index 000000000..c2bcd9ef2 --- /dev/null +++ b/.github/workflows/android-release.yml @@ -0,0 +1,86 @@ +name: Android Release + +on: + push: + branches: + - main + + workflow_dispatch: + inputs: + log_level: + type: choice + description: Log Level + required: false + default: info + options: + - error + - quiet + - warning + - lifecycle + - info + - debug + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number }} + cancel-in-progress: true + +jobs: + build: + name: Assemble Release + runs-on: ubuntu-latest + + permissions: + pull-requests: 'write' + id-token: 'write' + + steps: + - uses: actions/checkout@v3 + + - uses: ./.github/actions/setup-cloud + with: + workload_identity_provider: ${{ secrets.GOOGLE_WORKLOAD_IDENTITY }} + service_account: ${{ secrets.GOOGLE_SERVICE_ACCOUNT_ID }} + setup_gcloud: false + + - uses: ./.github/actions/setup-gradle + + - uses: 1password/load-secrets-action/configure@v2 + with: + service-account-token: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }} + + - id: op-secrets + uses: 1password/load-secrets-action@v2 + with: + export-env: false + env: + GOOGLE_SERVICES: op://development/google services/playground release/base64 + KEYSTORE_BASE64: op://development/playground keystore/keystore base64 + KEYSTORE_PASSWORD: op://development/playground keystore/keystore password + KEY_ALIAS: op://development/playground keystore/key alias + KEY_PASSWORD: op://development/playground keystore/key password + + - env: + GOOGLE_SERVICES: ${{ steps.op-secrets.outputs.GOOGLE_SERVICES }} + KEYSTORE_BASE64: ${{ steps.op-secrets.outputs.KEYSTORE_BASE64 }} + run: | + echo $GOOGLE_SERVICES | base64 --decode > app-launcher/android/google-services.json + echo $GOOGLE_SERVICES | base64 --decode > conferences-app/google-services.json + echo $KEYSTORE_BASE64 | base64 --decode > keystore.jks + + - id: gradle + env: + ANDROID_API_KEY: ${{ secrets.ANDROID_API_KEY }} + BROWSER_API_KEY: ${{ secrets.BROWSER_API_KEY }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + KEY_STORE_FILE: keystore.jks + KEY_STORE_PASSWORD: ${{ steps.op-secrets.outputs.KEYSTORE_PASSWORD }} + RELEASE_KEY_ALIAS: ${{ steps.op-secrets.outputs.KEY_ALIAS }} + RELEASE_KEY_PASSWORD: ${{ steps.op-secrets.outputs.KEY_PASSWORD }} + SERVER_CLIENT_ID: ${{ secrets.SERVER_CLIENT_ID }} + run: ./gradlew assembleRelease --console=plain + + - uses: actions/upload-artifact@v2 + with: + if-no-files-found: error + name: apk-release + path: "**/*-release.apk" diff --git a/.github/workflows/pre-merge.yml b/.github/workflows/pull-request.yml similarity index 94% rename from .github/workflows/pre-merge.yml rename to .github/workflows/pull-request.yml index 95276deff..c971258bc 100644 --- a/.github/workflows/pre-merge.yml +++ b/.github/workflows/pull-request.yml @@ -1,4 +1,4 @@ -name: Pre Merge Checks +name: Pull Request on: [ pull_request ] concurrency: @@ -7,7 +7,7 @@ concurrency: jobs: build: - name: Pre Merge Checks + name: Build runs-on: ubuntu-latest permissions: @@ -17,8 +17,7 @@ jobs: steps: - uses: actions/checkout@v3 - - id: setup-cloud - uses: ./.github/actions/setup-cloud + - uses: ./.github/actions/setup-cloud with: workload_identity_provider: ${{ secrets.GOOGLE_WORKLOAD_IDENTITY }} service_account: ${{ secrets.GOOGLE_SERVICE_ACCOUNT_ID }} diff --git a/.gitignore b/.gitignore index 31771504a..f8a94cd21 100644 --- a/.gitignore +++ b/.gitignore @@ -3,10 +3,10 @@ /.idea # Android -/gradle.properties -**/local.properties /captures *.hprof +*.apk +*.ap_ # Database *.db @@ -22,9 +22,14 @@ **/*.log # Gradle +/gradle.properties +**/local.properties .gradle build -/reports + +# Keystore Files +*.jks +*.keystore # Kotlin .kotlin/sessions diff --git a/app-launcher/android/google-services.aes b/app-launcher/android/google-services.aes deleted file mode 100644 index c60b6fa3eb67082c05ffbc0fa55c958ec1bd59b7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1408 zcmV-`1%LWeVQh3|WM5z8GtTc*rVW3JelSxek^PGNM+j9yySde~s`w%(%aov4)}EaI zPVgKVscSm_20BaPhI?2w>^%3aO8i&ASIH=NL;mgWWzFXa#!DE9uo@%nN}$}lhp=je z4LSZa&!;P?$A3VS2PN|$b!wHQhg-T%d9Zz~fAlbq!tHm(evjL@V=?!eP63zwHRC~J zbA0_sW(>NbYp+@fGc}bN2;)OD${tBBYXu2cndX*Sp)+V~NY6axb+xG@U0?`NAGqb1*>{trG8C#)$la$ZCwdkCoFTo1r1PgWeFvk{F+%Ky155lkY(n8;-Hv+RV_bfkrO8>VKB4Y6 zEJ)vT?M>`2wX7|4?poZG!%YF{wm8(=S2e@&GuT=k35u7Rz zw1WPPi|GZz{hDUKP$FI-4AvbEDg~)IEC|kUs1DCHr}0p_1q2)=;mbntP2&9)BZT#23RE z!Rg+v!o@pM`qkMPP4)zK5q7_87Q%!q4OPHq)3Mj;Tf!;KQ1B>sFe7w_B7owh$sVD6 zIML-(-kh&(?v78|tIZ3=ncnGbNhRyGKBlIwt44iC>8H9KIpKL!1nCr^jpf^q4u}Hg zt(hWQ#H@H$_2LSUUvKgtb^q>lfc>82>K8hg^Qg8iJjX%iZ(#^Mk~Awn+o(AD)+kC(py2L!gjJt*?O zDs3#yOGGnOi-|g$6_h=E>!*E>=6I;5Q|ylZ&;jpdDO!!EZT?F%rRs{k zL1@<|`z=JNe2?N6nZleQQBd%fg6!kMCf%IH|Bvzeb=HCz$PaTFnl~9)f6|CPzC8Un zU#4i))Q}#Ji28~3v8k5XR!XOK#kVNyGm!zBUqbh6aGN@%b@U9MS-5t%@BTh;yn93o z>*=En|7x;R{)vRfE}~PGUYyDO0|3(%wcJ|AP1K>wxN*Sj>^cAhA3I}0YA+VgQ2BqEl$iJ= z{bRIWSyunsg9pB1V?nEt*u8%)qQyII)t3CReKju9SDXiGxGWHwHwxYHM^^}((7 O>qI;eLREw)qNoLWRLp4r diff --git a/asg-service/src/.gitignore b/asg-service/src/.gitignore deleted file mode 100644 index aa0e80b49..000000000 --- a/asg-service/src/.gitignore +++ /dev/null @@ -1 +0,0 @@ -**/schema.graphqls diff --git a/conferences-app/build.gradle.kts b/conferences-app/build.gradle.kts index 32111b9ae..58cde9201 100644 --- a/conferences-app/build.gradle.kts +++ b/conferences-app/build.gradle.kts @@ -17,6 +17,48 @@ android { res.srcDirs("src/androidMain/res") } + val release by signingConfigs.creating { + val debug by signingConfigs.getting + initWith(debug) + + enableV3Signing = true + enableV4Signing = true + + val keyStoreFile by stringPropertyOrNull { + if (it != null) storeFile = rootProject.file(it) + } + + val keyStorePassword by stringPropertyOrNull { + if (it != null) storePassword = it + } + + val releaseKeyAlias by stringPropertyOrNull { + if (it != null) keyAlias = it + } + + val releaseKeyPassword by stringPropertyOrNull { + if (it != null) keyPassword = it + } + } + + buildTypes { + debug { + applicationIdSuffix = ".debug" + } + + release { + isMinifyEnabled = true + isShrinkResources = true + + signingConfig = release + + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro", + ) + } + } + defaultConfig { versionName = "1.0" versionCode = 1 @@ -26,13 +68,8 @@ android { } buildConfig { - val androidApiKey by stringProperty { value -> - buildConfigField("ANDROID_API_KEY", value) - } - - val androidStrictMode by booleanProperty { value -> - buildConfigField("ANDROID_STRICT_MODE", value) - } + val androidApiKey by stringPropertyWithTag(::buildConfigField) + val androidStrictMode by booleanPropertyWithTag(::buildConfigField) packageName.set(android.namespace) } diff --git a/conferences-app/google-services.aes b/conferences-app/google-services.aes deleted file mode 100644 index c60b6fa3eb67082c05ffbc0fa55c958ec1bd59b7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1408 zcmV-`1%LWeVQh3|WM5z8GtTc*rVW3JelSxek^PGNM+j9yySde~s`w%(%aov4)}EaI zPVgKVscSm_20BaPhI?2w>^%3aO8i&ASIH=NL;mgWWzFXa#!DE9uo@%nN}$}lhp=je z4LSZa&!;P?$A3VS2PN|$b!wHQhg-T%d9Zz~fAlbq!tHm(evjL@V=?!eP63zwHRC~J zbA0_sW(>NbYp+@fGc}bN2;)OD${tBBYXu2cndX*Sp)+V~NY6axb+xG@U0?`NAGqb1*>{trG8C#)$la$ZCwdkCoFTo1r1PgWeFvk{F+%Ky155lkY(n8;-Hv+RV_bfkrO8>VKB4Y6 zEJ)vT?M>`2wX7|4?poZG!%YF{wm8(=S2e@&GuT=k35u7Rz zw1WPPi|GZz{hDUKP$FI-4AvbEDg~)IEC|kUs1DCHr}0p_1q2)=;mbntP2&9)BZT#23RE z!Rg+v!o@pM`qkMPP4)zK5q7_87Q%!q4OPHq)3Mj;Tf!;KQ1B>sFe7w_B7owh$sVD6 zIML-(-kh&(?v78|tIZ3=ncnGbNhRyGKBlIwt44iC>8H9KIpKL!1nCr^jpf^q4u}Hg zt(hWQ#H@H$_2LSUUvKgtb^q>lfc>82>K8hg^Qg8iJjX%iZ(#^Mk~Awn+o(AD)+kC(py2L!gjJt*?O zDs3#yOGGnOi-|g$6_h=E>!*E>=6I;5Q|ylZ&;jpdDO!!EZT?F%rRs{k zL1@<|`z=JNe2?N6nZleQQBd%fg6!kMCf%IH|Bvzeb=HCz$PaTFnl~9)f6|CPzC8Un zU#4i))Q}#Ji28~3v8k5XR!XOK#kVNyGm!zBUqbh6aGN@%b@U9MS-5t%@BTh;yn93o z>*=En|7x;R{)vRfE}~PGUYyDO0|3(%wcJ|AP1K>wxN*Sj>^cAhA3I}0YA+VgQ2BqEl$iJ= z{bRIWSyunsg9pB1V?nEt*u8%)qQyII)t3CReKju9SDXiGxGWHwHwxYHM^^}((7 O>qI;eLREw)qNoLWRLp4r diff --git a/conferences-app/proguard-rules.pro b/conferences-app/proguard-rules.pro new file mode 100644 index 000000000..232a0d400 --- /dev/null +++ b/conferences-app/proguard-rules.pro @@ -0,0 +1 @@ +-dontwarn org.slf4j.impl.StaticLoggerBinder diff --git a/conferences-app/src/androidMain/kotlin/io/ashdavies/party/MainActivity.kt b/conferences-app/src/androidMain/kotlin/io/ashdavies/party/MainActivity.kt index c35859dda..2e5fe062f 100644 --- a/conferences-app/src/androidMain/kotlin/io/ashdavies/party/MainActivity.kt +++ b/conferences-app/src/androidMain/kotlin/io/ashdavies/party/MainActivity.kt @@ -46,13 +46,13 @@ internal class MainActivity : ComponentActivity() { } setContent { - LauncherApp { intent.getStringExtra(it) } + LauncherApp() } } } @Composable -private fun LauncherApp(context: Context = LocalContext.current, extra: (String) -> String?) { +private fun LauncherApp(context: Context = LocalContext.current) { ProvideHttpClient( config = { install(DefaultRequest) { diff --git a/fused-properties/src/main/kotlin/StringProperty.kt b/fused-properties/src/main/kotlin/StringProperty.kt index 9a53bfee7..837002dd9 100644 --- a/fused-properties/src/main/kotlin/StringProperty.kt +++ b/fused-properties/src/main/kotlin/StringProperty.kt @@ -6,39 +6,60 @@ import kotlin.properties.ReadOnlyProperty public fun interface ReadOnlyDelegateProvider : PropertyDelegateProvider> -private fun Project.stringPropertyProvider(propertyName: String): Provider { +private interface PropertyDefinition { + val gradlePropertyName: String + val envPropertyName: String +} + +private fun PropertyDefinition(propertyName: String) = object : PropertyDefinition { + private val propertyNameParts = propertyName.split(Regex("(?=[A-Z])")) + override val gradlePropertyName = propertyNameParts.joinToString(".") { it.lowercase() } + override val envPropertyName = propertyNameParts.joinToString("_") { it.uppercase() } +} + +private fun Project.stringPropertyProvider(definition: PropertyDefinition): Provider { val rootPropertiesProvider = rootProject.cachedLocalPropertiesProvider() val localPropertiesProvider = cachedLocalPropertiesProvider() val startPropertiesProvider = startParameterProvider() - val propertyNameParts = propertyName.split(Regex("(?=[A-Z])")) - val gradlePropertyName = propertyNameParts.joinToString(".") { it.lowercase() } - val envPropertyName = propertyNameParts.joinToString("_") { it.uppercase() } - - return startPropertiesProvider.mapOrNull { it[gradlePropertyName] } - .orElse(localPropertiesProvider.map { it.getProperty(gradlePropertyName) }) - .orElse(rootPropertiesProvider.map { it.getProperty(gradlePropertyName) }) - .orElse(providers.gradleProperty(gradlePropertyName)) - .orElse(providers.environmentVariable(envPropertyName)) + return startPropertiesProvider.mapOrNull { it[definition.gradlePropertyName] } + .orElse(localPropertiesProvider.map { it.getProperty(definition.gradlePropertyName) }) + .orElse(rootPropertiesProvider.map { it.getProperty(definition.gradlePropertyName) }) + .orElse(providers.gradleProperty(definition.gradlePropertyName)) + .orElse(providers.environmentVariable(definition.envPropertyName)) } public fun Project.booleanProperty(block: (Boolean) -> Unit = { }): ReadOnlyDelegateProvider { - return readOnlyDelegateProvider { it.get().toBoolean().also(block) } + return readOnlyDelegateProvider { provider, _ -> provider.get().toBoolean().also(block) } +} + +public fun Project.booleanPropertyWithTag(action: (String, Boolean) -> Unit): ReadOnlyDelegateProvider { + return readOnlyDelegateProvider { provider, tag -> provider.get().toBoolean().also { action(tag, it) } } } public fun Project.stringProperty(block: (String) -> Unit = { }): ReadOnlyDelegateProvider { - return readOnlyDelegateProvider { it.get().also(block) } + return readOnlyDelegateProvider { provider, _ -> provider.get().also(block) } +} + +public fun Project.stringPropertyWithTag(action: (String, String) -> Unit): ReadOnlyDelegateProvider { + return readOnlyDelegateProvider { provider, tag -> provider.get().also { action(tag, it) } } } public fun Project.stringPropertyOrNull(block: (String?) -> Unit = { }): ReadOnlyDelegateProvider { - return readOnlyDelegateProvider { it.orNull.also(block) } + return readOnlyDelegateProvider { provider, _ -> provider.orNull.also(block) } } -private fun Project.readOnlyDelegateProvider(transform: (Provider) -> T): ReadOnlyDelegateProvider { - return ReadOnlyDelegateProvider { _, property -> - val value = transform(stringPropertyProvider(property.name)) - ReadOnlyProperty { _, _ -> value } - } +private fun Project.readOnlyDelegateProvider( + transform: (provider: Provider, tag: String) -> T, +): ReadOnlyDelegateProvider = ReadOnlyDelegateProvider { _, property -> + val definition = PropertyDefinition(property.name) + + val value = transform( + stringPropertyProvider(definition), + definition.envPropertyName, + ) + + ReadOnlyProperty { _, _ -> value } } private fun Provider.mapOrNull(block: (S) -> T?): Provider { diff --git a/keystore.properties b/keystore.properties new file mode 100644 index 000000000..e69de29bb