flutter Jenkins+fastlane 自动化打测试包, 并上传蒲公英

文章目录

自动化打包上传是一个释放双手的过程, 本文就简单重复一下打包的过程

本文包括 iOS 和 android 篇.

如果有需要, macOS 和 web 也可以加进来, 步骤并不复杂, 但本篇暂时不讲

开发环境

  • macOS, 如果你不是 macOS, 那你只能打包安卓部分
  • Xcode, 因为 Xcode 会自动安装包含 git 在内的一些其他环境, 所以理论上有这个就够了
  • brew
  • android sdk
  • flutter sdk
  • jenkins
  • fastlane

到 flutter sdk 为止就不讲了, 我相信看这篇文章的人, 起码 flutter 程序已经开发完了, 只是因为修改个文案就要打包的事被搞的不胜其扰, 才决定自动化的

而开发 flutter 基本上可以理解为包含如下工具/SDK

  • ruby
  • git
  • Xcode
  • brew
  • Android SDK
  • Flutter SDK

Jenkins

有几点要写在前面, 这东西建议使用 brew 安装, 当然你要是坚持, 也可以用 jendins 的 war/jar 包自己部署,见仁见智

不要使用 docker 的方式, 因为 docker 不包含 xcode 的环境, 执行起来比较麻烦

  1. brew install jenkins-lts 安装命令, 执行这个等着就好了, 具体的过程就不说了, 用过 brew 的人都懂
  2. 启动, 这里使用一个小技巧启动nohup jenkins-lts > /tmp/jenkins.log 2>&1 & 用 nohup 的方式启动, 好处有如下两个
    1. 命令行窗口可以关闭, 不重启则不用重新运行, 当然你要是自己建 service 也可以, 看你自己的水平
    2. 可以记录一些日志, 当然我这里觉得不重要, 所以暂时先放在/tmp 文件夹里, 如果需要保留, 则放在别的文件夹里, 因为 /tmp 内的文件重启后会被自动删除
  3. 启动,在浏览里打开http://localhost:8080/

这里要注意几点

  1. 初始化运行的时候, 根据提示, 去查看对应的初始化密码, macOS 一般是在启动后在 ~/.jenkins/secrets/initialAdminPassword位置, ~ 代表你的用户目录, 比如我的就是 /Users/Caijinglong
  2. 初始化后, 建议安装的插件你就都安装上就行, 不用考虑那么多
  3. 用户名和密码一定要记住, 不然后面很麻烦

image-20200819102817684

当一切准备就绪的时候, 可能是这样的, 也可能是这样的

image-20200819102855120

我这里是因为已经加入了一些任务, 初始化的应该是空的.

安装插件

系统管理, 插件管理

image-20200819103024213 image-20200819103046638

这里建议安装的插件有如下几个

  1. , 发布测试包到蒲公英的插件
  2. 这个插件是能把多个任务结合到一起的插件, 方便插入子任务, 比如我有 build ios 和 build android 两个任务, 这东西可以指定顺序, 让两个任务顺序执行

Jenkins 的安装告一段落, 具体的配置到了配置篇再写

FastLane

这东西的安装看你的实际情况, 一般情况下, 使用 brew 安装也是可以的, 然后, 这东西是一个 ruby 写的自动化打包的工具, 也可以用 gem 安装

1$ brew install fastlane
1$ sudo gem install fastlane

brew 发布的版本不是最新的, 所以, 根据你的情况选择使用哪种方式安装, 提示: sudo 会要求输入密码(系统的登录密码, 输入完回车)

蒲公英账号

官网:

具体的申请步骤就不讲了, 然后, 现在发布 apk 需要实名制, 自己传身份吧

image-20200819105740584

image-20200819105811061

这里的信息上传的时候会用到, 先

编写打包脚本

在使用 Jenkins 之前需要先在外部环境尝试, 因为 Jenkins 中调试错误比较麻烦

创建个测试项目先

1$ flutter create auto_build_example
2$ code auto_build_example

我这里用 vscode 打开, 大家呢则根据自己的习惯

安卓打包脚本

这个很简单, 其实只需要flutter build apk --release -v --split-per-abi 就可以了, 你则看自己的情况修改参数即可

