Fastlane

Fastlane简介

Fastlane是用Ruby语言编写的一套自动化工具集和框架,每一个工具实际都对应一个Ruby脚本,用来执行某一个特定的任务,而Fastlane核心框架则允许使用者通过类似配置文件的形式,将不同的工具有机而灵活的结合在一起,从而形成一个个完整的自动化流程。
到目前为止,Fastlane的工具集大约包含170多个小工具,基本上涵盖了打包、签名、测试、部署、发布、库管理等等移动开发中涉及到的内容。如果这些工具仍然没有符合你需求的,没有关系,得益于Fastlane本身强大的Action和Plugin机制,如果你恰好懂一些Ruby开发的话,可以很轻易的编写出自己想要的工具。


Fastlane依赖环境

  • OS X 10.9 (Mavericks) or newer
  • Ruby 2.0 or newer
  • Xcode Command Line Tools (CLT)
  • Paid Apple Developer Account

Fastlane安装

  • 安装最新版Xcode command line tools
xcode-select --install
  • 安装Fastlane
sudo gem install fastlane --verbose
或:sudo gem install -n /usr/local/bin fastlane --verbose

Fastlane工具链

Fastlane工具链.png

Fastlane使用

  • 进入项目文件夹,初始化fastlane
fastlane init

完成后,如有提示新版本更新,然后你按照指令 sudo gem update fastlane 执行后, 可能出现 Nothing to update 提示,那就是当前设置的ruby源并没有同步这项,添加ruby源可解决

gem sources --add https://rubygems.org

该步骤结束后,会在项目根目录生成一个fastlane的文件夹,如下图:


fastlane初始化.png

Appfile里面存放了App的基本信息包括app_identifier、apple_id、team_id等等。如果在第一步init的时候你输入了正确的appId账号和密码会在这里生成正确的team_id信息。
Fastfile是最重要的一个文件,可以编写和定制我们打包脚本的一个文件,我们自定义的一些功能就写在这里。
Fastfile文件中内容:


Fastfile文件.png

从platform :ios do开始下面的就是fastlane可以执行的任务。
1.执行所有lane之前都先执行before_all,该功能里执行了cocoapods 这个action,cocoapods用于为项目执行pod install操作
  before_all do
    # ENV["SLACK_URL"] = "https://hooks.slack.com/services/..."
    cocoapods
  end

2.test这个lane执行了scan这个action,scan用于运行单元测试
  desc "Runs all the tests"
  lane :test do
    scan
  end

3.beta这个lane执行了gym和pilot,gym用于build、sign程序,pilot用于上传应用到TestFlight
  desc "Submit a new Beta Build to 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: "YourAppScheme") # Build your app - more options available
    pilot
    # sh "your_script.sh"
    # You can also use other beta testing services here (run `fastlane actions`)
  end

4.release这个lane执行了gym和deliver,deliver用于上传应用到App Store
  desc "Deploy a new version to the App Store"
  lane :release do
    # match(type: "appstore")
    # snapshot
    gym(scheme: "YourAppScheme") # Build your app - more options available
    deliver(force: true)
    # frameit
  end

5.自定义lane

6.被执行的lane成功之后,执行after_all
  after_all do |lane|
    # This block is called, only if the executed lane was successful
    # slack(
    #   message: "Successfully deployed new App Update."
    # )
  end

7.被执行的lane失败之后,执行error
  error do |lane, exception|
    # slack(
    #   message: exception.message,
    #   success: false
    # )
  end

Fastlane中部分action介绍

  • gym:build、sign应用
gym(
  workspace: "MyApp.xcworkspace",  # 指定.xcworkspace文件的路径。
  scheme: "MyApp",  # 指定项目的scheme名称
  clean: true,  # 在打包前是否先执行clean。
  output_directory: "path/to/dir",  # 指定.ipa文件的输出目录,默认为当前文件夹。
  output_name: "my-app.ipa",  # 指定生成的.ipa文件的名称,应包含文件扩展名。
  configuration: "Debug",  # 指定打包时的配置项,默认为Release。
  silent: true,  # 是否隐藏打包时不需要的信息。
  codesigning_identity: "iPhone Distribution: xxx Co.,Ltd. (5JC8GZ432G)",  # 代码签名证书。(XCode8 该配置已忽略)
  include_symbols: true, 
  include_bitcode: true,
  export_method: "ad-hoc",  # 指定导出.ipa时使用的方法,可用选项:app-store, ad-hoc, package, enterprise, development, developer-id
)
  • xcode_select:根据安装路径指定要使用的Xcode
