美文网首页征服iOSiOS猛码计划iOS成长之路
24. 自动化工具Fastlane笔记一: 安装, 打包,上传

24. 自动化工具Fastlane笔记一: 安装, 打包,上传

作者: 春田花花幼儿园 | 来源:发表于2017-02-12 22:45 被阅读3434次

在使用Fastlane打包之前,你的项目必须是正确的。我的意思是说,你得用原生的方式成功Archive,upload你的测试项目!

前话

因为初探,只是简单的介绍了fastlane安装针对fastlane生成目录下的配置文件中的内容进行分析。关于如何screenShot等其他的用法并没有探究和写明,之后在自己尝试之后,肯定会更新相关文章。

这里还是先使用,穿插概念的模式。文章中大量引用了 Wang Hailong 文章中段落,在这里非常感谢Wang Hailong,因为他写的已经足够的简练,所以我没必要再重复造轮子,他的文章地址会在本章最末写出。

业务需求

我想大家能够快速搜索到这篇文章,已经对自己的业务需要了解的很清楚了。就我们公司而言,因为是同时服务很多个客户,所以每次打包的时候,都会花费开发人员很多时间。 所以,上马一个自动化工具,迫在眉睫。
当然,我们也尝试过把打包这个没有任何技术含量的工作交给售后和其他岗位的同事去做,但是打包过程中,遇到一点问题,开发人员还是会被叫过去解决问题,一来二去,还不如开发人员自己打包快。

什么是Fastlane?

官方自己的定义是这样的:

fastlane is a tool for iOS, Mac, and Android developers to automate tedious tasks like generating screenshots, dealing with provisioning profiles, and releasing your application.

从这里我们可以看出,fastlane针对的developers主要是 iOS, Mac, and Android。目的在于替代完成一切 没有技术含量的、乏味的工作。简单列举了比如screenshot,provisioning profiles,deliver。

我们第一次接触fastlane或者之前从来没有使用过CLI工具的人,对于上边的概念描述只能知道他的功能,但是不知道fastlane到底是哪个类型的工具,怎么实现上边功能。 关于这一点,我在阅读 Wang Hailong 的文章得到了很简练的答案,摘录如下:

fastlane命令是一个流程控制的命令行工具(CLI),通过内部集成action和第三方的action完成一系列控制流程。运行fastlane命令行工具,会读取当前目录或者./fastlane目录下的Fastfile配置文件。

在Fastfile中:

action => Fastlane中的每一条命令都是一个扩展(action),上面提到的deliver,sigh之类的工具本身是CLI,但是在Fastlane中内嵌了对他们支持的action
lane => Fastlane中流程的合集,每一个动作即可以是action,也可以是其他的lane。语法和ruby中的rake非常像
一个简单的发布流程:

lane :deploy do
  # 执行 pod instasll
  cocoapods
  # 执行 carthage bootstrap
  carthage
  # 增加build版本号
  increment_build_number
  # 编译代码
  gym
  # 发布到Apple Store
  deliver(force: true)
end

读到这里,如果你不是很清楚的理解lane和action的具体含义,那么可以暂时放下,我在下边小结中会提到这个两个概念。 只要关注第一段话就好。

安装

很负责任的告诉大家,fastlane的基础使用非常通俗易上手,所以我们大部进行不下去的环节是安装的部分。 尤其是并不经常使用终端工具的朋友。

首先:
fastlane安装的方式有三种,分别是HomebrewInstaller ScriptRubygems。 这里需要注意的是,不管选择哪种方式,你都要安装Ruby。

我使用的是Rubygems的方式。

关于我自己在安装的过程中遇到的坑,主要是集中在系统是OS X 10.11的时候,ruby版本2.4,安装fastlane遇到问题,后来在issues上发现有很多人遇到了和我同样的问题,最后在我自己的电脑上安装成功。年后上班,开始在公司的电脑上安装fastlane, 把系统升级到10.12最新版本之后,Rubygems安装一遍过。
关于这一点,我猜想是fastlane的最新版本在安装的时候,针对非最新系统支持不好。当然,只是猜测。

其他:

接着:

  • 确保Xcode Command Line Tools 安装了最新版
    xcode-select --install 
  • 如果你单独安装过ruby(如果你能看得懂这句),去掉sudo。如果使用系统自带的ruby,需要sudo权限
    [sudo] gem install fastlane

—(引用自 Wang Hailong的文章)

提示安装成功,恭喜你,你已经成功了一半。

初始化

cd到项目根目录下,然后执行
fastlane init
执行过程中,会让你手动输入Apple IDPassword,Team等相关问题。成功之后,你将会在跟目录下看到fatlane这个文件夹

这个目录下,就包含了我们之后关于打包上传以及屏幕截图等等的配置文件。

关于这个部分的具体过程和fatlane的目录结构,还是援引Wang Hailong的文章,如下:

在项目根目录下,初始化Fastlane:

fastlane init


执行fastlane init

提问了你的Apple ID,Team的问题之后,fastlane会自动检测当前目录下项目的App Name和App Identifier。如果检测的不对,选择n自行输入。


输入对应项目的开发者帐号 名

接下来会问你这个app是否需要在iTC和ADC中创建(上一步中如果选择y会自动检测是否需要创建),fastlane会调用produce进行初始化,如果现在还不想创建,也可以之后再运行produce init进行这个流程。如果不执行produce的流程,deliver的流程也会被掠过,当然之后也可以deliver init运行完全一样的流程。


确认项目相关信息是否正确

在执行deliver init的过程中,会同步iTC中的所有语言的元数据和截图,并按照目录结构组织好。目录结构应该类似下面:

├── Appfile
├── Deliverfile
├── Fastfile
├── metadata
│ ├── copyright.txt
│ ├── en-US
│ │ ├── description.txt
│ │ ├── keywords.txt
│ │ ├── marketing_url.txt
│ │ ├── name.txt
│ │ ├── privacy_url.txt
│ │ ├── release_notes.txt
│ │ └── support_url.txt
│ ├── primary_category.txt
│ ├── primary_first_sub_category.txt
│ ├── primary_second_sub_category.txt
│ ├── secondary_category.txt
│ ├── secondary_first_sub_category.txt
│ ├── secondary_second_sub_category.txt
│ └── zh-Hans
│ ├── description.txt
│ ├── keywords.txt
│ ├── marketing_url.txt
│ ├── name.txt
│ ├── privacy_url.txt
│ ├── release_notes.txt
│ └── support_url.txt
└── screenshots
├── README.txt
├── en-US
│ ├── 一堆png图片

