Flutter开发实战之CI/CD与发布流程

第12章:CI/CD与发布流程

在前面的章节中,我们学习了Flutter应用开发的各个方面,从基础UI构建到复杂的状态管理,从网络请求到本地存储。现在,我们将探讨一个同样重要但常被忽视的话题:如何将我们精心开发的应用高效、可靠地发布到各大应用商店。

想象一下,你花费了数月时间开发出一款功能完善的Flutter应用,但每次发布新版本时都需要手动打包、签名、上传,这不仅耗时耗力,还容易出错。本章将带你建立一套完整的自动化发布流程,让发布应用变得像点击一个按钮一样简单。

12.1 多环境配置管理

在实际开发中,我们通常需要维护多个环境:开发环境(dev)、测试环境(test)、预发布环境(staging)和生产环境(prod)。每个环境可能使用不同的API地址、数据库连接、第三方服务密钥等配置。

12.1.1 环境配置的重要性

为什么需要多环境配置?想象一下这样的场景:

  • 开发环境:使用本地或开发服务器的API,可以随意测试和调试
  • 测试环境:使用稳定的测试数据,供QA团队进行功能测试
  • 预发布环境:与生产环境配置几乎相同,用于最终验证
  • 生产环境:真实用户使用的环境,配置最为严格

如果没有合理的环境配置管理,你可能会遇到以下问题:

  • 开发时误连生产数据库,造成数据污染
  • 测试环境的配置意外发布到生产环境
  • 不同环境的切换需要手动修改代码

12.1.2 创建环境配置文件

首先,我们在项目根目录下创建不同环境的配置文件:

// lib/config/app_config.dart
class AppConfig {
   
   
  static const String appName = String.fromEnvironment('APP_NAME', defaultValue: 'MyApp');
  static const String apiBaseUrl = String.fromEnvironment('API_BASE_URL', defaultValue: 'https://api.example.com');
  static const String environment = String.fromEnvironment('ENVIRONMENT', defaultValue: 'dev');
  static const bool enableDebugMode = bool.fromEnvironment('DEBUG_MODE', defaultValue: true);
  static const String analyticsKey = String.fromEnvironment('ANALYTICS_KEY', defaultValue: '');
  
  // 环境判断方法
  static bool get isDevelopment => environment == 'dev';
  static bool get isProduction => environment == 'prod';
  static bool get isStaging => environment == 'staging';
  
  // 获取完整的API URL
  static String getApiUrl(String endpoint) {
   
   
    return '$apiBaseUrl$endpoint';
  }
}

然后创建环境特定的配置文件:

// lib/config/environments/dev_config.dart
class DevConfig {
   
   
  static const Map<String, String> config = {
   
   
    'APP_NAME': 'MyApp Dev',
    'API_BASE_URL': 'https://dev-api.example.com',
    'ENVIRONMENT': 'dev',
    'DEBUG_MODE': 'true',
    'ANALYTICS_KEY': 'dev_analytics_key',
  };
}

// lib/config/environments/prod_config.dart
class ProdConfig {
   
   
  static const Map<String, String> config = {
   
   
    'APP_NAME': 'MyApp',
    'API_BASE_URL': 'https://api.example.com',
    'ENVIRONMENT': 'prod',
    'DEBUG_MODE': 'false',
    'ANALYTICS_KEY': 'prod_analytics_key',
  };
}

12.1.3 使用环境变量启动应用

为了在不同环境下启动应用,我们需要修改启动脚本。在项目根目录创建启动脚本:

# scripts/run_dev.sh
#!/bin/bash
flutter run --dart-define=APP_NAME="MyApp Dev" \
           --dart-define=API_BASE_URL="https://dev-api.example.com" \
           --dart-define=ENVIRONMENT="dev" \
           --dart-define=DEBUG_MODE="true"

# scripts/run_prod.sh
#!/bin/bash
flutter run --release \
           --dart-define=APP_NAME="MyApp" \
           --dart-define=API_BASE_URL="https://api.example.com" \
           --dart-define=ENVIRONMENT="prod" \
           --dart-define=DEBUG_MODE="false"

