Jenkins

iOS 自动化打包3 Fastlane

2022-07-03  本文已影响0人  f8d1cf28626a

自动化打包3 Fastlane

除了Jenkins可以自动打包,还有另一个方式:Fastlane,下面来了解下。

Fastlane是一个完全开源的项目,是一款为iOS和Android开发者提供的自动化构建工具。它可以帮助开发者将App打包、签名、测试、发布等流程串联起来,实现完全自动化的工作流。其官网地址为:https://fastlane.tools/

Fastlane安装

参考配置文档,安装步骤如下:

// 查看 Ruby 版本
ruby -v

// 查看 gem 的source
gem sources
xcode-select --install
sudo gem install fastlane --verbose

//如果安装出错,则使用以下命令
sudo gem install -n /usr/local/bin fastlane

//查看版本验证是否安装成功
fastlane --version
图名为借

Fastlane相关block、lanes、actions

在完善打包逻辑之前,首先介绍Fastlane中提供的常用的blocks、lanes和actions

Blocks

block主要分为三类:

其中before_all会先于before_each执行,且仅执行一次,

所以许多通用操作流入git代码拉取等,可以放在before_all或者befoe_each中

before_all do |lane| 
   # ...
end

<!--举例-->
before_all do |lane| 
   cocoapods #这里会执行pod install
end
before_each do |lane, options|
  # ...
end
before_each do |lane, options|
  # ...
end

after_all & after_each

after_all 会在 after_each 执行完后执行,且仅执行一次。

许多通用操作例如发送打包成功的通知等,可以放在 after_all 或 after_each 中。

after_all do |lane|
  # ...
end


<!--举例-->
after_all do |lane|
  say("Successfully finished release (#{lane})!")
  slack(
    message: "Successfully submitted new App Update"
  )
  sh("./send_screenshots_to_team.sh") # Example
end
after_each do |lane, options|
  # ...
end

例如下面的例子,before_each将after_each被调用 4 次

before_each do |lane, options|
  # ...
end

lane :deploy do
  archive
  sign
  upload
end

lane :archive do
  # ...
end

lane :sign do
  # ...
end

lane :upload do
  # ...
end

after_each do |lane, options|
  # ...
end

error

在任何流程中发生错误时,都会退出并执行error,错误后的after_all和after_each将不会执行

error do |lane, exception|
  slack(
    message: "Something went wrong with the release.",
    success: false,
    payload: { "Error Info" => exception.error_info.to_s } 
  )
end

Actions

一个命名的lane代表一个持续集成的任务,每个任务由多个步骤组成,步骤组成是已经定义好的action工具。在终端可以通过fastlane action actionName查看某个具体的action,也可以通过fastlane action查看fastlane中定义好的action及说明。

除了使用ruby代码在lane中实现的各种功能,fastlane也内置了许多写好的独立方法库即action每一个action都是一个独立Ruby脚本是fastlane的最小执行单位,下面介绍几个常用的action

调用cocoapods action会执行pod install,如果工程中使用了cocoapods管理三方库,需要在Gemfile中添加以下命令

gem "cocoapods"

在lane中使用直接调用即可

lane :debug do
  cocoapods  # 执行 pod install
end

gym是fastlane提供的用于构建、打包的action,是build_app的别名。可以根据配置参数编译iOS应用,并生成ipa包。以下是常用的参数

其他参数如下:

以下是CI中具体的例子

lane :release do
  # ...
  gym(
      workspace: "app.xcworkspace",
      scheme: "app",
      # 打包前clean
      clean: true,
      # Release、Debug、自定义
      configuration: "Release",
      # app-store, ad-hoc, package, enterprise, development
      export_method: "ad-hoc",
      # 文件输出路径
      output_directory: "/Users/user/Desktop/",
      # ipa名称
      output_name: "app.ipa",
      # 是否包含调试符号
      include_symbols: true,
      # 是否开启bitcode
      include_bitcode: false,
    )
    # ...
end

3、increment_build_number

currentBuildNumber = app_store_build_number(
  api_key: api_key,        # 使用app_store_connect_api_key认证
  live: false,             #  live 为 true 查询已发售的版本,false 查询测试的版本
  app_identifier: "com.zm.ZLGithubClient"
)
increment_build_number(
  build_number: currentBuildNumber + 1
)

4、match

match配置步骤

以下是在CI中使用match的例子

# match 中需要配置参数
# 证书类型   appstore , adhoc 还是 development
# app id  
# kaychain 证书保存的keychain
# git_url  证书保存的github远端库

lane :github_action_testFlight do

match(type: "appstore",       
      readonly: true,
      app_identifier: "com.used.id",
      keychain_name: ENV['MATCH_KEYCHAIN_NAME'],
      keychain_password: ENV['MATCH_PASSWORD'],
      git_url: ENV['MATCH_GIT_URL'])

end

# ENV['MATCH_GIT_URL'] 是加密保存的参数

match说明

5、App Store Connect API

