ios 通过 fastlane 自动化打包、发布到 appstore

前言

在之前开发 APP 的时候,公司的技术总监一直让我研究 app 自动化打包持续集成部署这种工程化的任务,对于这种技术我肯定是非常感兴趣去折腾的,鉴于 app 业务开发任务繁重,和任务优先级相对较低的原因,等到不怎么忙了才有时间来研究,最近看了许多相关文档,总结如下:

关于持续集成的方案大多是采用 jenkins,我们后端的服务也是集成了 jenkins,所以 CI 平台无需考虑,关于 ios 打包,大多数文章是基于在 jenkins安装 xcode 相关插件,以构建出 xcode 打包环境,以及证书、认证文件、钥匙串的相关插件,整体流程比较繁琐,稍有流程顺序出错和配置出错都会导致打包失败,而且听说 jenkins 的 xcode 插件更新频率极慢,可能不能满足最新 xcode11 的使用,我在参考各种文章的配置流程之后,打包出了测试版本的 ipa,并且推送到蒲公英,但是发布到 appstore 的构建一直未果,也折腾了两到三天了,有点心累了,直到我发现了 fastlane!

看到文档,感觉简直打开了新世界的大门,随便几个功能都足够吸引力

比如:

就这几个功能就已经省去相当多的时间了,身为一个 app 开发者,相信每个人都有过跟 xcode 的证书、认证文件各种折腾的爱恨情仇!

一张图总结 fastlane 的流程和功能

fastlane 管理许多工具集合,这些工具也都是通过 ruby 开发,每个工具只负责一项任务,可以通过 fastlane 的配置文件灵活的构建这些工具的使用和流程,从而实现打包 => 自动化测试 => 发布测试包 => 发布正式包,一条龙服务!

就问你妙不妙?

fastlane官网首页每天都在统计它帮助开发者节省了多少时间,真是相见恨晚啊。

开始折腾…..

经过两天的折腾,顺利完成了

  • ipa 文件推送到 TestFlight、蒲公英、firm;
  • 自动上传元数据到 ITC;
  • 自动上传 ipa 到 ITC 并提交appstore审核;

所以,如果看文章的你想要实现以上需求,可以参考以下我分享的内容。

正文

fastlane安装

1.安装 xcode 命令行工具

xcode-select --install

若提示如下,说明已经安装了Xcode命令行工具;否则会弹出对话框,选择安装即可。

$ xcode-select --install
xcode-select: error: command line tools are already installed, use "Software Update" to install updates

安装界面:

2.通过 RubyGems 安装 fastlane

推荐使用RubyGems来安装,fastlane就是用 ruby 写的一个工具,所以这种安装方式比较友好,后续不易出错。

如果你是 Mac 用户,其实 Mac 自带 ruby 环境,但是仅仅这样不够,需要2.0及以上版本,检查你的版本:

ruby -v

目前我的版本:

$ ruby -v
ruby 2.6.3p62 (2019-04-16 revision 67580) [x86_64-darwin19]

ruby 管理推荐使用 rvm,类似 node 的 nvm 的工具,可以提供一个便捷的多版本 Ruby 环境的管理和切换。

检查你当前正在使用系统Ruby

which ruby

如果结果是:

/usr/bin/ruby

说明目前是 Mac 系统自带的 ruby 环境,所以首先安装 rvm,而安装 rvm 又需要 gpg,还要安装mpapis公钥

(觉得麻烦不?)

没事,坑都趟完了,跟着走就对了….

3. ruby 环境搭建

  1. 通过 Homebrew 安装 gpg
brew install gnupg
  1. 安装完gpg之后,安装mpapis公钥

查看最新公钥: https://rvm.io/rvm/install

拿过来安装

gpg --keyserver hkp://pgp.mit.edu --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 7D2BAF1CF37B13E2069D6956105BD0E739499BDB
  1. 装最新版本的Ruby的RVM
\curl -sSL https://get.rvm.io | bash -s stable --ruby
  1. 列出可供RVM使用的Ruby版本rvm list
$ rvm list
=* ruby-2.6.3 [ x86_64 ]
# => - current
# =* - current && default
#  * - default
  1. 再次使用which ruby查看当前Ruby版本

返回 /Users/xxx/.rvm/rubies/ruby-2.6.3/bin/ruby

则说明当前使用了 rvm 安装的 ruby,ruby 环境至此告一段落!


通过RubyGems安装:

sudo gem install fastlane -NV

当然你也可以通过homebrew安装(费了这么大劲你肯定不会选这个):

brew cask install fastlane

4.初始化

在项目目录下执行fastlane init

