iOS 组件二进制化方案--(二)

之前在iOS 组件二进制化方案--(一)中说明了进行组件二进制化的背景和目标,但方案实施后发现了诸多不足之处。

方案一不足之处

  1. .a 文件存放在 git 仓库会占用仓库容量,影响代码拉取和提交速度;
  2. 每个人操作打包过程繁琐;

基于最终目的是想加快Jenkins构建、出包速度,解决从代码提交到测试装包时间冗长的问题,结合之前方案的不足,制定新的方案,实现组件二进制化过程自动化;

采用ftp存储

http_source.png

基于 cocoapods 支持通过http下载一个压缩包的形式拉取第三方库(详细介绍见:cocoapods_podspec_source),最终采用将组件的LICENSE、资源文件、需要暴露的 .h 以及 .a 文件压缩为 zip 格式,上传至 ftp 服务器存储,在 ftp 上的存储路径如下:
ftp_path.png

为统一 pod 拉取组件库后对资源文件和 .h 文件的读取,压缩包解压路径设置如下所示:
zip_path.png

初步应用可采用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/<TWITTER_USERNAME>'
  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首页,系统管理--->系统设置:
    系统设置.png

    全局属性,勾选环境变量,点击新增,键为PATH,值为本机命令行执行 echo $PATH 命令后所打印的内容:
    global_path.png

访问权限配置

  • 为了让Jenkins用户有权限访问你的私有git库,需要为Jenkins用户配置ssh key,并将公钥配置给git仓库,为方便可以直接将常用用户下的 /Users/your_user_name/.ssh 路径下的直接复制到Jenkins用户下的对应路径下;
  • 为Jenkins添加公钥:用户--->设置--->SSH Public Keys


    SSH_Public_Keys.png
  • 新建任务时需要创建凭据用户(后面会讲到),类型选择 SSH Username with private key,将私钥添加至 private key
    SSH_Private_Keys.png

创建任务

万事俱备,只差一试!建个任务试一下。。。。。

  • 新建任务


    new_task.png
  • 配置任务版本


    task_version.png
  • 配置任务对应git库
    填写git地址及要构建的分支;Credentials即选择凭据,无可选项可点击右侧 add 按钮添加凭据;
    task_git.png
  • 添加shell执行
    构建--->增加构建步骤--->执行shell


    run_shell.png
#!/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://git@192.168.6.111: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 root@192.168.6.49 "mkdir -p /home/ftpios/ios/${repo}/${version%.*}"

    sshpass -p 'lQ@8VhN@6o' scp -o StrictHostKeyChecking=no ${repo}/${repo}/${version%.*}/${repo}.zip root@192.168.6.49:/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 ".*"

推荐阅读更多精彩内容