1[   +3 ms] ✓ Built build/app/outputs/apk/release/app-armeabi-v7a-release.apk (5.1MB).
2[        ] ✓ Built build/app/outputs/apk/release/app-arm64-v8a-release.apk (5.4MB).
3[        ] ✓ Built build/app/outputs/apk/release/app-x86_64-release.apk (5.6MB).

打包结尾会有具体放置的位置, 我个人建议使用 v7a 的, 也就是build/app/outputs/apk/release/app-armeabi-v7a-release.apk, 因为某些 2018 2019 年的手机甚至都还是 v7a 架构(实测), 所以嘛, 先这样

我们需要记录的信息就是 打包命令 和 打包产物的位置

iOS 打包

这个就是需要装 fastlane 的原因了, 因为使用命令行打包的命令比较复杂, 而且还有证书相关的问题, 很容易遇到问题.

但很多问题 fastlane 都帮我们解决过了

注意: 本篇的打包是 ad-hoc 的, 而不是 appstore 的, 所以想要使用的人, udid 必须被包含在证书内, 然后, 这个是有名额限制的, 一个自然年内, 只可以有 100 个, 来年是否继续有效, 需要续约, 具体添加的方法可以自行搜索一下(提示, 直接通过 usb 线插在电脑上, 通过 xcode register device 的会被包含在这个名额内)

初始化 fastlane 配置

1$ fastlane init

image-20200819131404802

这里有 4 个选项

  1. 自动截图
  2. 自动发布到 beta 的 TestFlight
  3. 自动发布到 AppStore
  4. 手动配置

因为我们其实目前的目的是打包 ad-hoc 的包, 给测试人员安装, 而不用给每个人都插上手机, 所以我们选 4

image-20200819132250411

稍等片刻后会让连点几下回车, 主要就是把文档之类的地址什么的贴上来, 让有问题的朋友可以快速点进去

然后就是, 我这的版本不是最新版,提示应该更新之类的

修改配置文件

ios 目录下会多一个 fastlane 目录

1$ tree fastlane
2fastlane
3├── Appfile
4└── Fastfile

Appfile 是 app 的信息

Fastfile 是配置信息

我们先修改 Fastfile

默认的是这样的

 1# This file contains the fastlane.tools configuration
 2# You can find the documentation at https://docs.fastlane.tools
 3#
 4# For a list of all available actions, check out
 5#
 6#     https://docs.fastlane.tools/actions
 7#
 8# For a list of all available plugins, check out
 9#
10#     https://docs.fastlane.tools/plugins/available-plugins
11#
12
13# Uncomment the line if you want fastlane to automatically update itself
14# update_fastlane
15
16default_platform(:ios)
17
18platform :ios do
19  desc "Description of what the lane does"
20  lane :custom_lane do
21    # add actions here: https://docs.fastlane.tools/actions
22  end
23end

默认的长这样, 是 ruby 语法, 和 cocoapod 的配置文件语法基本相同, do end 可以简单理解为{}, 其中就是具体要执行的代码了

lane :custom_lane do 这行的 custom_lane 就是任务的名字, 里面可以添加 actions, 会按照顺序执行

我们可以按照提示打开 看看都有啥东西, 比如添加一个

然后我把任务名字修改为 ad_hoc

1default_platform(:ios)
2
3platform :ios do
4  desc "Description of what the lane does"
5  lane :ad_hoc do
6    # add actions here: https://docs.fastlane.tools/actions
7    build_ios_app
8  end
9end

然后执行

1$ fastlane ad_hoc
 12020-08-19 13:41:19.521 xcodebuild[64867:9469238] Unknown simulator cpu type (16777228)
 2
 3  error: Signing for "Runner" requires a development team. Select a development team in the Signing & Capabilities editor. (in target 'Runner' from project 'Runner')
 4
 5
 6** ARCHIVE FAILED **
 7[13:41:21]: Exit status: 65
 8[13:41:21]:
 9[13:41:21]: Maybe the error shown is caused by using the wrong version of Xcode
