Chuyển đến nội dung chính

Bài 21: CI/CD & App Signing

Android keystore & app signing. iOS certificates & provisioning profiles. GitHub Actions cho Flutter. Fastlane automation. Codemagic, App Center. Flavor/Environment configs.

💻 Lập trình — Bài 21 Bài 21: CI/CD & App Signing

Flutter & Dart: Từ Cơ bản đến Nâng cao

Phần 6: Testing, CI/CD & Production

xdev.asia

1. Android App Signing

# Tạo keystore
keytool -genkey -v -keystore upload-keystore.jks \
  -keyalg RSA -keysize 2048 -validity 10000 \
  -alias upload
# android/key.properties (KHÔNG commit vào git)
storePassword=your_store_password
keyPassword=your_key_password
keyAlias=upload
storeFile=../upload-keystore.jks
// android/app/build.gradle
def keystoreProperties = new Properties()
def keystorePropertiesFile = rootProject.file('key.properties')
if (keystorePropertiesFile.exists()) {
    keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
}

android {
    signingConfigs {
        release {
            keyAlias keystoreProperties['keyAlias']
            keyPassword keystoreProperties['keyPassword']
            storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
            storePassword keystoreProperties['storePassword']
        }
    }
    buildTypes {
        release {
            signingConfig signingConfigs.release
            minifyEnabled true
            shrinkResources true
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
}

2. Build Flavors

// lib/config/env.dart
enum Environment { dev, staging, production }

class EnvConfig {
  final String apiBaseUrl;
  final String appName;
  final Environment environment;

  const EnvConfig({required this.apiBaseUrl, required this.appName, required this.environment});

  static const dev = EnvConfig(
    apiBaseUrl: 'https://dev-api.example.com',
    appName: 'MyApp Dev',
    environment: Environment.dev,
  );

  static const production = EnvConfig(
    apiBaseUrl: 'https://api.example.com',
    appName: 'MyApp',
    environment: Environment.production,
  );
}
# Run with flavor
flutter run --dart-define=ENV=dev
flutter build apk --dart-define=ENV=production

3. GitHub Actions CI/CD

# .github/workflows/flutter.yml
name: Flutter CI/CD

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

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: subosito/flutter-action@v2
        with:
          flutter-version: '3.24.0'
          channel: stable
      - run: flutter pub get
      - run: flutter analyze
      - run: flutter test --coverage
      - uses: codecov/codecov-action@v4
        with:
          file: coverage/lcov.info

  build-android:
    needs: test
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'
    steps:
      - uses: actions/checkout@v4
      - uses: subosito/flutter-action@v2
        with:
          flutter-version: '3.24.0'
      - run: flutter build appbundle --release --dart-define=ENV=production
      - uses: actions/upload-artifact@v4
        with:
          name: app-release
          path: build/app/outputs/bundle/release/app-release.aab

  build-ios:
    needs: test
    runs-on: macos-latest
    if: github.ref == 'refs/heads/main'
    steps:
      - uses: actions/checkout@v4
      - uses: subosito/flutter-action@v2
        with:
          flutter-version: '3.24.0'
      - run: flutter build ipa --release --dart-define=ENV=production --no-codesign

4. Fastlane Automation

# android/fastlane/Fastfile
default_platform(:android)

platform :android do
  lane :deploy do
    upload_to_play_store(
      track: 'internal',
      aab: '../build/app/outputs/bundle/release/app-release.aab',
    )
  end
end
# ios/fastlane/Fastfile
default_platform(:ios)

platform :ios do
  lane :deploy do
    build_app(workspace: "Runner.xcworkspace", scheme: "Runner")
    upload_to_testflight
  end
end

Bài tiếp theo: Publish lên App Store & Google Play.