Game Framework
Flutter Integration

CI/CD Integration

Automate your Flutter package publishing and dependency management with CI/CD pipelines.

Prerequisites

Before setting up CI/CD:

  1. Create API Key with publishing permissions
  2. Add key as secret in your CI/CD platform
  3. Configure pub-tokens.json in pipeline

GitHub Actions

Basic Publishing Workflow

Create .github/workflows/publish.yml:

name: Publish Package

on:
  push:
    tags:
      - 'v*'  # Trigger on version tags

jobs:
  publish:
    runs-on: ubuntu-latest
    
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
      
      - name: Setup Flutter
        uses: subosito/flutter-action@v2
        with:
          flutter-version: '3.16.0'
          channel: 'stable'
      
      - name: Install dependencies
        run: flutter pub get
      
      - name: Run tests
        run: flutter test
      
      - name: Analyze code
        run: flutter analyze
      
      - name: Setup credentials
        env:
          GF_PUB_TOKEN: ${{ secrets.GAME_FRAMEWORK_TOKEN }}
        run: |
          mkdir -p ~/.pub-cache
          cat > ~/.pub-cache/pub-tokens.json << EOF
          {
            "version": 1,
            "hosted": [
              {
                "url": "https://registry.yourcompany.com",
                "token": "\${GF_PUB_TOKEN}",
                "env": "GF_PUB_TOKEN"
              }
            ]
          }
          EOF
      
      - name: Publish to Game Framework
        env:
          GF_PUB_TOKEN: ${{ secrets.GAME_FRAMEWORK_TOKEN }}
        run: |
          flutter pub publish --force --server=https://registry.yourcompany.com

Advanced Workflow with Matrix

Test on multiple Flutter versions:

name: Test and Publish

on:
  pull_request:
  push:
    branches: [main]
    tags: ['v*']

jobs:
  test:
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [ubuntu-latest, macos-latest, windows-latest]
        flutter-version: ['3.13.0', '3.16.0']
    
    steps:
      - uses: actions/checkout@v4
      
      - uses: subosito/flutter-action@v2
        with:
          flutter-version: ${{ matrix.flutter-version }}
      
      - run: flutter pub get
      - run: flutter test
      - run: flutter analyze

  publish:
    needs: test
    if: startsWith(github.ref, 'refs/tags/v')
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v4
      
      - uses: subosito/flutter-action@v2
        with:
          flutter-version: '3.16.0'
      
      - name: Publish
        env:
          GF_PUB_TOKEN: ${{ secrets.GAME_FRAMEWORK_TOKEN }}
        run: |
          mkdir -p ~/.pub-cache
          echo '{"version":1,"hosted":[{"url":"https://registry.yourcompany.com","token":"${GF_PUB_TOKEN}","env":"GF_PUB_TOKEN"}]}' > ~/.pub-cache/pub-tokens.json
          flutter pub get
          flutter pub publish --force --server=https://registry.yourcompany.com

Monorepo Publishing

Publish multiple packages:

name: Publish Packages

on:
  push:
    tags: ['v*']

jobs:
  publish:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        package: [auth, ui, utils, core]
    
    steps:
      - uses: actions/checkout@v4
      
      - uses: subosito/flutter-action@v2
        with:
          flutter-version: '3.16.0'
      
      - name: Publish ${{ matrix.package }}
        env:
          GF_PUB_TOKEN: ${{ secrets.GAME_FRAMEWORK_TOKEN }}
        working-directory: packages/${{ matrix.package }}
        run: |
          mkdir -p ~/.pub-cache
          echo '{"version":1,"hosted":[{"url":"https://registry.yourcompany.com","token":"${GF_PUB_TOKEN}","env":"GF_PUB_TOKEN"}]}' > ~/.pub-cache/pub-tokens.json
          flutter pub get
          flutter pub publish --force --server=https://registry.yourcompany.com

GitLab CI

Basic Configuration

Create .gitlab-ci.yml:

stages:
  - test
  - publish

variables:
  FLUTTER_VERSION: "3.16.0"

before_script:
  - apt-get update -qq && apt-get install -y -qq git curl unzip
  - git clone https://github.com/flutter/flutter.git -b stable --depth 1
  - export PATH="$PATH:`pwd`/flutter/bin"
  - flutter --version