10[13:41:21]: Found multiple versions of Xcode in '/Applications/'
11[13:41:21]: Make sure you selected the right version for your project
12[13:41:21]: This build process was executed using '/Applications/Xcode.app'
13[13:41:21]: If you want to update your Xcode path, either
14[13:41:21]:
15[13:41:21]: - Specify the Xcode version in your Fastfile
16[13:41:21]:  xcversion(version: "8.1") # Selects Xcode 8.1.0
17[13:41:21]:
18[13:41:21]: - Specify an absolute path to your Xcode installation in your Fastfile
19[13:41:21]:  xcode_select "/Applications/Xcode8.app"
20[13:41:21]:
21[13:41:21]: - Manually update the path using
22[13:41:21]:  sudo xcode-select -s /Applications/Xcode.app
23[13:41:21]:
24
25+-------------+-------------------------+
26|           Build environment           |
27+-------------+-------------------------+
28| xcode_path  | /Applications/Xcode.app |
29| gym_version | 2.156.1                 |
30| sdk         | iPhoneOS13.5.sdk        |
31+-------------+-------------------------+
32
33[13:41:21]:  note: Using new build system
34[13:41:21]:  note: Building targets in parallel
35[13:41:21]:  note: Planning build
36[13:41:21]:  note: Constructing build description
37[13:41:21]:  error: Signing for "Runner" requires a development team. Select a development team in the Signing & Capabilities editor. (in target 'Runner' from project 'Runner')
38[13:41:21]:
39[13:41:21]: ⬆️  Check out the few lines of raw `xcodebuild` output above for potential hints on how to solve this error
40[13:41:21]: 📋  For the complete and more detailed error log, check the full log at:
41[13:41:21]: 📋  /Users/jinglongcai/Library/Logs/gym/Runner-Runner.log
42[13:41:21]:
43[13:41:21]: Looks like fastlane ran into a build/archive error with your project
44[13:41:21]: It's hard to tell what's causing the error, so we wrote some guides on how
45[13:41:21]: to troubleshoot build and signing issues: https://docs.fastlane.tools/codesigning/getting-started/
46[13:41:21]: Before submitting an issue on GitHub, please follow the guide above and make
47[13:41:21]: sure your project is set up correctly.
48[13:41:21]: fastlane uses `xcodebuild` commands to generate your binary, you can see the
49[13:41:21]: the full commands printed out in yellow in the above log.
50[13:41:21]: Make sure to inspect the output above, as usually you'll find more error information there
51[13:41:21]:
52+------------------+------------+
53|         Lane Context          |
54+------------------+------------+
55| DEFAULT_PLATFORM | ios        |
56| PLATFORM_NAME    | ios        |
57| LANE_NAME        | ios ad_hoc |
58+------------------+------------+
59[13:41:21]: Error building the application - see the log above
60
61+------+------------------+-------------+
62|           fastlane summary            |
63+------+------------------+-------------+
64| Step | Action           | Time (in s) |
65+------+------------------+-------------+
66| 1    | default_platform | 0           |
67| 💥   | build_ios_app    | 5           |
68+------+------------------+-------------+
69
70[13:41:21]: fastlane finished with errors
71
72[!] Error building the application - see the log above

好吧, 有一堆的日志, 其中会告诉我们哪个任务出错了, 原因是啥

我这里检查了一下, 这里是签名问题, 查一下 fastlane 里的解决方案, 告诉我们使用来解决

配置 match

match 其实是将一些签名的配置文件储存在某个位置, 然后达到多设备能同步的目的

1$ fastlane match init
image-20200819134729836

fastlane 提供了三种储存方案, 你如果有谷歌云或者 S3, 也可以选择用那两个, 然后根据提示来

这里我选择 git, 毕竟贫穷限制了我, 我去 gitee 创建仓库, 你也可以存在任何你认为合适的 git 仓库

image-20200819135151406 image-20200819135222060

image-20200819135309803

这里输入你的苹果账号

image-20200819135402586

我这里因为这个账号对应有 2 个公司开发者, 所以需要选一个. 输入 1 或 2 回车继续, 如果你只有一个, 估计这一步会自动跳过

image-20200819135529057

下一步是输入你的 app 的 bundle id

image-20200819135746740

这一步是提示, 没有找到签名文件, 其实这是因为这一步只会找现成的签名, 如果没有的话则不会自动创建, 我们去 xcode 里创建一个签名

