Skip to content

Commit

Permalink
Feat/ci (#12)
Browse files Browse the repository at this point in the history
1. bump firebase deps;
2. skip google-services.json and keystore.properties for local dev and CI test.
3. separate key per flavor
4. upload google play with changelog
  • Loading branch information
kid1412621 authored May 11, 2024
1 parent 37087b9 commit 252e633
Show file tree
Hide file tree
Showing 6 changed files with 120 additions and 114 deletions.
94 changes: 54 additions & 40 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,41 +6,35 @@ on:
- '*'

jobs:
setup:
environment: production
runs-on: ubuntu-latest
steps:
- name: Generate keystore.properties
run: |
cat <<EOF > keystore.properties
storeFile:${{ vars.KEYSTORE_STORE_FILE_PATH }}
storePassword:${{ secrets.KEYSTORE_STORE_PASSWORD }}
keyAlias:${{ secrets.KEYSTORE_KEY_ALIAS }}
keyPassword:${{ secrets.KEYSTORE_KEY_PASSWORD }}
EOF
- name: Generate keystore.jks
run: echo "${{ secrets.KEYSTORE_JKS_BASE64 }}" | base64 --decode > keystore.jks
- name: Generate google-services.json
run: echo "${{ secrets.GOOGLE_SERVICES_BASE64 }}" | base64 --decode > google-services.json

build_release_github:
needs: setup
environment: production
env:
KEYSTORE_DIR: keystore
FLAVOR: github
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: set up JDK 17
- name: Setup JDK 17
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
cache: gradle
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Move google-services.json
run: mv google-services.json app/
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v3
- name: Generate keystore.properties
run: |
mkdir -p ${{ env.KEYSTORE_DIR }} && cat << EOF > "${{ env.KEYSTORE_DIR }}/${{ env.FLAVOR }}.properties"
storeFile:${{ vars.GH_KEYSTORE_STORE_FILE_PATH }}
storePassword:${{ secrets.GH_KEYSTORE_STORE_PASSWORD }}
keyAlias:${{ secrets.GH_KEYSTORE_KEY_ALIAS }}
keyPassword:${{ secrets.GH_KEYSTORE_KEY_PASSWORD }}
EOF
- name: Generate keystore.jks
run: echo "${{ secrets.GH_KEYSTORE_JKS_BASE64 }}" | base64 --decode > "${{ env.KEYSTORE_DIR }}/${{ vars.GH_KEYSTORE_STORE_FILE_PATH }}"
- name: Generate google-services.json
run: echo "${{ secrets.GOOGLE_SERVICES_BASE64 }}" | base64 --decode > app/google-services.json
- name: Build release APK for Github Release
run: ./gradlew assembleGithub --no-daemon
run: ./gradlew assembleGithubRelease --no-daemon
- name: Generate release notes
run: |
{
Expand All @@ -51,32 +45,52 @@ jobs:
- name: Publish APK on Github Release
uses: softprops/action-gh-release@v2
with:
files: app/build/outputs/apk/release/app-release.apk
files: app/build/outputs/apk/${{ env.FLAVOR }}/release/*.apk
generate_release_notes: true
append_body: true
body: ${{ env.RELEASE_NOTE }}

build_release_play:
needs: setup
build_release_google_play:
environment: production
env:
KEYSTORE_DIR: keystore
FLAVOR: play
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: set up JDK 17
- name: Setup JDK 17
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
cache: gradle
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Move google-services.json
run: mv google-services.json app/
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v3
- name: Generate keystore.properties
run: |
mkdir -p ${{ env.KEYSTORE_DIR }} && cat << EOF > "${{ env.KEYSTORE_DIR }}/${{ env.FLAVOR }}.properties"
storeFile:${{ vars.PLAY_KEYSTORE_STORE_FILE_PATH }}
storePassword:${{ secrets.PLAY_KEYSTORE_STORE_PASSWORD }}
keyAlias:${{ secrets.PLAY_KEYSTORE_KEY_ALIAS }}
keyPassword:${{ secrets.PLAY_KEYSTORE_KEY_PASSWORD }}
EOF
- name: Generate keystore.jks
run: echo "${{ secrets.PLAY_KEYSTORE_JKS_BASE64 }}" | base64 --decode > "${{ env.KEYSTORE_DIR }}/${{ vars.PLAY_KEYSTORE_STORE_FILE_PATH }}"
- name: Generate google-services.json
run: echo "${{ secrets.GOOGLE_SERVICES_BASE64 }}" | base64 --decode > app/google-services.json
- name: Generate release notes
run: |
mkdir -p whatsnew &&
{
awk -v version="${{ github.ref_name }}" '($0 ~ "^## " version "($|[[:space:]])") {p=1;next} /^## / {p=0} p && NF' CHANGELOG.md
} > whatsnew/whatsnew-en-US
- name: Build Release AAB for Google Play
run: ./gradlew bundlePlay --no-daemon
- name: Upload AAB
uses: actions/upload-artifact@v4
run: ./gradlew bundlePlayRelease --no-daemon
- name: Release to Google Play
uses: r0adkll/upload-google-play@v1
with:
name: subspace
path: app/build/outputs/bundle/release/app-release.aab

serviceAccountJsonPlainText: ${{ secrets.GOOGLE_SERVICE_ACCOUNT_JSON }}
packageName: me.nanova.subspace
releaseFiles: app/build/outputs/bundle/${{ env.FLAVOR }}Release/*.aab
# production,beta,alpha,internal
track: internal
whatsNewDirectory: whatsnew
35 changes: 5 additions & 30 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
name: Test

env:
CI_TEST: true

on:
push:
branches: [ "main" ]
Expand All @@ -16,35 +19,7 @@ jobs:
with:
java-version: '17'
distribution: 'temurin'
cache: gradle
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Generate fake google-services.json
run: |
cat << EOF > app/google-services.json
{
"project_info": {
"project_number": "0",
"project_id": "id",
"storage_bucket": "bucket"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "app-id",
"android_client_info": { "package_name": "me.nanova.subspace" }
},
"api_key": [{"current_key": "key"}]
},
{
"client_info": {
"mobilesdk_app_id": "app-id",
"android_client_info": { "package_name": "me.nanova.subspace.debug" }
},
"api_key": [{"current_key": "key"}]
}
]
}
EOF
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v3
- name: Run unit tests
run: ./gradlew test --no-daemon
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ render.experimental.xml
*.pem
*.keystore
keystore.properties
keystore/

# Google Services (e.g. APIs or Firebase)
google-services.json
Expand Down
29 changes: 19 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,15 @@

# Project setup

1. Add a dummy google-services.json to app directory:
## Local Dev

TBD

## Build Release

1. Provide an env var: `export PROD_RELEASE=true`;

2. Add `google-services.json` to app directory:

```bash
cat << EOF > app/google-services.json
Expand All @@ -19,26 +27,27 @@ cat << EOF > app/google-services.json
"android_client_info": { "package_name": "me.nanova.subspace" }
},
"api_key": [{"current_key": "key"}]
},
{
"client_info": {
"mobilesdk_app_id": "app-id",
"android_client_info": { "package_name": "me.nanova.subspace.debug" }
},
"api_key": [{"current_key": "key"}]
}
]
}
EOF
```

2. Add a dummy keystore.properties to project root directory:
3. Create `keystore.jks` and `keystore.properties` to project root directory:

keystore.jks:

```bash
keytool -genkey -v -keystore keystore.jks -keyalg RSA -keysize 2048 -validity 10000 -alias my-alias
```

keystore.properties:

```bash
cat << EOF > keystore.properties
storeFile:keystore.jks
storePassword:fake-password
keyAlias:fake-key-alias
keyAlias:my-alias
keyPassword:fake-password
EOF
```
67 changes: 41 additions & 26 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import com.android.build.api.dsl.ApkSigningConfig
import java.io.FileInputStream
import java.util.Properties

Expand All @@ -24,44 +25,50 @@ android {
targetSdk = 34
versionCode = 8
versionName = "0.2.1"
setProperty("archivesBaseName", "subspace-v${versionName}-${versionCode}")

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables {
useSupportLibrary = true
}
}

flavorDimensions += listOf("distribution")
productFlavors {
val keystorePropertiesFile = rootProject.file("keystore.properties")
// skip for test
val signKeyExists = keystorePropertiesFile.exists()
if (signKeyExists) {
val keystoreProperties = Properties()
keystoreProperties.load(FileInputStream(keystorePropertiesFile))

signingConfigs {
create("releaseConfig") {
storeFile = rootProject.file(keystoreProperties["storeFile"] as String)
storePassword = keystoreProperties["storePassword"] as String
keyAlias = keystoreProperties["keyAlias"] as String
keyPassword = keystoreProperties["keyPassword"] as String
signingConfigs {
val keystoreDir = "keystore"
fun buildSignConfig(keyStoreFile: String, apkSigningConfig: ApkSigningConfig) {
val keystorePropertiesFile = rootProject.file(keyStoreFile)
if (keystorePropertiesFile.exists()) {
val keystoreProperties = Properties()
keystoreProperties.load(FileInputStream(keystorePropertiesFile))
keystoreProperties.let {
apkSigningConfig.storeFile =
rootProject.file("${keystoreDir}/${it["storeFile"] as String}")
apkSigningConfig.storePassword = it["storePassword"] as String
apkSigningConfig.keyAlias = it["keyAlias"] as String
apkSigningConfig.keyPassword = it["keyPassword"] as String
}
}
}
create("github") {
buildSignConfig("${keystoreDir}/${name}.properties", this)
}
create("play") {
buildSignConfig("${keystoreDir}/${name}.properties", this)
}
}

flavorDimensions += listOf("distribution")
productFlavors {
create("github") {
dimension = "distribution"
if (signKeyExists) {
signingConfig = signingConfigs.getByName("releaseConfig")
}
signingConfig = signingConfigs.getByName(name)
}
create("play") {
dimension = "distribution"
if (signKeyExists) {
signingConfig = signingConfigs.getByName("releaseConfig")
}
signingConfig = signingConfigs.getByName(name)
}
}

buildTypes {
debug {
isDebuggable = true
Expand All @@ -77,6 +84,7 @@ android {
)
}
}

compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
Expand All @@ -95,6 +103,17 @@ android {
}
}

androidComponents {
val isCITest = System.getenv("CI_TEST")?.toBoolean() ?: false

onVariants { variant ->
// disable google service on non-prod build
val googleTask =
tasks.findByName("process${variant.name.replaceFirstChar(Char::uppercase)}GoogleServices")
googleTask?.enabled = !isCITest && "release" == variant.buildType
}
}

dependencies {
implementation("androidx.core:core-ktx:1.13.1")

Expand Down Expand Up @@ -153,7 +172,7 @@ dependencies {
implementation("com.squareup.okhttp3:logging-interceptor:4.12.0")

// firebase
implementation(platform("com.google.firebase:firebase-bom:32.8.1"))
implementation(platform("com.google.firebase:firebase-bom:33.0.0"))
implementation("com.google.firebase:firebase-crashlytics")
implementation("com.google.firebase:firebase-analytics")

Expand All @@ -167,7 +186,3 @@ dependencies {
androidTestImplementation("androidx.test.ext:junit:1.1.5")
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
}

task("printVersion") {
println("v" + android.defaultConfig.versionName + "(" + android.defaultConfig.versionCode + ")")
}
8 changes: 0 additions & 8 deletions app/src/main/kotlin/me/nanova/subspace/App.kt
Original file line number Diff line number Diff line change
@@ -1,19 +1,11 @@
package me.nanova.subspace

import android.app.Application
import com.google.firebase.crashlytics.FirebaseCrashlytics
import dagger.hilt.android.HiltAndroidApp

@HiltAndroidApp
class App : Application() {

override fun onCreate() {
super.onCreate()

// Enable Crashlytics only for non-debug builds
val isCrashlyticsEnabled = !BuildConfig.DEBUG
FirebaseCrashlytics.getInstance().setCrashlyticsCollectionEnabled(isCrashlyticsEnabled)
}
}


Expand Down

0 comments on commit 252e633

Please sign in to comment.