A bit of automation with Gradle Tasks

Ahmed Rizwan
AndroidPub

--

Gradle tasks make it real easy to automate the repetitive things we do, on a daily basis, aside from just building our APKs.

Signing a release APK

One of the first things that you can do is to automate the signing process of your app’s release build. Here are a few simple steps to do that

Create a file signing.gradle in /app directory (the file name can be anything but make sure it’s not tracked, so that you don’t accidently push your actual keystore credentials to your repo)

Paste the following into the file

ext.signing = [
storeFilePath : '/path/to/your/keystore-file',
storePassword : 'your-store-password',
keyAlias : 'your-key-alias',
keyPassword : 'your-key-password',
]

Extra properties — The ext properties are like project scoped variables, and we can access them anywhere in our gradle files, in this way: project.signing.storeFilePath.

In your build.gradle file, add the following

def signingFilePath = 'signing.gradle'
def performSigning = file(signingFilePath).exists()
if (performSigning) {
apply from: signingFilePath
}

and specify android.signingConfigs

android {
if (performSigning) {
signingConfigs {
release {
storeFile file(project.signing.storeFilePath)
storePassword project.signing.storePassword
keyAlias project.signing.keyAlias
keyPassword project.signing.keyPassword
v1SigningEnabled true
v2SigningEnabled true
}
}
}
}

also specify release build type with android.buildTypes

buildTypes {
release {
...
if (performSigning) {
signingConfig signingConfigs.release
}
}
}

Now if you run ./gradlew assembleRelease it’ll generate a signed apk for you! 🙌

Gradle Tasks

In gradle everything is pretty much either a project or a task. Each build can have one or more projects — and projects are composed up of tasks. If you checkout your project’s gradle properties you’ll see all the available tasks listed there!

A project has a one-to-one relation with the build.gradle file and whenever gradle initializes a project, it checks the settings.gradle file to see which modules are included. It then creates a hierarchy of projects. Evaluation of build.gradle is done for parent projects first and then the child projects. The dependencies are defined at project level, as well as on the task level.

We can write our custom tasks to do all sort of things.

In our example, we’d write some tasks to generate a release apk, move and rename that apk file to some other directory — after that send a notification to slack. The sequence would go something like this.

Verify target or destination folder exists where we want to move the apk

Build release apk

Verify it got generated succesfully

Copy/Paste it into the target folder

Rename the apk (to whatever we want)

Send a slack notification! (because why not?)

Task 1: Verify that the folder path exists

First thing we’d do is verify that the folder exists where we want to copy our apk to. The reason for doing this first is that we get an earlier error exception if something is not right (and not after the whole build process is completed, which takes a bit of time).

So… Let’s define our task…

def targetPath = file("/Your/Target/Path")

task verifyTargetPath {
doLast {
if (!targetPath.exists()) {
throw new GradleException("Target path not valid!")
}
}
}

To run this either use the terminal ./gradlew verifyTargetPath, or run it from the Gradle projects tool windows.

The doLast closure specifies the order of execution. We can also use doFirst. If we don’t write the code inside these blocks, it will execute every time no matter what task runs. And we most certainly don’t want that!

Task 2: Build a release apk

Now to build a release apk, we want to run verifyTargetPath first, then run the existing task assembleRelease and finally do our thing where we copy the apk to the target folder. So our task will look something like

task buildReleaseApk(dependsOn: 
['verifyTargetPath', 'assembleRelease']) {
doLast {
// create an apk name using versionCode // make sure that the file doesn't already exist // verify that the build was generated and move it target

}
}

The keyword dependsOn specifies the order of execution — first it’ll run verifyTargetPath, then assembleRelease and finally buildReleaseApk.

Let’s fill in the code…

task buildReleaseApk(dependsOn: 
['verifyTargetPath', 'assembleRelease']) {
doLast {
ext.versionCode = project.android.defaultConfig.versionCode
ext.apkName = "android-release-build${ext.versionCode}.apk"

// make sure that the file doesn't already exist
if (targetPath.list().contains(ext.apkName)) {
throw new GradleException("Build with versionCode ${ext.versionCode} already exists!")
}
// verify that the build was generated successfully
ext.apk = file("build/outputs/apk/release/app-release.apk")
if (ext.apk.exists()) {
copy {
from ext.apk.absolutePath
into targetPath
rename { ext.apkName }
}
}
}
}

This task first verifies the target path, then generates a release build, and copies it to the target path. Beautiful!

Task 3: A notification to Slack

We can send messages to slack using webhooks. You can get a webhook url for your slack workspace, which let’s you post to different channels. All you need is a url and some payload data that you just have to post.

I’d be using curl to send the payload to webhook endpoint. So the task for that will look something like…

task buildAndSendNotification(dependsOn: 'buildReleaseApk') {
doLast {
def text = "Build `${buildReleaseApk.apkName}` moved to `${targetPath}`."
def webHook = 'your-webhook-url' def channel = 'your-channel' def response = ['curl',
'-X',
'POST',
'--data-urlencode',
"payload={\"channel\": \"${channel}\", \"username\": \"Bot\", \"text\": \"${text}\", \"icon_emoji\": \":ghost:\"}", '${webHook}'].execute().text
println response
}
}

Executing this task will post a message to the specified channel. For example you can have your apk moved to some cloud service directory, and with this you can notify others that the apk is being uploaded.

Similar to the tasks above, Gradle tasks can be utilized to make our lives a bit more easier, depending on what your workflow is!

That’s it for now! Hope the article was helpful!

Some resources: Difference between doFirst and doLast, Project and tasks, Gradle User Manual

Happy coding!

--

--

Ahmed Rizwan
AndroidPub

Web | Android | Senior Software Engineer @QuixelTools