xcode_select "/Applications/Xcode6.1.app"
  • xcversion:根据版本号指定要使用的Xcode
xcversion(version: "8.1")
  • snapshot:生成多设备上的本地化屏幕截图,如果在fastlane init之后没有生成Snapfile文件需执行
fastlane snapshot init

1.将./SnapshotHelper.swift文加到UI Test target
2.在配置完成后在 UI Tests 的 setup 方法中添加

let app = XCUIApplication()
setupSnapshot(app)
app.launch()

3.在需要截屏的地方是调 snapshot("some name") 方法进行截屏。
4.可以修改 Snapfile 文件来配置需要截屏的设备型号、语言、输出路径等,如下修改设备和语言

devices([
   "iPhone 6",
   "iPhone 6 Plus",
   "iPhone 5",
   "iPhone 4s"
 ])
languages([
  "en-US",
  ["pt", "pt_BR"] # Portuguese with Brazilian locale
])
output_directory "./screenshots" #输出路径
clear_previous_screenshots true #清除之前图片
  • increment_build_number:设置Build号
increment_build_number  # 设置Build号每次自增1
increment_build_number(
  build_number: "75"  # 设置一个指定的Build号
)
  • set_info_plist_value:设置项目Info.plist文件中某个key
set_info_plist_value(
path: "./Info.plist",  # 指定Info.plist文件的路径
key: "CFBundleIdentifier",   # 指定要修改的key
value: "com.krausefx.app.beta"  # 指定key要设定的value
)
  • update_info_plist:更新项目Info.plist文件中的bundle identifier 和 display name
Note:这个action允许你在building之前修改项目的Info.plist文件
update_info_plist(
  display_name: "MyApp-Beta",  # 更新display_name
  app_identifier: "com.example.newappidentifier"  # 更新app_identifier,在Xcode7这里只会修改Info.plist请调用 update_app_identifier来更新
)
  • increment_version_number:设置Version号
Note:patch Version指补丁版本号(7.2.3中的3),minor Version指副版本号(7.2.3中的2),major  Version指主版本号(7.2.3中的7)
version = increment_version_number  # 设置patch Version每次自增1
increment_version_number  # 设置patch Version每次自增1
increment_version_number(
  bump_type: "patch"  # 设置需要自增1的版本类型,可用选项:patch, minor, major
)
increment_version_number(
  version_number: "2.1.1"  # 设置版本号,会覆盖bump_type参数的值
)
  • update_app_identifier:更新项目的bundle identifier
update_app_identifier(
  xcodeproj: PROJECT_FILE_PATH , #可选
  plist_path: "MyApp/Info.plist",  # 指定项目Info.plist文件的路径
  app_identifier: "com.test. MyApp"  # 设置bundle identifier
)
  • get_ipa_info_plist_value:获取.ipa文件中Info.plist文件里某个key的value
get_ipa_info_plist_value(
ipa: "path.ipa",  # 设置.ipa文件的路径
key: "KEY_YOU_READ"  # 指定要获取value的key
)
  • match:自动同步开发团队远程git中的证书和描述文件到本地
match(
git_url: "path",  # 指定包含所有证书的git私有仓库地址
git_branch: "branch", # 指定所使用的git分支
type: "appstore",  # 指定创建证书的类型,可用选项:appstore(生产)、development(开发)
app_identifier: ["tools.fastlane.app", "tools.fastlane.sleepy"],  # 程序的bundle identifier(s),多个时用逗号分隔
readonly: true,  # true:仅获取已经存在的证书和描述文件,而不生成新的
force: true,  # 每次执行match时,是否更新描述文件
force_for_new_devices: true  # 当Apple Developer Portal上的设备数量发生变化时,是否更新描述文件
)
  • sigh:生成描述文件并存入当前文件夹
