Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/ci #12

Merged
merged 18 commits into from
May 11, 2024
Merged
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