Syntax for EAS Workflows

Edit this page

Reference guide for the EAS Workflows configuration file syntax.


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 or see Examples 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.

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 file.

For example:

my-app
 .eas
  workflows
   create-development-builds.yml
   publish-preview-update.yml
   deploy-to-production.yml
 eas.json

name

The name of the workflow. This is displayed on the Expo dashboard on the workflows list page and is the title of the workflow's detail page.

name: My workflow

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-*']

on.push

Runs your workflow when you push a commit to matching branches and/or tags.

With the branches array, you can trigger the workflow only when those specified branches are pushed to. For example, if you use branches: ['main'], only push to the main branch will trigger the workflow. Supports globs.

With the tags array, 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.

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 arrays is provided the other defaults to [].

on:
  push:
    branches:
      - 'main'
      - 'feature/**'
      # other branch names and globs
    tags:
      - 'v1'
      - 'v2*'
      # other tag names and globs

on.pull_request

Runs your workflow when a pull request event occurs on the matching branches.

With the branches array, 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 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.

With the types array, 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:

  • opened
  • reopened
  • synchronize
  • labeled
on:
  pull_request:
    branches:
      - 'main'
      - 'feature/**'
      # other branch names and globs
    types:
      - 'opened'
      # other event types

on.pull_request_labeled

Runs your workflow when a pull request is labeled with a matching label.

With the labels array, 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 the list of matching labels directly to on.pull_request_labeled for simpler syntax.

on:
  pull_request_labeled:
    labels:
      - 'Test'
      - 'Preview'
      # other labels

Alternatively:

on:
  pull_request_labeled:
    - 'Test'
    - 'Preview'
    # other labels

jobs

A workflow run is made up of one or more jobs.

jobs:
  job_1:
    # ...
  job_2:
    # ...

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:
  my_job:
    # ...

jobs.<job_id>.name

The name of the job displayed on the workflow's detail page.

jobs:
  my_job:
    name: Build app

jobs.<job_id>.environment

The EAS environment variable environment to set for the job. There are three possible values:

  • production (default)
  • preview
  • development

The environment key is available on all jobs except for the pre-packaged build, submit, and get-build jobs.

jobs:
  my_job:
    environment: production | preview | development

jobs.<job_id>.defaults.run.working_directory

Sets the directory to run commands in for all steps in the job.

jobs:
  my_job:
    defaults:
      run:
        working_directory: ./my-app
    steps:
      - name: My first step
        run: pwd # prints: /home/expo/workingdir/build/my-app

Control flow

You can control when jobs run and what jobs must complete successfully before a job runs using 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 that 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:
    needs: [test] # This job will only run if the 'test' job succeeds
    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:
    after: [build] # This job will run after build completes (whether build succeeds or fails)

jobs.<job_id>.if

The if conditional determines if a job should run.

jobs:
  my_job:
    if: ${{ github.ref == 'refs/heads/main' }}

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:
    type: build

Learn about the different pre-packaged jobs below.

build

Creates an Android or iOS build of your project using EAS Build.

jobs:
  my_job:
    type: build
    params:
      platform: ios | android # required
      profile: string # optional, default: production

Build jobs can be customized so that you can execute custom commands during the build process. See Custom builds for more information.

deploy

Deploys your application using EAS Hosting.

jobs:
  my_job:
    type: deploy
    params:
      alias: string # optional
      prod: boolean # optional

get-build

Retrieves an existing build from EAS that matches the provided parameters.

jobs:
  my_job:
    type: get-build
    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

submit

Submits an Android or iOS build to the app store using EAS Submit. For environment it uses the environment used to create build referenced in build_id.

jobs:
  my_job:
    type: submit
    params:
      build_id: string # required
      profile: string # optional, default: production

update

Publishes an update using EAS Update.

jobs:
  my_job:
    type: update
    params:
      message: string # optional
      platform: string # optional - android, ios, or all
      branch: string # optional
      channel: string # optional - cannot be used with branch

maestro

Runs Maestro tests on a build.

maestro:
  type: maestro
  environment: production | preview | development # optional, defaults to preview
  image: string # optional, defaults to 'default'. See https://docs.expo.dev/build-reference/infrastructure/ for a list of available images.
  params:
    build_id: string # required
    flow_path: string | string[] # required

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>.runs_on

