Skip to content

GitHub Integration

The Setup Shorebird GitHub Action allows you to integrate Shorebird into your existing GitHub Workflows.

✅ Your app has been initialized with shorebird init, and shorebird.yaml is committed to your repository.

✅ A Shorebird key for the workflow, as described in Authentication, or a trust rule to sign in with OIDC.

To integrate Shorebird into your CI, use the setup-shorebird action. The setup-shorebird action downloads Shorebird and adds it to the system path.

name: Shorebird Workflow Example

on:
  workflow_dispatch:

jobs:
  example:
    defaults:
      run:
        shell: bash

    runs-on: ubuntu-latest

    steps:
      # Use the setup-shorebird action to configure Shorebird
      - name: 🐦 Setup Shorebird
        uses: shorebirdtech/setup-shorebird@v1
        with:
          cache: true # Optionally cache your Shorebird installation

      # Now we're able to use Shorebird CLI in our workflow
      - name: 🚀 Use Shorebird
        run: shorebird --version

In the above workflow, the setup-shorebird action is used to configure Shorebird in CI, and subsequent steps can execute any Shorebird commands.

By default, setup-shorebird installs the latest stable Shorebird release. To pin a CLI release, pass shorebird-version (the version shorebird --version prints, such as 1.6.136). Pinning isn’t recommended: Shorebird’s servers stop accepting old CLI versions over time, so a pinned workflow eventually breaks. To build with a specific Flutter version, pass --flutter-version to shorebird release instead, or flutter-version to the shorebird-release action, as in Create releases.

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.

Next, with the key copied, navigate to your GitHub repository secrets via:

"Settings" -> "Secrets and variables" -> "Actions".

Then, click "New repository secret" and paste the key:

name: SHOREBIRD_TOKEN
secret: <THE KEY YOU COPIED>

Now the SHOREBIRD_TOKEN can be used in the GitHub workflow to perform authenticated functions such as creating patches 🎉

A workflow can sign in with the short-lived OIDC token GitHub issues to each job, so no Shorebird secret is stored in GitHub and nothing has to be rotated. The job signs in as a service account in your organization and can do what that account’s role allows.

