Android build process
This page describes the process of building Android projects with EAS Build. You may want to read this if you are interested in the implementation details of the build service.
Let's take a closer look at the steps for building Android projects with EAS Build. We'll first run some steps on your local machine to prepare the project, and then we'll actually build the project on a remote service.
The first phase happens on your computer. EAS CLI is in charge of completing the following steps:
Check if the git index is clean - this means that there aren't any uncommitted changes. If it's not clean, an error is thrown. We use git to prepare a tarball of your project to upload to the build service.
Prepare the credentials needed for the build unless builds.android.PROFILE_NAME.withoutCredentials
is set to true
.
- Depending on the value of
builds.android.PROFILE_NAME.credentialsSource
, the credentials are obtained from either the local credentials.json
file or from the EAS servers. If the auto
or remote
mode is selected but no credentials exist yet, you're prompted to generate a new keystore.
Additional step for generic projects: Check if the Android project is configured to be buildable on the EAS servers.
In this step, EAS CLI checks whether
android/app/build.gradle
contains
apply from: "./eas-build.gradle"
.
If the project is not configured, EAS CLI runs auto-configuration steps (
learn more below).
Create the tarball containing your project sources - run git archive --format=tar.gz --prefix project/ -o project.tar.gz HEAD
.
Upload the project tarball to a private AWS S3 bucket and send the build request to EAS Build.
Next, this is what happens when EAS Build picks up your request:
Create a new Docker container for the build.
- Every build gets its own fresh container with all build tools installed there (Java JDK, Android SDK, NDK, and so on).
Download the project tarball from a private AWS S3 bucket and unpack it.
Run yarn install
in the project root (or npm install
if yarn.lock
does not exist).
Additional steps for managed projects:
- Run
expo eject
to convert the project to a generic one. - Configure the Android similarly to the step 3 from Local Steps.
Restore the keystore (if it was included in the build request).
Run ./gradlew COMMAND
in the android
directory inside your project.
COMMAND
is the command defined in your eas.json
at builds.android.PROFILE_NAME.gradleCommand
. It defaults to :app:bundleRelease
which produces the AAB (Android App Bundle).
Upload the build artifact to AWS S3.
- The artifact path can be configured in
eas.json
at builds.android.PROFILE_NAME.artifactPath
. It defaults to android/app/build/outputs/**/*.{apk,aab}
. We're using the fast-glob package for pattern matching.
Every time you want to build a new Android app binary, we validate that the project is set up correctly so we can seamlessly run the build process on our servers. This mainly applies to generic projects, but similar steps are run when building managed projects.
Android requires you to sign your application with a certificate. That certificate is stored in your keystore. The Google Play Store identifies applications based on the certificate. This means that if you lose your keystore, you may not be able to update your application in the store. However, with
Play App Signing, you can mitigate the risk of losing your keystore.
Your application's keystore should be kept private. Under no circumstances should you check it in to your repository. Debug keystores are the only exception because we don't use them for uploading apps to the Google Play Store.
Let's focus on building a release app binary. Like we previously mentioned, your app binary needs to be signed with the keystore. Since we're building the project on a remote server, we had to come up with a way to provide Gradle with credentials which aren't, for security reasons, checked in to your repository. When running eas build:configure
, we're writing the android/app/eas-build.gradle
file with the following contents:
import java.nio.file.Paths
android {
signingConfigs {
release {
}
}
buildTypes {
release {
}
}
}
def isEasBuildConfigured = false
tasks.whenTaskAdded {
def debug = gradle.startParameter.taskNames.any { it.toLowerCase().contains('debug') }
if (debug) {
return
}
if (isEasBuildConfigured) {
return
}
isEasBuildConfigured = true;
android.signingConfigs.release {
def credentialsJson = rootProject.file("../credentials.json");
if (credentialsJson.exists()) {
if (storeFile && System.getenv("EAS_BUILD") != "true") {
println("Path to release keystore file is already set, ignoring 'credentials.json'")
} else {
try {
def credentials = new groovy.json.JsonSlurper().parse(credentialsJson)
def keystorePath = Paths.get(credentials.android.keystore.keystorePath);
def storeFilePath = keystorePath.isAbsolute()
? keystorePath
: rootProject.file("..").toPath().resolve(keystorePath);
storeFile storeFilePath.toFile()
storePassword credentials.android.keystore.keystorePassword
keyAlias credentials.android.keystore.keyAlias
keyPassword credentials.android.keystore.keyPassword
} catch (Exception e) {
println("An error occurred while parsing 'credentials.json': " + e.message)
}
}
} else {
if (storeFile == null) {
println("Couldn't find a 'credentials.json' file, skipping release keystore configuration")
}
}
}
android.buildTypes.release {
signingConfig android.signingConfigs.release
} }
The most important part is the release
signing config. It's configured to read the keystore and passwords from the credentials.json
file at the project root. Even though you're not required to create this file on your own, it's created and populated with your credentials by EAS Build before running the build.
This file is imported in android/app/build.gradle
like this:
apply from: "./eas-build.gradle"