GitLab Integration
This guide shows you how to create Shorebird releases and patches from
GitLab CI/CD with a .gitlab-ci.yml file.
Prerequisites
Section titled “Prerequisites”✅ Your app has been initialized with shorebird init, and shorebird.yaml is
committed to your repository.
✅ A Shorebird key for the pipeline, as described in Authentication.
Quick start
Section titled “Quick start”There is no Shorebird component for GitLab. A job installs Shorebird with the
same script you use locally, then adds it to the PATH:
Shorebird installs its own copy of Flutter in ~/.shorebird, so the Flutter
version in the image does not matter. It does not install a JDK or the Android
SDK, so the image needs those. shorebird doctor --verbose lists what it finds
on the runner.
Authentication
Section titled “Authentication”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.
Next, with the key copied, navigate to your GitLab project’s CI/CD variables via:
"Settings" -> "CI/CD" -> "Variables".
Then, click "Add variable" and enter:
Masking keeps the key out of job logs. Protect variable makes GitLab pass the
key only to pipelines on protected branches and tags, so a pipeline on any other
branch can’t read it. GitLab protects your default branch unless your project or
group settings change that; if yours do, protect it under
"Settings" -> "Repository" -> "Protected branches".
GitLab passes CI/CD variables to every job as environment variables, so the
Shorebird CLI finds SHOREBIRD_TOKEN without any change to .gitlab-ci.yml.
Android code signing
Section titled “Android code signing”shorebird release android and shorebird patch android run flutter build,
so signing works exactly as it does for any Flutter app. Follow
Flutter’s Android signing instructions,
or the
Shorebird version of them.
The only difference on CI is that the keystore is not already on the machine. The pipeline below expects four more CI/CD variables:
ANDROID_KEYSTORE_BASE64: your upload keystore, base64-encoded on one line (base64 -i upload-keystore.jks | tr -d '\n').ANDROID_KEYSTORE_PASSWORD,ANDROID_KEY_ALIASandANDROID_KEY_PASSWORD: the values yourkey.propertiesholds locally.
Check Protect variable on all four, as for SHOREBIRD_TOKEN, and mask each one
GitLab accepts as masked (masked values need at least 8 characters and no
spaces). Leave Expand variable reference cleared, which is the default since
GitLab 18.6. If it is checked on ANDROID_KEYSTORE_PASSWORD or
ANDROID_KEY_PASSWORD, GitLab reads a $ in the password as a reference to
another variable and the build fails with a keystore password error.
The jobs decode the keystore to a path outside your checkout and write
android/key.properties before the Shorebird command runs.
Without key.properties, the release build fails, because the release signing
config from the Gradle setup above gets no storeFile. An app still on
Flutter’s default release config signs with the debug key instead, which the
Play Console rejects.
Create releases and patches
Section titled “Create releases and patches”Add this to .gitlab-ci.yml. It adds two manual jobs to pipelines on your
default branch: one creates an Android release, the other patches one. Your
other jobs are left as they are.
To run a job, open the pipeline and click Run (▶) on release-android or
patch-android. To patch a release other than the latest, click the job’s name
instead and set RELEASE_VERSION to that release’s version, such as 1.0.0+1,
before running it.
If your app uses flavors, pass --flavor to both commands and change the
artifact path to match, for example
build/app/outputs/bundle/<flavor>Release/app-<flavor>-release.aab.
GitLab sets CI=true in every job, so the Shorebird CLI runs non-interactively
and never waits for input. A prompt with no safe default fails with an error
that names the flag to pass. For example, shorebird patch fails on native code
changes unless you pass --allow-native-diffs. See
Scripting the Shorebird CLI for the full rules.
Run on tags instead
Section titled “Run on tags instead”To release or patch when you push a tag rather than by hand, give every
Shorebird job its own rules, including the iOS jobs below. extends merges
settings key by key but replaces lists, so a job’s rules replace the manual
rule from .shorebird rather than adding to it. In release-android and
release-ios:
and in patch-android and patch-ios:
Then remove allow_failure: true from .shorebird. It only keeps the pipeline
from waiting on a manual job. On a job that runs on its own, it reports a failed
release or patch as passed with warnings, so the tag pipeline looks green when
nothing was published.
Protect these tag patterns under
"Settings" -> "Repository" -> "Protected tags", and allow only Maintainers to
create them. Pipelines for unprotected tags don’t receive your protected
variables, and protecting the tags keeps anyone who can push a branch from
publishing a release or patch.
shorebird release ios and shorebird patch ios need Xcode, so iOS jobs run on
a macOS runner. On
GitLab’s hosted macOS runners,
which need a GitLab Premium or Ultimate plan, tags selects the machine and
image selects the macOS and Xcode version. Add a job like this next to the
Android ones:
A patch-ios job takes the same tags and image, with the variables of
patch-android and
shorebird patch ios --release-version="$RELEASE_VERSION" --track=stable as its
script. The hosted runner docs list the current images. Keep a release’s image
while you patch it, and move to a new one with a new release; see
Build patches with the same toolchain as the release.
Every job on a hosted macOS runner starts on a fresh VM with no signing
identity, so as written release-ios fails at code signing. Install your
distribution certificate and provisioning profile in the job’s script before
the Shorebird command, on every run. Use
fastlane match, or store a
base64-encoded .p12 and profile in CI/CD variables and import them with
security. To skip signing on CI, pass --no-codesign as the
generic CI guide
describes. That builds no IPA, only build/ios/archive/Runner.xcarchive, so
change the job’s artifact path to that archive to keep it, then sign and
distribute it from Xcode with Manage Version and Build Number unchecked (see
Uploading from Xcode).
On a self-hosted runner on a Mac, use your
runner’s tags instead. A shell runner ignores image and uses the Xcode
installed on the machine. If it keeps its home directory between jobs, the
install script stops when ~/.shorebird already exists, so override
before_script to install Shorebird only when it is missing:
A self-hosted runner needs the certificate and profile too, either installed in the keychain of the user the runner runs as or imported by the job as above.
For an example of a fully automated development workflow, see the Development Workflow Guide.