DEV Community

Samuel Adekunle
Samuel Adekunle

Posted on Originally published at techwithsam.dev

Automate Flutter Releases with Shorebird + GitHub Actions (Skip App Store Review)

A real pain point for mobile apps is waiting days or weeks for App Store and Play Store reviews whenever you need to ship a fix or a small update to users.

Elon Musk complaining about iOS App Review delay on X

That exact problem is what we are solving in this article. I’ll show you exactly how to automate your Flutter releases and patches using Shorebird and GitHub Actions, so you can push updates to users in minutes instead of waiting for store review.

We are going to create a clean, production-ready CI pipeline for both full releases and instant code-push updates.

Prerequisites

Before we start, here’s what you need:

  • A Shorebird account and an app already set up with Shorebird
  • Shorebird CLI installed and logged in on your machine
  • A Flutter project that already has Shorebird initialized
  • A GitHub repository
  • Basic understanding of GitHub Actions

If you haven’t set up Shorebird in your Flutter app yet, check out my video on YouTube on how to set up Shorebird. This article assumes that’s already done.

Authentication Setup

The first thing we need is authentication so GitHub Actions can talk to Shorebird.

Go to the Shorebird Console → Account → API Keys → Create API Key.

Shorebird API Key Interface

Give it a clear name, e.g “GitHub Actions”, choose an expiration, and set the required permissions.

Copy the key immediately because you won’t see it again.

Now go to your GitHub repository → Settings → Secrets and variables → Actions → New repository secret.

Name it exactly: SHOREBIRD_TOKEN

Paste the key and save.

GitHub Repository Secret Interface

This token will be available in all your workflows as ${{ secrets.SHOREBIRD_TOKEN }}.

That’s the only authentication step you need.

Official Shorebird GitHub Actions

Shorebird provides three official GitHub Actions that make life much easier:

  1. shorebirdtech/setup-shorebird@v1 — installs Shorebird on the runner
  2. shorebirdtech/shorebird-release@v1 — creates a release
  3. shorebirdtech/shorebird-patch@v1 — creates a patch

I strongly recommend using these instead of calling the CLI manually. They’re cleaner and maintained by the Shorebird team.

Release Workflow

Let’s build the release workflow first.

Create a new file in your project: .github/workflows/shorebird-release.yml

Here’s the structure:

  • Trigger on version tags: v1.0.0, v1.2.3, etc. or manually trigger the workflow
  • Set the SHOREBIRD_TOKEN as an environment variable
  • Pin your Flutter version (very important)
  • Checkout the code
  • Set up Java for Android (or Xcode signing for iOS)
  • Set up Shorebird with caching
  • Decode your keystore/certificates from secrets
  • Run the official shorebird-release action
  • Upload the APK, AAB, or IPA as artifacts

I’ll show both Android and iOS versions.

For Android, the key steps are decoding the keystore and creating the key.properties file from secrets.

GitHub Secrets You Need to Add for Android

Go to GitHub → repo → Settings → Secrets and variables → Actions → New repository secret

Secret name - Value
ANDROID_KEY_ALIAS - your_key_alias
ANDROID_KEY_PASSWORD - your_key_password
ANDROID_STORE_PASSWORD - your_store_password
ANDROID_KEYSTORE_BASE64 - run `base64 -i ~/path/to/upload.jks | tr -d '\n' | pbcopy`
The keystore base64 will be copied to your clipboard; paste it into GitHub Secrets
Enter fullscreen mode Exit fullscreen mode

GitHub Secrets You Need to Add for iOS

Go to GitHub → repo → Settings → Secrets and variables → Actions → New repository secret

Secret name - Value
IOS_CERTIFICATE_BASE64 - base64 of your .p12
IOS_CERTIFICATE_PASSWORD - Password you set when exporting the .p12
IOS_PROVISIONING_PROFILE_BASE64 - base64 of com.example.app profile
Enter fullscreen mode Exit fullscreen mode

For iOS, you need to import the certificate and provisioning profile into a temporary keychain.

# IOS_CERTIFICATE_BASE64 - export your .p12 from Keychain Access first, then:
base64 -i ~/path/to/distribution.p12 | tr -d '\n' | pbcopy

# IOS_PROVISIONING_PROFILE_BASE64 - download from Apple Developer Portal
base64 -i ~/path/to/YourApp_AppStore.mobileprovision | tr -d '\n' | pbcopy
Enter fullscreen mode Exit fullscreen mode

You can check out this document on how to create an Apple Distribution Certificate.

Repository secret

Once this workflow finishes, you get signed artifacts ready to upload to the stores.

This is your normal full release path.

name: Shorebird Release

on:
  workflow_dispatch:
    inputs:
      platform:
        description: 'Target platform'
        required: true
        default: 'both'
        type: choice
        options:
          - android
          - ios
          - both