image-20200819135904913

在这里选择一个 Team, 然后等成功后再重新执行上面的 match adhoc

这里可能会有朋友发现, 还是会报错, 无法 match, 其实这是因为你的帐号在 appstore 上并没有对应的 App 的原因, 两个解决方案

  1. 选择已有的 app bundle id
  2. 创建新的 app

我这里为了演示, 在公司的帐号上创建一个 app(不发布就行了 😁)

  1. 创建新的"套装 ID"
  2. 新建一个 app
image-20200819154315829 image-20200819154416408 image-20200819154427151

然后再去执行 match 就可以成功了

image-20200819154855400

然后查看 git 仓库

image-20200819154941746

发现也有东西了, 接着就可以回去配置 match 了

修改 Fastline

 1platform :ios do
 2  desc "Description of what the lane does"
 3  lane :ad_hoc do
 4    # add actions here: https://docs.fastlane.tools/actions
 5    match(
 6      type: "adhoc",
 7      app_identifier: "top.kikt.autobuildexample",
 8      team_id: "", # 你的team id, 只有一个可以直接删了这行
 9    ) # 添加这个
10    build_ios_app(
11      scheme: "Release",
12      export_options: {
13        method: "ad-hoc",
14      }
15    )
16  end
17end

team_id 注意: 如果你的 apple 帐号, 只对应一个公司, 则不用填这个, 填这个的目的是为了自动化

接着执行

1$ fastlane ad_hoc

image-20200819162851287

多出来的两个文件, 一个是 ipa 包, 一个是对应的 dsym(用于还原堆栈)

这个是 ad-hoc, 所以可能不需要两步验证, 但拿出来说一下, 也许后面会用到

两步验证的问题

iOS 中打包可能会遇到两步验证的问题, 而且经常要验证, 在自己本地命令行打包的时候还可以交互一下, 但 Jenkins 是不支持交互的, 这个能否解决呢?

答案是肯定的, 但需要经过如下的步骤

  1. image-20200819160414854

  2. 获取 Cookie

    1. 1$ export FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD=<PWD> # 上一步的app专用密码
      
    2. 1$ fastlane spaceauth -u <apple_id> # apple id
      
  3. 将 cookie 保存下载, 至于是环境变量还是文件看你自己的情况, 我这里选择的是文件的方式, 然后存到代码仓库中以便于 jenkins 读取, 你存到环境变量里也是可以的

比如如下的 cookie(为了安全性, 我这里删除了一部分字符, 所以你拿过去是用不了的) 请根据你自己的情况来拿到这个

1---\n- !ruby/object:HTTP::Cookie\n  name: DES5d68a2334f25aa752d511d\n  value: HSARMTKNSRVXWFla+6da0vxQCwQOICU+fwfLb6VaikjljuH6uDxOu6UWoLsJfDGnDolKcfUcKtnU5aQ5nskxgurrnxdOubXWidJF7JzxjbyzxmH6Tq/vD3JrCTSRVX\n  domain: idmsa.apple.com\n  for_domain: true\n  path: "/"\n  secure: true\n  httponly: true\n  expires:\n  max_age: 2592000\n  created_at: 2020-08-11 09:56:46.642442000 +08:00\n  accessed_at: 2020-08-18 13:45:59.234609000 +08:00\n- !ruby/object:HTTP::Cookie\n  name: myacinfo\n  value: DAWTKNV2da5c169c5551bd40dc5d6ffd58c42656bc39c57326c4686e7ba67cf58b38a017214dba13ef41247a84ae2909fa56863ffda004b4fcec4402e48d0a8c6a38a8b1593411a21da63fdf0b95ad37d33567598e684da6390f9b525c51cf6e1ea6e42a021dc1b798703c769d8380a23f8ac2b6890dfdc92a035acb48  domain: apple.com\n  for_domain: true\n  path: "/"\n  secure: true\n  httponly: true\n  expires:\n  max_age:\n  created_at: 2020-08-18 13:46:00.175666000 +08:00\n  accessed_at: 2020-08-18 13:46:00.176071000 +08:00\n- !ruby/object:HTTP::Cookie\n  name: dqsid\n  value: mp0aSI6IlFYS3E3MmI1SmVkVFV5THFcsh58p8zWIpcqviQIYYqJ8oW18qxcM\n  domain: appstoreconnect.apple.com\n  for_domain: false\n  path: "/"\n  secure: true\n  httponly: true\n  expires:\n  max_age: 1800\n  created_at: &1 2020-08-18 13:46:00.842456000 +08:00\n  accessed_at: *1\n

