在开发APP的过程中我们需要频繁地打包、发布测试版本,而且还需要打包Android、Ios不同的平台,每次都用xcode实在是太慢了,因此我们希望每次打包的时候都能自动化。

IOS

手动打包的过程官方文档已经写得很详细了,可以先参考一下。

基本设置

总结一下,主要步骤如下:

  1. xcode 打开 ios/Runner.xcworkspace 文件

  2. 设置签名 在Xcode项目导航器中选择 Runner ,然后,在主视图边栏中,选择 Runnertargetsigning & Capabilities a. 设置 DebugProfile 的签名都用 adhoc 类型的profile b. release 的签名用 distribution 类型的profile

  3. 修改Build Configuration 打开菜单栏 productschemeedit scheme , 然后选择 RunInfoBuild Configuration 改成 release

  4. 修改device为通用设备

自动化打包

上面的设置完成了以后我们开始进行自动化的打包,这需要用到 fastlane ,在MacOs下面直接通过 homebrew 安装即可。

  1. fastlane init

    1
    2
    
    cd ios
    fastlane init
    

    fastlane init 的时候会出现下面的选择界面,直接选 4 manual setup 就可以了,然后后面的直接按回车就可以了。

  2. 安装蒲公英插件

    1
    2
    3
    4
    
    cd ios
    # 增加蒲公英插件
    # cd fastlane这里装插件的时候不需要进入到fastlane目录里面去,在ios目录下面就可以了
    fastlane add_plugin pgyer
    
  3. 编写 Fastfile 文件 下面是我目前使用的 Fastfile 里面的内容

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    
    # coding: utf-8
    # This file contains the fastlane.tools configuration
    # You can find the documentation at https://docs.fastlane.tools
    #
    # For a list of all available actions, check out
    #
    #     https://docs.fastlane.tools/actions
    #
    # For a list of all available plugins, check out
    #
    #     https://docs.fastlane.tools/plugins/available-plugins
    #
    
    # Uncomment the line if you want fastlane to automatically update itself
    # update_fastlane
    
    default_platform(:ios)
    
    platform :ios do
      desc "编译adhoc版本测试"
      lane :adhoc do
        build_app(
          scheme: "Runner",
          workspace: "Runner.xcworkspace",
          output_directory:"~/Downloads/ipa",# ipa导出目录
          output_name: ENV['ipa_name'],
    
          export_options: {
            method: "ad-hoc",
            provisioningProfiles: {
              "net.dieya.scaffold" => "vinurs-adhoc-profile"
            }
          }
        )
        pgyer(api_key: "xxxxx",
              user_key: "xxxxx") # 蒲公英的key
      end
    
      desc "编译正式版"
      lane :release do
        gym(output_name: ENV['ipa_name'], # 导出的ipa名字
            scheme: "Runner",
            # export_method 可以根据打包类型进行相应设置。可选的值有:app-store、ad-hoc、development、enterprise
            export_method: "app-store",
            # 指定编译方式为Debug 还是 Release, 默认为Release,
            configuration: "Release",              # Debug or Release
            clean: true,                           #清空上次打包信息
            include_bitcode: true,
            workspace: "Runner.xcworkspace",
            output_directory:"~/Downloads/ipa")# ipa导出目录
    
      end
    
    end
    

    这个配置主要有下面几个功能 a. 指定ipa文件位置 通过 output_directory 来指定ipa文件位置,每次编译好了以后ipa文件会放在 ~/Downloads/ipa 目录下面 b. 指定ipa文件名 每次编译出来的ipa文件都会使用我们指定的文件名,ipa文件名是通过 ENV['ipa_name'] 变量来控制的,这个变量的存放位置就是 ios/fastlane/.env 文件,内容如下:

    1
    
    ipa_name=scaffold-1.0.1-20210216221517
    

    为了方便,我后面会通过一个脚本每次编译的时候来生成这个文件名。 c. 版本分为adhoc版本以及release版本 我们平时上传到 蒲公英 测试的时候只要编译 adhoc 版本就可以,上传到 AppStore 的时候就编译 release 版本

    ios 目录下面执行 fastlane adhoc 就可以自动编译 adhoc 版本上传到蒲公英了;执行 fastlane release 就可以编译出可以上传到 AppStore 的版本。

    另外为了方便,我们这里直接把fastlane这里生成的文件都加入到版本库里面去进行版本管理。

  4. 版本号管理 APP的每个版本都应该有其对应的版本号,flutter里面通过 ios/Flutter/Generated.xcconfig 文件里面的 FLUTTER_BUILD_NUMBERBUILD_NAME 变量来进行控制,这两个就相当于Xcode里面设置的 BuildVersion

    我们可以通过修改这两个变量来控制APP的版本号,例如 FLUTTER_BUILD_NUMBER 修改成时间戳,就能看出这个版本是什么时间编译的了。

    区别: a. BUILD_NAME 是给用户看的版本号,例如 1.0.1 b. FLUTTER_BUILD_NUMBER 内部版本号,是给我们自己看的,用户看不到这个

  5. 编译脚本 经过了上面的过程,我们就可以把整个过程放到一个脚本里面去进行了,因此我写了下面的脚本来自动编译上传

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    
    #!/usr/bin/env bash
    
    # 编译ios版本
    # 更新build number
    BUILD_NUM=`date '+%Y%m%d%H%M%S'`
    BUILD_NAME='1.0.1'
    sed -i "s/^FLUTTER_BUILD_NUMBER=.*/FLUTTER_BUILD_NUMBER=$BUILD_NUM/g" ios/Flutter/Generated.xcconfig
    sed -i "s/^FLUTTER_BUILD_NAME=.*/FLUTTER_BUILD_NAME=$BUILD_NAME/g" ios/Flutter/Generated.xcconfig
    
    # 修改包名
    sed -i "s/^ipa_name=.*/ipa_name=wyrapp-$BUILD_NAME-$BUILD_NUM/g" ios/fastlane/.env
    
    # fastlane自动编译
    cd ios; fastlane adhoc; cd -
    

    第一次编译的时候先执行一下 flutter clean ,清除之前的一些环境变量