> 这里肯定会被创建的是Appfile和Fastfile。如果Deliverfile,screenshots和metadata目录没被创建,可以运行deliver init来创建。
            ![fastlane init 命令执行成功](http:https://img.haomeiwen.com/i594219/c83597703e39c665.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

> 1. Fastfile => 用来定义所有的lane任务Fastfile帮助
> 2. Appfile => 是用来存储一些公共信息的,比如app_identifier,apple_id,team_id,itc_team_id等。Appfile帮助
> 3. Deliverfile => deliver的配置文件Deliverfile帮助
> PS:
    
> 1. 这里有个小问题,iTC和ADC中的Team ID是不一样的,在fastlane init中只会自动在Appfile里写入ADC的team_id,所以在这个过程中会不停的问你iTC的Team ID,所以在创建完Appfile后,手动在里面添加itc_team_id。
> 2. 这个问答对不同的项目可能有各种各样的分支。我已经用不同的项目试过很多次了,但是可能还不是全部,所以你还需要见招拆招。
> 3. 在这里可以安心的输入密码,所有的密码都加密保存在系统的Keychain里。



其中`Fastfile`,`Appfile`,`Deliverfile`这三个文件,功能如上介绍,如果不出问题的话,大家的这三个文件内容应该是和我一样的如下(可以选择sublime打开,然后把当前显示样式设置为`Ruby`,显示效果如下:

#####Fastfile:

    fastlane_version "2.14.2"
    
    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 "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: "BlusinessAreaPlat") # Build your app - more options available
        pilot
    
        # sh "your_script.sh"
        # You can also use other beta testing services here (run `fastlane actions`)
      end
    
      desc "Deploy a new version to the App Store"
      lane :release do
        # match(type: "appstore")
        # snapshot
        gym(scheme: "BlusinessAreaPlat") # Build your app - more options available
        deliver(force: true)
        # frameit
      end
    
      # You can define as many lanes as you want
    
      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



#####Appfile:


    app_identifier "com.xxxx.xxxx" # The bundle identifier of your app
    apple_id "xxxxx@xxxx.com" # Your Apple email address
    
    team_id "XXXXXXXXX"  # Developer Portal Team ID



#####Deliverfile:

    app_identifier "com.xxxx.xxxx" # The bundle identifier of your app
    username "xxxxx@xxxx.com" # your Apple ID user



在**Fastfile**这个文件中,就是我们`打包`,`发布到fir,testFlight,appstore`等等操作的具体配置文件。

##关于Fastfile文件
对照我们上边`fastlane init`命令创建出来的默认是的`Fastfile`默认文件,我们一次解析文件中的「元素」所代表的含义:
>* fastlane_version => 指定fastlane最小版本
>
>* default_platform => 指定当前平台,可选ios,android,mac
 
>* desc => 一个lane(英文翻译是:小巷,小路,我们这里可以理解为任务)的描述,一般说明这个lane是做什么的
 
>* lane : 任务的名字 do => 指定任务的名字,我们在执行这个任务的时候,调用命令的格式为 `fastlane 任务的名字`,比如`fastlane release`。 在`Fastfile`文件中会有很多的`lane`,我们通俗的把理解为小任务,每个小任务都负责一个功能。 然后我们调用不同的小任务,来实现打包、上传到development、上传到testFlight、上传到app store等功能。 `换句话来说,也就是,我们具体关于怎么打包、怎么上传的配置是放到对应的lane里边的`
 
>* (lane......)end  => lane到end标识之间的内容,声明了一个任务具体执行哪些操作,不指定哪些操作



**我们来看默认生成的一个具体的例子,如下:**
![](http:https://img.haomeiwen.com/i594219/57b2d0bd304c7b1b.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

>* 这个模块是表示我们上传到app store的。

>* 这里默认指定了`gym`和`deliver`两个功能模块,这两个模块已经能够保证我们应用打包上传到app store. 但是我们根据自己公司的项目的实际需求,还会定制特有的配置,也是写到这里边。

>* 这里`gym`,`deliver`是`fastlane`工具集中的一个功能模块,这个功能模块包括很多的配置选项,在`lane`中,以` 功能模块名字 (配置选项1,配置选项2......)`这种形式表示。这样类似的功能模块还有很多。

 官方关于上述的功能模块的列表如下:
![](http:https://img.haomeiwen.com/i594219/147c9bfdbec9650a.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
中文的解释,可以在[Wang Hailong](https://www.zhihu.com/people/hailong)中找到很详细的归纳,如下:


> Fastlane组件
Fastlane是一套工具集,包括:

>* 测试
    * scan => 自动运行测试工具,并且可以生成漂亮的HTML报告
* 证书,配置文件
    * cert => 自动创建管理iOS代码签名证书
    * sigh => 一声叹息啊,这么多年和Provisioning Profile战斗过无数次。总是有这样那样的问题导致配置文件过期或者失效。sigh是用来创建、更新、下载、修复Provisioning Profile的工具。
    * pem => 自动生成、更新推送配置文件
    * match => 一个新的证书和配置文件管理工具。我会另写一篇文章专门介绍这个工具。他会所有需要用到的证书传到git私有库上,任何需要配置的机器直接用match同步回来就不用管证书问题了,小团队福音啊!
* 截图
    * snapshot => 用Xcode7推出的UI test功能实现自动化截图
    * frameit => 可以把截的图片自动套上一层外边框
* 编译
    * shenzhen => 当年大名鼎鼎的自动编译工具,现在已经被弃用了
    * gym => Fastlane家族的自动化编译工具,和其他工具配合的非常默契
* 发布
    * produce => 如果你的产品还没在iTunes Connect(iTC)或者Apple Developer Center(ADC)建立,produce可以自动帮你完成这些工作
    * deliver => 自动上传截图,APP的元数据,二进制(ipa)文件到iTunes Connect
* TestFlight管理
    * pilot => 管理TestFlight的测试用户,上传二进制文件
    * boarding => 建立一个添加测试用户界面,发给测试者,可自行添加邮件地址,并同步到iTC
辅助工具
    * spaceship => 为pilot,boarding和deliver等工具提供和 iTC 和 ADC 的交互API。spaceship本来是个独立的项目,后来被Fastlane收编进来
* WatchBuild => 是一个独立的iTC监控工具,开启WatchBuild可以监控iTC上的文件状态,弹出MacOS自带的Notification
* Android
    * supply => 自动上传到Google Play工具(如果有时间,我想把国内提供API的Android Store都写个插件自动上传,这个问题从10年我刚开始工作就觉得是个痛点)
    * screengrab => Android的自动截图工具



每个小功能模块下,都有针对自己的具体的`配置项`,这里我就不拉长篇幅了,在[Wang Hailong](https://www.zhihu.com/people/hailong)的文章中也可找到,他已经做了很好的归纳。


##打包

这里再执行打包就很简单了,默认`Fastfile`中包含`gym`这一个功能模块的`lane`,都可以执行**打包**操作。 在`gym`中指定`output_directory`一项来指定输出的.ipa文件的输出目录,如果不指定的话,只在项目根目录下。

执行`lane`命令:

    fastlane lane的名字

##上传

在我们`fastlabe init`命令成功执行完毕,并且各项profile安装正确的情况下, 直接执行下边命令:

     fastlane release
  
  经过一大串的运行之后,就可以上传到app store。至此,基础的使用部分分享就结束了。


###扩展
当然,我这里这是说的默认没有特殊需求的情况下,如果你有特殊需求,你完全可以自定义一个名字叫做**deploy**的关于上传到app store的`lane`。
 这里给大家附上一个[莮亾](http://www.jianshu.com/u/b0fe052dee94)自定义的`lane`,如下:
 
     # You can define as many lanes as you want
    desc "Deploy a new version to the App Store"
    lane :release do |op|
    increment_version_number(version_number: op[:version]) #根据入参version获取app版本号
    increment_build_number(build_number: op[:version])  #将build号设置与app版本号相同

    # 设置app的info.plist文件项
    set_info_plist_value(path: "./xxx/Info.plist",  #info.plist文件目录
                        key: "UIFileSharingEnabled",  # key,将plist文件以Source Code形式打开可查询对应的key
                        value: false)  # value

    # 设置自定义plist文件项,用于给app配置不同的服务器URL
    set_info_plist_value(path: "./xxx/hostAddress.plist",
                        key: "host",
                        value: "https:/zhengshiServer:xx/xxx/xxx")

    # 更新Provisioning Profile
    # 在项目当前目录下创建provisions文件夹,并将App Store版本的.mobileprovision文件保存在里面,名称随意。
    update_project_provisioning(profile: "./provisions/appstore.mobileprovision")

    # 更新项目团队
    update_project_team(path: "xxx.xcodeproj",
                  teamid: "5JC8GZ432G")

    # 开始打包
    gym(use_legacy_build_api: true,
        output_name: "appstore",  # 输出的ipa名称
        silent: true,  # 隐藏没有必要的信息
        clean: true,  # 在构建前先clean
        configuration: "Release",  # 配置为Release版本
        codesigning_identity: "iPhone Distribution: xxx Co.,Ltd. (5JC8GZ432G)",  # 代码签名证书
        buildlog_path: "./fastlanelog",  # fastlane构建ipa的日志输出目录
        output_directory: "/Users/xxx/Desktop")  # ipa输出目录
    end

  
**其他部分我只测试过上传到testFlight。其实,经过我们的试验,我们只要打开testFlight,我们不管是单独上传到testFlight,还是上传到app store,只要版本号大于testFlight正在部署的构建版本,那么苹果会自动把最新(刚上传)的构建版本,自动部署,并且给测试用户发送邮件通知,提示他们更新内测应用。所以,这里关于testFlight上传到哪里,并不是很重要了。**
  

  
##结尾

因为是初探,所以我们只写了fastlane非常基础的功能部分。fastlane作为一个流行的自动化打包工具,他的强大和功能的丰富性,远远不止如此,他可以帮助我们管理profile文件,也可以利用UI Test来实现屏幕展示截图screenShot等等解放我们双手的操作。如果大家想要研究screenShot可以看下[《fastlane-screenshot》](https://docs.fastlane.tools/getting-started/ios/screenshots/)和喵神的[《WWDC15 Session笔记 - Xcode 7 UI 测试初窥》](https://onevcat.com/2015/09/ui-testing/)

还有, fastlane还有丰富的插件提供给我们使用。这些插件是独立发布在fastlane之外的,实质是一个或者一组action的打包。[Wang Hailong](https://www.zhihu.com/people/hailong)的文章中有关于`fastlane-plugin-versioning`和`fastlane add_plugin firim`这个两插件的应用。

另外,因为fastlane其实是`Ruby`语言写的。如果插件和fastlane仍然满足不了我们的需求和流程需求,我们可以自己写一个action。

##Wang Hailong的文章
[《小团队的自动化发布-Fastlane带来的全自动化发布》](https://whlsxl.github.io/#to_app_store)

##其他参考资料
1. [《fastlane docs(官方)》](https://docs.fastlane.tools/getting-started/ios/beta-deployment/),
2. [《fastlane github地址》](https://github.com/fastlane/fastlane)
3. [《Fastlane实战(一):移动开发自动化之道》](http://www.jianshu.com/p/1aebb0854c78)
4. 2. [《使用Fastlane实现iOS项目自动打包》](http://www.jianshu.com/p/840943eff17b)



#交流

![](http:https://img.haomeiwen.com/i594219/cea3f887e6abdadc.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

相关文章

网友评论

  • Bruce_XHG:请问itc_team_id 是什么 在哪里找的
  • 一抹相思泪成雨:问下,输入命令fastlane init 到
    What would you like to use fastlane for?一直在这等待,然后就提示crash?为啥啊
    春田花花幼儿园:@Hoolink init只是创建配置文件夹,很快的. 你是不是fastline没有配置好? 这个问题我也没有遇到过, 你还是github或者谷歌下
  • TimHsieh:我按照您的文章配置了一遍,但是在 后出现了问题,求指教啊
    ```
    ** EXPORT FAILED **
    [13:24:37]: Exit status: 70
    [13:24:37]: No provisioning profile provided
    [13:24:37]: Make sure to pass a valid provisioning for each required target
    [13:24:37]: Check out the docs on how to fix this: https://github.com/fastlane/fastlane/tree/master/gym#export-options

    +---------------+-------------------------+
    | Build environment |
    +---------------+-------------------------+
    | xcode_path | /Applications/Xcode.app |
    | gym_version | 2.62.1 |
    | export_method | enterprise |
    | sdk | iPhoneOS11.1.sdk |
    +---------------+-------------------------+

    [13:24:37]: ▸ cd /Users/Stevens/SquirrelPay
    [13:24:37]: ▸ export PATH="/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin:/Applications/Xcode.app/Contents/Developer/usr/bin:/Users/Stevens/.rvm/gems/ruby-2.4.0/bin:/Users/Stevens/.rvm/gems/ruby-2.4.0@global/bin:/Users/Stevens/.rvm/rubies/ruby-2.4.0/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/go/bin:/Users/Stevens/.rvm/bin"
    [13:24:37]: ▸ /usr/bin/touch -c /Users/Stevens/Library/Developer/Xcode/DerivedData/SquirrelPay-gwgxfkfdjldgwchhopdkiowimjch/Build/Intermediates.noindex/ArchiveIntermediates/SquirrelPay/BuildProductsPath/Release-iphoneos/SquirrelPay.app.dSYM
    [13:24:37]: ▸ ** ARCHIVE SUCCEEDED **
    [13:24:37]:
    [13:24:37]: ⬆️ Check out the few lines of raw `xcodebuild` output above for potential hints on how to solve this error
    [13:24:37]: 📋 For the complete and more detailed error log, check the full log at:
    [13:24:37]: 📋 /Users/Stevens/Library/Logs/gym/SquirrelPay-SquirrelPay.log
    [13:24:37]:
    [13:24:37]: There seems to be a mismatch between your provided `export_method` in gym
    [13:24:37]: and the selected provisioning profiles. You passed the following options:
    ```
  • Twenty_:fastlane 二步认证 如何将密码传给 fastlane
  • fighter0501:你好 有具体的fastlane文件参考下不?谢谢了~~
  • TimHsieh:你好,我报错提示:Add 'gem "cocoapods"' to your Gemfile and restart fastlane
    请问这个Gemfile里面要怎么添加 'gem "cocoapods"
    春田花花幼儿园:@像绅士一样去征服 这个百度安装命令就行
  • 会疼的白痴:你好,我想问一下,就是match管理证书时,上传证书到私有库,之后配置lane,可是我们别人说的可以使用match development/adhoc/appstore即可获取相应的证书,可是没想明白怎么使用match development,是创建一个lane吗
    春田花花幼儿园:@会疼的白痴 不好意思哈,我这边没有用到match管理证书
  • PisPMPL:current directory:
    /Users/zhangji/.fastlane/bin/bundle/lib/ruby/gems/2.2.0/gems/json-2.1.0/ext/json/ext/generator
    make "DESTDIR="
    compiling generator.c
    clang: warning: no such sysroot directory:
    '/Applications/Xcode-7.3.1.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk'
    [-Wmissing-sysroot]
    In file included from generator.c:1:
    In file included from ./../fbuffer/fbuffer.h:5:
    In file included from /Users/zhangji/.fastlane/bin/bundle/include/ruby-2.2.0/ruby.h:33:
    In file included from /Users/zhangji/.fastlane/bin/bundle/include/ruby-2.2.0/ruby/ruby.h:29:
    /Users/zhangji/.fastlane/bin/bundle/include/ruby-2.2.0/ruby/defines.h:26:10: fatal error: 'stdio.h' file not
    found
    #include <stdio.h>
    ^
    1 error generated.
    make: *** [generator.o] Error 1

    make failed, exit code 2

    Gem files will remain installed in /Users/zhangji/.fastlane/bin/bundle/lib/ruby/gems/2.2.0/gems/json-2.1.0 for
    inspection.
    Results logged to
    /Users/zhangji/.fastlane/bin/bundle/lib/ruby/gems/2.2.0/extensions/x86_64-darwin-15/2.2.0-static/json-2.1.0/gem_make.out

    An error occurred while installing json (2.1.0), and Bundler cannot continue.
    Make sure that `gem install json -v '2.1.0'` succeeds before bundling.

    这是我在install plugin时候遇到的,请问知道怎么解决嘛~~
    PisPMPL:@春田花花幼儿园 这个命令我试了,没用
    春田花花幼儿园:@perfectisshittt " Make sure that `gem install json -v '2.1.0'` succeeds before bundling."
  • 缭雾:感谢分享:smiley:
  • 747071ac3c1d:我执行过fastlane init后 我的工程文件里面没有出现和你类似的信息 我看了看他打印的东西 出现了这个原因 下面的都是false 我怎么修改下面的fase选项为true
    +-----------------------+------------------------+
    | deliver 2.27.0 Summary |
    +-----------------------+------------------------+
    | screenshots_path | ./fastlane/screenshots |
    | metadata_path | ./fastlane/metadata |
    | username | xxxxx |
    | app_identifier | xxxxxx |
    | edit_live | false |
    | platform | ios |
    | skip_binary_upload | false |
    | skip_screenshots | false |
    | skip_metadata | false |
    | force | false |
    | submit_for_review | false |
    | automatic_release | false |
    | dev_portal_team_id | W398QN8ZGP |
    | overwrite_screenshots | false |
  • 五蕴盛:以前整过,没成功,就放那里了,看来得向你学习。不错

本文标题:24. 自动化工具Fastlane笔记一: 安装, 打包,上传

本文链接:https://www.haomeiwen.com/subject/dhczittx.html