手把手教你搭建自己的持续集成环境

前言
作为一个iOS开发者,打包是我们工作中的一部分,特别是在项目完成集中测试的时候,打包是很频繁的事情。
“XXX,重新打个包”
“打完了么?”
“改完打个包哈”
测试小哥的嗡嗡嗡~~~~
不想再听到啦!!!!
出于懒和烦的考虑研究了下 Continuous Integration(也就是大🐂口中的CI)
作为开发你要懂点运维
CI是软件(产品)研发生命周期中对代码质量、系统集成的一个持续构进的过程,当作为一个团队开发产品时,每个人都要开发自己的功能模块,最终都需要集成在一起,代码也需要集中托管到同一个地方,通过使用一些自动化的代码打包、测试工具,能够在开发人员每提交一次代码的时候,系统自动对程序进行打包和单元测试,如果出现问题,及时通过邮件等方式通知相关的开发人员。
持续集成的优点:
-
缩减开发周期
-
自动化流水线操作带来的高效
-
随时可部署
-
极大程度避免低级错误
出于以上优点,是时候搞一波持续集成了。
本文主讲 Jenkins + fastlane + cocopods + 蒲公英 + Gitlab的持续性构建
工欲善其事必先利其器
Jenkins

官网https://jenkins.io
功能
- 构建项目
- 跑测试用例检测bug
- 静态代码检测
- 部署
官方给了这样的功能定义,不过 2、3 我没有用到,感兴趣的小伙伴可以折腾下,毕竟生命在于折腾嘛!!
注意!前方高能!
-
本文讲的是iOS的持续集成,所以Jenkins必须部署在MasOS上
-
不要用pkg文件安装Jenkins,因为这样会生成一个共享用户jenkins,而这个用户的权限和系统登录用户的权限不同,在编译iOS应用的时候会有证书校验和签名的问题,所以请下载war包,放到Java容器中执行,比如Tomcat。我在系统启动的时候自启动了Tomcat,所以开机以后Jenkins会自动启动(推荐用Docker最为容器,这里就不介绍了,不然写不完了)
下载&&安装
-
war包下载地址 http://updates.jenkins-ci.org/download/war/ (我下载的是最新的)
-
终端执行命令
java -jar jenkins.war
-
浏览器访问 http://localhost:8080

看到这个页面就说明Jenkins启动成功了
按照提示找到密码解个锁

中间可能会需要修改权限
打开initialAdminPassword文件,复制出密码,就可以填到网页上去重置密码了。如下图




一路安装过来,输入用户名,密码,邮件这些,就算安装完成了。
接下来下载插件
- 安装GitLab插件
因为我们用的是GitLab来管理源代码,Jenkins本身并没有自带GitLab插件,所以我们需要依次选择 系统管理->管理插件,在“可选插件”中选中“GitLab Plugin”和“Gitlab Hook Plugin”这两项,然后安装。
- 安装Xcode插件
同安装GitLab插件的步骤一样,我们依次选择系统管理->管理插件,在“可选插件”中选中“Xcode integration”安装。
- 分支选择插件
Git Parameter
- 日志主题插件
AnsiColor
安装完了这个,我们就可以配置一个构建项目了。


输入一个项目名字,并且选择第一个,构建一个自由风格的软件项目

-
丢弃旧的构建
电脑的内存还是限滴,太久的就丢掉吧
-
参数化构建
WechatIMG4.jpeg
我没有弄特别复杂的,小伙伴根据自己需求定制哈
主要用到的参数

Chiuce Parameter 可选择参数

Git Parameter 安装插件后才会出现的参数,会显示项目远程上所有tag和分支

String Parameter 字符串参数,一般用于描述
- 源码管理

1 项目地址 (ssh和http都行,我配的ssh)
2 如果是SSH

⚠️注意:UserName是取一个名字,填写的Key是私钥。
3 取参数 根据选择的分支打包

让打印的log有颜色
- 构建
我没有配置jenkins打包上传到itunesconnect,这个我觉得还是自己打包比较好

我的项目包含cocopods,所以打包要先更新pods再打包,但是pod install
的时候很慢所以写了一个脚本

update-pods.sh
#!/bin/bash
function update_team_repo_specs {
echo "===> Updating repo specs from team ..."
readonly MY_SPECS_NAME="YHSpecs"
readonly MY_SPECS_URL="http://XXXXXXX/YOHO8/YHSpecs.git"
readonly MY_POD_SPECS="$HOME/.cocoapods/repos/$MY_SPECS_NAME"
if [[ -d $MY_POD_SPECS ]]; then
pod repo update $MY_SPECS_NAME
else
pod repo add $MY_SPECS_NAME $MY_SPECS_URL
fi
echo "===> Team repo specs is updated."
}
function update_master_repo_specs_if_needed {
echo "===> Checking repo master ..."
readonly MASTER_REPO_LAST_UPDATE_FLAG="$HOME/.cocoapods/master_last_update"
readonly NOW=`date +%s`
readonly UPDATED_AT=`cat $MASTER_REPO_LAST_UPDATE_FLAG`
readonly TIME_ELAPSED=$[$NOW - $UPDATED_AT]
echo "===> Repo master updated about $TIME_ELAPSED seconds ago"
if [[ $TIME_ELAPSED -gt 86400 ]]; then
echo "===> Will execute: pod repo update master"
pod repo update master
echo "===> Repo master is updated."
else
echo "===> Repo master updating is skipped."
fi
echo $NOW > $MASTER_REPO_LAST_UPDATE_FLAG
}
function execute_pod_install {
echo "===> Running pod install"
pod install
echo "===> Pod install done."
}
update_team_repo_specs
update_master_repo_specs_if_needed
execute_pod_install
debugPgy.sh