Android

同样,手动打包的过程官方文档已经写得很详细了,可以先参考一下。

基本设置

总结一下,主要步骤如下:

  1. 检查 App Manifest 查看 android/app/src/main/AndroidManifest.xml 里面的 application 里面的名称是不是我们想要的

  2. 查看构建配置 主要是 android/app/build.gradle 文件的 defaultConfig 部分检查下面几个字段 applicationIdversionCodeversionName : a. applicationId: 指定始终唯一的 (Application Id)appid b. versionCode & versionName: 指定应用程序版本号和版本号字符串。有关详细信息,请参考版本文档 c. minSdkVersion & targetSdkVersion: 指定最低的API级别以及应用程序设计运行的API级别。有关详细信息,请参阅版本文档中的API级别部分。

  3. app签名 a. 创建 keystore

    1
    
    keytool -genkey -v -keystore ~/key.jks -keyalg RSA -keysize 2048 -validity 100000 -alias key
    

    b. 引用应用程序中的keystore 创建 touch android/key.properties

    1
    2
    3
    4
    
    storePassword=<password from previous step>
    keyPassword=<password from previous step>
    keyAlias=key
    storeFile=<location of the key store file, e.g. /Users/<user name>/key.jks>
    

    注意: 保持文件私密; 不要将它加入公共源代码控制中;由于我这个是自己的私有仓库,因此我纳入到版本控制里面去了 c. 在gradle中配置签名 修改 android/app/build.gradle 文件,

    1. 替换:

      1
      
      android {
      

      1
      2
      3
      4
      5
      6
      7
      
      def keystoreProperties = new Properties()
      def keystorePropertiesFile = rootProject.file('key.properties')
      if (keystorePropertiesFile.exists()) {
          keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
      }
      
      android {
      
    2. 替换:

      1
      2
      3
      4
      5
      6
      7
      
      buildTypes {
          release {
              // TODO: Add your own signing config for the release build.
              // Signing with the debug keys for now, so `flutter run --release` works.
              signingConfig signingConfigs.debug
          }
      }
      

       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      11
      12
      13
      
      signingConfigs {
          release {
              keyAlias keystoreProperties['keyAlias']
              keyPassword keystoreProperties['keyPassword']
              storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
              storePassword keystoreProperties['storePassword']
          }
      }
      buildTypes {
          release {
              signingConfig signingConfigs.release
          }
      }
      

    d. 开启混淆

    1. 配置混淆 创建 android/app/proguard-rules.pro 文件,并添加以下规则:

      1
      2
      3
      4
      5
      6
      7
      8
      
      ## Flutter wrapper
      -keep class io.flutter.app.** { *; }
      -keep class io.flutter.plugin.**  { *; }
      -keep class io.flutter.util.**  { *; }
      -keep class io.flutter.view.**  { *; }
      -keep class io.flutter.**  { *; }
      -keep class io.flutter.plugins.**  { *; }
      -dontwarn io.flutter.embedding.**
      
    2. 开启混淆/压缩 打开 android/app/build.gradle 文件,定位到 buildTypes ,在 release 配置中将 minifyEnabled 和 useProguard 设为 true,再将混淆文件指向上一步创建的文件。

       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      
      android {
      
          // ...
      
          buildTypes {
      
              release {
      
                  signingConfig signingConfigs.release
      
                  minifyEnabled true
                  useProguard true
      
                  proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
      
              }
          }
      }
      

自动化打包

上面的设置完成了以后我们开始进行自动化的打包,同样需要用到 fastlane

  1. fastlane init

    1
    2
    
    cd android
    fastlane init
    

    fastlane init 的时候会出现下面的选择界面,下图中标红的地方就是我们要注意的,一个是修改applicationId,一个是选择n就可以了。

  2. 安装蒲公英插件

    1
    2
    3
    4
    
    cd android
    # 增加蒲公英插件
    # cd fastlane这里装插件的时候不需要进入到fastlane目录里面去,在android目录下面就可以了
    fastlane add_plugin pgyer
    
  3. 编写 Fastfile 文件 下面是我目前使用的 Fastfile 里面的内容

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    
    # This file contains the fastlane.tools configuration
    # You can find the documentation at https://docs.fastlane.tools
    #
    # For a list of all available actions, check out
    #
    #     https://docs.fastlane.tools/actions
    #
    # For a list of all available plugins, check out
    #
    #     https://docs.fastlane.tools/plugins/available-plugins
    #
    
    # Uncomment the line if you want fastlane to automatically update itself
    # update_fastlane
    
    default_platform(:android)
    
    platform :android do
      desc "编译安卓正式版本"
      lane:release do
    
        pgyer(api_key: "xxxxxxxxx",
              apk: "../build/app/outputs/flutter-apk/"+ENV['apk_name'],
              user_key: "xxxxx") # 蒲公英的key
    
      end
    end
    

    这个配置主要有下面几个功能 a. 指定apk文件名 flutter编译出来的apk的名称就是 app-release.apk ,我们同样在 android/fastlane/.env 里面增加内容

    1
    
    apk_name=app-release.apk
    

    通过 flutter build apk 每次编译出apk文件以后就会自动上传到蒲公英上面去,同样这个文件也是可以直接发布到应用商店的。

  4. 版本号管理 apk可以通过 --build-name 以及 -build-number 来控制apk的版本

    • build-name 给用户看到的版本信息
    • build-number 我们自己看到的内部版本信息
  5. 编译脚本 经过上面的过程,我们把整个编译过程放到一个脚本里面去进行了,因此我写了下面的脚本来实现自动编译上传到蒲公英

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    
    #!/usr/bin/env bash
    
    # 编译安卓版本
    # 给用户看的版本号
    BUILD_VERSION='1.0.0'
    BUILD_NUM=`date '+%m%d%H%M'`
    APP_NAME='scaffold'
    
    flutter build apk --build-name "$BUILD_VERSION" --build-number "$BUILD_NUM" --release
    
    cd android; fastlane release; cd -
    cp build/app/outputs/flutter-apk/app-release.apk ~/Downloads/apk/$APP_NAME-$BUILD_VERSION-$BUILD_NUM.apk
    

    编译好的apk会重新命名同时拷贝到 ~/Downloads/apk 目录下面去,同时上传到蒲公英。

更新历史

2021/03/05

  • 初稿