GitHub Actions Integration for iOS TestFlight Deployment

image

Rostislav Ulinets

image

This section walks you through how to automate your iOS app deployment to TestFlight using GitHub Actions, in conjunction with the Fastlane setup described in the Fastlane Setup Guide.

Prerequisites

Before setting up the GitHub Action, ensure the following are in place:

  • Fastlane is initialized and configured.
  • Match is set up and working for code signing.
  • App Store Connect API Key is generated and saved on your machine.

Step 1: Add SSH Deploy Key to Your Certificates Repository

To allow GitHub Actions to pull certificates from your private Match repository:

  • Open your terminal
  • Generate a new SSH key pair (Don’t set any passphrase):
ssh-keygen -t rsa -b 4096 -C "github-actions" -f ./github_actions_key

ssh-keygen -t rsa -b 4096 -C "github-actions" -f ./github_actions_key

ssh-keygen -t rsa -b 4096 -C "github-actions" -f ./github_actions_key

ssh-keygen -t rsa -b 4096 -C "github-actions" -f ./github_actions_key

ssh-keygen -t rsa -b 4096 -C "github-actions" -f ./github_actions_key

ssh-keygen -t rsa -b 4096 -C "github-actions" -f ./github_actions_key

ssh-keygen -t rsa -b 4096 -C "github-actions" -f ./github_actions_key

ssh-keygen -t rsa -b 4096 -C "github-actions" -f ./github_actions_key

ssh-keygen -t rsa -b 4096 -C "github-actions" -f ./github_actions_key

ssh-keygen -t rsa -b 4096 -C "github-actions" -f ./github_actions_key

ssh-keygen -t rsa -b 4096 -C "github-actions" -f ./github_actions_key

ssh-keygen -t rsa -b 4096 -C "github-actions" -f ./github_actions_key

ssh-keygen -t rsa -b 4096 -C "github-actions" -f ./github_actions_key

ssh-keygen -t rsa -b 4096 -C "github-actions" -f ./github_actions_key

ssh-keygen -t rsa -b 4096 -C "github-actions" -f ./github_actions_key

ssh-keygen -t rsa -b 4096 -C "github-actions" -f ./github_actions_key

ssh-keygen -t rsa -b 4096 -C "github-actions" -f ./github_actions_key

ssh-keygen -t rsa -b 4096 -C "github-actions" -f ./github_actions_key

ssh-keygen -t rsa -b 4096 -C "github-actions" -f ./github_actions_key

This creates:

  • github_actions_key (private key)
  • github_actions_key.pub (public key)

3. Go to your certificates GitHub repo (the one used by match)

Navigate to: Settings > Deploy keys > Add deploy key

  • Title: GitHub Actions Key
  • Key: Paste the content of github_actions_key.pub
  • Check Allow write access
  • Click Add key

Step 2: Add GitHub Secrets to Your Main Project Repo

Go to your main app repository on GitHub (the one running the action):

1. Navigate to your repository → Settings Secrets and variablesActions Secrets

2. Add the following secrets:

Important: Do not commit your .p8 file or SSH keys to version control. Store them securely

Step 3: Encode AuthKey.p8 and Save in GitHub Secrets

To securely use your App Store Connect API key in GitHub Actions, you need to encode the AuthKey.p8 file in Base64 and store it as a secret.

Steps:

1. Encode AuthKey.p8 to base64

Run this command in your terminal where the .p8 file is located:

base64 -i AuthKey.p8 -o AuthKey_base64.txt

base64 -i AuthKey.p8 -o AuthKey_base64.txt

base64 -i AuthKey.p8 -o AuthKey_base64.txt

base64 -i AuthKey.p8 -o AuthKey_base64.txt

base64 -i AuthKey.p8 -o AuthKey_base64.txt

base64 -i AuthKey.p8 -o AuthKey_base64.txt

base64 -i AuthKey.p8 -o AuthKey_base64.txt

base64 -i AuthKey.p8 -o AuthKey_base64.txt

base64 -i AuthKey.p8 -o AuthKey_base64.txt

base64 -i AuthKey.p8 -o AuthKey_base64.txt

base64 -i AuthKey.p8 -o AuthKey_base64.txt