Note:当生成、匹配证书时,推荐使用match
sigh(
  adhoc: true,  # true:生成AdHoc profiles,false:生成App Store Profiles
  development: ???  # 更新开发证书而不是生产证书
  skip_install: ???  # 默认会自动添加证书到你的本地机器上,设置该参数可以跳过该步骤
  force: true,  # 更新描述文件并忽略其状态,同时自动为ad hoc profiles添加所有设备
  provisioning_name: ??? # 指定Apple Developer Portal(苹果开发者中心网)上使用的描述文件名称
  ignore_profiles_with_different_name: true  # 与provisioning_name参数联合使用,true:当描述文件名称完全匹配provisioning_name时才下载,false:不完全匹配也下载
  filename: "myFile.mobileprovision"  # 设置所生成描述文件的名称,必须包含文件类型后缀. mobileprovision
)
  • cert:获取或生成最新可用的code signing identity
Note:当生成、匹配证书时,推荐使用match
cert
  • update_project_provisioning:通过描述文件更新项目的code signing设置
update_project_provisioning(
  xcodeproj: "Project.xcodeproj",  # 项目.xcodeproj文件路径
  profile: "./watch_app_store.mobileprovision",  # 描述文件路径,必须包含文件类型后缀. mobileprovision
  target_filter: ".*WatchKit Extension.*",  # 使用正则表达式来过滤target名称
  build_configuration: "Release",  # 使用正则表达式来过滤build configuration项,如果不指定该参数,则表示update_project_provisioning这个action作用于所有build configuration项
  certificate: "path"  #  苹果根证书路径
)
  • resign:为已存在的. ipa文件重新Codesign
Note:当你的项目包含嵌套程序或者程序扩展,而这些嵌套程序、程序扩展需要使用各自的描述文件时,你需要提供多个描述文件
resign(
  ipa: "path/to/ipa",  # 指定需要进行重新签名的.ipa文件路径
  signing_identity: "iPhone Distribution: Luka Mirosevic (0123456789)",  # 指定签名类型
  #provisioning_profile: "path/to/profile"  # 指定描述文件路径,单个bundle identifier、描述文件时
  provisioning_profile: {
    "com.example.awesome-app" => "path/to/profile",
    "com.example.awesome-app.app-extension" => "path/to/app-extension/profile"
  }  # 指定描述文件路径,多个bundle identifier、描述文件时
)
  • register_devices:注册新设备到Apple Developer Portal
Note:这个action默认使用Appfile文件中指定的apple_id作为username参数的值,你可以自己指定username参数的值、或者使用环境变量ENV['DELIVER_USER']来覆盖默认值
register_devices(
  #devices_file: "./devices.txt",  # 指定包含设备信息的文件路径,文件具体格式参考https://devimages.apple.com.edgekey.net/downloads/devices/Multiple-Upload-Samples.zip
  devices: {
    "Luka iPhone 6" => "1234567890123456789012345678901234567890",
    "Felix iPad Air 2" => "abcdefghijklmnopqrstvuwxyzabcdefghijklmn"
  },  # 指定要注册的设备列表,格式为:设备名称 => UDID
  username: "luka@goonbee.com"  # 设置Apple ID
) 
  • pilot:上传应用到TestFlight
testflight  # pilot的别名
pilot
testflight(
  changelog: “change”,  # 当上传一个新测试包时,提供测试包的更新信息 
  beta_app_description: “description”,  # 当上传一个新测试包时,提供测试包的描述信息
  beta_app_feedback_email: “***@163.com”,  # 当上传一个新测试包时,提供测试包的email地址
  skip_submission: true, # 是否跳过pilot这个action的发布步骤,而仅仅是上传.ipa文件
  distribute_external: true  # 是否需要将应用发布给外部测试者
)
  • pem:确保推送描述文件有效,如有需要则自动创建推送描述文件
pem(
  development: true,  # true:更新开发推送证书,false:更新生产推送证书
  generate_p12: true,  # 生成p12和gem文件
  force: true, # true:即使旧推送描述文件依然可用,仍然创建新的推送描述文件
  app_identifier: "net.sunapps.9", # optional app identifier,
  save_private_key: true,
  p12_password: 123456,  # 所生成p12文件的密码
  new_profile: proc do |profile_path|  # 如果生成了新的推送描述文件,该block会被调用
    puts profile_path  # 新的PEM文件的绝对路径
    # 添加上传PEM文件到服务器的代码
  end
)
  • deliver:上传元数据、屏幕截图、应用到AppStore
appstore  # deliver的别名
deliver(
  force: true,  #忽略认证
  itc_provider: "abcde12345"  # iTMSTransporter的名字
)
  • commit_version_bump:创建一个’Version Bump’提交操作,在increment_build_number之后使用