Specifies the worker that will execute the job. Available only on custom jobs.

jobs:
  my_job:
    runs_on: linux-medium | linux-large | linux-medium-nested-virtualization | linux-large-nested-virtualization | macos-medium | macos-large
WorkervCPUMemory (GiB RAM)SSD (GiB)Notes
linux-medium41614Default worker.
linux-large83228
linux-medium-nested-virtualization41614Allows running Android emulators.
linux-large-nested-virtualization41628Allows running Android emulators.
macos-medium51285Runs iOS jobs, including simulators.
macos-large102085Runs iOS jobs, including simulators.

Note: For iOS builds and iOS simulator jobs, you must use a macos-* worker. For Android emulator jobs, you must use a linux-*-nested-virtualization worker.

jobs.<job_id>.outputs

A list of outputs for the job. Job outputs are available to all downstream jobs that depend on this job. Set outputs in a step using the set-output function.

In the example below, the set-output function sets the test output to hello world in job_1's step_1 step. Later in job_2, it's accessed in step_2 using needs.job_1.outputs.output_1.

jobs:
  job_1:
    outputs:
      output_1: ${{ steps.step_1.outputs.test }}
    steps:
      - id: step_1
        run: set-output test "hello world"
  job_2:
    needs: [job_1]
    steps:
      - id: step_2
        run: echo ${{ needs.job_1.outputs.output_1 }}

jobs.<job_id>.steps

A job contains a sequence of tasks called steps. Steps can run commands.

jobs:
  my_job:
    steps:
      - name: My first step
        run: echo "Hello World"

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:
      - id: step_1
        run: set-output test "hello world"

jobs.<job_id>.steps.<step>.name

The name of the step, which is displayed in the job's logs.

jobs:
  my_job:
    steps:
      - name: My first step
        run: echo "Hello World"

jobs.<job_id>.steps.<step>.run

The command to run in the step.

jobs:
  my_job:
    steps:
      - run: echo "Hello World"

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
        working_directory: ./my-app

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:
      - uses: eas/checkout
      - uses: eas/install_node_modules
      - uses: eas/prebuild
      - name: List files
        run: ls -la

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:
      - uses: eas/checkout
eas/checkout source code

View the source code for the eas/checkout function on GitHub.

eas/install_node_modules

Installs node modules using the package manager (bun, npm, pnpm, or Yarn) detected based on your project. Works with monorepos.

example.yml
jobs:
  my_job:
    steps:
      - uses: eas/checkout
      - uses: eas/install_node_modules
eas/install_node_modules source code

View the source code for the eas/install_node_modules function on GitHub.

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
      - uses: eas/prebuild
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
      - uses: eas/prebuild
        with:
          clean: false
          apple_team_id: ${{ steps.resolve_apple_team_id_from_credentials.outputs.apple_team_id }}
PropertyTypeDescription
cleanbooleanOptional property defining whether the function should use --clean flag when running the command. Defaults to false.
apple_team_idstringOptional property defining Apple team ID which should be used when doing prebuild. It should be specified for iOS builds using credentials.
eas/prebuild source code

View the source code for the eas/prebuild function on GitHub.

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: ${{ eas.job.expoBuildUrl }}', Build finished with status: ${{ steps.run_fastlane.status_text }}, Build failed with error: ${{ steps.run_gradle.error_text }}. Either "message" or "payload" has to be specified, but not both.

jobs:
  my_job:
    steps:
      - uses: eas/send_slack_message
        with:
          message: 'This is a message to plain input URL'
          slack_hook_url: 'https://hooks.slack.com/services/[rest_of_hook_url]'
PropertyTypeDescription
messagestringThe 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.
payloadstringThe 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_urlstringThe URL of 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 secrets like slack_hook_url: ${{ eas.env.ANOTHER_SLACK_HOOK_URL }}, or set the SLACK_HOOK_URL secret, which will serve as a default webhook URL (in this last case, there is no need to provide the slack_hook_url property).
eas/send_slack_message source code

View the source code for the eas/send_slack_message function on GitHub.

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.

example.yml
jobs:
  my_job:
    name: Install private npm modules
    steps:
      - uses: eas/checkout
      - uses: eas/use_npm_token
      - name: Install dependencies
        run: npm install # <---- Can now install private packages
eas/use_npm_token source code

View the source code for the eas/use_npm_token function on GitHub.