base64 -i AuthKey.p8 -o AuthKey_base64.txt

base64 -i AuthKey.p8 -o AuthKey_base64.txt

base64 -i AuthKey.p8 -o AuthKey_base64.txt

base64 -i AuthKey.p8 -o AuthKey_base64.txt

base64 -i AuthKey.p8 -o AuthKey_base64.txt

base64 -i AuthKey.p8 -o AuthKey_base64.txt

base64 -i AuthKey.p8 -o AuthKey_base64.txt

base64 -i AuthKey.p8 -o AuthKey_base64.txt

Navigate to your repository → Settings Secrets and variablesActionsSecrets

Add a new secret:

Name: ASC_KEY_BASE64

Value: Paste the entire base64 string of your AuthKey.p8

Step 4: Add GitHub Actions Workflow

Create a new file in your project at the following location:
.github/workflows/ios-testflight.yml

Paste the following:

name: iOS TestFlight Deployment
on:
  workflow_dispatch:
jobs:
  deploy-to-testflight:
    name: Build and deploy to TestFlight
    runs-on: macos-15
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
        with:
          fetch-depth: 1


      - name: Install SSH Key
        uses: webfactory/ssh-agent@v0.9.1
        with:
          ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}


      - name: Set up Ruby
        uses: ruby/setup-ruby@v1
        with:
          ruby-version: "3.0"
          bundler-cache: true
          bundler: "2.4.22"


      - name: Set up Flutter
        uses: subosito/flutter-action@v2
        with:
          flutter-version: "3.29.2"
          channel: "stable"


      - name: Install Flutter dependencies
        run: flutter pub get


      - name: Install Ruby dependencies
        working-directory: ./ios
        run: bundle install


      - name: Decode App Store Connect API Key
        run: |
          echo "${{ secrets.ASC_KEY_BASE64 }}" | base64 --decode > ./ios/AuthKey.p8


      - name: Build and upload to TestFlight
        working-directory: ./ios
        env:
          ASC_KEY_ID: ${{ secrets.ASC_KEY_ID }}
          ASC_ISSUER_ID: ${{ secrets.ASC_ISSUER_ID }}
          ASC_KEY_PATH: ./AuthKey.p8
          MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
        run: bundle exec fastlane upload_to_testflight_function

name: iOS TestFlight Deployment
on:
  workflow_dispatch:
jobs:
  deploy-to-testflight:
    name: Build and deploy to TestFlight
    runs-on: macos-15
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
        with:
          fetch-depth: 1


      - name: Install SSH Key
        uses: webfactory/ssh-agent@v0.9.1
        with:
          ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}


      - name: Set up Ruby
        uses: ruby/setup-ruby@v1
        with:
          ruby-version: "3.0"
          bundler-cache: true
          bundler: "2.4.22"


      - name: Set up Flutter
        uses: subosito/flutter-action@v2
        with:
          flutter-version: "3.29.2"
          channel: "stable"


      - name: Install Flutter dependencies
        run: flutter pub get


      - name: Install Ruby dependencies
        working-directory: ./ios
        run: bundle install


      - name: Decode App Store Connect API Key
        run: |
          echo "${{ secrets.ASC_KEY_BASE64 }}" | base64 --decode > ./ios/AuthKey.p8


      - name: Build and upload to TestFlight
        working-directory: ./ios
        env:
          ASC_KEY_ID: ${{ secrets.ASC_KEY_ID }}
          ASC_ISSUER_ID: ${{ secrets.ASC_ISSUER_ID }}
          ASC_KEY_PATH: ./AuthKey.p8
          MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
        run: bundle exec fastlane upload_to_testflight_function

name: iOS TestFlight Deployment
on:
  workflow_dispatch:
jobs:
  deploy-to-testflight:
    name: Build and deploy to TestFlight
    runs-on: macos-15
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
        with:
          fetch-depth: 1


      - name: Install SSH Key
        uses: webfactory/ssh-agent@v0.9.1
        with:
          ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}


      - name: Set up Ruby
        uses: ruby/setup-ruby@v1
        with:
          ruby-version: "3.0"
          bundler-cache: true
          bundler: "2.4.22"


      - name: Set up Flutter
        uses: subosito/flutter-action@v2
        with:
          flutter-version: "3.29.2"
          channel: "stable"


      - name: Install Flutter dependencies
        run: flutter pub get


      - name: Install Ruby dependencies
        working-directory: ./ios
        run: bundle install


      - name: Decode App Store Connect API Key
        run: |
          echo "${{ secrets.ASC_KEY_BASE64 }}" | base64 --decode > ./ios/AuthKey.p8


      - name: Build and upload to TestFlight
        working-directory: ./ios
        env:
          ASC_KEY_ID: ${{ secrets.ASC_KEY_ID }}
          ASC_ISSUER_ID: ${{ secrets.ASC_ISSUER_ID }}
          ASC_KEY_PATH: ./AuthKey.p8
          MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
        run: bundle exec fastlane upload_to_testflight_function

name: iOS TestFlight Deployment
on:
  workflow_dispatch:
jobs:
  deploy-to-testflight:
    name: Build and deploy to TestFlight
    runs-on: macos-15
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
        with:
          fetch-depth: 1


      - name: Install SSH Key
        uses: webfactory/ssh-agent@v0.9.1
        with:
          ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}


      - name: Set up Ruby
        uses: ruby/setup-ruby@v1
        with:
          ruby-version: "3.0"
          bundler-cache: true
          bundler: "2.4.22"


      - name: Set up Flutter
        uses: subosito/flutter-action@v2
        with:
          flutter-version: "3.29.2"
          channel: "stable"


      - name: Install Flutter dependencies
        run: flutter pub get


      - name: Install Ruby dependencies
        working-directory: ./ios
        run: bundle install


      - name: Decode App Store Connect API Key
        run: |
          echo "${{ secrets.ASC_KEY_BASE64 }}" | base64 --decode > ./ios/AuthKey.p8


      - name: Build and upload to TestFlight
        working-directory: ./ios
        env:
          ASC_KEY_ID: ${{ secrets.ASC_KEY_ID }}
          ASC_ISSUER_ID: ${{ secrets.ASC_ISSUER_ID }}
          ASC_KEY_PATH: ./AuthKey.p8
          MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
        run: bundle exec fastlane upload_to_testflight_function

name: iOS TestFlight Deployment
on:
  workflow_dispatch:
jobs:
  deploy-to-testflight:
    name: Build and deploy to TestFlight
    runs-on: macos-15
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
        with:
          fetch-depth: 1


      - name: Install SSH Key
        uses: webfactory/ssh-agent@v0.9.1
        with:
          ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}


      - name: Set up Ruby
        uses: ruby/setup-ruby@v1
        with:
          ruby-version: "3.0"
          bundler-cache: true
          bundler: "2.4.22"


      - name: Set up Flutter
        uses: subosito/flutter-action@v2
        with:
          flutter-version: "3.29.2"
          channel: "stable"


      - name: Install Flutter dependencies
        run: flutter pub get


      - name: Install Ruby dependencies
        working-directory: ./ios
        run: bundle install


      - name: Decode App Store Connect API Key
        run: |
          echo "${{ secrets.ASC_KEY_BASE64 }}" | base64 --decode > ./ios/AuthKey.p8


      - name: Build and upload to TestFlight
        working-directory: ./ios
        env:
          ASC_KEY_ID: ${{ secrets.ASC_KEY_ID }}
          ASC_ISSUER_ID: ${{ secrets.ASC_ISSUER_ID }}
          ASC_KEY_PATH: ./AuthKey.p8
          MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
        run: bundle exec fastlane upload_to_testflight_function

name: iOS TestFlight Deployment
on:
  workflow_dispatch:
jobs:
  deploy-to-testflight:
    name: Build and deploy to TestFlight
    runs-on: macos-15
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
        with:
          fetch-depth: 1


      - name: Install SSH Key
        uses: webfactory/ssh-agent@v0.9.1
        with:
          ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}


      - name: Set up Ruby
        uses: ruby/setup-ruby@v1
        with:
          ruby-version: "3.0"
          bundler-cache: true
          bundler: "2.4.22"


      - name: Set up Flutter
        uses: subosito/flutter-action@v2
        with:
          flutter-version: "3.29.2"
          channel: "stable"


      - name: Install Flutter dependencies
        run: flutter pub get


      - name: Install Ruby dependencies
        working-directory: ./ios
        run: bundle install


      - name: Decode App Store Connect API Key
        run: |
          echo "${{ secrets.ASC_KEY_BASE64 }}" | base64 --decode > ./ios/AuthKey.p8


      - name: Build and upload to TestFlight
        working-directory: ./ios
        env:
          ASC_KEY_ID: ${{ secrets.ASC_KEY_ID }}
          ASC_ISSUER_ID: ${{ secrets.ASC_ISSUER_ID }}
          ASC_KEY_PATH: ./AuthKey.p8
          MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
        run: bundle exec fastlane upload_to_testflight_function

name: iOS TestFlight Deployment
on:
  workflow_dispatch:
jobs:
  deploy-to-testflight:
    name: Build and deploy to TestFlight
    runs-on: macos-15
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
        with:
          fetch-depth: 1


      - name: Install SSH Key
        uses: webfactory/ssh-agent@v0.9.1
        with:
          ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}


      - name: Set up Ruby
        uses: ruby/setup-ruby@v1
        with:
          ruby-version: "3.0"
          bundler-cache: true
          bundler: "2.4.22"


      - name: Set up Flutter
        uses: subosito/flutter-action@v2
        with:
          flutter-version: "3.29.2"
          channel: "stable"


      - name: Install Flutter dependencies
        run: flutter pub get


      - name: Install Ruby dependencies
        working-directory: ./ios
        run: bundle install


      - name: Decode App Store Connect API Key
        run: |
          echo "${{ secrets.ASC_KEY_BASE64 }}" | base64 --decode > ./ios/AuthKey.p8


      - name: Build and upload to TestFlight
        working-directory: ./ios
        env:
          ASC_KEY_ID: ${{ secrets.ASC_KEY_ID }}
          ASC_ISSUER_ID: ${{ secrets.ASC_ISSUER_ID }}
          ASC_KEY_PATH: ./AuthKey.p8
          MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
        run: bundle exec fastlane upload_to_testflight_function

name: iOS TestFlight Deployment
on:
  workflow_dispatch:
jobs:
  deploy-to-testflight:
    name: Build and deploy to TestFlight
    runs-on: macos-15
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
        with:
          fetch-depth: 1


      - name: Install SSH Key
        uses: webfactory/ssh-agent@v0.9.1
        with:
          ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}


      - name: Set up Ruby
        uses: ruby/setup-ruby@v1
        with:
          ruby-version: "3.0"
          bundler-cache: true
          bundler: "2.4.22"


      - name: Set up Flutter
        uses: subosito/flutter-action@v2
        with:
          flutter-version: "3.29.2"
          channel: "stable"


      - name: Install Flutter dependencies
        run: flutter pub get


      - name: Install Ruby dependencies
        working-directory: ./ios
        run: bundle install


      - name: Decode App Store Connect API Key
        run: |
          echo "${{ secrets.ASC_KEY_BASE64 }}" | base64 --decode > ./ios/AuthKey.p8


      - name: Build and upload to TestFlight
        working-directory: ./ios
        env:
          ASC_KEY_ID: ${{ secrets.ASC_KEY_ID }}
          ASC_ISSUER_ID: ${{ secrets.ASC_ISSUER_ID }}
          ASC_KEY_PATH: ./AuthKey.p8
          MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
        run: bundle exec fastlane upload_to_testflight_function

name: iOS TestFlight Deployment
on:
  workflow_dispatch:
