---
title: "CI Integration"
description: "Run Shorebird releases and patches on any CI system: install the CLI, sign in with an API key in SHOREBIRD_TOKEN, and build patches with the same toolchain as the release."
---

Building with Shorebird in CI should be a very small change from your existing Flutter CI builds. Changes required:

1. Installing Shorebird on the builder.
2. Getting Shorebird credentials onto the builder.
3. Replacing `flutter build --release` with `shorebird release` or `shorebird patch`.

## Prerequisites

Shorebird installs its own copy of Flutter in `~/.shorebird`. It does not install native platform toolchains. `shorebird release` and `shorebird patch` run `flutter build`, so a builder needs the same toolchain Flutter requires for your target platforms: a JDK and the Android SDK for Android, Xcode and CocoaPods for Apple platforms. See Flutter’s [install requirements](https://docs.flutter.dev/get-started/install) for the full list.

`shorebird doctor --verbose` lists the Android Studio, Android SDK, ADB, `JAVA_HOME`, Java version, and Gradle it detects on the builder.

```
shorebird doctor --verbose
```

## Supported CI providers

[Continuous Integration](/code-push/ci/) lists a guide for each documented provider. If yours isn’t there, follow the steps below.

## Installing Shorebird

Install Shorebird with the same script you use locally, then add it to the `PATH`. On Linux and macOS builders:

```
curl -fsSL https://shorebird.dev/install.sh | bash
export PATH="$HOME/.shorebird/bin:$PATH"
```

On Windows builders, in PowerShell:

```
Set-ExecutionPolicy RemoteSigned -Scope CurrentUser
irm https://shorebird.dev/install.ps1 | iex
$env:PATH = "$HOME\.shorebird\bin;$env:PATH"
```

The install scripts add Shorebird to your shell profile or your user `PATH`, which later steps of a CI job don’t read, so add `~/.shorebird/bin` to the `PATH` yourself, the way your CI passes it between steps. On Linux and macOS, when `XDG_CONFIG_HOME` is set, Shorebird installs to `$XDG_CONFIG_HOME/shorebird` instead; use that path in place of `~/.shorebird` in the snippets on this page.

On a builder that keeps its home directory between jobs, such as a self-hosted agent, `install.sh` stops with `Error: Shorebird is already installed` once `~/.shorebird` exists. Install only when it’s missing:

```
[ -d "$HOME/.shorebird" ] || curl -fsSL https://shorebird.dev/install.sh | bash
```

`install.ps1` leaves an existing install in place without failing.

## Getting Shorebird credentials onto the builder

Creating releases and patches needs a signed-in Shorebird CLI. In CI, the CLI reads an API key from the `SHOREBIRD_TOKEN` environment variable, so nobody has to run `shorebird login` on the builder.

Use a key that belongs to a [service account](/account/service-accounts/). A service account belongs to your organization rather than to a person, so its key keeps working when whoever set up CI leaves or changes roles, and the audit log shows the job’s actions under the service account’s name. Service accounts are available on every plan. To create one, you need the Admin or Owner role in the organization:

1. In the [Shorebird Console](https://console.shorebird.dev), open your organization’s **Settings** and select **Service accounts**.
2. Click **Create service account**, name it after the CI system that will use it (such as `GitHub Actions`), and click **Create**.
3. Keep the **Developer** role, which can create releases and patches. Choose **App Manager** instead if the job also creates apps, for example by running `shorebird init`.
4. Under **API keys**, click **Create key**, enter a name, and choose an expiration.
5. Copy the key. It is shown only once.

Store the key as `SHOREBIRD_TOKEN` in your CI platform’s secrets manager, as the steps below show.

Caution

`SHOREBIRD_TOKEN` is a secret. Do not check it into source control or share it publicly.

If you can’t create a service account, a personal API key from **Account → API Keys** goes in `SHOREBIRD_TOKEN` the same way. It acts as you, can do at most what your own role allows, and stops working if your account is deleted. See [API Keys](/account/api-keys/).

Migrating from `shorebird login:ci`

Tokens from `shorebird login:ci` stopped working on October 1, 2026. Replace the value of your `SHOREBIRD_TOKEN` secret with a new key; the variable name and your workflow files stay the same.

Now that you have a token, it is important to keep that token secure. Using a secrets manager for the token, or secure environment variables, is recommended, depending on your CI setup. `shorebird` commands will look for the token to be in a `SHOREBIRD_TOKEN` environment variable.

## Replacing `flutter build --release` with `shorebird`.

### Releasing

If you’re already building with Flutter in CI, you should see a line similar to `flutter build aab` or `flutter build ipa` in your CI config.

To move to Shorebird, you need only replace the `flutter build` line with `shorebird release`. For example:

```
flutter build ipa --dart-define=HERO=Mario
```

Would become:

```
shorebird release ios --dart-define=HERO=Mario
```

`shorebird release` takes the common `flutter build` arguments directly. If you ever see an error with `shorebird release` not supporting an argument that `flutter build` does, put it after `--` to pass it down to `flutter build`:

```
shorebird release ios --dart-define=HERO=Mario -- <flutter build arguments>
```

`shorebird` can support building with different versions of Flutter. By default `shorebird` will use whatever the most recent Flutter stable is, which can change. If you’d like to pin your CI to a specific Flutter version you can do this by adding `--flutter-version` to your command, e.g.

```
shorebird release ios --flutter-version=3.47.9 --dart-define=HERO=Mario
```

### Patching

Patching is identical to releasing, just use `shorebird patch` instead of `shorebird release`.

Patches with Shorebird can only ever apply to an existing Release. By definition a patch is a set of changes to apply to a Release.

`shorebird patch` does not take a `--flutter-version` argument, instead takes a `--release-version` argument, to specify which release version of your app you’re trying to patch. `shorebird` will look up the exact version of Flutter used to build the release and use that exact version to build the patch.

## CI best practices

When integrating Shorebird into a continuous integration pipeline, following these best practices is recommended to ensure reliable and efficient builds:

### Pin the Flutter version for releases

By default, `shorebird release` builds with the latest stable version of Flutter. Because the stable version of Flutter is periodically updated, your CI builds could use a different version of Flutter than what you developed and tested with.

To ensure consistent builds, pin the Flutter version using the `--flutter-version` flag:

```
shorebird release ios --flutter-version=3.47.5
```

Note

You do not need to pass `--flutter-version` to `shorebird patch`. Shorebird automatically detects the exact Flutter version used to build the original release and uses it to compile the patch.

### Build patches with the same toolchain as the release

Shorebird builds each patch with the release’s Flutter version, but it doesn’t install Xcode or CocoaPods: a patch uses whatever the builder has. For iOS and macOS, build each patch with the same Xcode version, on the same runner image or machine type, as the release it patches.

A patch built with a different toolchain can report changes you didn’t make. A different Xcode can compile your asset catalog differently, and the CocoaPods version is recorded in `Podfile.lock`. `shorebird patch` then reports `Your app contains asset changes` or `Your ios/Podfile.lock is different from the one used to build the release.`, and in CI the patch [stops with exit code 70](/code-push/troubleshooting/#patch-stops-in-ci-with-not-publishing-exit-code-70).

Labels such as GitHub’s `macos-latest`, or a CI provider’s default Xcode, move to new versions when the provider updates them. So:

* Pin the runner image or machine and the Xcode version in the jobs that release and patch, rather than using a “latest” label.
* Pin CocoaPods too. Providers update tools inside an image without renaming it, so a pinned image can still move to a newer CocoaPods. The `COCOAPODS:` line at the end of the release’s `Podfile.lock` names the version it was built with; `pod --version` prints the builder’s. If they differ, install the release’s version on the builder before patching.
* Keep that pin while you patch the release, and record which Xcode each release was built with.
* Move to a new Xcode with a new release, not partway through patching one.

The [GitHub](/code-push/ci/github/#pin-the-runner-and-xcode-for-ios), [GitLab](/code-push/ci/gitlab/#ios) and [Codemagic](/code-push/ci/codemagic/#pin-the-xcode-version) guides show how to pin each.

### Verify PR builds with `--dry-run`

To verify that code changes do not break your release or patch builds, you can add a CI check that runs `shorebird release` or `shorebird patch` on every pull request.

To prevent these validation builds from uploading artifacts or creating actual releases/patches in the Shorebird Console, add the `--dry-run` (or `-n`) flag to the command:

```
shorebird patch android --release-version=latest --dry-run
```

### Build without code signing in restricted environments

If your CI runners do not have access to Apple distribution certificates or provisioning profiles, you can still build and package Shorebird releases and patches for iOS by passing the `--no-codesign` flag:

```
shorebird release ios --no-codesign
```

Releases built with `--no-codesign` cannot be launched via `shorebird preview` or installed directly on devices. You must manually codesign the generated `.xcarchive` in Xcode or through your signing pipeline before distribution.

### Run CLI commands non-interactively

The Shorebird CLI detects CI environments, for example when the `CI` environment variable is set, and runs in non-interactive mode. In this mode it never waits for input. Some prompts use a default instead, and others fail with an error that names the flag to pass. For example, `shorebird patch` fails on native code changes unless you pass `--allow-native-diffs`. Always pass `--release-version` (or `--release-version=latest`) to `shorebird patch`: without it, the CLI builds the app first to guess which release to patch, which costs an extra build or can build the patch without the release’s settings. See [Patching](/code-push/patch/) for details.

Set the `SHOREBIRD_TOKEN` environment variable so the CLI never needs to log in interactively. See [Scripting the Shorebird CLI](/code-push/ci/scripting) for the full rules and for JSON output.

Thank you for reading this guide. If you have any questions or suggestions, feel free to reach out on [Discord](https://discord.gg/shorebird).
