iOS使用fastlane实现自动化打包

一、fastlane简介

fastlane是一套使用Ruby写的自动化工具集,用于iOS和Android的自动化打包、发布等工作,可以节省大量的时间。

fastlane官网:
https://fastlane.tools/

fastlane的官方Github:
https://github.com/fastlane/fastlane

fastlane文档说明:
https://docs.fastlane.tools/

使用fastlane前,确保你已经可以使用Xcode手动打包成功(说明你已经配置好证书)

证书配置

1. 配置环境

1.安装HomeBrew

# 查看电脑有没有安装  HomeBrew 没有的话进行下面的安装
brew -v

#安装
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
#更新到最新版
brew update
#更新包
brew upgrade

2.安装ruby

# 查看电脑是否已经安装ruby 没有的话进行下面的安装
ruby -v  

#安装
brew install ruby

#查看版本
ruby -v  
# 例如我的电脑的ruby版本为
# ruby 2.2.3p173 (2015-08-18 revision 51636) [x86_64-darwin17]

3.安装fastlane

确保你的电脑上已安装最新版本的Xcode命令行工具(Xcode command line tools)

xcode-select --install

如果没有安装,会弹出对话框,点击安装。如果提示xcode-select: error: command line tools are already installed, use "Software Update" to install updates表示已经安装

开始安装fastlane

#安装
gem install fastlane -NV

或者使用

brew cask install fastlane

安装完之后

# 确认下是否安装完成和当前使用的版本号
fastlane -v 

# 我的目前电脑版本
# fastlane installation at path:/Users/hf/.rvm/gems/ruby-2.2.3/gems/fastlane-2.108.0/bin/fastlane
# -----------------------------
# [✔] 🚀 
# fastlane 2.108.0

二、使用fastlane

创建一个测试demo,我创建的是HFMyTest
我的项目的bundle id为www.hf.mytest,这个bundle id需要在开发者中注册provisioning profile
同时注意设置schemeShared,不然fastlane init的时候会失败,一般默认是选择Shared的,可以不用去管

设置方法为:A、打开项目的Manage Schemes

scheme1

B、勾选上Shared
scheme2

  1. 先cd到项目路径
cd /Users/hf/MyTest/HFMyTest/Example
  1. 初始化fastlane
fastlane init 
fastlane init

可以看见有四个选项

  1. 自动化截图
  2. 将测试版分发自动化到TestFlight
  3. 自动上传、发布到App Store
  4. 手动设置 - 手动设置您的项目以使您的任务自动化

3. 我这里选择的是3

select scheme

  1. 可能需要你去select scheme,注意这里面的sheme不是你的项目名,例如我的项目名为HFMyTestsheme就是上面介绍的勾选的Manage ShemesHFMyTest-Example,所以这里选择1

    select sheme

  2. 由于选择3是要发布都AppStore,所以接下来输入用户的app id,这个id应该是具有开发者资格的账号
    输入id之后接下来输入id的密码

Apple ID
  1. 输入账号和密码后
    如果开发者账号上没有对应的bundle id的App,会提示是否创建一个新的App,这里我们选择否(n),因为这样快速创建的App设置的信息有限


  2. 接下来等待加载,按照步骤回车就可以完成初始化


    初始化完成
  1. 初始化完成后,打开项目目录


    HFMyTest
  2. 发现项目中多出了fastlane目录,通过sublime或者文本编辑打开Appfile和Fastfile

app_identifier("www.hf.mytest") # The bundle identifier of your app
apple_id("webmaster@xspcf.com") # Your Apple email address

itc_team_id("118090429") # App Store Connect Team ID
team_id("MQJL2427R9") # Developer Portal Team ID

# For more information about the Appfile, see:
#     https://docs.fastlane.tools/advanced/#appfile

  1. 上面这个Appfile默认已经初始化好了,不用再去操作