Note:这个action将会创建一个’Version Bump’提交操作,与increment_build_number联合使用才有效。该action会检索git仓库,确保你仅仅修改了相关文件(如.plist、.xcodeproj文件)时,提交这些文件到git仓库。如果你还有其他未提交的修改,这个action将会失败。
commit_version_bump(
  message: "message"  # 设置提交信息,默认为Version Bump
)
  • push_to_git_remote:push本地修改到远程分支
push_to_git_remote  # push”master”分支到远程”origin”
push_to_git_remote(
  local_branch: "develop",  # 将被push的本地分支,默认为当前分支
  remote_branch: "develop",  # push的目的分支,默认为本地分支
  force: true,  # 是否push到远程,默认为false
  tags: false,  # ???,默认为true
  remote: "origin"  # ???,默认为”origin”
)
  • add_git_tag:给当前分支添加tag
add_git_tag(
  tag: "my_custom_tag"  # 设置tag
)
  • ensure_git_branch:如果当前lane不是在指定git分支上进行的,将会抛出一个异常
Note:这个action会检查你当前使用的git仓库是不是从指定git分支上check out的,比如,一般我们会在特定的分支上发布应用,此时如果不是在正确的分支上,则该action会终止lane
ensure_git_branch  # 默认为master分支
ensure_git_branch(
  branch: 'develop'  # 指定分支名,可用分支全名,也可用正则表达式自动匹配
)
  • mailgun:发送成功、错误信息给你的团队
mailgun(
  postmaster: "MY_POSTMASTER",
  apikey: "MY_API_KEY",
  to: "DESTINATION_EMAIL",  # 目标对象
  from: "EMAIL_FROM_NAME",  # 发送对象名称
  message: "Mail Body",  # 邮件内容
  subject: “subject”,  # 邮件主题
  success: true,  # 本次build是否成功
  app_link: "http://www.myapplink.com",  # 所发布的应用链接
  ci_build_link: "http://www.mycibuildlink.com",
  template_path: "HTML_TEMPLATE_PATH”,  # HTML邮件模板
  reply_to: "EMAIL_REPLY_TO"
)

Fastlane进阶

  • 传递参数
从command line传递参数给你自定义的lane:
fastlane [lane] key:value key2:value2
e.g. fast lane deploy submit:false build_number:24
同时,你需要修改自定义的lane声明包含|options|,才能访问所传的值:
before_all do |lane, options|
  # ...
end
before_each do |lane, options|
  # ...
end
lane :deploy do |options|
  # ...
  if options[:submit]
    # Only when submit is true
  end
  # ...
  increment_build_number(build_number: options[:build_number])
  # ...
end
after_all do |lane, options|
  # ...
end
after_each do |lane, options|
  # ...
end
error do |lane, exception, options|
  if options[:debug]
    puts "Hi :)"
  end
end
  • 切换lanes
正在执行lane时,切换lanes:
lane :deploy do |options|
  # ...
  build(release: true) # that's the important bit
  hockey
  # ...
end
lane :staging do |options|
  # ...
  build # it also works when you don't pass parameters
  hockey
  # ...
end
lane :build do |options|
  scheme = (options[:release] ? "Release" : "Staging")
  ipa(scheme: scheme)
end
  • 返回values
lane中定义的最后一行为返回值
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
关键字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
切换lane期间使用next时,会返回到上一个正在执行的lane
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
当你使用next提前终止正在执行的lane时,你所定义的after_each、after_all blocks依然会如常触发;
在任意lane被调用前会先执行before_each blocks;
在任意lane成功执行完后会执行after_each blocks,如果lane执行失败,则不会调用after_each blocks,而是调用error block;
e.g. 如下案例,会执行4次before_each、after_each
lane :deploy do
  archive
  sign
  upload
end
lane :archive do
  # ...
end
lane :sign do
  # ...
end
lane :upload do
  # ...
end
  • 直接执行actions
如果你未将actions加入Fastfile中就想执行某个action,如下,但是这种方式在某些类型的parameters 时会失败:
fastlane run notification message:"My Text" title:"The Title"
查看action具体信息(如可用options):
fastlane action [action_name]
  • parameters、options的优先级
1.CLI parameter (e.g. gym --scheme Example) or Fastfile (e.g. gym(scheme: 'Example'))
2.Environment variable (e.g. GYM_SCHEME)
3.Tool specific config file (e.g. Gymfile containing scheme 'Example')
4.Default value (which might be taken from the Appfile, e.g. app_identifier from the Appfile)
5.If this value is required, you'll be asked for it (e.g. you have multiple schemes, you'll be asked for it)
  • Note