image-20200819162543869

image-20200819162529836

注意, 这里有两端"乱码", 复制第一段, 第二段是 fastlane 给的示例而已, 这里提示输入 y,则自动复制到剪切板

我创建一个文件 cookies.txt, 把这段东西粘贴进去, 这一步是为了 Jenkins 考虑

小结

总体来说, 配置好了以后

安卓:

1$ flutter build apk

iOS:

1$ flutter build ios --release -v
2$ cd ios
3$ fastlane ad_hoc

打包的命令熟悉完毕后, 就到了 Jenkins 的配置了

Jenkins 的配置

配置仓库权限

Jenkins 中支持 git 来管理源码, 然后, git 需要凭证来访问, 我这里是 gitee 的, 你可以根据你的情况自己配置

image-20200819165130173

image-20200819171443197

安卓自动化

image-20200819164129138

我先新建一个组叫"自动构建", 然后点击创建新的任务

image-20200819164247769

选择构建一个自由风格的软件项目

配置源码

先配置项目的 git 地址

image-20200819171549309

然后选择凭证, 接着确定后, 点下面的保存, 先试试是否能拉到源码

image-20200819172400991

这里构建完成后, 可以看到构建过程

然后点工作空间看看代码

image-20200819172426826

现在, 就能看到完整的代码了

配置打包脚本

这里还没完, 需要配置打包脚本, 继续点击配置

拉到后面, 点击构建=>执行 shell

这里就是自动构建的步骤了.

1flutter build apk --release -v --split-per-abi
2ls build/app/outputs/apk/release/

这里简单打包, 然后查看下, 保存后点击立即构建

image-20200819172922516

这里就是打包过程了

image-20200819173000100

查看一下控制台输出

image-20200819173021433

打包完成了, 顺便执行了 ls 命令查看了一下

上传

上传直接使用插件就可以了,还记得之前安装过 pgyer 的插件吗, 如果你要传 fir 或者自己的服务器, 那就需要你自己探索一下了

image-20200819173455311

使用 v1 还是 v2 看你自己的情况, 都可以, 我这里使用 v2 的, 把 api_key 放过来, 然后使用

scandir 是你上传的目录 ${WORKSPACE}/build/app/outputs/apk/release 工作空间的根目录, 然后下面是文件名

然后保存, 立即构建

image-20200819173649791

地址:

image-20200819173710059

到这一步, 安卓就算是成功了

iOS 自动化

创建一个叫"iOS 自动化"的任务

配置源码仓库

和安卓完全一样, 略过

打包脚本

还是在构建位置,使用 shell 脚本, 但和安卓不同的时, 需要有如下的步骤

  1. flutter build ios
  2. 两步验证
  3. fastlane

1, 3 在外部测试阶段完成了, 也就是说, 唯一需要注意的是两步验证的问题, 仓库里储存有 cookies, 这个东西就是用来干这个的了

编写脚本

1flutter build ios --release -v
2cd ios
3export FASTLANE_SESSION=$(cat login_cookie)
4fastlane ad_hoc
5ls Runner.*

上传

image-20200820090230922

然后, 我们遇到了一次失败

image-20200820093343644

去查一下原因吧, 进入控制台输出

image-20200820093506157

我们看到, 蒲公英上传失败了.

嗯, 那我们换一个方式, 不通过 jenkins 的插件了, 而是通过 fastlane 的 pgyer action 直接在 fastlane 里完成这一步,

先添加

1$ fastlane add_plugin pgyer

image-20200820094011798

中间需要 y,确定一下