# 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 "Push a new release build to the App Store"
  lane :release do
    increment_build_number(xcodeproj: "HFMyTest.xcodeproj")
    build_app(workspace: "HFMyTest.xcworkspace", scheme: "HFMyTest-Example")
    upload_to_app_store(skip_metadata: true, skip_screenshots: true)
  end
end

  1. 以上Fastfile代码中,默认已经设置为上传到AppStore的操作
    release为lane的名字,在执行lane的时候会用到,你可以为lane取任意的名字,也就是release可以取其它的,如appRelease
    build_app用来编译app,你可以为其指定更多的的参数
    打开终端cd到项目目录,运行就可以打包发布到
    AppStore
    使用以下命令来执行这个lane:
cd /Users/hf/MyTest/HFMyTest/Example
fastlane release

由于上面我们选择了否(n),也就是现在AppStore上面还没有我们这个HFMyTest这个app,所以上面的操作会最终失败的

上传AppStore失败

  1. 但是这时候打开HFMyTest本地目录会发现里面会有打出的ipa包
    HFMyTest

可能出现的问题

[10:00:00]: Installing dependencies for you...
[10:00:00]: $ bundle update

bundle update过程太慢,可能又是被墙掉了。
关闭终端,打开项目文件夹,找到fastlane init过程中生成的Gemfile文件,并打开:

source "https://rubygems.org"

gem "fastlane"

修改为:

source "https://gems.ruby-china.com"

gem "fastlane"

然后打开终端,cd到当前项目,再输入bundle update

bundle update

到此fastlane init成功了,会出现在目录下出现了Gemfile.lock文件和Fastfile文件。

三、自定义Fastfile

我们知道上面的Fastfile是我们在fastlane init初始化时4个选择中选择了3发布到AppStore,所以生成的Fastfile的功能是只具有发布到AppStore的功能

打开本地的HFMyTest目录
删除下面三个文件

HFMyTest

然后重新运行一次初始化

cd /Users/hf/MyTest/HFMyTest/Example
fastlane init

然后在四个选项中选择第二个


TestFlight

然后按照开始的步骤来,最终的到的Fastfie如下

default_platform(:iOS)

platform :iOS do
  desc "Push a new beta build to TestFlight"
  lane :beta do
    increment_build_number(xcodeproj: "HFMyTest.xcodeproj")
    build_app(workspace: "HFMyTest.xcworkspace", scheme: "HFMyTest-Example")
    upload_to_testflight
  end
end

这个Fastfile主要是用于发布到TestFlight进行测试的,
所以我们可以再终端运行

fastlane beta

进行发布到testflight,和上面的一样,AppStore是上面没有创建对应的app,这个运行最终会失败。

Fastfile自定义,它的格式如下

···
# 自动更新fastlane 工具
# update_fastlane
 
#需要的fastlane的最小版本,在每次执行之后会检查是否有新版本,如果有会在最后末尾追加新版本提醒
fastlane_version "2.100.0"
 
#默认使用平台是 ios,也就是说文件可以定义多个平台
default_platform :iOS
 
platform :iOS do
  before_all do
    # ENV["SLACK_URL"] = "https://hooks.slack.com/services/..."
 
  end
 
  desc "Runs all the tests"
  lane :test do
    scan
  end
 
  desc "提交一个新的Beta版本到 Apple TestFlight"
  desc "This will also make sure the profile is up to date"
  lane :beta do
    # match(type: "appstore") # more information: https://codesigning.guide
    gym(scheme: "app_ scheme") # Build your app - more options available
    pilot
 
    # sh "your_script.sh"
  end
 
  desc "部署一个新版本到App Store"
  lane :release do
    # match(type: "appstore")
    # snapshot
    gym(scheme: "app_ scheme") # Build your app - more options available
    deliver(force: true)
    # frameit
  end
 
  # 你可以定义自己的lane
 
  #执行lane成功后的回调
  after_all do |lane|
    # slack(
    #   message: "Successfully deployed new App Update."
    # )
  end
 
  # 如果流程发生异常会走这里并终止
  error do |lane, exception|
    # slack(
    #   message: exception.message,
    #   success: false
    # )
  end