1.如果需要,请在自定义lane声明前导入其他Fastfile
2.定义一个新的lane时,请确保不会出现名字冲突
3.如果你用重写一个已经存在的lane(比如重写导入的Fastfile中的lane),请使用关键字override_lane
  • 环境变量
你可以在Fastfile文件的同级目录下的.env 或 .env.default文件中定义环境变量
  • 私有lanes
直接执行某些lanes可能并没有意义,这时你可以私有化他们:
private_lane :build do |options|
  # ...
end

Fastlane实战

Appfile

app_identifier "your identifier" # The bundle identifier of your app
apple_id "your id" # # Your Apple email address

team_id "3MK2KJ67U7"  # Developer Portal Team ID

for_lane :testBeta do  # 可在这里对自定义lane修改appId,identifier,也可以在自定义lane里面修改
    app_identifier "YourAppId"
    apple_id "YourAppleAccount"
end
# you can even provide different app identifiers, Apple IDs and team names per lane:
# More information: https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Appfile.md

Fastfile

# Customise this file, documentation can be found here:
# https://github.com/fastlane/fastlane/tree/master/fastlane/docs
# All available actions: https://docs.fastlane.tools/actions
# can also be listed using the `fastlane actions` command

# Change the syntax highlighting to Ruby
# All lines starting with a # are ignored when running `fastlane`

# If you want to automatically update fastlane if a new version is available:
# update_fastlane

# This is the minimum version number required.
# Update this, if you use features of a newer version
fastlane_version "2.3.0"

default_platform :ios

PROJECT_FILE_PATH = './test1.xcodeproj'
APP_NAME = 'test1'
SCHEME_NAME = 'test1'

APPSTORE_IDENTIFIER = 'com.test.app'
TESTFlIGHT_IDENTIFIER = 'com.test.app6'
PUSH_IDENTIFIER = 'com.test.apptest'
PLIST_FILE_PATH = 'test1/Info.plist'

# 更新bundle信息
def update_app_bundle(bundle)
  update_app_identifier(
    xcodeproj: PROJECT_FILE_PATH ,
    plist_path: "#{PLIST_FILE_PATH}",
    app_identifier: bundle
  )
end

# build number++
def prepare_version(options)
    #增加版本号
    say 'version number:'
    say options[:version]
    increment_version_number(
        version_number: options[:version],
        xcodeproj: PROJECT_FILE_PATH,
    )
    #增加build号  只能是整数和浮点数
    say 'build number:'
    say options[:build]
    increment_build_number(
        build_number: options[:build],
        xcodeproj: "#{PROJECT_FILE_PATH}",
    )
end

# 设置Info_plist_value里的值(也可直接用 update_info_plist )
def set_info_plist_value(path,key,value)
    #sh 这里是fastline目录里
    sh "/usr/libexec/PlistBuddy -c \"set :#{key} #{value}\" ../#{path}"
end

# 打包 注:这里needClean 针对本人项目使用
def generate_ipa(needClean,exportMethod,options)
    #say 'generate ipa'
    fullVersion = options[:version] + '.' + options[:build]
    gym(
      project: "test1.xcodeproj",
      #workspace: ‘./test1.xcworkspace',
      scheme: 'test1',
      clean: needClean,
      output_directory: "../Test" + fullVersion,
      output_name: 'test1.ipa',
      #configuration: 'Release',
      include_symbols: 'true',
      include_bitcode: 'false',
      archive_path: "../Test" + fullVersion,
      export_method: "#{exportMethod}”
    )
    # sh "mv ./../build/#{APP_NAME}.app.dSYM.zip ./../build/#{APP_NAME}_#{fullVersion}_#{typePrefix}.app.dSYM.zip"
end