jobs:
  deploy-to-testflight:
    name: Build and deploy to TestFlight
    runs-on: macos-15
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
        with:
          fetch-depth: 1


      - name: Install SSH Key
        uses: webfactory/ssh-agent@v0.9.1
        with:
          ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}


      - name: Set up Ruby
        uses: ruby/setup-ruby@v1
        with:
          ruby-version: "3.0"
          bundler-cache: true
          bundler: "2.4.22"


      - name: Set up Flutter
        uses: subosito/flutter-action@v2
        with:
          flutter-version: "3.29.2"
          channel: "stable"


      - name: Install Flutter dependencies
        run: flutter pub get


      - name: Install Ruby dependencies
        working-directory: ./ios
        run: bundle install


      - name: Decode App Store Connect API Key
        run: |
          echo "${{ secrets.ASC_KEY_BASE64 }}" | base64 --decode > ./ios/AuthKey.p8


      - name: Build and upload to TestFlight
        working-directory: ./ios
        env:
          ASC_KEY_ID: ${{ secrets.ASC_KEY_ID }}
          ASC_ISSUER_ID: ${{ secrets.ASC_ISSUER_ID }}
          ASC_KEY_PATH: ./AuthKey.p8
          MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
        run: bundle exec fastlane upload_to_testflight_function

name: iOS TestFlight Deployment
on:
  workflow_dispatch:
jobs:
  deploy-to-testflight:
    name: Build and deploy to TestFlight
    runs-on: macos-15
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
        with:
          fetch-depth: 1


      - name: Install SSH Key
        uses: webfactory/ssh-agent@v0.9.1
        with:
          ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}


      - name: Set up Ruby
        uses: ruby/setup-ruby@v1
        with:
          ruby-version: "3.0"
          bundler-cache: true
          bundler: "2.4.22"


      - name: Set up Flutter
        uses: subosito/flutter-action@v2
        with:
          flutter-version: "3.29.2"
          channel: "stable"


      - name: Install Flutter dependencies
        run: flutter pub get


      - name: Install Ruby dependencies
        working-directory: ./ios
        run: bundle install


      - name: Decode App Store Connect API Key
        run: |
          echo "${{ secrets.ASC_KEY_BASE64 }}" | base64 --decode > ./ios/AuthKey.p8


      - name: Build and upload to TestFlight
        working-directory: ./ios
        env:
          ASC_KEY_ID: ${{ secrets.ASC_KEY_ID }}
          ASC_ISSUER_ID: ${{ secrets.ASC_ISSUER_ID }}
          ASC_KEY_PATH: ./AuthKey.p8
          MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
        run: bundle exec fastlane upload_to_testflight_function

name: iOS TestFlight Deployment
on:
  workflow_dispatch:
jobs:
  deploy-to-testflight:
    name: Build and deploy to TestFlight
    runs-on: macos-15
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
        with:
          fetch-depth: 1


      - name: Install SSH Key
        uses: webfactory/ssh-agent@v0.9.1
        with:
          ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}


      - name: Set up Ruby
        uses: ruby/setup-ruby@v1
        with:
          ruby-version: "3.0"
          bundler-cache: true
          bundler: "2.4.22"


      - name: Set up Flutter
        uses: subosito/flutter-action@v2
        with:
          flutter-version: "3.29.2"
          channel: "stable"


      - name: Install Flutter dependencies
        run: flutter pub get


      - name: Install Ruby dependencies
        working-directory: ./ios
        run: bundle install


      - name: Decode App Store Connect API Key
        run: |
          echo "${{ secrets.ASC_KEY_BASE64 }}" | base64 --decode > ./ios/AuthKey.p8


      - name: Build and upload to TestFlight
        working-directory: ./ios
        env:
          ASC_KEY_ID: ${{ secrets.ASC_KEY_ID }}
          ASC_ISSUER_ID: ${{ secrets.ASC_ISSUER_ID }}
          ASC_KEY_PATH: ./AuthKey.p8
          MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
        run: bundle exec fastlane upload_to_testflight_function

name: iOS TestFlight Deployment
on:
  workflow_dispatch:
jobs:
  deploy-to-testflight:
    name: Build and deploy to TestFlight
    runs-on: macos-15
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
        with:
          fetch-depth: 1


      - name: Install SSH Key
        uses: webfactory/ssh-agent@v0.9.1
        with:
          ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}


      - name: Set up Ruby
        uses: ruby/setup-ruby@v1
        with:
          ruby-version: "3.0"
          bundler-cache: true
          bundler: "2.4.22"


      - name: Set up Flutter
        uses: subosito/flutter-action@v2
        with:
          flutter-version: "3.29.2"
          channel: "stable"


      - name: Install Flutter dependencies
        run: flutter pub get


      - name: Install Ruby dependencies
        working-directory: ./ios
        run: bundle install


      - name: Decode App Store Connect API Key
        run: |
          echo "${{ secrets.ASC_KEY_BASE64 }}" | base64 --decode > ./ios/AuthKey.p8


      - name: Build and upload to TestFlight
        working-directory: ./ios
        env:
          ASC_KEY_ID: ${{ secrets.ASC_KEY_ID }}
          ASC_ISSUER_ID: ${{ secrets.ASC_ISSUER_ID }}
          ASC_KEY_PATH: ./AuthKey.p8
          MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
        run: bundle exec fastlane upload_to_testflight_function