#!/bin/sh
out_path=`date "+%Y-%m-%d-%H-%M-%S"`
IPANAME="Ysleta"
if [ ! -d "./pgyer_build_ipa" ];
then
mkdir ./pgyer_build_ipa
fi
mkdir ./pgyer_build_ipa
rm -f code.jpg
result=""
qrcodepath=""
if [ $# -eq 0 ]
then
fastlane gym --workspace "Ysleta.xcworkspace" --scheme "Ysleta" --configuration "Debug" --export_method ad-hoc --output_name ${IPANAME}${out_path} --output_directory ./pgyer_build_ipa
result=$(curl -F "file=@$PWD/pgyer_build_ipa/${IPANAME}${out_path}.ipa" -F "uKey=XXXXXXXXX" -F "_api_key=XXXXXXXXX" -F "updateDescription=更新Debug版本" https://qiniu-storage.pgyer.com/apiv1/app/upload)
else
MSG=$*
echo $MSG
fastlane gym --workspace "Ysleta.xcworkspace" --scheme "Ysleta" --configuration "Release" --export_method ad-hoc --output_name ${IPANAME}${out_path} --output_directory ./pgyer_build_ipa
result=$(curl -F "file=@$PWD/pgyer_build_ipa/${IPANAME}${out_path}.ipa" -F "uKey=XXXXXXXXX" -F "_api_key=XXXXXXXXXXX" -F "updateDescription=${MSG}" https://qiniu-storage.pgyer.com/apiv1/app/upload)
fi
# 提取二维码地址
# 删除 result 中的字符串 保留 "appQRCodeURL":" 之后的字符串
qrcodepath=${result#*\"appQRCodeURL\":\"}
# echo $qrcodepath
# 删除 qrcodepath 中的字符串 保留 "}} 之前的字符串
qrcodepath=${qrcodepath%\"\}\}*}
# echo $qrcodepath
# 替换 \/ 为 /
qrcodepath=${qrcodepath//\\/}
echo Desc: $qrcodepath
# curl -o code.jpg $qrcodepath
- 构建后操作
显示二维码
这里要用到一个插件(可用可不用) Set Build description

上面已经把二维码显示到log中
这里是显示出来

发送邮件
系统管理->系统设置最下面就是邮件通知配置

我配的是163企业邮箱的,其他的小伙伴选择去弄下哈


我选择的HTML模版的
这是发送的样子
Jenkins 是我注册的一个邮箱专门用来发邮件的

fastlane
fastlane 是一个 ruby 脚本集合成的工具套件,旨在实现iOS应用发布流程的自动化。Fastlane 包括了向 App Store 提交新应用或更新已有应用所需要的常用任务。
- gym 编译打包生成 ipa 文件
- deliver 用于上传应用的二进制代码,应用截屏和元数据到 App Store
- sigh 可以生成并下载开发者的 App Store 配置文件
- snapshot 可以自动化iOS应用在每个设备上的本地化截屏过程
使用 gem 安装 fastlane
使用 gem 安装 fastlane
sudo gem install fastlane
确保 Xcode 命令行工具已安装:
xcode-select --install
进入工程目录,初始化 fastlane 配置,运行命令
fastlane init
过程中会要求你输入Your Apple ID,输入苹果开发者账号。fastlane 会自动获取工程文件名,目录等其他数据。这一步“Please confirm the above values”,确认信息,没问题则输入 y。然后,fastlane 会进行一系列的初始化操作,包括下载 App Store 上的元数据和截屏文件。等待初始化完成之后,工程目录下就多了一个 fastlane
剩下我就没配置了,因为在脚本内已经完成了
⚠️
运行脚本的过程中可能会报错,报错忘记截图了。。。
不过搜了一下是要指定工具版本

Gemfile文件内容
source 'https://ruby.taobao.org'
gem 'cocoapods', '~>1.3.1'
gem 'fastlane', '~>2.56.0'
plugins_path = File.join(File.dirname(__FILE__), 'fastlane', 'Pluginfile')
eval_gemfile(plugins_path) if File.exist?(plugins_path)
蒲公英
一个api/apk分发平台(有能力的小伙伴可以自己写一个),用来发布每次的应用

脚本中的APIKEY/UseKey 在这里
搭建完成!!!
过程中有的图是偷来的哈,安装过程没截图,就偷了几张.....莫怪莫怪