在应用中使用配置:

// lib/main.dart
import 'package:flutter/material.dart';
import 'config/app_config.dart';

void main() {
   
   
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
   
   
  
  Widget build(BuildContext context) {
   
   
    return MaterialApp(
      title: AppConfig.appName,
      debugShowCheckedModeBanner: AppConfig.enableDebugMode,
      home: HomeScreen(),
    );
  }
}

// lib/services/api_service.dart
import '../config/app_config.dart';

class ApiService {
   
   
  static Future<Map<String, dynamic>> fetchUserData() async {
   
   
    final url = AppConfig.getApiUrl('/users/profile');
    
    if (AppConfig.isDevelopment) {
   
   
      print('DEV: Fetching from $url');
    }
    
    // 网络请求逻辑
    // ...
  }
}

12.2 代码签名与证书配置

代码签名是移动应用发布的关键步骤,它确保应用的完整性和来源可信度。简单来说,代码签名就像是给你的应用盖上一个官方印章,证明这个应用确实是你开发的,并且没有被恶意篡改。

12.2.1 Android代码签名

Android使用密钥库(keystore)进行应用签名。我们需要创建一个签名密钥并配置构建脚本。

创建签名密钥
# 创建密钥库文件
keytool -genkey -v -keystore ~/my-release-key.keystore \
        -alias my-key-alias \
        -keyalg RSA \
        -keysize 2048 \
        -validity 10000

这个命令会询问你一系列问题,包括密码、组织信息等。请务必记住密码和别名,并将密钥库文件保存在安全的地方。

配置签名信息

android/app/build.gradle文件中配置签名信息:

android {
    ...
    
    signingConfigs {
        release {
            if (project.hasProperty('myapp.signing.keystore')) {
                storeFile file(project.property('myapp.signing.keystore'))
                storePassword project.property('myapp.signing.store_password')
                keyAlias project.property('myapp.signing.key_alias')
                keyPassword project.property('myapp.signing.key_password')
            }
        }
    }
    
    buildTypes {
        release {
            signingConfig signingConfigs.release
            minifyEnabled true
            useProguard true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

创建android/gradle.properties文件存储签名配置:

# 签名配置(敏感信息,不要提交到版本控制)
myapp.signing.keystore=../my-release-key.keystore
myapp.signing.store_password=your_store_password
myapp.signing.key_alias=my-key-alias
myapp.signing.key_password=your_key_password

重要提醒: gradle.properties文件包含敏感信息,应该添加到.gitignore文件中,避免提交到版本控制系统。

12.2.2 iOS代码签名

iOS的代码签名相对复杂,需要在苹果开发者中心配置证书、标识符和描述文件。

配置开发者账号
  1. 注册Apple Developer账号:访问developer.apple.com注册账号(年费99美元)
  2. 创建App ID:在开发者中心创建应用标识符
  3. 生成证书:创建开发和发布证书
  4. 创建Provisioning Profile:关联证书、设备和App ID
在Xcode中配置签名

打开ios/Runner.xcworkspace,在Xcode中配置签名:

Target: Runner
-> Signing & Capabilities
-> Team: 选择你的开发团队
-> Bundle Identifier: 输入你的应用包名

对于自动化构建,我们还需要配置ios/Runner/Info.plist

<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleDisplayName</key>
<string>$(APP_DISPLAY_NAME)</string>

12.2.3 证书管理最佳实践

  1. 使用环境变量存储敏感信息
# 在CI/CD系统中设置环境变量
export ANDROID_KEYSTORE_PASSWORD="your_password"
export IOS_CERTIFICATE_PASSWORD="your_password"
  1. 定期更新证书

    • Android密钥库建议25年有效期
    • iOS证书每年需要更新
  2. 备份重要文件

    • 密钥库文件
    • 证书文件
    • 密码信息
  3. 使用专用的签名服务器
    对于企业级应用,考虑使用专门的签名服务器,避免在开发机器上存储生产环境的签名证书。

12.3 GitHub Actions自动化构建

GitHub Actions是GitHub提供的CI/CD服务,可以自动化构建、测试和部署流程。对于Flutter项目,我们可以配置Actions来自动构建Android和iOS应用。

12.3.1 创建基础工作流

在项目根目录创建.github/workflows/build.yml文件:

name: Build and Test

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

jobs:
  test:
    runs-on: ubuntu-latest
    
    steps:
    - uses: actions/checkout@v3
    
    - name: Setup Flutter
      uses: subosito/flutter-action@v2
      with:
        flutter-version: '3.16.0'
        
    - name: Install dependencies
      run: flutter pub get
      
    - name: Run tests
      run: flutter test
      
    - name: Analyze code
      run: flutter analyze

  build-android:
    needs: test
    runs-on: ubuntu-latest
    
    steps:
    - uses: actions/checkout@v3
    
    - name: Setup Flutter
      uses: subosito/flutter-action@v2
      with:
        flutter-version: '3.16.0'
        
    - name: Install dependencies
      run: flutter pub get
      
    - name: Build APK
      run: flutter build apk --release
      
    - name: Upload APK
      uses: actions/upload-artifact@v3
      with:
        name: release-apk
        path: build/app/outputs/flutter-apk/app-release.apk

  build-ios:
    needs: test
    runs-on: macos-latest
    
    steps:
    - uses: actions/checkout@v3
    
    - name: Setup Flutter
      uses: subosito/flutter-action@v2
      with:
        flutter-version: '3.16.0'
        
    - name: Install dependencies
      run: flutter pub get
      
    - name: Build iOS
      run: flutter build ios --release --no-codesign
      
    - name: Upload iOS build
      uses: actions/upload-artifact@v3
      with:
        name: release-ios
        path: build/ios/iphoneos/Runner.app

12.3.2 配置签名自动化

为了在CI/CD中进行签名,我们需要将签名文件和密码作为secrets存储在GitHub中。

Android签名配置
  1. 上传密钥库文件
# 将密钥库文件转换为base64编码
base64 my-release-key.keystore > keystore.base64
  1. 在GitHub设置secrets

    • ANDROID_KEYSTORE_BASE64:密钥库文件的base64编码
    • ANDROID_KEYSTORE_PASSWORD:密钥库密码
    • ANDROID_KEY_ALIAS:密钥别名
    • ANDROID_KEY_PASSWORD:密钥密码
  2. 更新工作流配置

  build-android-signed:
    needs: test
    runs-on: ubuntu-latest
    
    steps:
    - uses: actions/checkout@v3
    
    - name: Setup Flutter
      uses: subosito/flutter-action@v2
      with:
        flutter-version: '3.16.0'
        
    - name: Install dependencies
      run: flutter pub get
      
    - name: Decode keystore
      run: |
        echo "${
   
   { secrets.ANDROID_KEYSTORE_BASE64 }}" | base64 --decode > android/app/my-release-key.keystore
        
    - name: Create key.properties
      run: |
        cat > android/key.properties << EOF
        storePassword=${
   
   { secrets.ANDROID_KEYSTORE_PASSWORD }}
        keyPassword=${
   
   { secrets.ANDROID_KEY_PASSWORD }}
        keyAlias=${
   
   { secrets.ANDROID_KEY_ALIAS }}
        storeFile=my-release-key.keystore
        EOF
        
    - name: Build signed APK
      run: flutter build apk --release
      
    - name: Build App Bundle
      run: flutter build appbundle --release
iOS签名配置

iOS的签名配置更加复杂,需要配置证书和描述文件:

  build-ios-signed:
    needs: test
    runs-on: macos-latest
    
    steps:
    - uses: actions/checkout@v3
    
    - name: Setup Flutter
      uses: subosito/flutter-action@v2
      with:
        flutter-version: '3.16.0'
        
    - name: Install dependencies
      run: flutter pub get
      
    - name: Import certificates
      <
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值