name: iOS TestFlight Deployment
on:
  workflow_dispatch:
jobs:
  deploy-to-testflight:
    name: Build and deploy to TestFlight
    runs-on: macos-15
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
        with:
          fetch-depth: 1


      - name: Install SSH Key
        uses: webfactory/ssh-agent@v0.9.1
        with:
          ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}


      - name: Set up Ruby
        uses: ruby/setup-ruby@v1
        with:
          ruby-version: "3.0"
          bundler-cache: true
          bundler: "2.4.22"


      - name: Set up Flutter
        uses: subosito/flutter-action@v2
        with:
          flutter-version: "3.29.2"
          channel: "stable"


      - name: Install Flutter dependencies
        run: flutter pub get


      - name: Install Ruby dependencies
        working-directory: ./ios
        run: bundle install


      - name: Decode App Store Connect API Key
        run: |
          echo "${{ secrets.ASC_KEY_BASE64 }}" | base64 --decode > ./ios/AuthKey.p8


      - name: Build and upload to TestFlight
        working-directory: ./ios
        env:
          ASC_KEY_ID: ${{ secrets.ASC_KEY_ID }}
          ASC_ISSUER_ID: ${{ secrets.ASC_ISSUER_ID }}
          ASC_KEY_PATH: ./AuthKey.p8
          MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
        run: bundle exec fastlane upload_to_testflight_function

name: iOS TestFlight Deployment
on:
  workflow_dispatch:
jobs:
  deploy-to-testflight:
    name: Build and deploy to TestFlight
    runs-on: macos-15
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
        with:
          fetch-depth: 1


      - name: Install SSH Key
        uses: webfactory/ssh-agent@v0.9.1
        with:
          ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}


      - name: Set up Ruby
        uses: ruby/setup-ruby@v1
        with:
          ruby-version: "3.0"
          bundler-cache: true
          bundler: "2.4.22"


      - name: Set up Flutter
        uses: subosito/flutter-action@v2
        with:
          flutter-version: "3.29.2"
          channel: "stable"


      - name: Install Flutter dependencies
        run: flutter pub get


      - name: Install Ruby dependencies
        working-directory: ./ios
        run: bundle install


      - name: Decode App Store Connect API Key
        run: |
          echo "${{ secrets.ASC_KEY_BASE64 }}" | base64 --decode > ./ios/AuthKey.p8


      - name: Build and upload to TestFlight
        working-directory: ./ios
        env:
          ASC_KEY_ID: ${{ secrets.ASC_KEY_ID }}
          ASC_ISSUER_ID: ${{ secrets.ASC_ISSUER_ID }}
          ASC_KEY_PATH: ./AuthKey.p8
          MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
        run: bundle exec fastlane upload_to_testflight_function

name: iOS TestFlight Deployment
on:
  workflow_dispatch:
jobs:
  deploy-to-testflight:
    name: Build and deploy to TestFlight
    runs-on: macos-15
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
        with:
          fetch-depth: 1


      - name: Install SSH Key
        uses: webfactory/ssh-agent@v0.9.1
        with:
          ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}


      - name: Set up Ruby
        uses: ruby/setup-ruby@v1
        with:
          ruby-version: "3.0"
          bundler-cache: true
          bundler: "2.4.22"


      - name: Set up Flutter
        uses: subosito/flutter-action@v2
        with:
          flutter-version: "3.29.2"
          channel: "stable"


      - name: Install Flutter dependencies
        run: flutter pub get


      - name: Install Ruby dependencies
        working-directory: ./ios
        run: bundle install


      - name: Decode App Store Connect API Key
        run: |
          echo "${{ secrets.ASC_KEY_BASE64 }}" | base64 --decode > ./ios/AuthKey.p8


      - name: Build and upload to TestFlight
        working-directory: ./ios
        env:
          ASC_KEY_ID: ${{ secrets.ASC_KEY_ID }}
          ASC_ISSUER_ID: ${{ secrets.ASC_ISSUER_ID }}
          ASC_KEY_PATH: ./AuthKey.p8
          MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
        run: bundle exec fastlane upload_to_testflight_function