platform :ios do
  before_all do
    # ENV["SLACK_URL"] = "https://hooks.slack.com/services/..."
    #cocoapods  #执行cocoapods
  end

  desc "打App Store包"
  lane :release do
    ensure_git_branch(
      branch: "develop"
    )
    # match(type: "appstore")
    # snapshot
    needClean = true
    prepare_version(options)
    update_app_bundle("#{APPSTORE_IDENTIFIER}")
    generate_ipa(needClean,"app-store",options)
    deliver(force: true)
    # frameit
  end

 desc "打develop包"
  lane :Develop do |options| #测试都是在本地已经有证书和描述文件情况下,如果没有证书和对应描述文件,可打开cert和sigh(待测试)
    #cert
    #sigh(
      #adhoc:true.
      #app_identifier: "#{TESTFlIGHT_IDENTIFIER}"
    #)
    needClean = false
    prepare_version(options)
    update_app_bundle("#{TESTFlIGHT_IDENTIFIER}")
    generate_ipa(needClean,"ad-hoc",options)

  end

  desc "打testFlight"
  lane :beta do |options|
   #cert
    #sigh
    needClean = false
    prepare_version(options)
    update_app_bundle("#{TESTFlIGHT_IDENTIFIER}")
    generate_ipa(needClean,"app-store",options)
    pilot
  end

  desc "打不同identifier Push包"
  lane :enterprise do |options|
   #cert
    #sigh
   #pem
    needClean = false
    prepare_version(options)
    update_app_bundle("#{PUSH_IDENTIFIER}")
    generate_ipa(needClean,"development",options)
  end

  after_all do |lane|
    # This block is called, only if the executed lane was successful
    # slack(
    #   message: "Successfully deployed new App Update."
    # )
  end

  error do |lane, exception|
    # slack(
    #   message: exception.message,
    #   success: false
    # )
  end
end

# More information about multiple platforms in fastlane: https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Platforms.md
# All available actions: https://docs.fastlane.tools/actions

# fastlane reports which actions are used
# No personal data is recorded. Learn more at https://github.com/fastlane/enhancer

Snapfile

# Uncomment the lines below you want to change by removing the # in the beginning

# A list of devices you want to take the screenshots from
 devices([
   #"iPhone 6",
   #"iPhone 6 Plus",
   #"iPhone 5",
   "iPhone 4s"
 ])

languages([
  "en-US",
  ["pt", "pt_BR"] # Portuguese with Brazilian locale
])

# The name of the scheme which contains the UI Tests
# scheme "SchemeName"

# Where should the resulting screenshots be stored?
 output_directory "./screenshots"

 clear_previous_screenshots true # remove the '#' to clear all previously generated screenshots before creating new ones

# Choose which project/workspace to use
# project "./Project.xcodeproj"
# workspace "./Project.xcworkspace"

# Arguments to pass to the app on launch. See https://github.com/fastlane/snapshot#launch-arguments
# launch_arguments(["-favColor red"])

# For more information about all available options run
# snapshot --help

执行

 fastlane develop version:B1.0 build:1

如需要打多个包可添加如下脚本

#!/bin/sh

#
# usage:
# > sh build.sh 1.0.0 1
#

versionNumber=$1 # 1.0.0
buildNumber=$2 # 1

rm -rf build
basicLanes="develop enterprise beta release"
for laneName in $basicLanes
do
    fastlane $laneName version:$versionNumber build:$buildNumber
done

可添加参数 区别渠道
# channelIds="fir 91"
# for channelId in $channelIds
# do
    # fastlane Channel version:$versionNumber build:$buildNumber channel_id:$channelId
done

使用homebrew安装Jenkins

brew install jenkins

执行

sh build.sh 1.0.0 1

参考资料

1.Fastlane官网
2.fastlane Tutorial: Getting Started
3.fastlane 教程: 入门
4.iOS 持续集成之 fastlane + jenkins

推荐阅读更多精彩内容

  • fastlane运行所需要的环境: OS X 10.9以上 Ruby 2.0 以上 Xcode 拥有一个开发者账号...
    阿姣_0405阅读 2,127评论 0 4
  • 更新节点:2019-05-15由于fastlane更新频繁,已更新到 2.105.2 2.122.0更新内容:fa...
    就叫yang阅读 56,620评论 72 136
  • 有的公司分工比较细,诸如项目打包、发布这些工作,都会有专门的测试人员去负责,这就为开发人员省去了大部分时间。当然,...
    莮亾阅读 8,554评论 30 40
  • fastlane 介绍 Fastlane是一套使用Ruby写的自动化工具集,旨在简化Android和iOS的部署过...
    MirL阅读 3,086评论 0 1
  • 作为一个旅游爱好者,经常有朋友问我一些关于美国热水的问题。 比如: “听说过美国人不喝热茶,宾馆里也没有热开水,也...
    我的傻豆豆阅读 1,085评论 1 2