Signing in with OIDC needs Shorebird 1.6.129 or newer. On an older CLI, use an API key in SHOREBIRD_TOKEN instead.

  1. In the console, open your organization’s Settings and select Service accounts. You need the Admin or Owner role to manage service accounts.
  2. Create a service account for the workflow, or pick an existing one. Its role decides what the workflow can do: Developer, the default, can create releases and patches, and App Manager can also create, update, and delete apps. A workflow that signs in with OIDC needs no API key.
  3. Under GitHub Actions, click Add workflow and enter:
    • Owner and Repository: where the workflow lives.
    • Workflow file: the path of the workflow file that defines the job, such as .github/workflows/release.yaml. If the job is in a reusable workflow, enter that file, not the workflow that calls it. A reusable workflow from another repository cannot sign in.
    • Environment (recommended): the GitHub environment the job runs in. Requiring one lets you add reviewers or branch restrictions in GitHub before a release can run.
    • Ref pattern (optional): the branches or tags allowed to sign in, such as refs/heads/main or refs/tags/*. Leave it empty to accept any ref.

Trust rules explains how each field is matched. The console then shows your project’s ID, which the workflow needs. It is also the number after /orgs/ in the console’s address bar, and shorebird orgs list lists it (shorebird account orgs before 1.6.135).

shorebird service-accounts (Shorebird 1.6.135 or newer) makes the same changes. Create the service account, then add a trust rule to it. Run these commands signed in with shorebird login and with SHOREBIRD_TOKEN unset. API keys can’t create, change, or delete service accounts, their keys, or their trust rules.

shorebird service-accounts create release --role developer --org-id <project id>
shorebird service-accounts trust add release --org-id <project id> \
  --github-repo acme/app --workflow release.yaml --ref main

A rule needs --ref, --environment, or both, unless you pass --any-ref. A rule for any ref and no environment lets anyone who can push a branch sign in. --ref main means refs/heads/main; to match several refs, give a full ref ending in *, such as refs/tags/*. The command prints the permissions and env the workflow needs. Run shorebird service-accounts trust add --help for its other options.

See Manage service accounts from the CLI for the other commands.

The job needs permission to request an OIDC token, the environment the rule names (if any), and SHOREBIRD_PROJECT_ID set to your project’s ID. The ID is not a secret.

name: Shorebird Release

on:
  workflow_dispatch:

jobs:
  release:
    runs-on: ubuntu-latest
    environment: production # Only if your trust rule names an environment
    permissions:
      contents: read
      id-token: write
    env:
      SHOREBIRD_PROJECT_ID: '123' # Your project's ID from the console

    steps:
      - name: 📚 Git Checkout
        uses: actions/checkout@v7

      - name: 🐦 Setup Shorebird
        uses: shorebirdtech/setup-shorebird@v1
        with:
          cache: true

      - name: 🚀 Shorebird Release
        run: shorebird release android

Do not set SHOREBIRD_TOKEN in a job that signs in with OIDC. When SHOREBIRD_TOKEN is set, the Shorebird CLI uses it instead. If you are moving a workflow from an API key to OIDC, remove the SHOREBIRD_TOKEN line and the repository secret, then revoke the API key.

If the sign-in is refused, the CLI prints the reason, such as No trust rule matches this workflow. Check that the repository, workflow file, environment and ref of the job match the trust rule. Jobs started by pull_request_target can’t sign in; see when a sign-in is refused.

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. Store it as a base64-encoded repository secret, decode it to a path outside your checkout, and point key.properties at that path before the Shorebird step 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.

The simplest way to create a release is using the official Shorebird GitHub Actions:

name: Shorebird Release

on:
  workflow_dispatch:

env:
  SHOREBIRD_TOKEN: ${{ secrets.SHOREBIRD_TOKEN }}

jobs:
  release:
    defaults:
      run:
        shell: bash

    # For iOS, see "Release and patch iOS" below.
    runs-on: ubuntu-latest

    steps:
      - name: 📚 Git Checkout
        uses: actions/checkout@v7

      - name: 🐦 Setup Shorebird
        uses: shorebirdtech/setup-shorebird@v1
        with:
          cache: true

      - name: Set up Java
        uses: actions/setup-java@v6
        with:
          distribution: 'temurin'
          java-version: '17'

      - name: 🚀 Shorebird Release
        uses: shorebirdtech/shorebird-release@v1
        with:
          flutter-version: 3.47.9
          platform: android

flutter-version is required. It’s the Flutter version the release is built with, and every patch to that release is built with it too. Pass a version from shorebird flutter versions list, or latest for the newest stable Flutter version Shorebird supports. shorebird release --help lists the other values.

name: Shorebird Patch

on:
  workflow_dispatch:

env:
  SHOREBIRD_TOKEN: ${{ secrets.SHOREBIRD_TOKEN }}

jobs:
  patch:
    defaults:
      run:
        shell: bash

    # For iOS, see "Release and patch iOS" below.
    runs-on: ubuntu-latest

    steps:
      - name: 📚 Git Checkout
        uses: actions/checkout@v7

      - name: 🐦 Setup Shorebird
        uses: shorebirdtech/setup-shorebird@v1
        with:
          cache: true

      - name: Set up Java
        uses: actions/setup-java@v6
        with:
          distribution: 'temurin'
          java-version: '17'

      # Note: all signing information (key.properties, etc.) must be set up on
      # this runner for `shorebird patch android` to work.
      - name: 🚀 Shorebird Patch
        uses: shorebirdtech/shorebird-patch@v1
        with:
          platform: android
          release-version: latest
          track: stable

latest patches the most recently updated release. If a newer release might still be in store review, pass the exact version of the release your users have instead; see Patching.

Pass more arguments: flavors and monorepos

Section titled “Pass more arguments: flavors and monorepos”

Both actions take two more inputs:

  • args: arguments added to the shorebird release or shorebird patch command, such as --flavor, --target or --dart-define. Arguments after -- go to flutter build.
  • working-directory: where to run the command, for an app that isn’t at the root of your repository. It defaults to the root.

For example, to release and patch the production flavor of an app in apps/my_app:

- name: 🚀 Shorebird Release
  uses: shorebirdtech/shorebird-release@v1
  with:
    flutter-version: 3.47.9
    platform: android
    working-directory: apps/my_app
    args: --flavor production --target lib/main_production.dart

- name: 🚀 Shorebird Patch
  uses: shorebirdtech/shorebird-patch@v1
  with:
    platform: android
    release-version: 1.2.3+4
    track: stable
    working-directory: apps/my_app
    args: --flavor production --target lib/main_production.dart

Give the patch the same --flavor, --target and --dart-define values as the release it patches; see Apps with flavors. The flutter-version, release-version and track inputs already pass --flutter-version, --release-version and --track, so leave those out of args.

shorebird release ios and shorebird patch ios need Xcode, so iOS jobs run on a macOS runner.

Build each iOS patch with the same Xcode, on the same runner image, as the release it patches. A different toolchain can make the patch report asset or Podfile.lock changes you didn’t make, which stops it in CI; see Build patches with the same toolchain as the release.

macos-latest moves to a new macOS image, with a new default Xcode, when GitHub updates it. Name a versioned runner instead, such as macos-26, and select an Xcode that the image lists in GitHub’s runner images:

jobs:
  release-ios:
    runs-on: macos-26
    env:
      SHOREBIRD_TOKEN: ${{ secrets.SHOREBIRD_TOKEN }}
    steps:
      - name: 📚 Git Checkout
        uses: actions/checkout@v7

      - name: 🔨 Select Xcode
        run: sudo xcode-select -s /Applications/Xcode_26.6.app

      - name: 🐦 Setup Shorebird
        uses: shorebirdtech/setup-shorebird@v1
        with:
          cache: true

      # Install your signing certificate and profile here; see below.

      - name: 🚀 Shorebird Release
        uses: shorebirdtech/shorebird-release@v1
        with:
          flutter-version: 3.47.9
          platform: ios
          args: --export-options-plist ios/ExportOptions.plist

      - name: 📦 Keep the IPA
        uses: actions/upload-artifact@v7
        with:
          name: ipa
          path: build/ios/ipa/*.ipa

Use the same runs-on and Xcode in the patch job. Change both only when you create a new release. GitHub can update CocoaPods inside an image without renaming it, so before patching, check that pod --version on the runner matches the COCOAPODS: line in the release’s Podfile.lock; see Build patches with the same toolchain as the release.

Each job on a GitHub-hosted runner starts on a fresh machine with no signing certificate, so the job has to install your distribution certificate and App Store provisioning profile before the Shorebird step runs. shorebird patch ios builds the app the way the release did, so do the same in the patch job, with the same --export-options-plist.

GitHub’s guide Installing an Apple certificate on macOS runners shows how. Store the base64-encoded .p12 certificate, its password, the base64-encoded profile and a keychain password as repository secrets, then add this step before the Shorebird step:

- name: 🔑 Install signing certificate and profile
  env:
    BUILD_CERTIFICATE_BASE64: ${{ secrets.BUILD_CERTIFICATE_BASE64 }}
    P12_PASSWORD: ${{ secrets.P12_PASSWORD }}
    BUILD_PROVISION_PROFILE_BASE64:
      ${{ secrets.BUILD_PROVISION_PROFILE_BASE64 }}
    KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
  run: |
    CERTIFICATE_PATH=$RUNNER_TEMP/build_certificate.p12
    KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db
    PROFILES_DIR="$HOME/Library/Developer/Xcode/UserData/Provisioning Profiles"

    echo -n "$BUILD_CERTIFICATE_BASE64" | base64 --decode -o "$CERTIFICATE_PATH"
    security create-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
    security set-keychain-settings -lut 21600 "$KEYCHAIN_PATH"
    security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
    security import "$CERTIFICATE_PATH" -P "$P12_PASSWORD" -A -t cert -f pkcs12 -k "$KEYCHAIN_PATH"
    security set-key-partition-list -S apple-tool:,apple: -k "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
    security list-keychain -d user -s "$KEYCHAIN_PATH"

    mkdir -p "$PROFILES_DIR"
    echo -n "$BUILD_PROVISION_PROFILE_BASE64" | base64 --decode -o "$PROFILES_DIR/app_store.mobileprovision"

Xcode 16 and newer read provisioning profiles from ~/Library/Developer/Xcode/UserData/Provisioning Profiles; older Xcode versions read them from ~/Library/MobileDevice/Provisioning Profiles.

In Xcode, set the Runner target’s Release configuration to sign manually with that certificate and profile. Then commit an export options file that names the profile, and pass it to shorebird release ios with --export-options-plist, as in the job above:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>method</key>
  <string>app-store-connect</string>
  <key>signingStyle</key>
  <string>manual</string>
  <key>teamID</key>
  <string>YOUR_TEAM_ID</string>
  <key>provisioningProfiles</key>
  <dict>
    <key>com.example.app</key>
    <string>Your App Store profile name</string>
  </dict>
  <key>manageAppVersionAndBuildNumber</key>
  <false/>
</dict>
</plist>

Replace the team ID, bundle ID and profile name with your own. Leave manageAppVersionAndBuildNumber false: Shorebird refuses an export options file that sets it to true, because Xcode would then change the build number of the IPA you upload, and patches wouldn’t match the release. If you manage signing with fastlane match, run it in the job instead of the step above; see Fastlane.

To build without signing, pass --no-codesign instead, as the generic CI guide describes, and sign the archive outside CI.

For an example of a fully automated development workflow, see the Development Workflow Guide.

Development Workflow GuideLearn how to set up a fully automated development workflow with Shorebird.