name: iOS TestFlight Deployment
on:
  workflow_dispatch:
jobs:
  deploy-to-testflight:
    name: Build and deploy to TestFlight
    runs-on: macos-15
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
        with:
          fetch-depth: 1


      - name: Install SSH Key
        uses: webfactory/ssh-agent@v0.9.1
        with:
          ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}


      - name: Set up Ruby
        uses: ruby/setup-ruby@v1
        with:
          ruby-version: "3.0"
          bundler-cache: true
          bundler: "2.4.22"


      - name: Set up Flutter
        uses: subosito/flutter-action@v2
        with:
          flutter-version: "3.29.2"
          channel: "stable"


      - name: Install Flutter dependencies
        run: flutter pub get


      - name: Install Ruby dependencies
        working-directory: ./ios
        run: bundle install


      - name: Decode App Store Connect API Key
        run: |
          echo "${{ secrets.ASC_KEY_BASE64 }}" | base64 --decode > ./ios/AuthKey.p8


      - name: Build and upload to TestFlight
        working-directory: ./ios
        env:
          ASC_KEY_ID: ${{ secrets.ASC_KEY_ID }}
          ASC_ISSUER_ID: ${{ secrets.ASC_ISSUER_ID }}
          ASC_KEY_PATH: ./AuthKey.p8
          MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
        run: bundle exec fastlane upload_to_testflight_function

name: iOS TestFlight Deployment
on:
  workflow_dispatch:
jobs:
  deploy-to-testflight:
    name: Build and deploy to TestFlight
    runs-on: macos-15
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
        with:
          fetch-depth: 1


      - name: Install SSH Key
        uses: webfactory/ssh-agent@v0.9.1
        with:
          ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}


      - name: Set up Ruby
        uses: ruby/setup-ruby@v1
        with:
          ruby-version: "3.0"
          bundler-cache: true
          bundler: "2.4.22"


      - name: Set up Flutter
        uses: subosito/flutter-action@v2
        with:
          flutter-version: "3.29.2"
          channel: "stable"


      - name: Install Flutter dependencies
        run: flutter pub get


      - name: Install Ruby dependencies
        working-directory: ./ios
        run: bundle install


      - name: Decode App Store Connect API Key
        run: |
          echo "${{ secrets.ASC_KEY_BASE64 }}" | base64 --decode > ./ios/AuthKey.p8


      - name: Build and upload to TestFlight
        working-directory: ./ios
        env:
          ASC_KEY_ID: ${{ secrets.ASC_KEY_ID }}
          ASC_ISSUER_ID: ${{ secrets.ASC_ISSUER_ID }}
          ASC_KEY_PATH: ./AuthKey.p8
          MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
        run: bundle exec fastlane upload_to_testflight_function

name: iOS TestFlight Deployment
on:
  workflow_dispatch:
jobs:
  deploy-to-testflight:
    name: Build and deploy to TestFlight
    runs-on: macos-15
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
        with:
          fetch-depth: 1


      - name: Install SSH Key
        uses: webfactory/ssh-agent@v0.9.1
        with:
          ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}


      - name: Set up Ruby
        uses: ruby/setup-ruby@v1
        with:
          ruby-version: "3.0"
          bundler-cache: true
          bundler: "2.4.22"


      - name: Set up Flutter
        uses: subosito/flutter-action@v2
        with:
          flutter-version: "3.29.2"
          channel: "stable"


      - name: Install Flutter dependencies
        run: flutter pub get


      - name: Install Ruby dependencies
        working-directory: ./ios
        run: bundle install


      - name: Decode App Store Connect API Key
        run: |
          echo "${{ secrets.ASC_KEY_BASE64 }}" | base64 --decode > ./ios/AuthKey.p8


      - name: Build and upload to TestFlight
        working-directory: ./ios
        env:
          ASC_KEY_ID: ${{ secrets.ASC_KEY_ID }}
          ASC_ISSUER_ID: ${{ secrets.ASC_ISSUER_ID }}
          ASC_KEY_PATH: ./AuthKey.p8
          MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
        run: bundle exec fastlane upload_to_testflight_function