在早先访问App Store Connect信息时需要双因素认证。而在持续集成的过程中一般无法人机交互(例如github-action),会导致持续集成无法完成。在WWDC18中,苹果提出了App Store Connect API,提供另外的认证方式。Fastlane也对App Store Connect API提供了支持,具体查看Using App Store Connect API文档

Using App Store Connect API是一个官方公共API用于管理应用元数据、定价、配置等。但是在使用之前,需要获取AppStore Connect的访问权限,即获取issuer ID和apiKey

app_store_connect_api_key 是用来为其他 action 生成 App Store Connect API token 的 action; match,pilot以及 deliver等 action 都可以使用 App Store Connect API token,有如下参数:

以下是在CI中的使用例子

lane :release do
  api_key = app_store_connect_api_key(
    key_id: "xxxxxxxxx",
    issuer_id: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
    key_filepath: "./private_keys/AuthKey_xxxxxxxxxx.p8",
    duration: 1200, # optional
    in_house: false, # optional but may be required if using match/sigh
  )
   
  # 在piolt中使用app_store_connect_api_key
  pilot(api_key: api_key)
end

Lanes

lanes的使用方式请参考lanes文档,可以分为以下几种:

命令行参数传递

//格式
fastlane [lane] key:value key2:value2

//举例
fastlane deploy submit:false build_number:24
lane :deploy do |options| 
    # 获取传入的submit
    if options[:submit] 
        # Only when submit is true 
    end 
    # build号增加,获取传入的build_number 
    increment_build_number(build_number: options[:build_number])  
 end

lane之间的调用

lane之间的调用,有点类似于函数的调用,通过lane的名字来调用

lane :deploy do |options|
  # deploy调用名为build的lanes
  build(release: true) # 传入打包的方式
end

lane :staging do |options|
  # deploy调用名为build的lanes
  build # 不传参也可以工作
end

lane :build do |options|
  build_config = (options[:release] ? "Release" : "Staging")
  build_ios_app(configuration: build_config)
end

返回值

除此之外,lane还可以检索返回值,在Ruby中,lane定义的最后一行是返回值,在其他lane中通过options进行使用

lane :deploy do |options|
  value = calculate(value: 3)
  puts value # => 5
end

lane :calculate do |options|
  # 返回值
  2 + options[:value] # the last line will always be the return value
end

如何停止执行中的lane

在lane中可以通过next关键字来停止执行中的lane,如下所示

lane :build do |options|
  if cached_build_available?
    UI.important 'Skipping build because a cached build is available!'
    next # skip doing the rest of this lane
  end
  match
  gym
end

private_lane :cached_build_available? do |options|
  # ...
  true
end

当next作用在切换lane时,会控制流程返回到前lane正在执行的位置,即当前lane的next以后的代码将不会执行

lane :first_lane do |options|
  puts "If you run: `fastlane first_lane`"
  puts "You'll see this!"
  second_lane
  puts "As well as this!"
end

private_lane :second_lane do |options|
  next
  puts "This won't be shown"
end

lane的上下文通信

lane的上下文通信,简单来说就是如何在不同的action间通信。不同的action可以通过分享哈希来进行通信,如下所示

lane_context[SharedValues::VARIABLE_NAME_HERE]

//举例
lane_context[SharedValues::BUILD_NUMBER]                # Generated by `increment_build_number`
lane_context[SharedValues::VERSION_NUMBER]              # Generated by `increment_version_number`
lane_context[SharedValues::SNAPSHOT_SCREENSHOTS_PATH]   # Generated by _snapshot_
lane_context[SharedValues::PRODUCE_APPLE_ID]            # The Apple ID of the newly created app
lane_context[SharedValues::IPA_OUTPUT_PATH]             # Generated by _gym_
lane_context[SharedValues::DSYM_OUTPUT_PATH]            # Generated by _gym_
lane_context[SharedValues::SIGH_PROFILE_PATH]           # Generated by _sigh_
lane_context[SharedValues::SIGH_UDID]                   # The UDID of the generated provisioning profile
lane_context[SharedValues::HOCKEY_DOWNLOAD_LINK]        # Generated by `hockey`
lane_context[SharedValues::GRADLE_APK_OUTPUT_PATH]      # Generated by `gradle`
lane_context[SharedValues::GRADLE_ALL_APK_OUTPUT_PATHS] # Generated by `gradle`
lane_context[SharedValues::GRADLE_FLAVOR]               # Generated by `gradle`
lane_context[SharedValues::GRADLE_BUILD_TYPE]           # Generated by `gradle`

如果访问lane的属性

我们也可以通过lane_context动态访问当前lane的属性,如下所示

lane_context[SharedValues::PLATFORM_NAME]        # Platform name, e.g. `:ios`, `:android` or empty (for root level lanes)

lane_context[SharedValues::LANE_NAME]            # The name of the current lane preceded by the platform name (stays the same when switching lanes)

lane_context[SharedValues::DEFAULT_PLATFORM]     # Default platform

同时这些属性也可用作.env文件的环境变量

ENV["FASTLANE_PLATFORM_NAME"]
ENV["FASTLANE_LANE_NAME"]

私有lane