然后, 修改 fastfile

 1default_platform(:ios)
 2
 3platform :ios do
 4  desc "Description of what the lane does"
 5  lane :ad_hoc do
 6    # add actions here: https://docs.fastlane.tools/actions
 7  	...
 8    build_ios_app(
 9      scheme: "Release",
10      export_options: {
11        method: "ad-hoc",
12      }
13    )
14    pgyer(api_key: "75123fxxx10", user_key: "4853910323xxx22105382")
15  end
16end

把 pgyer 的 action 直接加载 build_ios_app 后, 然后设置到 api_key 和 user_key, 在 查询

接着点击立即构建, 就可以触发了

上传完毕后是这样

image-20200820094616533

Jenkins 的触发器

Jenkins 支持一些触发器

image-20200820090604447

  1. 远程触发, 设置 token, 然后通过访问 url 的方式, 或是脚本之类的方式触发

  2. 其他工程构建后触发, 顾名思义, 就是比如我构建完安卓, 触发构建 iOS 的操作, 这样是可以细化构建步骤, 但如果本身没有关联, 强行搞到一起反而不美

  3. 定时构建, 这个支持比较多的方式, 很类似于 Spring 的定时任务, 具体语法可以查看说明或示例

    image-20200820090817688

  4. github 的钩子

  5. 轮询 SCM

Fastlane 的一些配置

fastlane 其实也支持安卓, 不过本篇因为安卓 flutter build apk 已经足够简单, 所以个人考虑不需要使用这个, 如果你的项目是 flutter module 配合 maven 或 aar 的方式置入原生的, 需要更多步骤的情况下, 可以尝试使用 fastlane 来帮你解放双手

后记

本篇主要探索了测试阶段如何解放双手, 使用 jenkins 配合 fastlane 达到自动打包, 上传的过程

如果需要额外告知某些人员, 可以尝试对接办公系统的 api 来达到自动通知的目的, 钉钉等程序应该都有对应接口

本篇设计到的文件相对完整版

之所以是相对, 是因为有一些敏感信息, 例如 pgyer 的 api key, 苹果的登录 cookie, 签名仓库等等, 并不方便向所有人开放

  1. 安卓篇

    1. Jenkins 构建脚本

      1flutter build apk --release
      
    2. 构建后

      scandir: ${WORKSPACE}/build/app/outputs/apk/release

      File name: app-armeabi-v7a-release.apk

  2. iOS 篇

    1. Fastfile

       1# This file contains the fastlane.tools configuration
       2# You can find the documentation at https://docs.fastlane.tools
       3#
       4# For a list of all available actions, check out
       5#
       6#     https://docs.fastlane.tools/actions
       7#
       8# For a list of all available plugins, check out
       9#
      10#     https://docs.fastlane.tools/plugins/available-plugins
      11#
      12
      13# Uncomment the line if you want fastlane to automatically update itself
      14# update_fastlane
      15
      16default_platform(:ios)
      17
      18platform :ios do
      19  desc "Description of what the lane does"
      20  lane :ad_hoc do
      21    # add actions here: https://docs.fastlane.tools/actions
      22    match(
      23      type: "adhoc",
      24      app_identifier: "top.kikt.autobuildexample",
      25      team_id: "xxx",
      26    )
      27    build_ios_app(
      28      scheme: "Release",
      29      export_options: {
      30        method: "ad-hoc",
      31      }
      32    )
      33    pgyer(api_key: "xxx", user_key: "xxx")
      34  end
      35end
      
    2. Matchfile

       1git_url("https://gitee.com/kikt/test_adhoc_sign_store.git")
       2
       3storage_mode("git")
       4
       5type("adhoc") # The default type, can be: appstore, adhoc, enterprise or development
       6
       7app_identifier(["top.kikt.autobuildexample"])
       8username("xxx") # Your Apple Developer Portal username
       9
      10# For all available options run `fastlane match --help`
      11# Remove the # in the beginning of the line to enable the other options
      12
      13# The docs are available on https://docs.fastlane.tools/actions/match
      
    3. Pluginfile

      1# Autogenerated by fastlane
      2#
      3# Ensure this file is checked in to source control!
      4
      5gem 'fastlane-plugin-pgyer'
      
    4. jenkins

      1flutter build ios --release -v
      2cd ios
      3export FASTLANE_SESSION=$(cat login_cookie)
      4fastlane ad_hoc
      5ls Runner.*
      

以上