Skip to content

CI Integration

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.

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

Continuous Integration lists a guide for each documented provider. If yours isn’t there, follow the steps below.

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

Section titled “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. 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, 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.

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.

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.

Section titled “Replacing flutter build --release with shorebird.”

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

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

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

Build patches with the same toolchain as the release

Section titled “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.

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, GitLab and Codemagic guides show how to pin each.

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

Section titled “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.

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 for details.

Set the SHOREBIRD_TOKEN environment variable so the CLI never needs to log in interactively. See Scripting the Shorebird CLI 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.