当我们有不同lane调用同一个lane时,可以将这个lane定义为私有的lane,防止在外部通过fastlane laneName进行调用,如下所示,我们不能通过fastlane build来访问私有的lane

lane :production do
  # ...
  build(release: true)
  appstore # Deploy to the AppStore
  # ...
end

lane :beta do
  # ...
  build(release: false)
  crashlytics # Distribute to testers
  # ...
end

lane :build do |options|
  # ...
  ipa
  # ...
end

<!--更改为-->
lane :production do
  # ...
  build(release: true)
  appstore # Deploy to the AppStore
  # ...
end

lane :beta do
  # ...
  build(release: false)
  crashlytics # Distribute to testers
  # ...
end

private_lane :build do |options|
  # ...
  ipa
  # ...
end

如何配置多个lane的环境变量

通常Appfile只会使用配置项的第一个值,如下所示,app_identifier配置了两个值,我们一般只会取第一个com.used.id,而com.ignored.id将被忽略。

app_identifier "com.used.id"
app_identifier "com.ignored.id"

为了避免以上情况,fastlane提供了for_lane和for_platform来解决多个配置的情况,所有的配置文件中都可使用

locales ['en-US', 'fr-FR', 'ja-JP']

for_lane :screenshots_english_only do
  locales ['en-US']
end

for_lane :screenshots_french_only do
  locales ['fr-FR']
end
app_identifier "com.default.id"

for_lane :enterprise do
  app_identifier "com.forlane.enterprise"
end

for_platform :Mac do
  app_identifier "com.forplatform.mac"

  for_lane :release do
    app_identifier "com.forplatform.mac.forlane.release"
  end
end

Fastlane配置

Fastlane的配置主要分为三步:

【第一步】fastlane的初始化配置

主要执行以下命令

cd [项目根目录,xcodeproj的同级目录]
fastlane init

init操作主要完成以下操作:

下面分别介绍下fastlane文件夹中常用的文件:

图名为借

除此之外,还需要关注fastlane同级的Gemfile和Gemfile.lock文件

【第二步】创建.env配置全局变量,并修改Appfile

注:因为是.env文件是.开头文件,默认是在finder中隐藏的,可以通过快捷键来显示隐藏文件:CMD + Shift + .

其命名规则为:.env.<environment>,例如:.env.development、.env.release。

fastlane <lane-name> --env development
//.env内容
WORKSPACE=YourApp.xcworkspace 
HOCKEYAPP_API_TOKEN=your-hockey-api-token

//Fastfile中使用.env文件
xcworkspace ENV['WORKSPACE']
hockey-api-token ENV['HOCKEYAPP_API_TOKEN'] 

【第三步】Fastfile完善打包逻辑

编辑fastlane的逻辑可参考fastlane自动部署iOS AppStore文档,构建步骤如下:

以下是具体的编译、打包代码

# 因为fastlane存在新老版本兼容问题,所以一般会指定fastlane版本
fastlane_version "2.205.1"
# 设置默认的平台
default_platform(:iOS)

# ======================== .env文件配置获取相关参数 ========================
# 定义两个方便使用和修改的常量
scheme = ENV['Scheme'] #scheme名称
xcodeproj = ENV['Xcodeproj'] 
workspace = ENV['Workspace']
info_plist = ENV['Info_Plist']

# 定义指定平台的操作
platform :iOS do

  # ======================== 执行lane前的操作 ========================
  # 所有lane执行之前,可以做如执行cocoapods的pod install
  before_all do |lane, options|
    # 更新pod
    cocoapods(use_bundle_exec: FALSE)
    # 清理缓存
    xcclean(scheme: scheme)
  end

  # 将正式应用打包并上传到App Store,release是自己取的名字,因为是发布正式版,所以取名叫 release
  desc "Push a new release build to the App Store"
  lane :release do

    # 打包之前,先将build号递增
    increment_build_number(xcodeproj: "#{xcodeproj}")

    # 对应用进行打包
    build_app(
      workspace: "#{workspace}",
      scheme: "#{scheme}",
      export_method: "app-store",
      clean: true,
      xcargs: "-allowProvisioningUpdates"
    )

    # 将打包完的应用上传到AppStore
    upload_to_app_store(
      skip_metadata: true,
      skip_screenshots: true
    )
  end
  
  # ======================== 执行lane成功后的操作 ========================
  # 所有lane完成之后,可以使用参数lane来区分
  after_all do |lane, options|
    puts "所有lane执行完毕"
  end

# ======================== 执行lane失败后的操作 ========================
  # 所有lane失败之后,可以使用参数lane来区分
  error do |lane, options|
    puts "lane执行失败"
  end
  
end

到此,fastlane就配置完成了

Fastlane使用

除了可以在本地终端执行,还可以在Jenkins构建打包服务,具体步骤参考文档:Jenkins集成fastlane

最终,Fastlane整体流程汇总如下

iOS中打包方式汇总如下

Fastlane 不错的案例
Fastlane一键自动化打包发布 iOS 项目
使用fastlane进行iOS打包
iOS 使用fastlane实现自动化打包

上一篇下一篇

猜你喜欢

热点阅读