基于Jenkins服务搭建Flutter持续集成打包平台(代理模式)

背景

由于项目之前一直在赶进度,没有统一集成的App打包平台,测试阶段的打包方式一直采用的是开发在本地手动打包提交给测试的模式,这种方式产生了以下几个问题:

  1. 打包效率低下
    手动打包受开发电脑工作环境影响,如果电脑环境出了问题一时半会没法打包或打包卡住,会导致测试工作无法正常开展
  2. 测试节奏严重依赖开发打包节奏
    比如每天打一个包,测试每天只能对当天打的包进行验证,开发不出包就无法进行新一轮的测试和已修复bug的回归验证
  3. 无法保证代码的时效性
    由于第3点产生,测试针对旧代码提出的bug,可能开发今天已经进行过修复或优化,那么测试就浪费了宝贵的测试时间去测试本身就错误的代码
  4. 不可避免的人为错误
    本地代码与远端仓库是否保持一致,是否会出现未提交或没拉取的代码,是否会出现选择了错误的分支打包,依赖打包开发人员的自觉性

为了解决上述几个问题,有必要对项目搭建一个集成持续打包平台,方便后续开发和测试同事工作的开展。
使用集成持续打包平台的好处:

  • 服务器自动打包,想什么时候要就什么时候要,不局限于开发是否有空
  • 打包发版转交给测试进行实施,安全闭环进一步锁牢,风险降低
  • 持续集成,可配置相关UI自动化脚本形成持续集成,高大上

网上有很多基于Jenkins服务搭建集成打包平台的教程,但是关于使用代理模式的资料相对较少。由于公司已经搭建有Jenkins服务,但是服务器上有很多旧的项目在维护,内存不足,配置无法满足Flutter项目高效打包的要求,正好公司有一台空闲的iMac电脑可以用来打包(非mac环境不能打包ios),所以我采用的是master-node的代理模式来搭建Jenkins持续集成打包平台,同时,借助蒲公英来生成打包出来的apk/ipa文件的下载链接和二维码,方便下载安装,最后配置了钉钉机器人推送构建成功的消息到指定群通知,并且@到对应的测试人员。

先来给大家看下平台搭建完成后,打包的效果:

该平台主要实现的功能:

  • 支持指定BUILD_TYPE类型打生产或测试包
  • 可选择BUILD_BRANCH分支打包
  • 打包完成后生成下载二维码和链接
  • 构建完成后通过钉钉推送到指定群通知

流程

首先我们需要准备两台电脑,一台为打包机,作为真正执行构建任务的代理节点,一台为控制机(部署了Jenkins服务器或者任意一台可登录Jenkins服务器的电脑),这里我用的是自己的工作电脑,用来配置Jenkins上的项目构建参数以及权限等,接下来我们的搭建工作也将分两台电脑的部分去执行

Android

一、打包机(代理机)
  1. 安装Flutter、Android、JDK等构建所需SDK,以及各种环境变量,ios打包的话还要配置好项目相关证书,确保在本地使用flutter build命令能够正常打包;
  2. 使用brew安装jenkins----命令行安装
brew install jenkins-lts
二、控制机
  1. 用SSH方式添加agent节点
    Jenkins- 插件管理- 可选插件- 搜索SSH Agent、Environment Injector- 安装完成后重启jenkins服务
    安装SSH插件
    安装Environment Injector插件
    Jenkins- 系统管理 - 全局安全配置, 把 SSH Server 设置为启用(默认是禁用)
    配置启用SSH Server
    添加代理机的ROOT账号密码为凭据
    添加从节点
    配置从节点
    添加节点页面解释
    名称:自定义一个节点名称
    描述:使得该代理更加容易被识别的可选项
    Number of executors:可以同时执行的job线程数,随便写个数字
    远程工作目录:代理机上Workspace目录
    标签:自定义,方便后期识别,作为限制项目运行节点的标记
    用法:只允许运行绑定到这台机器的Job
    启动方式:Launch agent agents via SSH
    主机:输入要连接的远程代理机IP地址
    Credentials:添加远程代理机的ROOT账号和密码的凭据
    Host Key Verification Strategy:这项选择Non verifying Verifcation Stragegy
    添加节点成功
    参考Jenkins中node节点添加之SSH方式2
  2. 新建项目
  3. 配置项目打包参数

    WORKSPACE_MASTER:Jenkins服务器上的任务工作目录
    WORKSPACE_MASTER:代理机上的任务工作目录
    BUILD_APP_NAME:安装包的包名
    MASTER_ROOT_USERNAME:Jenkins服务器的管理员用户名
    MASTER_IP:Jenkins服务器的ip地址