end
fastlane主要的操作就是action,常用的action有

scan

release情况下无法正常运行scan,需要手动去Build Setting中更改enable Testability 在release 下的状态,改为 yes才可以运行。但是官方不建议做release下开启,Test一般在development configuration 下执行。

gym

常用参数:
scheme :指定打的哪个scheme
project :指定project (未使用cocopods)
workspace :指定workspace (使用cocopods)
clean :打包前clean
xcargs : 附加一些参数传递给xcodebuild 如: xcargs: 'DEBUG_INFORMATION_FORMAT="dwarf-with-dsym"',
export_method :出包方法 app-store, ad-hoc, package, enterprise, development
configuration : 指定构建App的配置  Release、Debug、自定义
output_directory : 输出目录
output_name :输出名称
include_symbols :是否包含调试符号
include_bitcode :是否开启bitcode

pilot

#用于发布**testflight**内部测试,属于**testflight action**的别名
#常用参数:
#ipa :要提交的包地址
#team_name、team_id :如果有多个team 用于区分team
#skip_waiting_for_build_processing : 在提交完成后的等待是否跳过,一般跳过changelog
pilot(
  ipa : '../xx.ipa'
)

deliver

#用于**直接发包到appstore**,可以选择跳过图片和元数据上传,只提包,后面再配图和数据:如下 skip_screenshots 和  skip_metadata 参数
 deliver(
    ipa: "#{OUTPUT_DIRECTORY}" + "/" + "#{IPA_NAME}",
    skip_screenshots: true,
    skip_metadata: true
)

四、插件的使用

  1. 查看所支持的插件
fastlane search_plugins
  1. 查看某种插件
fastlane search_plugins [query] #query为插件名
  1. 添加插件
fastlane add_plugin [name] #name 为插件名
  1. 插件使用实例:上传ipa包至蒲公英pyger
    首先添加pyger插件:
# cd 到项目目录
cd /Users/hf/MyTest/HFMyTest/Example
# 安装插件
fastlane add_plugin pyger 
#或者添加fir插件 fastlane add_plugin firim 上传到fir平台

编辑FastFile,添加以下信息

desc "打包并上传测试版至蒲公英"
  lane :beta_pgyer do 
    #编译并导出ipa包
    gym(
      clean: true,
      scheme: "scheme_name",
      export_method: "development",
     )
    #上传至蒲公英
    pgyer(
      api_key: "", 
      user_key: "", 
    )
 end

然后终端输入

fastlane beta_pgyer

就可以上传包到蒲公英
其中蒲公英的api_key和user_key需要用户去注册蒲公英账号,然后去获取


蒲公英

在Fastfile中可以自定义一个有外部传进来参数的方法options,这个options类似于一个字典,可以传入不同的key来获取value,例如通过options[:myKey] 就可以获取value

lane :myTest do |options|
    myTestStr = options[:key1]
end

传入options的值

fastlane myTest key1:myValue1 key2:myValue2
# 传入了两个数字
{"key1": myValue1,"key2":"myValue2"},
# 获取数值方式为
options[:key1] = myvalue1

在Fastfile中设置了increment_build_number进行自动build数字增加,作用是防止本地版本的build号比App Store(或上次)低而做的自动增长版本号的处理,需要先在Xcode中配置如下

incrementBuild

下面给出我的一套写好的Fastfile

注意:app_scheme

app_scheme
# 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



################## READ  FIRST ##################

######## 使用方法1 自定义version和build #########
###### fastlane iosDebug version:1.0.1  build:1 ######

######## 使用方法2 不写version和build 这时build会根据之前的build+1 #########
###### fastlane iosDebug  ######

