Aug 10, 2023 by
Szymon Dziedzic
It’s extremely satisfying when you eas build
on a new project and it Just Works — at Expo, we know what we need to do to build Android and iOS React Native apps, and we handle all of that automatically for you. In many cases, this is all a project ever needs; but, not always.
We’ve come across a number of cases where developers have asked to replace single steps in their build process with their own implementations (such as for installing dependencies for Rush or Nx monorepos) or even replace the majority of the build process in order to build for another platform (such as Electron app or one of the many TV platforms). We’ve also heard from developers that want to use EAS Build to run their test suite, so that they could consolidate their CI into a single service.
To enable all of these use cases and more, today we are releasing a preview of our new fully customizable build process. You can use it by creating a yml
file in .eas/build
and pointing to that file from the config
field on your build profile. For example, you can configure a build profile to run tests with the following:
eas.json
{
"build": {
"test": {
"config": "test.yml",
"withoutCredentials": true
}
// other build profiles...
}
}
.eas/build/test.yml
build:
name: Run tests
steps:
- eas/checkout
- eas/install_node_modules
- run:
name: Unit tests
command: |
echo "Running tests..."
npm test
When you run eas build -p android --profile test
, you'll see the following:
If you needed to rewrite the entire build process from scratch as soon as you wanted to replace a single step, this wouldn’t be particularly useful. Each step in the standard build process can be used in your custom build definition. In the above test.yml example, we are using eas/checkout
to check out the repository and eas/install_node_modules
to re-use the same logic for installing Node modules (select the correct package manager, handle monorepos appropriately, etc).
For example, the following steps will create a development build for Android:
build:
name: Development build - Android
steps:
- eas/checkout
- eas/use_npm_token
- eas/install_node_modules
- eas/prebuild
- eas/run_gradle
- eas/find_and_upload_build_artifacts
And the equivalent for iOS:
build:
name: Development build - iOS
steps:
- eas/checkout
- eas/use_npm_token
- eas/install_node_modules
- eas/prebuild
- run:
name: Install pods
working_directory: ./ios
command: pod install
- eas/generate_gymfile_from_template
- eas/run_fastlane
- eas/find_and_upload_build_artifacts
Example configurations for store-ready builds are available in the eas-custom-builds-example repository: Android, iOS.
You can learn more about the provided reusable steps and others in the “Built-in EAS functions” documentation. You can also fork these steps in your own JavaScript / TypeScript functions, or write your own build steps from scratch.
If you’d like to fork a default build step, or if you just want to use JavaScript or TypeScript instead of Bash because the logic for a step is complex, you can run npx create-eas-build-function@latest .eas/build/my-new-function
to create a new function (we suggest creating the function in the .eas/build
directory, next to your YAML configuration — but you can put the functions anywhere in your repository).
After you’ve defined your function, you can refer to it from your build configuration YAML file. Refer to the README.md in the generated directory for more information about how to use it. Your configuration will look something like this:
eas.json
{
"build": {
"test": {
"config": "test.yml",
"withoutCredentials": true
}
// other build profiles...
}
}
.eas/build/test.yml
build:
name: Run tests
steps:
- eas/checkout
- eas/install_node_modules
- run:
name: Unit tests
command: npm run test
- my-new-function
functions:
my-new-function:
name: my-new-function
path: ./my-new-function
.eas/build/my-new-function/src/index.ts
import { BuildStepContext } from '@expo/steps';
async function myFunction(ctx: BuildStepContext): Promise<void> {
ctx.logger.info('Hello from my TypeScript function!');
}
export default myFunction;
Learn more about creating EAS Build functions with TypeScript.
eas/configure-eas-update-if-installed
step only supports EAS Update. Example of configuring EAS Update.This new feature helps bring EAS Build more in alignment with the spirit of how we think about building apps at Expo — our tools and services give you great defaults out of the box, and when you need to, you can opt-out of any those default choices and customize any part of your experience without having to face a sudden complexity cliff. Keep what works for you, change what doesn’t.
Some ideas for areas we’d like feedback on:
eas/
functions. Are they the right level of abstraction? Should they accept additional inputs for further customization, or provide additional outputs for use in subsequent steps?Feel free to send us feedback on Discord, @expo, Threads, or Bluesky.
A special mention goes out to Dominik Sokal, who built out the foundation of custom builds.