test:
  stage: test
  image: cirrusci/flutter:stable
  script:
    - flutter pub get
    - flutter test
    - flutter analyze
  only:
    - branches
    - tags

publish:
  stage: publish
  image: cirrusci/flutter:stable
  script:
    - export GF_PUB_TOKEN=$GAME_FRAMEWORK_TOKEN
    - mkdir -p ~/.pub-cache
    - echo '{"version":1,"hosted":[{"url":"https://registry.yourcompany.com","token":"${GF_PUB_TOKEN}","env":"GF_PUB_TOKEN"}]}' > ~/.pub-cache/pub-tokens.json
    - flutter pub get
    - flutter pub publish --force --server=https://registry.yourcompany.com
  only:
    - tags
  environment:
    name: production

With Docker

publish:
  stage: publish
  image: google/dart:latest
  services:
    - docker:dind
  before_script:
    - git clone https://github.com/flutter/flutter.git -b stable
    - export PATH="$PATH:$PWD/flutter/bin"
  script:
    - flutter pub get
    - flutter test
    - |
      mkdir -p ~/.pub-cache
      cat > ~/.pub-cache/pub-tokens.json << EOF
      {
        "version": 1,
        "hosted": [
          {
            "url": "https://registry.yourcompany.com",
            "token": "\${GF_PUB_TOKEN}",
            "env": "GF_PUB_TOKEN"
          }
        ]
      }
      EOF
    - flutter pub publish --force --server=https://registry.yourcompany.com
  only:
    - tags

Jenkins

Jenkinsfile

pipeline {
    agent any
    
    environment {
        GF_PUB_TOKEN = credentials('game-framework-token')
        FLUTTER_HOME = '/opt/flutter'
        PATH = "${FLUTTER_HOME}/bin:${env.PATH}"
    }
    
    stages {
        stage('Setup') {
            steps {
                sh '''
                    flutter --version
                    flutter pub get
                '''
            }
        }
        
        stage('Test') {
            steps {
                sh '''
                    flutter test
                    flutter analyze
                '''
            }
        }
        
        stage('Publish') {
            when {
                tag "v*"
            }
            steps {
                sh '''
                    mkdir -p ~/.pub-cache
                    cat > ~/.pub-cache/pub-tokens.json << EOF
{
  "version": 1,
  "hosted": [
    {
      "url": "https://registry.yourcompany.com",
      "token": "${GF_PUB_TOKEN}",
      "env": "GF_PUB_TOKEN"
    }
  ]
}
EOF
                    flutter pub publish --force --server=https://registry.yourcompany.com
                '''
            }
        }
    }
    
    post {
        always {
            cleanWs()
        }
    }
}

CircleCI

.circleci/config.yml

version: 2.1

orbs:
  flutter: circleci/flutter@2.0.0

jobs:
  test:
    executor:
      name: flutter/default
      flutter-version: "3.16.0"
    steps:
      - checkout
      - flutter/install_sdk_and_pub
      - run: flutter test
      - run: flutter analyze
  
  publish:
    executor:
      name: flutter/default
      flutter-version: "3.16.0"
    steps:
      - checkout
      - flutter/install_sdk_and_pub
      - run:
          name: Setup credentials
          command: |
            mkdir -p ~/.pub-cache
            echo '{"version":1,"hosted":[{"url":"https://registry.yourcompany.com","token":"${GF_PUB_TOKEN}","env":"GF_PUB_TOKEN"}]}' > ~/.pub-cache/pub-tokens.json
      - run:
          name: Publish package
          command: flutter pub publish --force --server=https://registry.yourcompany.com

workflows:
  test-and-publish:
    jobs:
      - test
      - publish:
          requires:
            - test
          filters:
            tags:
              only: /^v.*/
            branches:
              ignore: /.*/

Azure DevOps

azure-pipelines.yml

trigger:
  tags:
    include:
      - v*

pool:
  vmImage: 'ubuntu-latest'

variables:
  FLUTTER_VERSION: '3.16.0'

steps:
- task: FlutterInstall@0
  inputs:
    channel: 'stable'
    version: $(FLUTTER_VERSION)

- script: flutter pub get
  displayName: 'Install dependencies'

- script: flutter test
  displayName: 'Run tests'

- script: flutter analyze
  displayName: 'Analyze code'