安装的时候出现了下面的选项

   
1. 📸  Automate screenshots (自动截屏功能配置)
2. 👩‍✈️  Automate beta distribution to TestFlight (发布到Testfilght配置)
3. 🚀  Automate App Store distribution (自动发布到 appstore配置)
4. 🛠  Manual setup - manually setup your project to automate your (选择手动配置)

新手在这里肯定会纠结,根据情况,自动截屏功能肯定不是首选的刚需,发布到 appstore 也不是一上来就要搞定的,手动配置刚上来你会配置吗?

所以这里选择 2 ,然后一路回车,会让你输入apple 开发者ID,就是你的邮箱,以及密码,通过命令行登录苹果开发者来下载你的证书等配置,登录 ITC 和从 ITC 拉取项目已存在的信息,如果开启了苹果的双重认证,会提示输入验证码登录

Two-factor Authentication (6 digits code) is enabled for account 'xxx@xxx.com'

为避免每次登录输验证码,官方给出解决方案

命令中也有相关 issue 以供查阅解决方案,界面和提示非常入门化和友好,这真是我用过最贴心的的命令行工具了!

执行完毕后,会生成fastlane文件夹、Gemfile文件,AppfileFastfile文件。

Appfile: 存储有关开发者账号相关信息

Fastfile: 核心文件,用于命令行调用和处理具体的流程,lane相对于一个action方法或函数

Gemfile 类似于cocopods 的Podfile文件