name: iOS TestFlight Deployment
on:
  workflow_dispatch:
jobs:
  deploy-to-testflight:
    name: Build and deploy to TestFlight
    runs-on: macos-15
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
        with:
          fetch-depth: 1


      - name: Install SSH Key
        uses: webfactory/ssh-agent@v0.9.1
        with:
          ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}


      - name: Set up Ruby
        uses: ruby/setup-ruby@v1
        with:
          ruby-version: "3.0"
          bundler-cache: true
          bundler: "2.4.22"


      - name: Set up Flutter
        uses: subosito/flutter-action@v2
        with:
          flutter-version: "3.29.2"
          channel: "stable"


      - name: Install Flutter dependencies
        run: flutter pub get


      - name: Install Ruby dependencies
        working-directory: ./ios
        run: bundle install


      - name: Decode App Store Connect API Key
        run: |
          echo "${{ secrets.ASC_KEY_BASE64 }}" | base64 --decode > ./ios/AuthKey.p8


      - name: Build and upload to TestFlight
        working-directory: ./ios
        env:
          ASC_KEY_ID: ${{ secrets.ASC_KEY_ID }}
          ASC_ISSUER_ID: ${{ secrets.ASC_ISSUER_ID }}
          ASC_KEY_PATH: ./AuthKey.p8
          MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
        run: bundle exec fastlane upload_to_testflight_function

What Happens When the Workflow Runs

When the iOS TestFlight Deployment workflow is triggered manually via GitHub Actions, it follows a defined series of steps to build and distribute your iOS app using TestFlight:

1. Сheckout the Repository

The workflow checks out the code from your GitHub repository so the runner has access to all project files.

2. Install the SSH Key

Installs the SSH key stored in GitHub Secrets to allow secure access to your private repository for provisioning profiles and certificates.

3. Set Up Ruby, Bundler, and Flutter

  • Installs the specified Ruby version and Bundler (with caching enabled).
  • Sets up the specified Flutter version

4. Install Project Dependencies

  • Runs flutter pub get to fetch all Flutter package dependencies.
  • Runs bundle install in the ios directory to install all required Ruby gems (including Fastlane and its plugins).

5. Decode App Store Connect API Key

Takes a base64-encoded App Store Connect API key from GitHub Secrets, decodes it, and writes it as AuthKey.p8 to be used for authentication with Apple Developer APIs.

6. Build and Upload to TestFlight

Runs the Fastlane lane upload_to_testflight_function, which performs the following tasks:

  • Downloads certificates and provisioning profiles using Match.
  • Builds the iOS application.
  • Authenticates with App Store Connect using the decoded API key.
  • Uploads the app build to TestFlight for internal or external beta testing.

How to Run the GitHub Action Manually

Ensure that your workflow YAML file (ios-testflight.yml) is committed and pushed to the default branch of your repository (typically main or master).

Once it is available in the default branch, you can manually trigger the GitHub Action from the GitHub interface:

1. Access GitHub Actions

  • Go to your GitHub repository.
  • Click on the Actions tab at the top of your repository page.

2. Select the Workflow

  • On the left sidebar, you’ll see a list of workflows. Find and click on iOS TestFlight Deployment.

3. Trigger the Workflow

  • Click on the Run workflow button on the right side of the page.
  • Make sure to select the correct branch if it’s not set to the default.
  • Then, click Run workflow to trigger the build process.

4. Monitor the Workflow Execution

  • After the workflow is triggered, you can monitor its progress directly in the Actions tab. You’ll see logs of each step and can inspect any errors or issues that arise.
  • If the build is successful, the app will be deployed to TestFlight!

Feel free to contact TBR Group and articulate any concept directly with the dev team.

Share your idea with us, and we’ll come up with the best development solution for your case.

Get in touch today