jobs:
  release_android:
    name: Release Android
    if: ${{ github.event.inputs.platform == 'android' || github.event.inputs.platform == 'both' }}
    runs-on: ubuntu-latest
    steps:
      - name: Checkout Repository
        uses: actions/checkout@v4

      - name: Setup Java
        uses: actions/setup-java@v4
        with:
          distribution: 'temurin'
          java-version: '17'

      - name: Setup Android Keystore
        run: |
          echo "${{ secrets.ANDROID_KEYSTORE_BASE64 }}" | base64 --decode > android/app/keystore.jks
          cat > android/key.properties <<EOF
          storePassword=${{ secrets.ANDROID_STORE_PASSWORD }}
          keyPassword=${{ secrets.ANDROID_KEY_PASSWORD }}
          keyAlias=${{ secrets.ANDROID_KEY_ALIAS }}
          storeFile=keystore.jks
          EOF
        shell: bash

      - name: Setup Shorebird
        uses: shorebirdtech/setup-shorebird@v1

      - name: Shorebird Release (Android)
        uses: shorebirdtech/shorebird-release@v1
        id: shorebird-release
        with:
          flutter-version: latest
          platform: android
        env:
          SHOREBIRD_TOKEN: ${{ secrets.SHOREBIRD_TOKEN }}

      - name: Print Release Version
        run: echo "Released version ${{ steps.shorebird-release.outputs.release-version }}"
        shell: bash
Enter fullscreen mode Exit fullscreen mode

Full release on GitHub Gist.

Patch Workflow — The Real Power

Now, the part that actually lets you skip App Store review.

Create another file: .github/workflows/shorebird-patch.yml

name: Shorebird Patch

on:
  push:
    branches:
      - main
  workflow_dispatch:
    inputs:
      platform:
        description: 'Target platform'
        required: true
        default: 'both'
        type: choice
        options:
          - android
          - ios
          - both
      release-version:
        description: 'Release version to patch (e.g. 1.0.0+1). Defaults to latest.'
        required: false
        default: 'latest'

jobs:
  patch_android:
    name: Patch Android
    if: ${{ github.event_name == 'push' || github.event.inputs.platform == 'android' || github.event.inputs.platform == 'both' }}
    runs-on: ubuntu-latest
    steps:
      - name: Checkout Repository
        uses: actions/checkout@v4

      - name: Setup Java
        uses: actions/setup-java@v4
        with:
          distribution: 'temurin'
          java-version: '17'

      - name: Setup Android Keystore
        run: |
          echo "${{ secrets.ANDROID_KEYSTORE_BASE64 }}" | base64 --decode > android/app/keystore.jks
          cat > android/key.properties <<EOF
          storePassword=${{ secrets.ANDROID_STORE_PASSWORD }}
          keyPassword=${{ secrets.ANDROID_KEY_PASSWORD }}
          keyAlias=${{ secrets.ANDROID_KEY_ALIAS }}
          storeFile=keystore.jks
          EOF
        shell: bash

      - name: Setup Shorebird
        uses: shorebirdtech/setup-shorebird@v1

      - name: Shorebird Patch (Android)
        uses: shorebirdtech/shorebird-patch@v1
        id: shorebird-patch
        with:
          platform: android
          release-version: ${{ github.event.inputs.release-version || 'latest' }}
          args: --allow-asset-diffs --allow-native-diffs
        env:
          SHOREBIRD_TOKEN: ${{ secrets.SHOREBIRD_TOKEN }}

      - name: Print Patch Number
        run: echo "Patch number ${{ steps.shorebird-patch.outputs.patch-number }}"
        shell: bash
Enter fullscreen mode Exit fullscreen mode

Full release on GitHub Gist.

Trigger this one on hotfix tags, for example: v1.0.0-hotfix.1

The structure is almost identical to the release workflow, but we use the shorebird-patch action instead.

Important points:

  • A patch can only be applied to an existing release
  • Use the — staging flag first so you can preview the update
  • After testing with shorebird preview — track=staging, promote it to production from the Shorebird Console.

This is the real magic. Users get the fix over-the-air (OTA) in minutes. No waiting for review. No forcing them to download a new version from the store.

Recommended Development Workflow

Here’s the clean workflow I recommend (and the one Shorebird documents):

  1. Work on features and fixes on feature branches
  2. Open a pull request into main
  3. CI runs formatting, analysis, and tests
  4. Squash and merge into main (main stays always releasable)
  5. When you’re ready for a full release → create a tag like v1.2.0 → the release workflow runs automatically
  6. If a critical bug appears → fix it on main → cherry-pick into the release branch → create a hotfix tag like v1.2.0-hotfix.1 → the patch workflow runs
  7. Preview on staging → promote to production

This keeps everything automated, clean, and low-risk.

Best Practices

A few important tips:

  • Always pin the Flutter version on releases using — flutter-version
  • Use — dry-run on pull requests so you catch build issues early without creating real releases or patches
  • Keep your SHOREBIRD_TOKEN secure — never hardcode it
  • For iOS environments without certificates, you can use — no-codesign (then sign later)
  • Shorebird automatically runs in non-interactive mode in CI when the token is present

Follow these, and your pipeline will be solid.

Resources

All of this is based on the official Shorebird documentation:

I’m also a Shorebird Ambassador, so if you have questions, drop them in the comments.


That’s it.

You now have a complete automated system:

  • Full releases go through the stores as usual
  • Critical updates and bug fixes go live to users in minutes via Shorebird

No more waiting for review every single time.

If this was useful, hit the like button, subscribe, and turn on notifications so you don’t miss the next post.

If you want personalized help setting this up for your project, you can send a DM.

I’ll see you in the next one. Peace.

Top comments (0)