CI Integration
Building with Shorebird in CI should be a very small change from your existing Flutter CI builds. Changes required:
- Installing Shorebird on the builder.
- Getting Shorebird credentials onto the builder.
- Replacing
flutter build --releasewithshorebird releaseorshorebird patch.
Prerequisites
Section titled “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 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.
Supported CI providers
Section titled “Supported CI providers”Continuous Integration lists a guide for each documented provider. If yours isn’t there, follow the steps below.
Installing Shorebird
Section titled “Installing Shorebird”Install Shorebird with the same script you use locally, then add it to the
PATH. On Linux and macOS builders:
On Windows builders, in PowerShell:
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:
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:
- In the Shorebird Console, open your organization’s Settings and select Service accounts.
- Click Create service account, name it after the CI system that will use
it (such as
GitHub Actions), and click Create. - 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. - Under API keys, click Create key, enter a name, and choose an expiration.
- 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.”Releasing
Section titled “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:
Would become:
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 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.
Patching
Section titled “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
Section titled “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
Section titled “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:
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’sPodfile.locknames the version it was built with;pod --versionprints 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.
Verify PR builds with --dry-run
Section titled “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:
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:
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
Section titled “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 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.