之前在iOS 组件二进制化方案--(一)中说明了进行组件二进制化的背景和目标,但方案实施后发现了诸多不足之处。
方案一不足之处
- .a 文件存放在 git 仓库会占用仓库容量,影响代码拉取和提交速度;
- 每个人操作打包过程繁琐;
基于最终目的是想加快Jenkins构建、出包速度,解决从代码提交到测试装包时间冗长的问题,结合之前方案的不足,制定新的方案,实现组件二进制化过程自动化;
采用ftp存储
基于 cocoapods 支持通过http下载一个压缩包的形式拉取第三方库(详细介绍见:cocoapods_podspec_source),最终采用将组件的LICENSE、资源文件、需要暴露的 .h 以及 .a 文件压缩为 zip 格式,上传至 ftp 服务器存储,在 ftp 上的存储路径如下:
为统一 pod 拉取组件库后对资源文件和 .h 文件的读取,压缩包解压路径设置如下所示:
初步应用可采用iOS 组件二进制化方案--(一)中说的方式生成.a文件,然后手动配置路径、压缩、上传至ftp(可采用界面化工具 FileZilla )
调整 podspec 文件
生成zip文件之后,就要在工程中拉取组件库,为保证组件库被拉取时可在源码与二进制包之间切换 ,需要对 podspec 进行调整,举例如下:
Pod::Spec.new do |s|
s.name = 'JLTestKit'
s.version = '1.2.0'
s.summary = '组件库'
s.description = <<-DESC
TODO: Add long description of the pod here.
DESC
s.homepage = 'http://https://github.com/JLTest/JLTestKit'
# s.screenshots = 'www.example.com/screenshots_1', 'www.example.com/screenshots_2'
s.license = { :type => 'MIT', :file => 'LICENSE' }
s.author = { 'name' => 'email' }
# s.social_media_url = 'https://twitter.com/'
s.ios.deployment_target = '9.0'
#控制安装 Pod 的时候判断使用源码还是二进制库
$lib = ENV['use_lib']
$lib_name = ENV["#{s.name}_use_lib"]
if $lib || $lib_name
#此处的source为 zip 文件在ftp服务器上的存储路径;
s.source = { :http => 'http://example.zip' }
s.ios.vendored_libraries = "Library/*.a"
s.source_files = 'Library/*.h'
s.resources = 'Resource/JLTest.xcassets'
else
#源码
s.source = { :git => 'http://example.git', :tag => s.version.to_s }
s.source_files = 'CMInvoicesCommon/Classes/**/*.{h,m}'
s.resources = 'JLTest/Assets/JLTest.xcassets'
end
s.dependency 'YYModel'
s.requires_arc = true
end
source及source_files、resources的设置:
- 拉取二进制时设置为组件库对应的zip文件在ftp服务器的存储路径;
- 拉取源码时则为git仓库地址;
- 资源文件及.h、.m文件的暴露要根据在拉取的库中的实际路径配置;
- 拉取二进制只需要暴露外需要引用的.h,即在生成zip文件时只需将需要暴露的.a与其他需要压缩的文件一起生成zip文件;
流程自动化
由于生成.a文件、生成zip、配置ftp存储路径、上传至ftp服务器过程繁琐,且重复操作耗费时间较多,因此很有必要将此流程实现自动化。
- 生成.a文件:脚本在iOS 组件二进制化方案--(一)已贴出来;
- 生成zip文件:脚本名称salib_zip(贴在文章最后),包含配置指定路径,以便修改podspec文件;将资源文件、LICENSE、需要暴露的.h文件、.a文件移至指定路径下;生成zip文件;
- 将zip上传至ftp:脚本名称salib_sshpass,采用sshpass完成文件上传ftp服务器;关于sshpass,找了篇文章贴在这儿,sshpass专题---免交互式ssh&MAC下使用SSHPASS,便不再赘述;
因为组件库较多,服务器资源有限的原因,最终采用每个人本机配置Jenkins构建环境去完成流程,Jenkins安装过程见持续集成环境搭建(二)--iOS Jenkins搭建持续集成环境,这里就直接说跟自动化生成并上传二进制文件相关的配置;
全局环境配置
- 将完成 .a文件生成(salib)、zip文件生成(salib_zip)、将zip上传至ftp(salib_sshpass) 这三个过程的脚本放在本地全局环境路径 /usr/local/bin 下;
- 配置Jenkins访问本机全局命令
Jenkins首页,系统管理--->系统设置:
全局属性,勾选环境变量,点击新增,键为PATH,值为本机命令行执行 echo $PATH 命令后所打印的内容:
访问权限配置
- 为了让Jenkins用户有权限访问你的私有git库,需要为Jenkins用户配置ssh key,并将公钥配置给git仓库,为方便可以直接将常用用户下的 /Users/your_user_name/.ssh 路径下的直接复制到Jenkins用户下的对应路径下;
-
为Jenkins添加公钥:用户--->设置--->SSH Public Keys
- 新建任务时需要创建凭据用户(后面会讲到),类型选择 SSH Username with private key,将私钥添加至 private key
创建任务
万事俱备,只差一试!建个任务试一下。。。。。
-
新建任务
-
配置任务版本
- 配置任务对应git库
填写git地址及要构建的分支;Credentials即选择凭据,无可选项可点击右侧 add 按钮添加凭据;
-
添加shell执行
构建--->增加构建步骤--->执行shell
#!/bin/bash
if [ -e Example/*.xcworkspace ];then
rm -rf Example/Podfile.lock Example/Pods Example/*.xcworkspace
fi
cd Example
# 更新私有repo库
if [ -e ~/.cocoapods/repos/SASpecs ];then
pod repo update SASpecs
else
pod repo add PrivateRepo ssh://[email protected]:8080/test.git
fi
pod install
cd ../
# 压缩为zip
# 此处 1 为需要暴露.h文件,因为工程采用组件化,使用中间件沟通的业务库是不需要暴露.h文件给外部的,这种情况下此处的1就不写;
salib_zip ${version} 1
# 上传至ftp
salib_sshpass JLTestKit ${version}
总结,压缩成zip文件存储于ftp服务器可解决掉上面说的方案一中的不足 1;流程自动化则解决了操作流程繁琐的问题;
应用中遇到的问题
在工程中拉取组件库在源码和二进制之间切换时,由于两种形式的source不同,不会同时缓存在本地cocoapods cache中。受此影响,在切换时需要将工程的pods文件夹、.xcworkspace、podfile.lock及要切换的组件库在本机的cocoapods cache删掉,重新执行pod install;首次拉取新版本时不需做此操作,因为本地还没有针对新版本的缓存;
脚本
salib: 见 iOS 组件二进制化方案--(一)
salib_zip:
#!/bin/bash
#
#------------iOS 组件二进制化:将资源文件、需要暴露的.h文件、.a文件压缩为zip--------------
if [ -e *.podspec ];then
#存在 podspec 文件
#采用 podspec 的名字指定 workspace 与需要编译的 scheme
project_name=$(basename *.podspec .podspec)
else
echo "ERROR: not find podspec file, please run shell in project root path"
exit 1
fi
if [ -n "$1" ];then
version=${1%.*}
else
echo "ERROR: Please input the version which you want to build"
exit 1
fi
salib $1
if [ $? -eq 0 ]; then
echo
echo "--- Setup $project_name.zip ---"
upload_path=$project_name/$project_name
if [ -e $upload_path/$version ]; then
# 删除之前生成的
rm -r $upload_path/$version
fi
mkdir -p $upload_path/$version
# mkdir -p $upload_path/$version/Library
#
# if [ -e $project_name/**/*.xcassets ] || [ -e $project_name/**/*.bundle ]; then
# mkdir -p $upload_path/$version/Resource
# fi
# 复制LICENSE
cp -rf LICENSE $upload_path/$version/LICENSE
if [ $2 ];then
# 复制.h
cp -rf $project_name/Classes/**/*.h $upload_path/$version
fi
# 复制 .a 文件
cp -rf $project_name/Library/$version/lib$project_name.a $upload_path/$version
# 复制资源文件 Assets bundle
if [ -e $project_name/**/*.xcassets ];then
cp -rf $project_name/**/*.xcassets $upload_path/$version
fi
if [ -e $project_name/**/*.bundle ];then
cp -rf $project_name/**/*.bundle $upload_path/$version
fi
if [ -e $project_name/**/*.mp4 ];then
cp -rf $project_name/**/*.mp4 $upload_path/$version
fi
# 删除 Library
rm -r $project_name/Library
count=`ls $upload_path/$version|wc -l`
if [ ${count} -gt 0 ]; then
cd $upload_path/$version
pwd
echo "开始压缩"
# 压缩生成zip文件
zip -r $project_name.zip * */
rm -rf *.a *.h *.xcassets *.bundle *.mp4 LICENSE
else
echo "The directory is null, no file to create zip"
fi
fi
salib_sshpass:
#!/bin/bash
#
#------------iOS 组件二进制化:采用sshpass将压缩文件上传至ftp--------------
repo=$1
version=$2
if [ $? -eq 0 ];then
# 192.168.6.49 为ftp地址
sshpass -p 'lQ@8VhN@6o' ssh -o StrictHostKeyChecking=no [email protected] "mkdir -p /home/ftpios/ios/${repo}/${version%.*}"
sshpass -p 'lQ@8VhN@6o' scp -o StrictHostKeyChecking=no ${repo}/${repo}/${version%.*}/${repo}.zip [email protected]:/home/ftpios/ios/${repo}/${version%.*}
echo "============================================================"
# 打印zip在ftp的存储路径
echo "url: http://192.168.6.49/ios/${repo}/${version%.*}/${repo}.zip"
fi
# 任务执行完,删除 Jenkins workspace下的相关文件
rm -rf * && rm -rf ".*"