Jenkins master-slave 的应用及 iOS 项目持续集成

背景

在公司一台服务机上搭建了JenkinsLinux环境,主要用于WEP 前端AndroidiOSCI服务,在没有过多去了解的情况下,我想当然的以为如果要打包 iOSJenkins宿主机系统环境必须是MacOS,因为知道除了Mac,其它系统均无法打包iOSMac应用程序(当然这点在目前看来仍然是对的),于是在公司那台硬盘只剩下可怜巴巴的20G空间的Mac mini上安装了Jenkins,叫安卓同事把他的Jenkins job又重新放在了Mac mini上,本想着事情就这么不完美的收尾了,今天忽然在群里看见开发者朋友讨论Jenkins说到Jenkins支持master-slave(主从)模式,支持非宿主机作为一个Node节点为其服务,一脸蒙蔽,又要把Jenkins放回Ubuntu,让这台mini单独为iOS打包服务。

新建Node节点机

基本设置

步骤 Manage Jenkins -> Manage Nodes -> New Node
取名,比如叫slave-iOS,勾选Permanent Agent,下一步;
看说明填写:

  • Name: 节点机名称
  • Description: 节点机描述
  • of executors:节点机最大job并发数

  • Remote root directory : 节点机用于存放workspace的目录,确保你要使用的用户有该目录的读写权限,比如设置为/Users/VanJay/Documents/Work/tungee/jenkins
  • Labels:标签,用于Jenkins查找Node
  • Usage:自行选择,
    • Use this node as much as possible:最大程序使用
    • Only build jobs with label expressions matching this node:只在使用该节点机才使用
  • Launch method:连接节点机的方式
    • Launch agent agents via SSH:通过SSH连接
      • Host:节点机IP
      • Credentials:凭据,自行添加,如果使用Username with password,记得把验证策略下面这项选第三项
      • Host Key Verification Strategy:
        • Known hosts file Verification Strategy:把master``ssh公钥拷贝到slaveknown_hosts
        • Manually provided key Verification Strategy:手动提供 key
        • Manually trusted key Verification Strategy:手动信任
        • Non verifying Verification Strategy:不验证
    • Launch agent via execution of command on the master:指定shell 脚本连接
  • AvailabilityL slave机可用性
    • Keep this agent online as much as possible:让slave尽可能在线
    • Take this agent online according to a schedule:根据计划保持在线
    • Take this agent online when in demand, and offline when idle:需要时在线,闲置时离线

Node Properties:节点机属性设置

可以在slave机上设置工具位置和环境变量

  • Environment variables:自定义环境变量,比如可以导出PATH=$PATH:/usr/local/bin
  • Tool Locations:工具位置:比如可以设置JDK目录/Library/Java/JavaVirtualMachines/jdk1.8.0_91.jdk/Contents/Home

使用slave机器

jobGeneral项下勾选Restrict where this project can be run,指定slavelabel就可以使用了。

事情总不会那么一帆风顺

使用slave机器后你会发现原本在slave本机跑没事的shell在这里可能有问题,基本都是commomand not fuond,都是命令找不到,那就export这些命令,把整个/usr/local/bin或者/usr/bin加进去(没必要,个人建议用到什么加什么)。
比如加入报pod command not found,在slave机运行which pod

☁  blog [master] ⚡ which pod
/usr/local/bin/pod

嵌入环境变量

安装Inject environment variables插件,安装方法可在本博客搜索 Ubuntu-18-04-搭建-Jenkins 查看文章。

正常shell打包

source ~/.alias

# 清除上一次生成的 beta 包
rm -rf ${OUTPUT_FOLDER}

export LANG=en_US.UTF-8

PODS_DIR="Pods"

if [[ -d "$PODS_DIR" ]]; then
	rm -rf "$PODS_DIR"
fi

setproxy

pod install

unsetproxy

# 解锁 keychain
security default-keychain -s login.keychain
security unlock-keychain -p "123456." login.keychain

fastlane beta

source ~/.alias是为了能找到setproxyunsetproxy ``shell 翻墙加快 pod install 速度,必须解锁keychain,否则无法正常签名,导致构建失败,如果是在本机操作则不需要这两项。

fastlane 内容

只编写了打betarelease

fastlane_version "2.28.3"

default_platform :ios

scheme_name = "DongKe"
workspace = 'DongKe.xcworkspace'

configuration_Debug = 'Debug'
configuration_Release = 'Release'

export_method_ad_hoc = 'ad-hoc'
export_method_appstore = 'app-store'

team_id = 'TEAM_ID'
username = 'xxx@xxx.com'
app_identifier = 'app_identifier'

platform :ios do
    before_all do
   		# cocoapods
    end

    desc "截图"
    lane :snap do
      snapshot(
        scheme: scheme_name
      )
      frameit(white: true, path: 'fastlane/screenshots')
    end

  	lane :test do
    end

    desc "打包测试包"
    lane :beta do |options|

        #根据传入参数version设置app的版本号
      # increment_version_number(version_number: option[:version])
      # #自动增加build号
      # increment_build_number

      # 获取是第几轮测试
      # test_turn = options[:test_turn]

      # 自动运行测试工具
      # scan
      # 崩溃分析
      # crashlytics
      #证书签名
      # sigh(
      #   adhoc: true,
      #   output_path: 'fastlane/mobileprovision'
      # )

      #编译打包
      version = get_info_plist_value(path: "#{scheme_name}/Info.plist", key: "CFBundleShortVersionString")
      build = get_info_plist_value(path: "#{scheme_name}/Info.plist", key: "CFBundleVersion")
      output_directory = File.expand_path("..", Dir.pwd) + File::Separator + 'build/beta/' + Time.now.strftime('%Y-%m-%d')
      output_name = "#{scheme_name}_#{build}_#{export_method_ad_hoc}_#{Time.now.strftime('%Y-%m-%d %H%M')}.ipa"

      gym(
        silent: true,
        clean: true,
        buildlog_path: "fastlane/fastlane_log",
        workspace: workspace,
        scheme: scheme_name,
        configuration: configuration_Release,
        output_directory: output_directory,
        output_name: output_name,
        export_method: export_method_ad_hoc,
	      export_xcargs: "-allowProvisioningUpdates"
        )
        # 上传到 fir
        # sh "fir publish #{output_directory}/#{output_name} -V -Q"
  	end

    desc "打包正式包"
    lane :release do |options|
      #证书签名
      # sigh(
      #   adhoc: false,
      #   output_path: './fastlane/mobileprovision'
      # )

      #编译打包
      version = get_info_plist_value(path: "#{scheme_name}/Info.plist", key: "CFBundleShortVersionString")
      build = get_info_plist_value(path: "#{scheme_name}/Info.plist", key: "CFBundleVersion")
      output_directory = File.expand_path("..", Dir.pwd) + File::Separator + 'build/release/' + Time.now.strftime('%Y-%m-%d')
      output_name = "#{scheme_name}_#{build}_#{export_method_appstore}_#{Time.now.strftime('%Y-%m-%d %H%M')}.ipa"
      gym(
        silent: true,
        clean: true,
        buildlog_path: "fastlane/fastlane_log",
        workspace: workspace,
        scheme: scheme_name,
        configuration: configuration_Release,
        output_directory: output_directory,
        output_name: output_name,
        export_method: export_method_appstore,
        export_xcargs: "-allowProvisioningUpdates"
        )
    end

    lane :all do |options|
      beta
      release
    end

  	after_all do |lane|

  	end

  	error do |lane, exception|

  	end
end