.env 配置环境变量(在fastlane init进行初始化后并不会自动生成,如果需要可以自己创建

打开项目,看看刚生成的文件,你可能发现都没有代码高亮….
因为 fastlane 使用 ruby 写的,所以要支持 ruby 啊
我这里用的是 VS Code,搜索 ruby 插件安装。

(webstorm 没有发现 ruby 插件)

5.安装所需插件

Fastlane的插件是一个或者一组action的打包,单独发布在fastlane之外。

你可以通过命令行搜索插件

#查看所有插件
fastlane search_plugins
# 安装方法
fastlane add_plugin [name] 
#常用插件
fastlane add_plugin versioning
fastlane add_plugin firim
fastlane add_plugin pgyer

首次安装插件会生成Pluginfile文件

# Autogenerated by fastlane
#
# Ensure this file is checked in to source control!
gem 'fastlane-plugin-firim'
gem 'fastlane-plugin-pgyer'
gem 'fastlane-plugin-versioning'

Gemfile文件会自动添加一下内容

plugins_path = File.join(File.dirname(__FILE__), 'fastlane', 'Pluginfile')
eval_gemfile(plugins_path) if File.exist?(plugins_path)

插件说明:

  • fastlane-plugin-versioning

    用来修改 build 版本号和 version 版本号,fastlane 内嵌的actionincrement_build_number使用的是苹果提供的agvtool, 在更改Build的时候会改变所有target的版本号。如果你在一个工程里有多个target,每次编译,所有的Build都要加1。
    有了fastlane-plugin-versioning不仅可以指定target增加Build,当然也可以直接设定Version, 并且可以指定版本号的版本(major/miner/patch),这一点非常重要,而且这个插件也可以非常方便的修改 android 的版本号,插件排行榜第一位。

  • fastlane-plugin-pgyer

    上传到蒲公英分发平台。

  • fastlane-plugin-firim

    上传到 firim。

6. 配置环境变量

新建.env文件,这里可以配置所有的账号、秘钥、路径等自定义变量,定义之后,然后Fastlane的三个配置文件(Appfile、Deliverfile、Fastfile)分别从.env文件中读取配置信息。

官方文档参考

你可能又发现 env 文件中没有语法高亮,强迫症又表示不舒服,可以在 VS Code 安装 dotEnv 插件。

https://github.com/bkeepers/dotenv

给出.env文件配置做参考:

App_Identifier = "com.xx.xxx"
# Apple email address
Apple_Id = "xxx@sina.com" 
# TeamId
Team_Id = "xxx"
# target scheme
Scheme = "xxx"
# xcodeproj
Xcodeproj ="xxx.xcodeproj"
# xcworkspace
Workspace="xxx.xcworkspace"
# ipa输出路径-- Appstore
Appstore_Output_Path  = "builds/appstore"
# ipa输出路径-- 蒲公英
Pgy_Output_Path  = "builds/pgy"
# ipa输出路径-- TestFlight
TF_Output_Path = "./builds/testflight"
# 蒲公英 的api_key
Pgy_Api_Key = "xxx"
# 蒲公英 的user_key
Pgy_User_Key = "xxx"

使用方式:ENV['Apple_Id']

apple_id("xxx@xxx.com") ===> apple_id ENV['Apple_Id']

Fastfile 配置:

default_platform(:ios)
platform :ios do
    # 所有lane执行之前
    # 使用环境变量提供这个密码给fastlane,解决双重认证生成的特殊密码
    before_all do
    ENV["FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD"] = "xxx"
    end
  desc "发布 测试版本到 TestFlight"
  lane :beta_tf do
    increment_build_number_in_plist(target: [target_name])
    increment_version_number_in_plist(
        target: [target_name],
       )
    get_certificates( # Create or get certificate, and install it
      output_path: "./builds" # Download certificate in the build folder (you don't need to create the folder)
    )
    get_provisioning_profile( # Create or get provisioning profile
      output_path: "./builds",  # Download provisioning profile in the build folder
      filename: "provisioning.mobileprovision" # Rename the local provisioning profile
    )
    update_project_provisioning( # Set the project provisioning profile (related in Xcode to the General > Signing Release section)
      xcodeproj: ENV['Xcodeproj'],
      target_filter: ENV['Scheme'], # Name of your project
      profile: "./builds/provisioning.mobileprovision",
      build_configuration: "Release"
    )
    update_project_team( # Set the right team on your project
      teamid: CredentialsManager::AppfileConfig.try_fetch_value(:team_id)
    )
    
    gym(
        workspace: ENV['Workspace'],
        scheme: ENV['Scheme'],
        clean: true,
        export_method: "ad-hoc", # Valid values are: app-store, ad-hoc, package, enterprise, development
        export_options: {
          provisioningProfiles: { 
              "com.xxx.xxx" => "xxx",
          }
        },
        build_path: "./builds/testflight",
        output_directory: "./builds/testflight",
        output_name: logDirectory
    )
    upload_to_testflight
  end
  # 发布到蒲公英
  lane :beta_pgy do
    desc "发布 测试版本 到 蒲公英"
    # 这里是打的 Debug 版本
    gym(
    scheme: ENV['Scheme'],
    clean:true, #打包前clean项目
    export_method: "development", #导出方式  Defaults to 'Release'
    configuration: "Debug",#环境
    output_directory: ENV['Pgy_Output_Path'],
    output_name: logDirectory,
    export_options: {
          provisioningProfiles: { 
              "com.xxx.xxx" => "xxx",
          }
        }
    )
    # 上传版本到蒲公英平台
    pgyer(
      api_key: ENV["Pgy_Api_Key"], 
      user_key: ENV["Pgy_User_Key"],
      update_description: "update by fastlane"
    )
    # 在macOS 通知栏发送通知
    notification(subtitle: "上传完成", message: "最新测试包已经上传至蒲公英平台")
  end
  lane :release do
    desc "发布正式版到 appstore"
    # 下载证书
    get_certificates(
        output_path: "./builds"
    );    
    # 下载认证文件
    get_provisioning_profile(
        output_path: "./builds"
    ); 
    #get_push_certificate # 获取正式推送证书
    # 拉取最新代码
    git_pull
    # 增加 version number
    # 使用文档 https://github.com/SiarheiFedartsou/fastlane-plugin-versioning
    # eg: version_number: '2.1.1' # Set a specific version number
    # eg: bump_type: 'minor' # Automatically increment minor version number
    increment_version_number_in_plist(
        bump_type: 'patch', # Automatically increment patch version number(patch/minor/major)
        target: ENV['Scheme']
        );
    # 从最新 release 分支名中获取 version.
    # `pattern` is pattern by which version number will be found, `#` is place where action must find version number.
    # Default value is 'release-#'(for instance for branch name 'releases/release-1.5.0' will extract '1.5.0')
    # version = get_version_number_from_git_branch(pattern: 'release-#')
    # 增加 build number
    latest_build_number = lane_context[SharedValues::LATEST_BUILD_NUMBER];
    # Automatically increments the last part of the build number.
    increment_build_number_in_plist(
        target: ENV['Scheme']
    );
    # see code signing guide for more information
    #sync_code_signing(type: "appstore");  
    # 打包
    gym(
        scheme: ENV['Scheme'],
        configuration: "Release",
        clean: true,
        export_method:"app-store",
        export_options: {
            provisioningProfiles: { 
                "com.xxx.lph" => "xxx",
            }
          }
        );
    # 上传到 ITC
    deliver 
    # 在macOS 通知栏发送通知
    notification(subtitle: "上传完成", message: "最新正式包已经上传至 appstore")
  end
end

未完待续。。。

https://juejin.im/post/5e19a1c2f265da3e097e8da4

「点点赞赏,手留余香」

    还没有人赞赏,快来当第一个赞赏的人吧!
0 条回复 A 作者 M 管理员
    所有的伟大,都源于一个勇敢的开始!
欢迎您,新朋友,感谢参与互动!欢迎您 {{author}},您在本站有{{commentsCount}}条评论