###如果需要上传到蒲公英,请运行前先安装插件安装蒲公英的fastlane add_plugin pgyer ###


default_platform(:iOS)   # 设置默认的平台为iOS

############# 需要根据项目自定义设置 ############
APP_NAME = "HFMyTest"             #输出包时app的名字
APP_SCHEME = "HFMyTest-Example"   #这个app打开manage schemes下选中的名称
APP_IPA_OUTPUT_DIRECTORY = "build/packages"    # 打包所保存的文件目录,可以不设置
# 配置蒲公英的apiKey 和 userKey 需要设置 
APP_PGYER_API_KEY = ""
APP_PGYER_USER_KEY = ""
# 配置fir的token
APP_FIR_API_TOKEN = ""


######## 不用设置的 #####################
APP_XCODEPROJ = "#{APP_NAME}.xcodeproj"      # app的xcodeproj
APP_WORKSPACE = "#{APP_NAME}.xcworkspace"    # app的xcworkspace
APP_IPA_TIME = Time.now.strftime("%Y-%m-%d_%H:%M")  # 打包的时间
# APP_INFO_PLIST_PATH = './HFMyTest/HFMyTest-Info.plist'
APP_ENV_PREFIX = ""   # 打包完成后的包文件名字的前缀 区别release和debug



# 版本 build number++
def prepare_version(options)

    #增加version版本号
    if options[:version]
      increment_version_number(
        version_number: options[:version],
        xcodeproj: "#{APP_XCODEPROJ}",
      )
    else
      # 可以不设置
    end

    
    #增加build号  只能是整数和浮点数
    if options[:build]
      increment_build_number(
        build_number: options[:build],
        xcodeproj: "#{APP_XCODEPROJ}",
      )
    else
      last_build = get_build_number(xcodeproj: "#{APP_XCODEPROJ}")
      now_build = last_build.to_i + 1
      increment_build_number(
        build_number: now_build,
        xcodeproj: "#{APP_XCODEPROJ}",
      )
    end
end


#统一的打包方法
def generate_ipa(exportMethod,configuration,options)
    
    # 设置version和build
    prepare_version(options)  
    # 得到最新的version
    app_version = get_version_number(target: "#{APP_SCHEME}")     
    # 最新的build
    app_build = get_build_number(xcodeproj: "#{APP_XCODEPROJ}")         
    # app包名
    app_ipa_name = "#{APP_NAME}_" "#{APP_ENV_PREFIX}" + "#{APP_IPA_TIME}_" + "#{app_version}_" + "#{app_build}"
   
    #打包
    gym(
      clean: true,       # 打包前clean项目
      silent: true,      # 隐藏没有必要的信息
      scheme: "#{APP_SCHEME}",
      workspace: "#{APP_WORKSPACE}", 
      configuration: "#{configuration}",  # 环境
      export_method: "#{exportMethod}",   # app-store、ad-hoc、development、enterprise
      output_directory: "#{APP_IPA_OUTPUT_DIRECTORY}", #ipa的存放目录
      output_name: "#{app_ipa_name}", # 输出ipa的文件名
      # 生成的ipa文件是否包含symbols,这个文件是内存标记文件,用来定位错误信息的,有了这个安装包大小会变大
      include_symbols: true,
      # 生成的ipa文件是否包含bitcode,在本身项目中也可以配置
      include_bitcode: false,
      # keychain授权 Xcode9不允许访问钥匙串密码,所以我们需要手动开权限
      export_xcargs: "-allowProvisioningUpdates"
      )

end

