In this post I will explain how to update Gradle file, setup signing options and build by types from Jenkins. Keeping sensitive keystore credential hidden.
Previous blog post
Step by step guide of how to setup Jenkins and Android SDK on VM and create a basic job that builds Android apps.
Steps
Update Gradle files
We are going to make our build.gradle file to support signing configurations for
release build types.
The concept
We don’t want to hardcode the passwords and sensitive info inside our code / build.gradle
files. So, we will put the placeholders (variables) in build.gradle file, but the
actual values will be injected during the build on Jenkins.
We will use gradle.properties file for this.
Update shared gradle.properties
Add default values to gradle.properties file. This file is located under root of the
project.
# Project-wide Gradle settings.
org.gradle.jvmargs=-Xmx1536m
# Undefined maps keys
KEYSTORE_FILE_PATH=undefined
KEYSTORE_PASSWORD=undefined
KEY_ALIAS=undefined
KEY_PASSWORD=undefined
Update module’s build.gradle
Update build.gradle of your app module. We will add signingConfigs and
update buildTypes.
android {
...
signingConfigs {
release {
storeFile file(KEYSTORE_FILE_PATH)
storePassword KEYSTORE_PASSWORD
keyAlias KEY_ALIAS
keyPassword KEY_PASSWORD
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.release
}
}
}
As you can see, the values of signing config are taken from gradle.properties file
variables.
Create signing keystore
We need to create the keystore file. We will use this file to sign the app.
1. Create
Open Android Studio -> Build -> Generate Signed APK. Fill the data and create the file. (Don’t forget the passwords)
2. Upload to Jenkins
Now we need to upload this file to Jenkins server. We’ll put the file in
/var/lib/jenkins/keystores/
folder.
You can use scp command to copy file from your local machine to remote server (Jenkins
machine).
scp [LOCAL_PATH_TO_FILE] [USERNAME]@[REMOTE_SERVER]:[REMOTE_PATH]
It can look something like this:
scp -i ~/.ssh/google_compute_engine ~/Desktop/my.jks roman@35.192.51.10:/home/roman/
Then move the file to /var/lib/jenkins/keystores/
# create folder
sudo mkdir /var/lib/jenkins/keystores
# move the file
sudo mv my.jks /var/lib/jenkins/keystores/
Update Jenkins job
We will add parameters to our job. These parameters are the ones we need for signing the app.
-
Open your Job -> Configure.
-
Under General check This project is parameterized.
-
Add String Parameter
- Name:
build_type - Default Value:
debug
- Name:
-
Add Password Parameter
- Name:
store_password - Default Value:
[YOUR_ACTUAL_PASSWORD]
- Name:
-
Add Password Parameter
- Name:
key_password - Default Value:
[YOUR_ACTUAL_PASSWORD]
- Name:
-
Add String Parameter
- Name:
key_alias - Default Value:
[YOUR_ACTUAL_ALIAS_NAME]
- Name:
-
Add String Parameter
- Name:
keystore_path - Default Value:
/var/lib/jenkins/keystores/my.jks
- Name:
Execution script - build.sh
We are going to write a script that updates the gradle.properties with values from Jenkins parameters and executes the build.
First of all create new folder in your root project and call it scripts. Then add new file and call it build.sh
The script will do next steps:
- Update key store values in
gradle.propertiesfile. - Build debug or release APK.
- Move ready apk to
artifactsfolder.
The Jenkins just need to execute this script.
Update Jenkins build step
Job -> Configure -> Build -> Execute shell
# make script executable
chmod +x scripts/build.sh
# run the script
./scripts/build.sh $branch $build_type $store_password $key_password $key_alias $keystore_path
The script build.sh
#!/usr/bin/env bash
branch=$1
buildType=$2
storePassword=$3
keyPassword=$4
keyAlias=$5
keystorePath=$6
echo "Running build script.."
echo "branch: $branch"
echo "buildType: $buildType"
# Update gradle.properties
sed -i "s|KEYSTORE_FILE_PATH=undefined|KEYSTORE_FILE_PATH=$keystorePath|g" gradle.properties
sed -i "s|KEYSTORE_PASSWORD=undefined|KEYSTORE_PASSWORD=$storePassword|g" gradle.properties
sed -i "s|KEY_ALIAS=undefined|KEY_ALIAS=$keyAlias|g" gradle.properties
sed -i "s|KEY_PASSWORD=undefined|KEY_PASSWORD=$keyPassword|g" gradle.properties
# Build
if [ $buildType = 'debug' ]; then
./gradlew clean assembleDebug
elif [ $buildType = 'release' ]; then
./gradlew clean assembleRelease
fi
# Revert gradle.properties (to prevent secret leaks)
git checkout gradle.properties
# Copy artifacts
mkdir artifacts
if [ $buildType = 'debug' ]; then
mv app/build/outputs/apk/debug/*.apk artifacts/
elif [ $buildType = 'release' ]; then
mv app/build/outputs/apk/release/*.apk artifacts/
fi
- The final script - Check here.
🎉 That’s it. Try running your job!
Next blog post
In this post I will show how to run lint and junit tests part of the build process and present the results in Jenkins job.
Questions
If you have any comments, please open an issue at https://github.com/sromku/build-android-jenkins