#!/bin/sh
if [ ${BUILD_TYPE} = "prod" ]; then
flutter clean
flutter build apk --release --flavor prod -t lib/entry/entry_pro.dart --dart-define=APP_CHANNEL=GP
else
flutter clean
flutter build apk --release --flavor qa -t lib/entry/entry_qa.dart  --dart-define=APP_CHANNEL=HOME
fi
exit 0

配置完成点击保存,然后开始第一次构建
结果构建失败,查看控制台输出,报flutter命令找不到的错误,但是打包机上明明是可以执行flutter命令的。解决方案:在代理节点添加环境变量配置。因为代理机在构建任务时是在我们配置的远端工作目录下执行的,也就是前面配置的/Users/mx16g,所有的脚本命令都只会在该目录下去运行,需要在节点下配置代理机的环境变量才会生效。
代理机终端输入echo $PATH,将打印的内容复制粘贴到键PATH对应的值里
Dashboard-节点列表-选择代理节点-配置从节点-节点属性-环境变量
保存后再次执行构建
构建成功

  1. 使用 Jenkins 插件上传应用到蒲公英
    1.下载插件:jenkins -> 系统管理 -> 插件管理,搜索 Upload to pgyer、description setter,点击下载,下载完的效果如下图:
    2.jenkins -> 系统管理 -> 全局安全配置->标记格式器切换为Safe HTML,要不然二维码出不来
    3.在 jenkins 的 job 配置页面 构建或 构建后操作中添加构建步骤 upload to pgyer with apiV2,如下图
    4.插件添加成功后,依次填入
    pgyer api_key:蒲公英的 api_key(需注册蒲公英账号申请,参考申请pgyer api_key
    scandir:ipa/apk 所在目录
    file widcard:上传文件的通配符
    5.执行构建
    构建失败,控制台报找不到目录以及需上传的ipa/apk文件,这是一个用代理机上传APP至蒲公英的坑,实际代理机上目录及文件是存在的,在对应的代理机上执行curl上传也是可以上传成功的。
    解决方法:将执行打包出来的APP拷贝至部署Jenkins的机器上,然后将Upload to pgyer with apiV2里的文件目录修改成Master节点的文件目录
    由于Jenkins是通过Docker镜像起的服务,因此只需将代理机生成的APP拷贝到对应的挂载目录去即可,因此在构建脚本中加入以下脚本(前提是两部机器间已做过免密操作,可参考:机器间免密操作
    scp ${WORKSPACE_AGENT}/build/app/outputs/flutter-apk/app-${BUILD_TYPE}-release.apk ${MASTER_ROOT_USERNAME}@${MASTER_IP}:${WORKSPACE_MASTER}/${BUILD_APP_NAME}.apk
#!/bin/sh
if [ ${BUILD_TYPE} = "prod" ]; then
flutter clean
flutter build apk --release --flavor prod -t lib/entry/entry_pro.dart --dart-define=APP_CHANNEL=GP
else
flutter clean
flutter build apk --release --flavor qa -t lib/entry/entry_qa.dart  --dart-define=APP_CHANNEL=HOME
fi
ssh ${MASTER_ROOT_USERNAME}@${MASTER_IP} "cd ${WORKSPACE_MASTER};rm -f ${WORKSPACE_MASTER}/*"
scp ${WORKSPACE_AGENT}/build/app/outputs/flutter-apk/app-${BUILD_TYPE}-release.apk ${MASTER_ROOT_USERNAME}@${MASTER_IP}:${WORKSPACE_MASTER}/${BUILD_APP_NAME}.apk
exit 0

ssh ${MASTER_ROOT_USERNAME}@${MASTER_IP} "cd ${WORKSPACE_MASTER};rm -f ${WORKSPACE_MASTER}/*"
这句命令的作用是每次构建删除之前打包存储在Jenkins服务器的产物,非必须。
再次执行构建成功,蒲公英上传插件将输出相应的 log,如下图:

6.生成下载链接和二维码
上传蒲公英成功后,可在 jenkins 中的其他构建中使用蒲公英上传成功后返回的参数,这会将蒲公英返回的参数注入为jenkins的全局变量,在其他构建步骤的使用方法直接引用这个全局变量即可,变量名称直接使用返回的 key值。例如:${appBuildURL},蒲公英上传成功后返回参数的参考请看这里
构建结果

  1. 构建成功发送钉钉机器人通知
    1.安装插件DingTalk
    2.钉钉群设置-智能群助手-添加机器人-通过webhook接入自定义服务-复制生成的webhook
    3.Jenkins-系统配置-钉钉-新增机器人-粘贴复制的webhook-保存
    4.任务配置里选择构建成功时通知,通知人里可以填入发送通知时需要@人的手机号码,自定义需要发送的内容,需使用markdown格式,参考自定义机器人接入
    5.等待构建完成,成功推送

iOS

iOS打包配置基本和Android的一样,除了构建脚本,这里需要借助fastlane来打ipa包。首先在打包机上安装fastlane,确保fastlane的工作目录位于工程的ios目录下

修改fastlane脚本

default_platform(:ios)

platform :ios do
    lane :export_ipa_qa do
        increment_build_number(xcodeproj: "Runner.xcodeproj")
        version = get_version_number(target: "Runner")
        build_num = get_build_number()
        ipa_name = "AppName_iOS_v" + version + "_" + "qa" + "_" +Time.new.strftime('%Y-%m-%d') + ".ipa"
        build_app(
        export_method: "development",
        clean: true,
        scheme: "dev",
        configuration: "Release-dev",
        output_directory: "~/workspace/AppName_iOS/build/ipa",
        output_name: ipa_name,
        )
    end

    lane :export_ipa_prod do
        increment_build_number(xcodeproj: "Runner.xcodeproj")
        version = get_version_number(target: "Runner")
        build_num = get_build_number()
        ipa_name = "AppName_iOS_v" + version + "_" + "prod" + "_" +Time.new.strftime('%Y-%m-%d') + ".ipa"
        build_app(
        export_method: "development",
        clean: true,
        scheme: "prod",
        configuration: "Release-prod",
        output_directory: "~/workspace/AppName_iOS/build/ipa",
        output_name: ipa_name,
        )
    end

end

Jenkins构建脚本如下

#!/bin/sh
export LANG=en_US.UTF-8
export LANGUAGE=en_US.UTF-8
export LC_ALL=en_US.UTF-8
if [ ${BUILD_TYPE} = "prod" ]; then
flutter clean
flutter build ios --release --flavor prod  -t lib/entry/entry_pro.dart --dart-define=APP_CHANNEL=IOS
cd ios
pod install
fastlane export_ipa_prod
else
flutter clean
flutter build ios --release --flavor dev  -t lib/entry/entry_qa.dart --dart-define=APP_CHANNEL=IOS
cd ios
pod install
fastlane export_ipa_qa
fi
ssh ${MASTER_ROOT_USERNAME}@${MASTER_IP} "cd ${WORKSPACE_MASTER};rm -f ${WORKSPACE_MASTER}/*"
scp ${WORKSPACE_AGENT}/build/ipa/${BUILD_APP_NAME}.ipa ${MASTER_ROOT_USERNAME}@${MASTER_IP}:${WORKSPACE_MASTER}/${BUILD_APP_NAME}.ipa
exit 0
构建成功
问题记录(遇到的坑)

构建过程中flutter pub get命令报403错误,一开始以为是文件读写权限的问题,检查了打包机的文件操作权限都是没问题的,然后推测是pub缓存的问题,就删除了整个任务的内容,再重新构建拉取源码,还是不行,又试了网上的几种方案都行不通,最后解决该问题的方式是自己试出来的,很简单,在打包机本地工程上执行一下flutter pub get命令就可以了。

总结

本次实现了从0到1的持续集成打包平台的搭建,期间遇到了很多问题,踩了很多坑,很多问题网上也找不到对应的解决方案,需要自己一步步去摸索实践,但最终完成下来确实是有所收获。

完结撒花🌸🌸🌸🌸🌸🌸🌸🌸🌸🌸🌸

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

推荐阅读更多精彩内容