- script: |
    mkdir -p ~/.pub-cache
    cat > ~/.pub-cache/pub-tokens.json << EOF
    {
      "version": 1,
      "hosted": [
        {
          "url": "https://registry.yourcompany.com",
          "token": "\$(GF_PUB_TOKEN)",
          "env": "GF_PUB_TOKEN"
        }
      ]
    }
    EOF
    flutter pub publish --force --server=https://registry.yourcompany.com
  env:
    GF_PUB_TOKEN: $(GAME_FRAMEWORK_TOKEN)
  displayName: 'Publish package'

Bitbucket Pipelines

bitbucket-pipelines.yml

image: cirrusci/flutter:stable

pipelines:
  tags:
    v*:
      - step:
          name: Test and Publish
          caches:
            - flutter
          script:
            - flutter --version
            - flutter pub get
            - flutter test
            - flutter analyze
            - mkdir -p ~/.pub-cache
            - echo '{"version":1,"hosted":[{"url":"https://registry.yourcompany.com","token":"${GF_PUB_TOKEN}","env":"GF_PUB_TOKEN"}]}' > ~/.pub-cache/pub-tokens.json
            - flutter pub publish --force --server=https://registry.yourcompany.com

definitions:
  caches:
    flutter: ~/.pub-cache

Docker-based CI

Dockerfile for CI

FROM cirrusci/flutter:stable

# Set environment
ENV PUB_HOSTED_URL=https://registry.yourcompany.com
ENV GF_PUB_TOKEN=${GF_PUB_TOKEN}

# Setup credentials
RUN mkdir -p /root/.pub-cache && \
    echo '{"version":1,"hosted":[{"url":"https://registry.yourcompany.com","token":"${GF_PUB_TOKEN}","env":"GF_PUB_TOKEN"}]}' \
    > /root/.pub-cache/pub-tokens.json

WORKDIR /workspace

# Copy package files
COPY pubspec.* ./
RUN flutter pub get

# Copy source
COPY . .

# Run tests
RUN flutter test && flutter analyze

# Publish
CMD ["flutter", "pub", "publish", "--force", "--server=https://registry.yourcompany.com"]

Best Practices

1. Version Tagging

Always trigger on version tags:

# Create and push tag
git tag -a v1.2.3 -m "Release 1.2.3"
git push origin v1.2.3

2. Dry Run First

Add a dry-run step before publishing:

- name: Dry run
  run: flutter pub publish --dry-run --server=https://registry.yourcompany.com

- name: Publish
  if: startsWith(github.ref, 'refs/tags/v')
  run: flutter pub publish --force --server=https://registry.yourcompany.com

3. Cache Dependencies

Speed up builds with caching:

# GitHub Actions
- name: Cache pub dependencies
  uses: actions/cache@v3
  with:
    path: ~/.pub-cache
    key: ${{ runner.os }}-pub-${{ hashFiles('**/pubspec.lock') }}
    restore-keys: |
      ${{ runner.os}}-pub-

4. Secrets Management

Never hardcode tokens:

# ✗ Bad
env:
  TOKEN: "gf_live_abc123..."

# ✓ Good
env:
  GF_PUB_TOKEN: ${{ secrets.GAME_FRAMEWORK_TOKEN }}

5. Notifications

Add notifications for failures:

- name: Notify on failure
  if: failure()
  uses: 8398a7/action-slack@v3
  with:
    status: ${{ job.status }}
    text: 'Package publish failed!'
    webhook_url: ${{ secrets.SLACK_WEBHOOK }}

Troubleshooting

"Unauthorized" in CI

Check:

  1. Secret is set correctly in CI/CD platform
  2. Secret name matches environment variable
  3. Token has necessary permissions

"pub-tokens.json not found"

Ensure you're creating the file:

mkdir -p ~/.pub-cache
echo '...' > ~/.pub-cache/pub-tokens.json

Version Already Published

Add version check:

# Check if version exists before publishing
VERSION=$(grep '^version:' pubspec.yaml | sed 's/version: //')
if curl -f https://registry.yourcompany.com/v1/packages/my_package/versions/$VERSION; then
  echo "Version $VERSION already published"
  exit 0
fi

Next Steps

Examples Repository: Check out our CI/CD examples repository for more templates.