Expo Cicd Workflows
expo/skillsThis skill assists developers in creating, editing, and validating EAS CI/CD workflow YAML files. It provides resources such as JSON schemas, syntax documentation, and pre-packaged jobs to ensure workflows are correctly configured and validated against up-to-date standards. It's designed for developers managing automated build and deployment processes using EAS workflows in their projects.
EAS Workflows Skill
Help developers write and edit EAS CI/CD workflow YAML files.
Reference Documentation
Fetch these resources before generating or validating workflow files. Use the fetch script (implemented using Node.js) in this skill's scripts/ directory; it caches responses using ETags for efficiency:
# Fetch resources
node {baseDir}/scripts/fetch.js <url>
- JSON Schema — https://api.expo.dev/v2/workflows/schema
- It is NECESSARY to fetch this schema
- Source of truth for validation
- All job types and their required/optional parameters
- Trigger types and configurations
- Runner types, VM images, and all enums
- Syntax Documentation — https://raw.githubusercontent.com/expo/expo/refs/heads/main/docs/pages/eas/workflows/syntax.mdx
- Overview of workflow YAML syntax
- Examples and English explanations
- Expression syntax and contexts
- Pre-packaged Jobs — https://raw.githubusercontent.com/expo/expo/refs/heads/main/docs/pages/eas/workflows/pre-packaged-jobs.mdx
- Documentation for supported pre-packaged job types
- Job-specific parameters and outputs Do not rely on memorized values; these resources evolve as new features are added.
Workflow File Location
Workflows live in .eas/workflows/*.yml (or .yaml).
Top-Level Structure
A workflow file has these top-level keys:
name— Display name for the workflowon— Triggers that start the workflow (at least one required)jobs— Job definitions (required)defaults— Shared defaults for all jobsconcurrency— Control parallel workflow runs Consult the schema for the full specification of each section.
Expressions
Use ${{ }} syntax for dynamic values. The schema defines available contexts:
github.*— GitHub repository and event informationinputs.*— Values fromworkflow_dispatchinputsneeds.*— Outputs and status from dependent jobsjobs.*— Job outputs (alternative syntax)steps.*— Step outputs within custom jobsworkflow.*— Workflow metadata
Generating Workflows
When generating or editing workflows:
- Fetch the schema to get current job types, parameters, and allowed values
- Validate that required fields are present for each job type
- Verify job references in
needsandafterexist in the workflow - Check that expressions reference valid contexts and outputs
- Ensure
ifconditions respect the schema's length constraints
Validation
After generating or editing a workflow file, validate it against the schema:
# Install dependencies if missing
[ -d "{baseDir}/scripts/node_modules" ] || npm install --prefix {baseDir}/scripts
node {baseDir}/scripts/validate.js <workflow.yml> [workflow2.yml ...]
The validator fetches the latest schema and checks the YAML structure. Fix any reported errors before considering the workflow complete.
Answering Questions
When users ask about available options (job types, triggers, runner types, etc.), fetch the schema and derive the answer from it rather than relying on potentially outdated information.
GitHub Owner
Owner: expo
GitHub Links
- Website: https://expo.dev
- Twitter: https://twitter.com/expo
- Email: support@expo.dev
- Verified domains:
expo,expo.dev
Files
syntax.mdx
- View: https://raw.githubusercontent.com/expo/expo/refs/heads/main/docs/pages/eas/workflows/syntax.mdx
- Raw: https://raw.githubusercontent.com/expo/expo/refs/heads/main/docs/pages/eas/workflows/syntax.mdx
---
title: Syntax for EAS Workflows
sidebar_title: Syntax
description: Reference guide for the EAS Workflows configuration file syntax.
maxHeadingDepth: 5
---
import { GithubIcon } from '@expo/styleguide-icons/custom/GithubIcon';
import { BoxLink } from '~/ui/components/BoxLink';
import { FileTree } from '~/ui/components/FileTree';
import { Terminal } from '~/ui/components/Snippet';
A workflow is a configurable automated process made up of one or more jobs. You must create a YAML file to define your workflow configuration.
To get started with workflows, see [Get Started with EAS Workflows](/eas/workflows/get-started) or see [Examples](/eas/workflows/examples/introduction) for complete workflow configurations.
## Workflow files
Workflow files use YAML syntax and must have either a `.yml` or `.yaml` file extension. If you're new to YAML and want to learn more, see [Learn YAML in Y minutes](https://learnxinyminutes.com/docs/yaml/).
Workflow files are located in the **.eas/workflows** directory in your project. The **.eas** directory should be at the same level as your [**eas.json**](/build/eas-json/) file.
For example:
<FileTree
files={[
['my-app/.eas/workflows/create-development-builds.yml', ''],
['my-app/.eas/workflows/publish-preview-update.yml', ''],
['my-app/.eas/workflows/deploy-to-production.yml', ''],
['my-app/eas.json', ''],
]}
/>
## Configuration reference
Below is a reference for the syntax of the workflow configuration file.
## `name`
The human-friendly name of the workflow. This is displayed on the EAS dashboard on the workflows list page and is the title of the workflow's detail page.
```yaml
# @info #
name: My workflow
# @end #
on
The on key defines which GitHub events trigger the workflow. Any workflow can be triggered with the eas workflow:run command, regardless of the on key.
on:
# Trigger on pushes to main branch
push:
branches:
- main
# And on pull requests starting with 'version-'
pull_request:
branches:
- version-*
info You can skip
pushandpull_requesttriggered workflow runs by including[eas skip],[skip eas], or[no eas]in the commit message.
on.push
Runs your workflow when you push a commit to matching branches and/or tags.
With the branches list, you can trigger the workflow only when those specified branches are pushed to. For example, if you use branches: ['main'], only pushes to the main branch will trigger the workflow. Supports globs. By using the ! prefix you can specify branches to ignore (you still need to provide at least one branch pattern without it).
With the tags list, you can trigger the workflow only when those specified tags are pushed. For example, if you use tags: ['v1'], only the v1 tag being pushed will trigger the workflow. Supports globs. By using the ! prefix you can specify tags to ignore (you still need to provide at least one tag pattern without it).
With the paths list, you can trigger the workflow only when changes are made to files matching the specified paths. For example, if you use paths: ['apps/mobile/**'], only changes to files in the apps/mobile directory will trigger the workflow. Supports globs. By default, changes to any path will trigger the workflow.
When neither branches nor tags are provided, branches defaults to ['*'] and tags defaults to [], which means the workflow will trigger on push events to all branches and will not trigger on tag pushes. If only one of the two lists is provided the other defaults to [].
on:
# @info #
push:
# @end #
branches:
- main
- feature/**
- !feature/test-** # other branch names and globs
tags:
- v1
- v2*
- !v2-preview** # other tag names and globs
paths:
- apps/mobile/**
- packages/shared/**
- !**/*.md # ignore markdown files
on.pull_request
Runs your workflow when you create or update a pull request that targets one of the matching branches.
With the branches list, you can trigger the workflow only when those specified branches are the target of the pull request. For example, if you use branches: ['main'], only pull requests to merge into the main branch will trigger the workflow. Supports globs. Defaults to ['*'] when not provided, which means the workflow will trigger on pull request events to all branches. By using the ! prefix you can specify branches to ignore (you still need to provide at least one branch pattern without it).
With the types list, you can trigger the workflow only on the specified pull request event types. For example, if you use types: ['opened'], only the pull_request.opened event (sent when a pull request is first opened) will trigger the workflow. Defaults to ['opened', 'reopened', 'synchronize'] when not provided. Supported event types:
openedready_for_reviewreopenedsynchronizelabeledWith thepathslist, you can trigger the workflow only when changes are made to files matching the specified paths. For example, if you usepaths: ['apps/mobile/**'], only changes to files in theapps/mobiledirectory will trigger the workflow. Supports globs. By default, changes to any path will trigger the workflow.
on:
# @info #
pull_request:
# @end #
branches:
- main
- feature/**
- !feature/test-** # other branch names and globs
types:
- opened
# other event types
paths:
- apps/mobile/**
- packages/shared/**
- !**/*.md # ignore markdown files
on.pull_request_labeled
Runs your workflow when a pull request is labeled with a matching label.
With the labels list, you can specify which labels, when assigned to your pull request, will trigger the workflow. For example, if you use labels: ['Test'], only labeling a pull request with the Test label will trigger the workflow. Defaults to [] when not provided, which means no labels will trigger the workflow.
You can also provide a list of matching labels directly to on.pull_request_labeled for simpler syntax.
on:
# @info #
pull_request_labeled:
# @end #
labels:
- Test
- Preview
# other labels
Alternatively:
on:
# @info #
pull_request_labeled:
# @end #
- Test
- Preview
# other labels
on.schedule.cron
Runs your workflow on a schedule using unix-cron syntax. You can use crontab guru and their examples to generate cron strings.
- Scheduled workflows will only run on the default branch of the repository. In many cases, this means crons inside workflow files on the
mainbranch will be scheduled, while crons inside workflow files in feature branches will not be scheduled. - Scheduled workflows may be delayed during periods of high load. High load times include the start of every hour. In rare circumstances, jobs may be skipped or run multiple times. Make sure that your workflows are idempotent and do not have harmful side effects.
- A workflow can have multiple
cronschedules. - Scheduled workflows run in the GMT time zone.
on:
schedule:
- cron: '0 0 * * *' # Runs at midnight GMT every day
on.workflow_dispatch.inputs
Defines inputs that can be provided when manually triggering a workflow using the eas workflow:run command. This allows you to create parameterized workflows that can accept different values each time they run.
on:
workflow_dispatch:
# @info #
inputs:
name:
type: string
required: false
description: 'Name of the person to greet'
default: 'World'
choice_example:
type: choice
options:
- to be
- not to be
required: true
# @end #
| Property | Type | Required | Description |
|---|---|---|---|
type | string | Yes | The input type (string, boolean, number, choice, or environment). |
description | string | No | Description for the input. |
required | boolean | No | Whether the input is required. Defaults to false. |
default | varies | No | Default value for the input. Must match the input type. |
options | string[] | Yes (for type: choice) | Available options for choice inputs. |
Providing inputs
When running a workflow with inputs, you can provide them in several ways:
- Command line flags: <Terminal cmd={[ '$ eas workflow:run .eas/workflows/deploy.yml -F environment=production -F debug=true -F version=1.2.3', ]} />
- JSON via stdin:
<Terminal
cmd={[
$ echo '{"environment": "production", "debug": true, "version": "1.2.3"}' | eas workflow:run .eas/workflows/deploy.yml, ]} /> - Interactive prompts:
If required inputs are missing and you're not using
--non-interactive, the CLI will prompt you for them: <Terminal cmd={['$ eas workflow:run .eas/workflows/deploy.yml']} />
Usage
Input values are available in your workflow jobs through the ${{ inputs.<input_name> }} syntax:
on:
workflow_dispatch:
inputs:
name:
type: string
required: true
description: 'Name of the person to greet'
jobs:
deploy:
steps:
- name: Deploy to environment
run: |
echo "Hello, ${{ inputs.name }}!"
# Note: you can use `||` to provide a default value
# for non-eas-workflow:run-run workflows.
echo "Hello, ${{ inputs.name || 'World' }}!"
jobs
A workflow run is made up of one or more jobs.
jobs:
# @info #
job_1:
# ...
job_2:
# ...
# @end #
jobs.<job_id>
Each job must have an ID. The ID should be unique within the workflow and can contain alphanumeric characters and underscores. For example, my_job in the following YAML:
jobs:
# @info #
my_job:
# @end #
# ...
jobs.<job_id>.name
The human-friendly name of the job displayed on the workflow's detail page.
jobs:
my_job:
# @info #
name: Build app
# @end #
jobs.<job_id>.environment
Sets the EAS environment variable environment for the job. There are three possible values:
production(default)previewdevelopmentTheenvironmentkey is available on all jobs.
jobs:
my_job:
# @info #
environment: production | preview | development
# @end #
jobs.<job_id>.env
Sets environment variables for the job. The property is available on all jobs running a VM (all jobs except for the pre-packaged require-approval, doc, get-build and slack jobs).
jobs:
my_job:
# @info #
env:
APP_VARIANT: staging
RETRY_COUNT: 3
PREV_JOB_OUTPUT: ${{ needs.previous_job.outputs.some_output }}
# @end #
jobs.<job_id>.defaults.run.working_directory
Sets the directory to run commands in for all steps in the job.
jobs:
my_job:
# @info #
defaults:
run:
working_directory: ./my-app
# @end #
steps:
- name: My first step
run: pwd # prints: /home/expo/workingdir/build/my-app
defaults
Parameters to use as defaults for all jobs defined in the workflow configuration.
defaults.run.working_directory
Default working directory to run the scripts in. Relative paths like "./assets" or "assets" are resolved from the app's base directory.
defaults.tools
Specific versions of tools that should be used for jobs defined in this workflow configuration. Follow each tool's documentation for available values.
| Tool | Description |
|---|---|
node | Version of Node.js installed via nvm. |
yarn | Version of Yarn installed via npm -g. |
corepack | If set to true, corepack will be enabled at the beginning of build process. Defaults to false. |
pnpm | Version of pnpm installed via npm -g. |
bun | Version of Bun installed by passing bun-v$VERSION to Bun install script. |
ndk | Version of Android NDK installed through sdkmanager. |
bundler | Version of Bundler that will be passed to gem install -v. |
fastlane | Version of fastlane that will be passed to gem install -v. |
cocoapods | Version of CocoaPods that will be passed to gem install -v. |
Example of workflow using defaults.tools: |
name: Set up custom versions
defaults:
tools:
node: latest
yarn: '2'
corepack: true
pnpm: '8'
bun: '1.0.0'
fastlane: 2.224.0
cocoapods: 1.12.0
on:
push:
branches: ['*']
jobs:
setup:
steps:
- name: Check Node version
run: node --version # should print a concrete version, like 23.9.0
- name: Check Yarn version
run: yarn --version # should print a concrete version, like 2.4.3
concurrency
Configuration for concurrency control. Currently only allows setting cancel_in_progress for same-branch workflows.
# @info #
concurrency:
cancel_in_progress: true
group: ${{ workflow.filename }}-${{ github.ref }}
# @end #
| Property | Type | Description |
|---|---|---|
cancel_in_progress | boolean | If true, new workflow runs started from GitHub will cancel current in-progress runs for the same branch. |
group | string | We do not support custom concurrency groups yet. Set this placeholder value so that when we do support custom groups, your workflow remains compatible. |
Control flow
You can control when a job runs with the needs and after keywords. In addition, you can use the if keyword to control whether a job should run based on a condition.
jobs.<job_id>.needs
A list of job IDs whose jobs must complete successfully before this job will run.
jobs:
test:
steps:
- uses: eas/checkout
- uses: eas/use_npm_token
- uses: eas/install_node_modules
- name: tsc
run: yarn tsc
build:
# @info #
needs: [test] # This job will only run if the 'test' job succeeds
# @end #
type: build
params:
platform: ios
jobs.<job_id>.after
A list of job IDs that must complete (successfully or not) before this job will run.
jobs:
build:
type: build
params:
platform: ios
notify:
# @info #
after: [build] # This job will run after build completes (whether build succeeds or fails)
# @end #
jobs.<job_id>.if
The if conditional determines if a job should run. When an if condition is met, the job will run. When the condition is not met, the job will be skipped. A skipped job won't have completed successfully and any downstream jobs will not run that have this job in their needs list.
jobs:
my_job:
# @info #
if: ${{ github.ref_name == 'main' }}
# @end #
Interpolation
You can customize the behavior of a workflow — commands to execute, control flow, environment variables, build profile, app version, and more — based on the workflow run's context.
Use the ${{ expression }} syntax to access context properties and functions. For example: ${{ github.ref_name }} or ${{ needs.build_ios.outputs.build_id }}.
Context properties
The following properties are available in interpolation contexts:
after
A record of all upstream jobs specified in the current job's after list. Each job provides:
{
"status": "success" | "failure" | "skipped",
/* @info Every job produces a different set of outputs. See the specific job's documentation for details. */
"outputs": {}
/* @end */
}
Example:
jobs:
build:
type: build
params:
platform: ios
notify:
after: [build]
steps:
- run: echo "Build status: ${{ after.build.status }}"
needs
A record of all upstream jobs specified in the current job's needs list. Each job provides:
{
"status": "success" | "failure" | "skipped",
/* @info Every job produces a different set of outputs. See the specific job's documentation for details. */
"outputs": {}
/* @end */
}
Most pre-packaged jobs expose specific outputs. You can set outputs in custom jobs using the set-output function.
Example:
jobs:
setup:
outputs:
date: ${{ steps.current_date.outputs.date }}
steps:
- id: current_date
run: |
DATE=$(date +"%Y.%-m.%-d")
set-output date "$DATE"
build_ios:
needs: [setup]
type: build
env:
# You might use process.env.VERSION_SUFFIX to customize
# app version in your dynamic app config.
VERSION_SUFFIX: ${{ needs.setup.outputs.date }}
params:
platform: ios
profile: development
steps
A record of all steps in the current job. Each step provides its outputs set using the set-output function.
Note: The
stepscontext is only available within a job's steps, not at the workflow level. To expose a step's output to other jobs, use theset-outputfunction andoutputsconfiguration of the job. Example:
jobs:
my_job:
outputs:
value: ${{ steps.step_1.outputs.value }}
steps:
- id: step_1
run: set-output value "hello"
- run: echo ${{ steps.step_1.outputs.value }}
another_job:
needs: [my_job]
steps:
- run: echo "Value: ${{ needs.my_job.outputs.value }}"
inputs
A record of inputs provided when manually triggering a workflow with workflow_dispatch. Available when the workflow is triggered via eas workflow:run command with input parameters.
Example:
on:
workflow_dispatch:
inputs:
name:
type: string
required: true
jobs:
greet:
steps:
- run: echo "Hello, ${{ inputs.name }}!"
github
To ease the migration from GitHub Actions to EAS Workflows we expose some context fields you may find useful.
type GitHubContext = {
triggering_actor?: string;
event_name: 'pull_request' | 'push' | 'schedule' | 'workflow_dispatch';
sha: string;
ref: string; // e.g. refs/heads/main
ref_name: string; // e.g. main
ref_type: 'branch' | 'tag' | 'other';
commit_message?: string; // Only available for push and schedule events
label?: string;
repository?: string;
repository_owner?: string;
event?: {
label?: {
name: string;
};
// Only available for push and schedule events
head_commit?: {
message: string;
id: string;
};
pull_request?: {
number: number;
title: string;
body: string | null;
state: 'open' | 'closed';
draft: boolean;
merged: boolean | null;
// ... Other fields from the GitHub Pull Request webhook payload
};
number?: number;
schedule?: string;
inputs?: Record<string, string | number | boolean>;
};
};
info The
eventobject contains the full GitHub webhook payload. Forpull_requestevents,event.pull_requestincludes all fields from GitHub's Pull Request webhook payload. The commonly used fields are shown above, but additional fields such asuser,labels,milestone, and others are also available. If a workflow run is started fromeas workflow:run, itsevent_namewill beworkflow_dispatchand all the rest of the properties will be empty. Example:
jobs:
build_ios:
type: build
# @info #
if: ${{ github.ref_name == 'main' }}
# @end #
params:
platform: ios
profile: production
workflow
Information about the current workflow.
type WorkflowContext = {
id: string;
name: string;
filename: string;
url: string;
};
Example:
jobs:
notify_slack:
after: [...]
type: slack
params:
message: |
Workflow run completed: ${{ workflow.name }}
View details: ${{ workflow.url }}
env
A record of environment variables available in the current job context.
Note: The
envcontext is only available within a job's context, not at the workflow level. Example:
jobs:
my_job:
steps:
- run: echo "API URL: ${{ env.API_URL }}"
Context functions
The following functions are available in interpolation contexts:
success()
Returns whether all previous jobs have succeeded.
jobs:
notify:
if: ${{ success() }}
steps:
- run: echo "All jobs succeeded"
failure()
Returns whether any previous job has failed.
jobs:
notify:
if: ${{ failure() }}
steps:
- run: echo "A job failed"
fromJSON(value)
Parses a JSON string. Equivalent to JSON.parse().
Example:
jobs:
publish_update:
type: update
print_debug_info:
needs: [publish_update]
steps:
- run: |
echo "First update group: ${{ needs.publish_update.outputs.first_update_group_id }}"
echo "Second update group: ${{ fromJSON(needs.publish_update.outputs.updates_json || '[]')[1].group }}"
toJSON(value)
Converts a value to a JSON string. Equivalent to JSON.stringify().
Example:
jobs:
my_job:
steps:
- run: echo '${{ toJSON(github.event) }}'
contains(value, substring)
Checks whether value contains substring.
Example:
jobs:
my_job:
if: ${{ contains(github.ref_name, 'feature') }}
steps:
- run: echo "Feature branch"
startsWith(value, prefix)
Checks whether value starts with prefix.
Example:
jobs:
my_job:
if: ${{ startsWith(github.ref_name, 'release') }}
steps:
- run: echo "Release branch"
endsWith(value, suffix)
Checks whether value ends with suffix.
Example:
jobs:
my_job:
if: ${{ endsWith(github.ref_name, '-production') }}
steps:
- run: echo "Production branch"
hashFiles(...globs)
Returns a hash of files matching the provided glob patterns. Useful for cache keys.
Note: The
hashFilesfunction is only available within a job's steps, not at the workflow level. Example:
jobs:
my_job:
steps:
- run: echo "Dependencies hash: ${{ hashFiles('package-lock.json', 'yarn.lock') }}"
replaceAll(input, stringToReplace, replacementString)
Replaces all occurrences of stringToReplace in input with replacementString.
Example:
jobs:
my_job:
steps:
- run: echo "${{ replaceAll(github.ref_name, '/', '-') }}"
substring(input, start, end)
Extracts a substring from input starting at start and ending at end. If end is not provided, the substring is extracted from start to the end of input. Uses String#substring under the hood.
Example:
jobs:
my_job:
steps:
- run: echo "${{ substring(github.ref_name, 0, 50) }}"
Pre-packaged jobs
jobs.<job_id>.type
Specifies the type of pre-packaged job to run. Pre-packaged jobs produce specialized UI according to the type of job on the workflow's detail page.
jobs:
my_job:
# @info #
type: build
# @end #
Learn about the different pre-packaged jobs below.
build
Creates an Android or iOS build of your project using EAS Build. See Build job documentation for detailed information and examples.
jobs:
my_job:
# @info #
type: build
# @end #
params:
platform: ios | android # required
profile: string # optional, default: production
message: string # optional
This job outputs the following properties:
{
"build_id": string,
"app_build_version": string | null,
"app_identifier": string | null,
"app_version": string | null,
"channel": string | null,
"distribution": "internal" | "store" | null,
"fingerprint_hash": string | null,
"git_commit_hash": string | null,
"platform": "ios" | "android" | null,
"profile": string | null,
"runtime_version": string | null,
"sdk_version": string | null,
"simulator": "true" | "false" | null
}
deploy
Deploys your application using EAS Hosting. See Deploy job documentation for detailed information and examples.
jobs:
my_job:
# @info #
type: deploy
# @end #
params:
alias: string # optional
prod: boolean # optional
This job outputs the following properties:
{
"deploy_json": string, // JSON object containing the deployment details (output of `npx eas-cli deploy --json`).
"deploy_url": string, // URL to the deployment. It uses production URL if this was a production deployment. Otherwise, it uses the first alias URL or the deployment URL.
"deploy_alias_url": string, // Alias URL to the deployment (for example, `https://account-project--alias.expo.app`).
"deploy_deployment_url": string, // Unique URL to the deployment (for example, `https://account-project--uniqueid.expo.app`).
"deploy_identifier": string, // Identifier of the deployment.
"deploy_dashboard_url": string, // URL to the deployment dashboard (for example, `https://expo.dev/projects/[project]/hosting/deployments`).
}
fingerprint
Calculates a fingerprint of your project. See Fingerprint job documentation for detailed information and examples.
jobs:
my_job:
# @info #
type: fingerprint
# @end #
environment: production # Should match your build profile
This job outputs the following properties:
{
"android_fingerprint_hash": string,
"ios_fingerprint_hash": string,
}
Note: For accurate fingerprint matching, ensure the fingerprint job's
environmentmatches your build profile. Consider using EAS environment variables instead ofenvfor better consistency across jobs.
get-build
Retrieves an existing build from EAS that matches the provided parameters. See Get Build job documentation for detailed information and examples.
jobs:
my_job:
# @info #
type: get-build
# @end #
params:
platform: ios | android # optional
profile: string # optional
distribution: store | internal | simulator # optional
channel: string # optional
app_identifier: string # optional
app_build_version: string # optional
app_version: string # optional
git_commit_hash: string # optional
fingerprint_hash: string # optional
sdk_version: string # optional
runtime_version: string # optional
simulator: boolean # optional
wait_for_in_progress: boolean # optional
This job outputs the following properties:
{
"build_id": string,
"app_build_version": string | null,
"app_identifier": string | null,
"app_version": string | null,
"channel": string | null,
"distribution": "internal" | "store" | null,
"fingerprint_hash": string | null,
"git_commit_hash": string | null,
"platform": "ios" | "android" | null,
"profile": string | null,
"runtime_version": string | null,
"sdk_version": string | null,
"simulator": "true" | "false" | null
}
submit
Submits an Android or iOS build to the app store using EAS Submit. See Submit job documentation for detailed information and examples.
jobs:
my_job:
# @info #
type: submit
# @end #
params:
build_id: string # required
profile: string # optional, default: production
groups: string[] # optional
This job outputs the following properties:
{
"apple_app_id": string | null, // Apple App ID. https://expo.fyi/asc-app-id
"ios_bundle_identifier": string | null, // iOS bundle identifier of the submitted build. https://expo.fyi/bundle-identifier
"android_package_id": string | null // Submitted Android package ID. https://expo.fyi/android-package
}
testflight
Distributes iOS builds to TestFlight internal and external testing groups. See TestFlight job documentation for detailed information and examples.
jobs:
my_job:
# @info #
type: testflight
# @end #
params:
build_id: string # required
profile: string # optional, default: production
internal_groups: string[] # optional
external_groups: string[] # optional
changelog: string # optional
submit_beta_review: boolean # optional
wait_processing_timeout_seconds: number # optional, default: 1800 (30 minutes)
This job outputs the following properties:
{
"apple_app_id": string | null, // Apple App ID. https://expo.fyi/asc-app-id
"ios_bundle_identifier": string | null // iOS bundle identifier of the submitted build. https://expo.fyi/bundle-identifier
}
update
Publishes an update using EAS Update. See Update job documentation for detailed information and examples.
jobs:
my_job:
# @info #
type: update
# @end #
params:
message: string # optional
platform: string # optional - android | ios | all, defaults to all
branch: string # optional
channel: string # optional - cannot be used with branch
private_key_path: string # optional
upload_sentry_sourcemaps: boolean # optional - defaults to "try uploading, but don't fail the job if it fails"
This job outputs the following properties:
{
"first_update_group_id": string, // ID of the first update group. You can use it to e.g. construct the update URL for a development client deep link.
"updates_json": string // Stringified JSON array of update groups. Output of `eas update --json`.
}
maestro
Runs Maestro tests on a build. See Maestro job documentation for detailed information and examples.
important Maestro tests are in alpha.
jobs:
my_job:
# @info #
type: maestro
# @end #
environment: production | preview | development # optional, defaults to preview
image: string # optional. See https://docs.expo.dev/eas/workflows/syntax/#jobsjob_idruns_on for a list of available images.
params:
build_id: string # required
flow_path: string | string[] # required
shards: number # optional, defaults to 1
retries: number # optional, defaults to 1
record_screen: boolean # optional, defaults to false. If true, uploads a screen recording of the tests.
include_tags: string | string[] # optional. Tags to include in the tests. Will be passed to Maestro as `--include-tags`.
exclude_tags: string | string[] # optional. Tags to exclude from the tests. Will be passed to Maestro as `--exclude-tags`.
maestro_version: string # optional. Version of Maestro to use for the tests. If not provided, the latest version will be used.
android_system_image_package: string # optional. Android emulator system image package to use.
device_identifier: string | { android?: string, ios?: string } # optional. Device identifier to use for the tests.
output_format?: string # optional. Maestro test report format. Will be passed to Maestro as `--format`. Can be `junit` or other supported formats.
maestro-cloud
Runs Maestro tests on a build in Maestro Cloud. See Maestro Cloud job documentation for detailed information and examples.
important Running tests in Maestro Cloud requires a Maestro Cloud account and Cloud Plan subscription. Go to Maestro docs to learn more.
jobs:
my_job:
# @info #
type: maestro-cloud
# @end #
environment: production | preview | development # optional, defaults to preview
image: string # optional. See https://docs.expo.dev/eas/workflows/syntax/#jobsjob_idruns_on for a list of available images.
params:
build_id: string # required. ID of the build to test.
maestro_project_id: string # required. Maestro Cloud project ID. Example: `proj_01jw6hxgmdffrbye9fqn0pyzm0`.
flows: string # required. Path to the Maestro flow file or directory containing the flows to run. Corresponds to `--flows` param to `maestro cloud`.
maestro_api_key: string # optional. The API key to use for the Maestro project. By default, `MAESTRO_CLOUD_API_KEY` environment variable will be used. Corresponds to `--api-key` param to `maestro cloud`.
include_tags: string | string[] # optional. Tags to include in the tests. Will be passed to Maestro as `--include-tags`.
exclude_tags: string | string[] # optional. Tags to exclude from the tests. Will be passed to Maestro as `--exclude-tags`.
maestro_version: string # optional. Version of Maestro to use for the tests. If not provided, the latest version will be used.
android_api_level: string # optional. Android API level to use for the tests. Will be passed to Maestro as `--android-api-level`.
maestro_config: string # optional. Path to the Maestro `config.yaml` file to use for the tests. Will be passed to Maestro as `--config`.
device_locale: string # optional. Device locale to use for the tests. Will be passed to Maestro as `--device-locale`. Run `maestro cloud --help` for a list of supported values.
device_model: string # optional. Model of the device to use for the tests. Will be passed to Maestro as `--device-model`. Run `maestro cloud --help` for a list of supported values.
device_os: string # optional. OS of the device to use for the tests. Will be passed to Maestro as `--device-os`. Run `maestro cloud --help` for a list of supported values.
name: string # optional. Name for the Maestro Cloud upload. Corresponds to `--name` param to `maestro cloud`.
branch: string # optional. Override for the branch the Maestro Cloud upload originated from. By default, if the workflow run has been triggered from GitHub, the branch of the workflow run will be used. Corresponds to `--branch` param to `maestro cloud`.
async: boolean # optional. Run the Maestro Cloud tests asynchronously. If true, the status of the job will only denote whether the upload was successful, *not* whether the tests succeeded. Corresponds to `--async` param to `maestro cloud`.
slack
Sends a message to a Slack channel using a webhook URL. See Slack job documentation for detailed information and examples.
jobs:
my_job:
# @info #
type: slack
# @end #
params:
webhook_url: string # required
message: string # required if payload is not provided
payload: object # required if message is not provided
github-comment
Automatically posts comprehensive reports of your workflow's completed builds, updates, and deployments to GitHub pull requests or content provided by you. See GitHub Comment job documentation for detailed information and examples.
jobs:
my_job:
# @info #
type: github-comment
# @end #
params:
message: string # optional - custom message to include in the report
build_ids: string[] # optional - specific build IDs to include, defaults to all related to the running workflow
update_group_ids: string[] # optional - specific update group IDs to include, defaults to all related to the workflow
deployment_ids: string[] # optional - specific deployment IDs to include, defaults to all related to the workflow
# instead of using message and the builds, updates, and deployments table, you can also override the comment contents with `payload`
custom_github_comment:
type: github-comment
params:
payload: string # optional - raw markdown/HTML content for fully custom comment
This job outputs the following properties:
{
"comment_url": string | undefined // URL of the posted GitHub comment
}
require-approval
Requires approval from a user before continuing with the workflow. A user can approve or reject which translates to success or failure of the job. See Require Approval job documentation for detailed information and examples.
jobs:
confirm:
# @info #
type: require-approval
# @end #
doc
Displays a Markdown section in the workflow logs. See Doc job documentation for detailed information and examples.
jobs:
next_steps:
# @info #
type: doc
params:
md: string
# @end #
repack
Repackages an app from an existing build. This job repackages the app's metadata and JavaScript bundle without performing a full native rebuild, which is useful for creating a faster build compatible with a specific fingerprint. See Repack job documentation for detailed information and examples.
jobs:
next_steps:
# @info #
type: repack
params:
build_id: string # required
profile: string # optional
embed_bundle_assets: boolean # optional
message: string # optional
repack_version: string # optional
# @end #
Custom jobs
Runs custom code and can use built-in EAS functions. Does not require a type field.
jobs:
my_job:
steps:
# ...
jobs.<job_id>.steps
A job contains a sequence of tasks called steps. Steps can run commands. steps may only be provided in custom jobs and build jobs.
jobs:
my_job:
# @info #
steps:
- name: My first step
run: echo "Hello World"
# @end #
jobs.<job_id>.outputs
A list of outputs defined by the job. These outputs are accessible to all downstream jobs that depend on this job. To set outputs, use the set-output function within a job step.
Downstream jobs can access these outputs using the following expressions within interpolation contexts:
needs.<job_id>.outputs.<output_name>after.<job_id>.outputs.<output_name>Here,<job_id>refers to the identifier of the upstream job, and<output_name>refers to the specific output variable you want to access. In the example below, theset-outputfunction sets the output namedtestto the valuehello worldinjob_1'sstep_1step. Later injob_2, it's accessed instep_2usingneeds.job_1.outputs.output_1.
jobs:
job_1:
# @info #
outputs:
output_1: ${{ steps.step_1.outputs.test }}
# @end #
steps:
- id: step_1
# @info #
run: set-output test "hello world"
# @end #
job_2:
needs: [job_1]
steps:
# @info #
- id: step_2
run: echo ${{ needs.job_1.outputs.output_1 }}
# @end #
jobs.<job_id>.image
Specifies the VM image to use for the job. See Infrastructure for available images.
jobs:
my_job:
# @info #
image: auto | string # optional, defaults to 'auto'
# @end #
jobs.<job_id>.runs_on
Specifies the worker that will execute the job. Available only on custom jobs.
jobs:
my_job:
# @info #
runs_on: linux-medium | linux-large |
linux-medium-nested-virtualization |
linux-large-nested-virtualization |
macos-medium | macos-large # optional, defaults to linux-medium
# @end #
{/* vale off */}
| Worker | vCPU | Memory (GiB RAM) | SSD (GiB) | Notes |
|---|---|---|---|---|
| linux-medium | 4 | 16 | 14 | Default worker. |
| linux-large | 8 | 32 | 28 | |
| linux-medium-nested-virtualization | 4 | 16 | 14 | Allows running Android Emulators. |
| linux-large-nested-virtualization | 4 | 32 | 28 | Allows running Android Emulators. |
| Worker | Efficiency cores | Unified memory (GiB RAM) | SSD (GiB) | Notes |
| ------------ | ---------------- | ------------------------ | --------- | ------------------------------------ |
| macos-medium | 5 | 20 | 125 | Runs iOS jobs, including simulators. |
| macos-large | 10 | 40 | 125 | Runs iOS jobs, including simulators. |
| {/* vale on */} |
Note: For Android Emulator jobs, you must use a
linux-*-nested-virtualizationworker. For iOS builds and iOS Simulator jobs, you must use amacos-*worker.
jobs.<job_id>.steps.<step>.id
The id property is used to reference the step in the job. Useful for using the step's output in a downstream job.
jobs:
my_job:
outputs:
test: ${{ steps.step_1.outputs.test }} # References the output from step_1
steps:
# @info #
- id: step_1
# @end #
run: set-output test "hello world"
jobs.<job_id>.steps.<step>.name
The human-friendly name of the step, which is displayed in the job's logs. When a step's name is not provided, the run command is used as the step name.
jobs:
my_job:
steps:
# @info #
- name: My first step
# @end #
run: echo "Hello World"
jobs.<job_id>.steps.<step>.run
The shell command to run in the step.
jobs:
my_job:
steps:
# @info #
- run: echo "Hello World"
# @end #
jobs.<job_id>.steps.<step>.shell
The shell to use for running the command. Defaults to bash.
jobs:
my_job:
steps:
# @info #
- run: echo "Hello World"
shell: bash
# @end #
jobs.<job_id>.steps.<step>.working_directory
The directory to run the command in. When defined at the step level, it overrides the jobs.<job_id>.defaults.run.working_directory setting on the job if it is also defined.
jobs:
my_job:
steps:
- uses: eas/checkout
- run: pwd # prints: /home/expo/workingdir/build/my-app
# @info #
working_directory: ./my-app
# @end #
jobs.<job_id>.steps.<step>.uses
EAS provides a set of built-in reusable functions that you can use in workflow steps. The uses keyword is used to specify the function to use. All built-in functions start with the eas/ prefix.
jobs:
my_job:
steps:
# @info #
- uses: eas/checkout
- uses: eas/install_node_modules
- uses: eas/prebuild
- name: List files
run: ls -la
# @end #
Below is a list of built-in functions you can use in your workflow steps.
eas/checkout
Checks out your project source files.
jobs:
my_job:
steps:
# @info #
- uses: eas/checkout
# @end #
eas/install_node_modules
Installs node_modules using the package manager (bun, npm, pnpm, or Yarn) detected based on your project. Works with monorepos.
jobs:
my_job:
steps:
- uses: eas/checkout
# @info #
- uses: eas/install_node_modules
# @end #
eas/download_build
Downloads application archive of a given build. By default, the downloaded artifact can be an .apk, .aab, .ipa, or .app file, or a .tar.gz archive containing one or more of these files. If the artifact is a .tar.gz archive, it will be extracted and the first file matching the specified extensions will be returned. If the build produced no application archive, the step will fail.
jobs:
my_job:
steps:
- uses: eas/download_build
with:
build_id: string # Required. ID of the build to download.
extensions: [apk, aab, ipa, app] # Optional. List of file extensions to look for. Defaults to ["apk", "aab", "ipa", "app"].
| Property | Type | Required | Default | Description |
|---|---|---|---|---|
build_id | string | Yes | – | The ID of the build to download. Must be a valid UUID. |
extensions | string[] | No | ["apk", "aab", "ipa", "app"] | List of file extensions to look for in the downloaded artifact or archive. |
| Outputs: |
artifact_path: The absolute path to the matching application archive. This output can be used as input into other steps in your workflow. For example, to upload or process the artifact further. Example usage:
jobs:
build_ios:
type: build
params:
platform: ios
profile: production
my_job:
needs: [build_ios]
steps:
- uses: eas/download_build
id: download_build
with:
build_id: ${{ needs.build_ios.outputs.build_id }}
- name: Print artifact path
run: |
echo "Artifact path: ${{ steps.download_build.outputs.artifact_path }}"
eas/prebuild
Runs the expo prebuild command using the package manager (bun, npm, pnpm, or Yarn) detected based on your project with the command best suited for your build type and build environment.
jobs:
my_job:
steps:
- uses: eas/checkout
- uses: eas/install_node_modules
# @info #
- uses: eas/prebuild
# @end #
jobs:
my_job:
steps:
- uses: eas/checkout
- uses: eas/install_node_modules
- uses: eas/resolve_apple_team_id_from_credentials
id: resolve_apple_team_id_from_credentials
# @info #
- uses: eas/prebuild
with:
clean: false
apple_team_id: ${{ steps.resolve_apple_team_id_from_credentials.outputs.apple_team_id }}
# @end #
| Property | Type | Description |
|---|---|---|
clean | boolean | Optional property defining whether the function should use --clean flag when running the command. Defaults to false. |
apple_team_id | string | Optional property defining Apple team ID which should be used when doing prebuild. It should be specified for iOS builds using credentials. |
| <BoxLink | ||
| title="eas/prebuild source code" | ||
| description="View the source code for the eas/prebuild function on GitHub." | ||
| Icon={GithubIcon} | ||
| href="https://github.com/expo/eas-build/blob/main/packages/build-tools/src/steps/functions/prebuild.ts" | ||
| /> |
eas/restore_cache
Restores a previously saved cache from a specified key. This is useful for speeding up builds by reusing cached artifacts like compiled dependencies, build tools, or other intermediate build outputs.
jobs:
my_job:
steps:
- uses: eas/checkout
- uses: eas/install_node_modules
- uses: eas/prebuild
# @info #
- uses: eas/restore_cache
with:
key: cache-${{ hashFiles('package-lock.json') }}
restore_keys: cache
path: /path/to/cache
# @end #
jobs:
my_job:
steps:
- uses: eas/checkout
- uses: eas/install_node_modules
- uses: eas/prebuild
# @info #
- uses: eas/restore_cache
with:
key: cache-${{ hashFiles('package-lock.json') }}
path: /path/to/cache
# @end #
| Property | Type | Required | Description |
|---|---|---|---|
key | string | Yes | The cache key to restore. You can use expressions like ${{ hashFiles('package-lock.json') }} to create dynamic keys based on file hashes. |
restore_keys | string | No | A fallback key or prefix to use if the exact key is not found. If provided, the cache system will look for any cache entry that starts with this prefix. |
path | string | Yes | The path where the cache should be restored. This should match the path used when saving the cache. |
| <BoxLink | |||
| title="eas/restore_cache source code" | |||
| description="View the source code for the eas/restore_cache function on GitHub." | |||
| Icon={GithubIcon} | |||
| href="https://github.com/expo/eas-build/blob/main/packages/build-tools/src/steps/functions/restoreCache.ts" | |||
| /> |
eas/save_cache
Saves a cache to a specified key. This allows you to persist build artifacts, compiled dependencies, or other intermediate outputs that can be reused in subsequent builds to speed up the build process.
jobs:
my_job:
steps:
- uses: eas/checkout
- uses: eas/install_node_modules
- uses: eas/prebuild
- uses: eas/restore_cache
with:
key: cache-${{ hashFiles('package-lock.json') }}
path: /path/to/cache
- name: Build Android app
run: cd android && ./gradlew assembleRelease
# @info #
- uses: eas/save_cache
with:
key: cache-${{ hashFiles('package-lock.json') }}
path: /path/to/cache
# @end #
| Property | Type | Required | Description |
|---|---|---|---|
key | string | Yes | The cache key to save the cache under. You can use expressions like ${{ hashFiles('package-lock.json') }} to create dynamic keys based on file hashes. This should match the key used when restoring the cache. |
path | string | Yes | The path to the directory or files that should be cached. This should match the path used when restoring the cache. |
| <BoxLink | |||
| title="eas/save_cache source code" | |||
| description="View the source code for the eas/save_cache function on GitHub." | |||
| Icon={GithubIcon} | |||
| href="https://github.com/expo/eas-build/blob/main/packages/build-tools/src/steps/functions/saveCache.ts" | |||
| /> |
eas/send_slack_message
Sends a specified message to a configured Slack webhook URL, which then posts it in the related Slack channel. The message can be specified as plaintext or as a Slack Block Kit message.
With both cases, you can reference build job properties and use other steps outputs in the message for dynamic evaluation. For example, Build URL: https://expo.dev/builds/${{ needs.build_ios.outputs.build_id }}, Build finished with status: ${{ after.build_android.status }}.
Either message or payload has to be specified, but not both.
jobs:
my_job:
steps:
# @info #
- uses: eas/send_slack_message
# @end #
with:
message: 'This is a message to plain input URL'
slack_hook_url: 'https://hooks.slack.com/services/[rest_of_hook_url]'
| Property | Type | Description |
|---|---|---|
message | string | The text of the message you want to send. For example, 'This is the content of the message'. Note: Either message or payload needs to be provided, but not both. |
payload | json | The contents of the message you want to send which are defined using Slack Block Kit layout. Note: Either message or payload needs to be provided, but not both. |
slack_hook_url | string | The previously configured Slack webhook URL, which will post your message to the specified channel. You can provide the plain URL like slack_hook_url: 'https://hooks.slack.com/services/[rest_of_hook_url]', use EAS Environment Variables like slack_hook_url: ${{ env.ANOTHER_SLACK_HOOK_URL }}, or set the SLACK_HOOK_URL Environment Variable, which will serve as a default webhook URL (in this last case, there is no need to provide the slack_hook_url property). |
| <BoxLink | ||
| title="eas/send_slack_message source code" | ||
| description="View the source code for the eas/send_slack_message function on GitHub." | ||
| Icon={GithubIcon} | ||
| href="https://github.com/expo/eas-build/blob/main/packages/build-tools/src/steps/functions/sendSlackMessage.ts" | ||
| /> |
eas/use_npm_token
Configures Node package managers (bun, npm, pnpm, or Yarn) for use with private packages, published either to npm or a private registry.
Set NPM_TOKEN in your project's secrets, and this function will configure the build environment by creating .npmrc with the token.
jobs:
my_job:
name: Install private npm modules
steps:
- uses: eas/checkout
# @info #
- uses: eas/use_npm_token
# @end #
- name: Install dependencies
run: npm install # <---- Can now install private packages
eas/download_artifact
Downloads an artifact from EAS given an artifact's ID or name. Useful for sending artifacts from previous jobs to other services.
jobs:
my_job:
steps:
- uses: eas/download_artifact
with:
name: string # Required if artifact_id is not provided. Name of the artifact to download.
artifact_id: string # Required if artifact_name is not provided. ID of the artifact to download.
Properties
| Property | Type | Required | Description |
|---|---|---|---|
name | string | No | The name of the artifact to download. Required if artifact_id is not provided. |
artifact_id | string | No | The ID of the artifact to download. Required if name is not provided. |
Outputs
| Property | Type | Description |
|---|---|---|
artifact_path | string | The path to the downloaded artifact. This output can be used as input into other steps in your workflow. For example, to send or process the artifact. |
Example
jobs:
maestro_tests:
type: maestro
params:
build_id: '123-abc'
flow_path: 'path/to/flow.yaml'
output_format: 'junit'
my_job:
needs: [maestro_tests]
steps:
- uses: eas/download_artifact
id: download_artifact
with:
name: 'iOS Maestro Test Report (junit)'
- name: Print Maestro output
run: echo ${{ steps.download_artifact.outputs.artifact_path }}
Built-in shell functions
EAS Workflows provides the following shell function that you can use in your workflow steps to set variable outputs.
set-output
Sets an output variable that can be accessed by other steps or other jobs in the workflow.
set-output <name> <value>
Example usage for sharing a variable with another step:
jobs:
my_job:
steps:
- id: step_1
# @info #
run: set-output variable_1 "Variable 1"
# @end #
- id: step_2
run: echo ${{ steps.step_1.outputs.variable_1 }} # prints: Variable 1
Example usage for sharing a variable with another job:
jobs:
job_1:
outputs:
variable_1: ${{ steps.step_1.outputs.variable_1 }}
steps:
- id: step_1
# @info #
run: set-output variable_1 "Variable 1"
# @end #
job_2:
needs: [job_1]
steps:
- run: echo ${{ needs.job_1.outputs.variable_1 }} # prints: Variable 1
set-env
Sets an environment variable that is available to subsequent steps within the same job. Environment variables exported using export in one step's command are not automatically exposed to other steps. To share an environment variable with other steps, use the set-env executable.
set-env <name> <value>
set-env expects to be called with two arguments: the environment variable's name and value. For example, set-env NPM_TOKEN "abcdef" will expose the $NPM_TOKEN variable with the value abcdef to subsequent steps.
Note: Variables shared with
set-envare not automatically exported locally. You need to callexportyourself if you want to use the variable in the current step. Example usage for sharing an environment variable with another step:
jobs:
my_job:
steps:
- name: Set environment variables
run: |
# Using export only makes it available in the current step
export LOCAL_VAR="only in this step"
# Using set-env makes it available in subsequent steps
# @info #
set-env SHARED_VAR "available in next steps"
# @end #
# SHARED_VAR is not yet available in current step's environment
echo "LOCAL_VAR: $LOCAL_VAR" # prints: only in this step
echo "SHARED_VAR: $SHARED_VAR" # prints: (empty)
- name: Use shared variable
run: |
# SHARED_VAR is now available
# @info #
echo "SHARED_VAR: $SHARED_VAR" # prints: available in next steps
# @end #
Sharing environment variables between jobs
The set-env function only shares environment variables with other steps within the same job. To share values between different jobs, use the job's outputs with set-output and pass them via the env property on the receiving job:
jobs:
job_1:
# @info Define outputs to expose values to other jobs #
outputs:
my_value: ${{ steps.step_1.outputs.my_value }}
# @end #
steps:
- id: step_1
run: set-output my_value "value from job_1"
job_2:
needs: [job_1]
# @info Use env to set environment variables from upstream job outputs #
env:
MY_VALUE: ${{ needs.job_1.outputs.my_value }}
# @end #
steps:
- run: echo "MY_VALUE: $MY_VALUE" # prints: value from job_1
### `pre-packaged-jobs.mdx`
- **View**: https://raw.githubusercontent.com/expo/expo/refs/heads/main/docs/pages/eas/workflows/pre-packaged-jobs.mdx
- **Raw**: https://raw.githubusercontent.com/expo/expo/refs/heads/main/docs/pages/eas/workflows/pre-packaged-jobs.mdx
title: Pre-packaged jobs in EAS Workflows sidebar_title: Pre-packaged jobs description: Learn how to set up and use pre-packaged jobs in EAS Workflows.
import { Collapsible } from '/ui/components/Collapsible';
import { FAQ } from '/ui/components/FAQ';
Pre-packaged jobs are ready-to-use workflow jobs that help you automate common tasks like building, submitting, and testing your app. They provide a standardized way to handle these operations without having to write custom job configurations from scratch. This guide covers the available pre-packaged jobs and how to use them in your workflows.
Build
Build your project into an Android or iOS app. Build jobs can be customized so that you can execute custom commands during the build process. See Custom builds for more information.
Prerequisites
To successfully use the build job, you'll need to complete a build with EAS CLI using the same platform and profile as the pre-packaged job. Learn how to create your first build to get started.
Syntax
jobs:
build_app:
type: build
runs_on: string # optional - see https://docs.expo.dev/build-reference/infrastructure/ for available options
params:
platform: android | ios # required
profile: string # optional - default: production
message: string # optional
Parameters
You can pass the following parameters into the params list:
| Parameter | Type | Description |
|---|---|---|
| platform | string | Required. The platform to build for. Can be either android or ios. |
| profile | string | Optional. The build profile to use. Defaults to production. |
| message | string | Optional. Custom message attached to the build. Corresponds to the --message flag when running eas build. |
Environment variables
If you need certain environment variables during the build process, you can include them in your eas.json, for your specified build profile. They will be pulled from EAS environment variables.
Outputs
You can reference the following outputs in subsequent jobs:
| Output | Type | Description |
|---|---|---|
| build_id | string | The ID of the created build. |
| app_build_version | string | The version code/build number of the app. |
| app_identifier | string | The bundle identifier/package name of the app. |
| app_version | string | The version of the app. |
| channel | string | The update channel used for the build. |
| distribution | string | The distribution method used. Can be internal or store. |
| fingerprint_hash | string | The fingerprint hash of the build. |
| git_commit_hash | string | The git commit hash used for the build. |
| platform | string | The platform the build was created for. Either android or ios. |
| profile | string | The build profile used. |
| runtime_version | string | The runtime version used. |
| sdk_version | string | The SDK version used. |
| simulator | string | Whether the build is for simulator. |
Examples
Here are some practical examples of using the build job: This workflow builds your iOS app whenever you push to the main branch.
name: Build iOS app
on:
push:
branches: ['main']
jobs:
build_ios:
name: Build iOS
type: build
params:
platform: ios
profile: production