platform :iOS do
  # before_all就是先于所有lane执行的任务
  before_all do
       # 根据安装路径指定要使用的Xcode 
       xcode_select "/Applications/Xcode.app"
       # 超时失败,默认的timeout是10秒
       ENV["FASTLANE_XCODEBUILD_SETTINGS_TIMEOUT"] = "120"
       # Appfile设置了可以忽略下面的证书设置
       # 这个action很重要cert就是下载和安装匹配的Provision Profile文件,不用你去管那些证书不匹配的事情了
       # cert(
          # Appfile设置了这边就可以不用了
          # username: "xxx@xx.com",
          # team_id: "xxxxxxx",
          # output_path:"#{APP_CERT_OUTPUT_DIRECTORY}"
        # )
     
       #这一步就是签名了
       # sigh(
         # Appfile设置了这边就可以不用了 
         # app_identifier: ENV["APP_IDENTIFIER"],
         # team_id: ENV['TEAM_ID'],

         # 更新开发证书而不是生产证书
         # development: ???  
         # 因为是根据BundleID下载,导致adhoc和appstore会优先appstore,导致最后导出报错,如果是adhoc包请设置为true
         # adhoc: true, 
         # 设置所生成描述文件的名称,必须包含文件类型后缀.mobileprovision 可以不设置
         # filename: "MyTest.mobileprovision",

        #  force:true,
        #  provisioning_name: 'MyTest AppStore',
        #  ignore_profiles_with_different_name: true,
        #  output_path: "build/sign",
        # )
  end

  # lane  自定义的任务

  #debug包 上传到蒲公英平台
  #option类似一个字典 option[:version] 取其中value值version  
  lane :iosDebug_pgy do |options|
    APP_ENV_PREFIX = "debug_"
    generate_ipa("development","Debug",options)

    #上传至蒲公英 在这之前请安装插件 fastlane add_plugin pgyer
    if APP_PGYER_API_KEY.length > 0 && APP_PGYER_USER_KEY.length > 0
      pgyer(
        api_key: "#{APP_PGYER_API_KEY}", 
        user_key: "#{APP_PGYER_USER_KEY}", 
      )
    notification(title: "发布成功!", message: "已成功上传到蒲公英平台, 赶快联系测试人员开始测试吧!", open: "https://www.pgyer.com/")
    end
  end

 #debug包 上传到fir
  lane :iosDebug_fir do |options|
    APP_ENV_PREFIX = "debug_"
    generate_ipa("development","Debug",options)

    #上传至fir 在这之前请安装插件 fastlane add_plugin firim
    if APP_FIR_API_TOKEN.length > 0 
      firim(
        firim_api_token: "#{APP_FIR_API_TOKEN}", 
      )
    notification(title: "发布成功!", message: "已成功上传到fir平台, 赶快联系测试人员开始测试吧!", open: "https://fir.im/apps")
    end
  end

  # release发布包
  lane :iosRelease do |options|
    APP_ENV_PREFIX = "appstore_"
    generate_ipa("app-store","Release",options)
    # 上传至app-store
    deliver(
      force: true,              #是否跳过HTML验证报告,默认false
      skip_metadata: true,      #是否跳过上传metadata,默认false
      skip_screenshots: true    #是否跳过上传屏幕截图,默认false
     )
     notification(title: "发布成功!", message: "已成功发布到appstore, 请查验!", open: "https://itunesconnect.apple.com")
  end

  # testFlight包  
  lane :iosTestFlight do |options|
    APP_ENV_PREFIX = "adhoc_"
    generate_ipa("ad-hoc","Release",options)
    # 管理TestFlight的测试用户,上传二进制文件
    pilot
    notification(title: "发布成功!", message: "已成功发布到appstore, 请查验!", open: "https://itunesconnect.apple.com")
  end

  # 当lane执行完成之后进行哪些操作
  after_all do |lane|
  #发送一个桌面通知
  #notification(title: "execute success", subtitle: "执行成功!", message: "lane已经执行成功了")
  end

  error do |lane, exception|
  puts("#{exception.message}")
  notification(title: "执行#{lane}发生异常!", message: "发生异常, 详情请查看控制台!")
  end

end
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270

推荐阅读更多精彩内容