Setting Up Continuous Integration/Continuous Deployment (CI/CD) Pipelines for Flutter Projects

Continuous Integration/Continuous Deployment (CI/CD) pipelines are essential for modern software development, particularly for mobile applications like those built with Flutter. Implementing CI/CD ensures that code changes are automatically built, tested, and deployed, leading to faster release cycles, reduced errors, and improved collaboration among developers. This article delves into how to set up effective CI/CD pipelines for Flutter projects, providing step-by-step guidance and best practices.

What is CI/CD?

Continuous Integration (CI) is the practice of automating the integration of code changes from multiple contributors into a single project. Automated tests are run to verify the changes before integration.

Continuous Deployment (CD) automates the release of software to the end-users. This involves automatically building, testing, and deploying code changes to staging or production environments.

Benefits of CI/CD for Flutter Projects

  • Faster Release Cycles: Automate repetitive tasks to deploy more frequently.
  • Reduced Errors: Automated testing catches bugs early.
  • Improved Collaboration: Developers can merge code confidently.
  • Increased Efficiency: Automation frees up developers to focus on new features.

Tools and Services for Flutter CI/CD Pipelines

  • Codemagic: A CI/CD platform built specifically for Flutter apps.
  • GitHub Actions: Native CI/CD service within GitHub.
  • Bitrise: Mobile CI/CD platform that supports Flutter.
  • Jenkins: Open-source automation server.
  • GitLab CI/CD: Integrated CI/CD within GitLab.

Setting Up CI/CD Pipeline Using Codemagic

Codemagic is tailored for Flutter, making it a popular choice. Here’s how to set it up:

Step 1: Connect Your Repository

  1. Sign up or log in to Codemagic.
  2. Connect your repository (e.g., GitHub, GitLab, Bitbucket).
  3. Select your Flutter project.

Step 2: Configure the Build Pipeline

  1. Configure the build settings in the Codemagic UI:
    • Branch: Select the branch you want to build (e.g., main or develop).
    • Build Trigger: Set up triggers based on Git events (e.g., push, pull request).
    • Flutter SDK version: Select the appropriate Flutter SDK version.
    • Build Mode: Choose the build mode (debug, release, profile).
  2. Add steps to your workflow in codemagic.yaml file or the Codemagic UI.

Step 3: Add Testing

Incorporate automated tests into your CI/CD pipeline to catch bugs early.


workflows:
  android-workflow:
    name: Android Workflow
    instance_type: mac_mini_m1
    max_build_duration: 120
    environment:
      flutter: stable
      android_sdk: '33'
    triggering:
      events:
        - push
        - tag
      branch_patterns:
        - pattern: develop
          include: true
        - pattern: main
          include: true
    scripts:
      - name: Flutter analyze
        script: flutter analyze
      - name: Flutter test
        script: flutter test
      - name: Flutter build apk
        script: flutter build apk --split-per-abi
    artifacts:
      - build/app/outputs/apk/**/*.apk
    publishing:
      email:
        recipients:
          - email@example.com
        notify_on_build_start: true
        notify_on_build_success: true
        notify_on_build_failure: true

This configuration runs flutter analyze and flutter test during each build.

Step 4: Set Up Code Signing

For releasing to app stores, code signing is necessary.

iOS Code Signing
  1. Automatic Code Signing: Let Codemagic handle code signing using App Store Connect API key.
  2. Manual Code Signing: Upload your provisioning profile and certificate to Codemagic.
Android Code Signing
  1. Upload your keystore file to Codemagic.
  2. Define the necessary environment variables in the Codemagic UI or codemagic.yaml:
    • CM_KEYSTORE_PATH: Path to the keystore file.
    • CM_KEYSTORE_PASSWORD: Keystore password.
    • CM_KEY_ALIAS: Key alias.
    • CM_KEY_PASSWORD: Key password.

Step 5: Publish Your App

Publishing involves deploying your app to app stores or other distribution channels.

Publish to Google Play Store
  1. Create a service account in the Google Play Console.
  2. Grant the necessary permissions to the service account.
  3. Upload the service account JSON key to Codemagic.
  4. Configure the publishing step in your codemagic.yaml:

publishing:
  google_play:
    service_account_json_path: credentials/google-play-service-account.json
    package_name: com.example.myapp
    track: alpha
Publish to App Store Connect
  1. Create an API key in App Store Connect.
  2. Upload the API key to Codemagic.
  3. Configure the publishing step:

publishing:
  app_store_connect:
    api_key_path: credentials/appstoreconnect-api-key.json
    issuer_id: YOUR_ISSUER_ID
    key_id: YOUR_KEY_ID

Setting Up CI/CD Pipeline Using GitHub Actions

GitHub Actions is a versatile tool for automating workflows directly within your GitHub repository.

Step 1: Create a GitHub Actions Workflow

  1. Create a new file in your repository at .github/workflows/ci_cd.yaml.

Step 2: Define the Workflow

Define the workflow using YAML syntax.


name: Flutter CI/CD

on:
  push:
    branches: [ main, develop ]
  pull_request:
    branches: [ main ]

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-java@v3
        with:
          distribution: 'zulu'
          java-version: '11'

      - uses: subosito/flutter-action@v2
        with:
          flutter-version: '3.0.0'

      - run: flutter pub get
      - run: flutter analyze
      - run: flutter test

      - name: Build APK
        run: flutter build apk --split-per-abi

      - name: Upload APK
        uses: actions/upload-artifact@v3
        with:
          name: app-release
          path: build/app/outputs/apk/release

Key aspects of this workflow:

  • Triggers: Runs on push events to main and develop branches, and pull requests to main.
  • Environment: Uses ubuntu-latest as the runner environment.
  • Steps:
    • Checks out the code.
    • Sets up Java and Flutter.
    • Gets Flutter dependencies.
    • Analyzes and tests the Flutter code.
    • Builds an APK.
    • Uploads the APK as an artifact.

Step 3: Code Signing for Android (GitHub Actions)

  1. Store your keystore file as a GitHub secret.
  2. Add environment variables for keystore details as secrets:
    • KEYSTORE_FILE
    • KEYSTORE_PASSWORD
    • KEY_ALIAS
    • KEY_PASSWORD
  3. Modify the workflow to include code signing steps:

      - name: Set up signing properties
        run: |
          echo "${{ secrets.KEYSTORE_FILE }}" | base64 --decode > android/app/keystore.jks
          echo "storePassword=${{ secrets.KEYSTORE_PASSWORD }}" > android/key.properties
          echo "keyPassword=${{ secrets.KEY_PASSWORD }}" >> android/key.properties
          echo "keyAlias=${{ secrets.KEY_ALIAS }}" >> android/key.properties

      - name: Build APK
        run: flutter build apk --split-per-abi

      - name: Upload APK
        uses: actions/upload-artifact@v3
        with:
          name: app-release
          path: build/app/outputs/apk/release

Step 4: Deploy to Google Play Store (GitHub Actions)

  1. Add a service account JSON file as a GitHub secret.
  2. Use the google-github-actions/deploy-serviceaccount action:

      - name: Deploy to Google Play Store
        uses: r0adkll/upload-google-play@v1
        with:
          serviceAccountJsonPlainText: ${{ secrets.SERVICE_ACCOUNT_JSON }}
          packageName: com.example.myapp
          releaseFiles: build/app/outputs/apk/release/app-release.apk
          track: alpha

Best Practices for Flutter CI/CD

  • Automate Everything: Automate all repetitive tasks, from testing to deployment.
  • Use Environment Variables: Avoid hardcoding sensitive information; use environment variables.
  • Cache Dependencies: Cache Flutter dependencies to speed up build times.
  • Implement Thorough Testing: Include unit, widget, and integration tests.
  • Monitor Your Pipeline: Continuously monitor the health and performance of your CI/CD pipeline.
  • Secure Your Credentials: Use secure methods to store and access credentials, like GitHub Secrets or environment variables in CI/CD tools.

Conclusion

Setting up robust CI/CD pipelines is crucial for Flutter projects to ensure faster, more reliable, and efficient software releases. By using tools like Codemagic or GitHub Actions, and following best practices, developers can automate their workflows, reduce errors, and focus on creating great applications. Whether you’re a small team or a large enterprise, integrating CI/CD will undoubtedly streamline your Flutter development process and lead to improved outcomes.