Skip to content

Commit

Permalink
GitHub build pipelines (#14)
Browse files Browse the repository at this point in the history
- Add internal testing pipeline that runs for `-qa` and `-rc` tags. It builds signed APKs for 3 networks and publishes them for internal testing in Firebase;
- Add release build pipeline that runs for `X.Y.Z` tags. It builds signed APKs and bundles for Mainnet and Testnet, for Google Play upload and standalone release;
- All the signing keys are merged into `signing.jks`. Sensitive keys are protected with passwords set in the GitHub environment.
  • Loading branch information
Radiokot authored Mar 21, 2024
1 parent 8f99d6d commit e3d086f
Show file tree
Hide file tree
Showing 9 changed files with 211 additions and 59 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4

- name: Set up JDK
uses: actions/setup-java@v3
uses: actions/setup-java@v4
with:
java-version: "${{env.java_version}}"
distribution: corretto
Expand Down
65 changes: 65 additions & 0 deletions .github/workflows/firebase_internal_testing.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
name: Distribute builds for internal testing through Firebase

on:
push:
# Run for QA builds and release candidates.
tags:
- "*-qa.*"
- "*-rc.*"
workflow_dispatch:

env:
java_version: "11"

jobs:
build:
runs-on: ubuntu-latest

environment:
name: Internal testing
url: https://console.firebase.google.com/project/concordiummobilewallet/appdistribution/

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Create Firebase credentials file
run: 'echo "$CREDENTIALS" > app/app-distribution-credentials.json'
shell: bash
env:
CREDENTIALS: ${{secrets.GOOGLE_APPLICATION_CREDENTIALS}}

- name: Set up JDK
uses: actions/setup-java@v4
with:
java-version: "${{env.java_version}}"
distribution: corretto
cache: gradle

- name: Check if build version matches the tag
run: |
buildVersion="$(./gradlew -q printVersionName)"
tagVersion="${GITHUB_REF#refs/tags/}"
if [ "$buildVersion" != "$tagVersion" ]; then
echo "Build version '$buildVersion' doesn't match the tag '$tagVersion'"
exit 1
fi
shell: bash
env:
SOURCE_TAG: ${{ steps.branch_name.outputs.SOURCE_TAG }}

- name: Build and distribute Testnet APK
run: ./gradlew app:assembleTstnetRelease app:appDistributionUploadTstnetRelease --stacktrace

- name: Build and distribute Stagenet APK
run: ./gradlew app:assembleStagenetRelease app:appDistributionUploadStagenetRelease --stacktrace

- name: Build and distribute Mainnet APK
run: ./gradlew app:assembleMainnetRelease app:appDistributionUploadMainnetRelease --stacktrace

- name: Upload the APKs
uses: actions/upload-artifact@v4
with:
name: APKs
path: app/build/outputs/**/*.apk
compression-level: 0
80 changes: 80 additions & 0 deletions .github/workflows/release_build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
name: Build release packages for Google Play and standalone publishing

on:
push:
# Run only for releases: ignore versions with meta part (1.2.3-qa.1, 3.4.5-rc.2, etc).
tags:
- "*.*.*"
- "!*-*"
workflow_dispatch:

env:
java_version: "11"

jobs:
build:
runs-on: ubuntu-latest

environment:
name: Releases

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Create signing configuration files
run: |
echo "$PLAY_UPLOAD_SIGNING_PROPERTIES" > app/play-upload-signing.properties
echo "$STANDALONE_RELEASE_SIGNING_PROPERTIES" > app/standalone-release-signing.properties
shell: bash
env:
PLAY_UPLOAD_SIGNING_PROPERTIES: ${{secrets.PLAY_UPLOAD_SIGNING_PROPERTIES}}
STANDALONE_RELEASE_SIGNING_PROPERTIES: ${{secrets.STANDALONE_RELEASE_SIGNING_PROPERTIES}}

- name: Set up JDK
uses: actions/setup-java@v4
with:
java-version: "${{env.java_version}}"
distribution: corretto
cache: gradle

- name: Check if build version matches the tag
run: |
buildVersion="$(./gradlew -q printVersionName)"
tagVersion="${GITHUB_REF#refs/tags/}"
if [ "$buildVersion" != "$tagVersion" ]; then
echo "Build version '$buildVersion' doesn't match the tag '$tagVersion'"
exit 1
fi
shell: bash
env:
SOURCE_TAG: ${{ steps.branch_name.outputs.SOURCE_TAG }}

- name: Build Testnet APK for standalone release
run: ./gradlew app:assembleTstnetRelease --stacktrace
env:
CONFIGURABLE_SIGNING_PROPERTIES_FILE: standalone-release-signing.properties

- name: Build Testnet bundle for Google Play upload
run: ./gradlew app:bundleTstnetRelease --stacktrace
env:
CONFIGURABLE_SIGNING_PROPERTIES_FILE: play-upload-signing.properties

- name: Build Mainnet APK for standalone release
run: ./gradlew app:assembleMainnetRelease --stacktrace
env:
CONFIGURABLE_SIGNING_PROPERTIES_FILE: standalone-release-signing.properties

- name: Build Mainnet bundle for Google Play upload
run: ./gradlew app:bundleMainnetRelease --stacktrace
env:
CONFIGURABLE_SIGNING_PROPERTIES_FILE: play-upload-signing.properties

- name: Upload the APKs and bundles
uses: actions/upload-artifact@v4
with:
name: APKs and bundles
path: |
app/build/outputs/**/*.apk
app/build/outputs/**/*.aab
compression-level: 0
5 changes: 0 additions & 5 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,3 @@ captures/
*.iml
.idea/
/workspace.xml

# Keys and secrets
*.jks
!app/dev-only-signing.jks
app/secret/
2 changes: 0 additions & 2 deletions app/app-distribution-release-notes.txt

This file was deleted.

108 changes: 58 additions & 50 deletions app/build.gradle
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import java.text.SimpleDateFormat
import com.android.build.gradle.internal.tasks.FinalizeBundleTask

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
Expand All @@ -12,46 +12,30 @@ def SCHEME_BASE = "cryptoxmwallet"
def WC_SCHEME_BASE = "cryptox-wc"
def PROVIDER_AUTHORITY_BASE = "com.pioneeringtechventures.wallet.DataFileProvider"

static def buildTime() {
def dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm'Z'")
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"))
return dateFormat.format(new Date())
}

static def buildTimeTicks() {
def time = System.currentTimeMillis()
return time
}

int extractVersionCode() {
def stdout = new ByteArrayOutputStream()
exec {
workingDir projectDir
executable 'git'
args 'rev-list', '--count', 'HEAD'
standardOutput = stdout
}
return stdout.toString().trim().toInteger()
}
def configurableSigningProperties = new Properties()
configurableSigningProperties.load(new FileInputStream(
file(System.getenv("CONFIGURABLE_SIGNING_PROPERTIES_FILE") ?: "dev-signing.properties")
))

android {
def vCode = 1300 + extractVersionCode()
// This project uses semantic versioning
// https://semver.org/
def versionMajor = 0
def versionMinor = 6
def versionPatch = 1
def versionMeta = "-qa.10"
def versionCodeIncremental = 1360

compileSdkVersion 34

defaultConfig {
applicationId "com.pioneeringtechventures.wallet"

minSdkVersion 26
targetSdkVersion 34
versionCode vCode
versionName "0.6.1-qa.10"

versionCode versionCodeIncremental
versionName "${versionMajor}.${versionMinor}.${versionPatch}${versionMeta}"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

buildConfigField("String", "BUILD_NUMBER", "\"${vCode}\"")
buildConfigField("String", "BUILD_TIME", "\"${buildTime()}\"")
buildConfigField("String", "BUILD_TIME_TICKS", "\"${buildTimeTicks()}\"")

buildConfigField("boolean", "USE_BACKEND_MOCK", "false")
buildConfigField("boolean", "INCL_DEV_OPTIONS", "false")
buildConfigField("boolean", "SHOW_GTU_DROP", "false")
Expand All @@ -68,20 +52,17 @@ android {
}

firebaseAppDistribution {
serviceCredentialsFile = file("secret/app-distribution-key.json")
releaseNotesFile = file("app-distribution-release-notes.txt")
// This file is created by CI.
serviceCredentialsFile = file("app-distribution-credentials.json")
}
}

signingConfigs {
// A signature to be used for pre-release app distribution.
// Unlike the debug signing config, it does not depend on
// developer's personal debug keypair.
dev {
storeFile file("dev-only-signing.jks")
storePassword "concordium"
keyAlias "dev-sign-1"
keyPassword "concordium"
configurable {
storeFile file(configurableSigningProperties['storeFile'])
storePassword configurableSigningProperties['storePassword']
keyAlias configurableSigningProperties['keyAlias']
keyPassword configurableSigningProperties['keyPassword']
}
}

Expand All @@ -92,8 +73,7 @@ android {

release {
debuggable false
// Only used for CLI builds.
signingConfig signingConfigs.dev
signingConfig signingConfigs.configurable
// Unlocking requires vigilant testing.
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
Expand All @@ -114,13 +94,13 @@ android {

buildConfigField("boolean", "SHOW_GTU_DROP", "true")

def scheme = SCHEME_BASE+"-stagenet"
def scheme = SCHEME_BASE + "-stagenet"
buildConfigField("String", "SCHEME", "\"${scheme}\"")
resValue "string", "scheme", "\"${scheme}\""

resValue "string", "wc_scheme", "\"${WC_SCHEME_BASE+"-stagenet"}\""
resValue "string", "wc_scheme", "\"${WC_SCHEME_BASE + "-stagenet"}\""

def providerAuthority = PROVIDER_AUTHORITY_BASE+"-stagenet"
def providerAuthority = PROVIDER_AUTHORITY_BASE + "-stagenet"
resValue "string", "PROVIDER_AUTHORITY", "\"${providerAuthority}\""
buildConfigField("String", "PROVIDER_AUTHORITY", "\"${providerAuthority}\"")

Expand All @@ -146,13 +126,13 @@ android {

buildConfigField("boolean", "SHOW_GTU_DROP", "true")

def scheme = SCHEME_BASE+"-testnet"
def scheme = SCHEME_BASE + "-testnet"
buildConfigField("String", "SCHEME", "\"${scheme}\"")
resValue "string", "scheme", "\"${scheme}\""

resValue "string", "wc_scheme", "\"${WC_SCHEME_BASE+"-testnet"}\""
resValue "string", "wc_scheme", "\"${WC_SCHEME_BASE + "-testnet"}\""

def providerAuthority = PROVIDER_AUTHORITY_BASE+"-testnet"
def providerAuthority = PROVIDER_AUTHORITY_BASE + "-testnet"
resValue "string", "PROVIDER_AUTHORITY", "\"${providerAuthority}\""
buildConfigField("String", "PROVIDER_AUTHORITY", "\"${providerAuthority}\"")

Expand Down Expand Up @@ -188,7 +168,7 @@ android {

firebaseAppDistribution {
appId = '1:124880082147:android:250469ea97158747c7e1d6'
groups = "concordium-team"
groups = "concordium-team, spaceseven-team"
}
}
}
Expand All @@ -213,6 +193,27 @@ android {
viewBinding true
}
namespace 'com.concordium.wallet'

// Set meaningful names for build artifacts.
applicationVariants.all { variant ->
def flavor = variant.productFlavors[0].name
def artifactName = "cryptox-" +
flavor + "-" +
variant.buildType.name + "-" +
variant.versionName

// APK.
variant.outputs.all {
outputFileName = artifactName + ".apk"
}

// AAB.
tasks.named("sign${variant.name.capitalize()}Bundle", FinalizeBundleTask) {
File file = finalBundleFile.asFile.get()
File finalFile = new File(file.parentFile, artifactName + ".aab")
finalBundleFile.set(finalFile)
}
}
}

dependencies {
Expand Down Expand Up @@ -321,3 +322,10 @@ dependencies {
// BIP39
implementation "cash.z.ecc.android:kotlin-bip39:1.0.4"
}

task printVersionName {
group = "help"
doLast {
println android.defaultConfig.versionName
}
}
Binary file removed app/dev-only-signing.jks
Binary file not shown.
6 changes: 6 additions & 0 deletions app/dev-signing.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Common signature for developers to share APKs without reinstalling.
# There is nothing sensitive here.
storeFile=signing.jks
storePassword=concordium
keyAlias=dev-sign-1
keyPassword=concordium
Binary file added app/signing.jks
Binary file not shown.

0 comments on commit e3d086f

Please sign in to comment.