White Labeling
White labeling means publishing the same app to the stores many times, once per customer, each with its own name, icon, and bundle ID. With Shorebird, you fix a bug in the shared Dart code once and one CI job patches every brand’s app. Your agreements with the app stores still apply; see Store compliance.
The setup is:
- Give each store app its own Shorebird app.
- Create a service account for CI.
- Run
shorebird initonce in each app. - Release each app with
shorebird release. - Patch every app from one CI job, trying each fix on a few apps first.
Give each store app its own Shorebird app
Section titled “Give each store app its own Shorebird app”Each app in the stores needs its own Shorebird app, with its own app ID. One Shorebird app covers both Android and iOS for that store app, so a fleet of 100 brands on both stores is 100 Shorebird apps.
Two brands can’t share a Shorebird app because a patch belongs to one release. Shorebird matches patches to devices by app ID and release version, and a release holds one build per platform. A patch is built against that build and installs only on it. Separate apps also give each brand its own releases, tracks, rollbacks, and insights, so you can roll back a patch for one brand without touching the others.
How you get one app per brand depends on how your project is laid out:
- One Flutter project per brand (for example, host apps generated from a
template that share the Dart code as a package): run
shorebird initin each project. Each gets its ownshorebird.yaml. - One Flutter project with a flavor per brand:
shorebird initcreates one Shorebird app per flavor and lists them underflavors:inshorebird.yaml. Pass--flavortoshorebird releaseandshorebird patch. See Apps with flavors.
Create a service account for CI
Section titled “Create a service account for CI”Give your pipeline a service account rather than a person’s key, so it keeps working when people change roles or leave. Pick its role by what the pipeline does:
- App Manager if CI runs
shorebird init, which creates apps. - Developer if CI only creates releases and patches.
Create a key for the account and store it in your CI’s secrets as
SHOREBIRD_TOKEN. The Shorebird CLI reads it from that environment variable.
See
Use a key from CI or a script.
Run shorebird init once in each app
Section titled “Run shorebird init once in each app”Run shorebird init in each brand’s project, for example as a step of the
template that generates a new brand, then commit the shorebird.yaml it creates
and the pubspec.yaml change:
In CI, shorebird init doesn’t ask questions. Without --display-name it names
the app after the name in pubspec.yaml, which is often the same for every
brand, and without --org-id it fails if the account can create apps in more
than one organization. See
Non-interactive mode.
Run it once per app. If shorebird.yaml already exists, shorebird init stops.
Don’t pass --force to an app you’ve already released: it creates a new
Shorebird app with a new app ID, and builds already in the stores won’t receive
patches published to it.
With flavors, add a brand by adding its flavor and running shorebird init
again without --force. It creates apps for the new flavors and keeps the
existing ones. It names each new app after the existing app with the flavor in
parentheses, and ignores --display-name, so rename it in the console if you
need to. It also rewrites shorebird.yaml with only app_id and flavors, so
check the diff and put back any other settings, such as auto_update: false,
before you commit.
Release each app
Section titled “Release each app”Every build you upload to the stores must be the one shorebird release made.
Release each brand’s app for both platforms in one command:
With flavors, run it once per flavor with --flavor=brand_a. Releasing and
patching for iOS need a Mac. Upload the builds the command produces; a build of
the same version made any other way can’t install patches (see
Patches can’t install because the app isn’t the build Shorebird released).
See Releasing for the details of each platform.
Patch every app from one CI job
Section titled “Patch every app from one CI job”Once each app has a release in the stores, a fix to the shared Dart code is a loop over the apps. This script patches each one and lists any that failed at the end, rather than stopping at the first failure:
With flavors, loop over the flavor names instead and pass --flavor "$flavor"
from the project’s root. The iOS patches need a macOS runner; on a Linux runner,
pass --platforms=android and patch iOS from a macOS job.
--release-version=latest patches each app’s most recently updated release for
each platform, so Android and iOS can land on different versions. If a brand has
more than one release in use, patch each version with
--release-version=<version>. Each patch is a build, so a large fleet takes a
while; split the apps across parallel CI jobs to shorten it.
If a patch changes native code or assets, shorebird patch stops in CI. See
Patch stops in CI with Not publishing.
Try each fix on a few apps first
Section titled “Try each fix on a few apps first”Before a fix reaches the whole fleet, try it on your flagship app and two or three other brands:
- Run the loop over those apps with
--track=staging. Devices check thestabletrack by default, so users don’t get these patches. - Test each patch with
shorebird preview --track=staging, or on a device subscribed to the staging track. - Promote the patches to
stablein the console, or withshorebird patches promote --release-version=<version> --patch-number=<n>in each app’s directory (with flavors, add--flavor=<brand>). Android and iOS each get their own patch, with its own number and possibly a different release version, so promote each platform’s patch.shorebird patch --jsonprints therelease_versionandpatch_numberof each. See Promoting a patch between tracks. - Run the loop over the remaining apps with
--track=stable.
See Staging Patches for a walkthrough of the staging track.