Jenkins 配置MAC节点,编译iOS项目

发布时间 2023-10-10 14:10:12作者: 木易-故事里的人

 

文章主要介绍Jenkins主从节点配置,mac机配置slave节点。从机已经搭建android和ios编译环境为例,介绍Jenkins节点配置。

环境介绍

  • 主机环境介绍:主机Jenkins运行在tomcat中。Jenkins本身安装的环境仅包括java环境和gradle环境。
    # set java environment
    export JAVA_HOME=/usr/java
    export JRE_HOME=/usr/java/jre
    export CLASS_PATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar:$JRE_HOME/lib
    export PATH=$PATH:$JAVA_HOME/bin:$JRE_HOME/bin
    
    # set gradle
    export GRADLE_HOME=/usr/local/gradle/gradle-3.3
    export PATH=$PATH:$GRADLE_HOME/bin
    
    # set tomcat
    export TOMCAT_HOME=/usr/local/tomcat/apache-tomcat-7.0.79
    
    # set jenkins
    export JENKINS_HOME=/usr/local/tomcat/apache-tomcat-7.0.79/webapps/jenkins

     

  • 从机环境介绍:从机mac可以不安装jenkins,只是配置好android和ios的编译环境,此处的IOS编译环境尽量是已经可以通过 xcode 编译项目。
    # set android sdk
    export ANDROID_HOME=/Users/aorise/Library/Android/sdk
    export PATH=$PATH:$ANDROID_HOME/tools:$ANDROID_HOME/platform-tools
      
    # set gradle
    GRADLE_HOME=/Users/aorise/.gradle/wrapper/dists/gradle-4.1-all/bzyivzo6n839fup2jbap0tjew/gradle-4.1;
    export GRADLE_HOME
    export PATH=$PATH:$GRADLE_HOME/bin
      
    # set tomcat
    export TOMCAT_HOME=/users/aorise/Library/ApacheTomcat
    export JENKINS_HOME=/users/aorise/Library/ApacheTomcat/webapps/jenkins

     

打开MAC电脑远程登录

MAC电脑进入 ‘系统偏好设置’ -> ‘共享’ -> ‘远程登录’ 打开远程登录。对应的账号设置可以被主节点访问后,mac 节点的 jenkins 工作区间要配置到对应的账号名下。本文中 mac 账号是 aorise ,对应配置为从节点的 jenkins 工作区间是 ‘/Users/aorise/Library/ApacheTomcat/webapps/jenkins’

 

主机配置

  • 安装 Xcode integration:ios编译(貌似不安装也可以),本文采用 shell 脚本编译,没有采用 xcode 的工具
  • 安装 description setter plugin 插件:生成二维码(同步打开 jenkins -> 系统配置 -> 全局安全配置 -> Markup Formatter -> Safe HTML)
    .*qrcodeHistory\\/(\S{64})
    <img src='http://www.pgyer.com/app/qrcodeHistory/\1' alt="二维码解析失败"/>

     

  • 安装 Git Parameter Plug-In 插件:参数构建配置git参数
  • 使用 Archive the artifacts 插件:存档构建输出物
    platform/build/outputs/apk/sample/release/*.apk
    build/Education/*.ipa

     



     

     

     

     

节点配置

在系统管理 / 节点管理 创建新节点。

 

 

 

 

 

 

 

编译命令配置

记住ios工程一定要是已经可以直接通过xcode编译,也即相关的签名文件已经记录在工程文件里面。

 

使用凭证管理账户号密码

 

实际编译脚本 ‘jenins-build.sh’

#!/bin/sh
set -x

echo "\n"
echo "================= jenkins-build start ================="
#项目名字
PRODUCT="cf_rn_base"
#SDK版本信息
SDK="iphoneos17.0"
#ipa导出目录
IPADIR="ipaDir"

echo "MAC HOME:${HOME}"
echo "WORKSPACE:${WORKSPACE}"
echo "GIT_BRANCH:${GIT_BRANCH}"
echo "JOB_NAME:${JOB_NAME}"
echo "SIGNATURE:${SIGNATURE}"

# 解锁对login.keychain的访问,codesign会用到(此处为关键)
set +x
security unlock-keychain -p "xxxx" $HOME/data/soft/key/login.keychain
set -x

export YARN_HOME=/opt/homebrew/bin/
export PATH=$YARN_HOME:$PATH
export PATH=/Users/cfhy/.nvm/versions/node/v18.12.0/bin/:$PATH

node -v

cd /Users/cfhy/data/soft/Specs
git pull

cd ${WORKSPACE}
# npm install -g react-native-update-cli
yarn gitInit
yarn install


cd ios

export LANG=en_US.UTF-8

rm -rf Podfile.lock

sed -i -e '1i\'$'\nsource \'/Users/cfhy/data/soft/Specs\'\n' Podfile

sed -i -e "s|:http => 'https://boostorg.jfrog.io/artifactory/main/release/1.76.0/source/boost_1_76_0.tar.bz2'|:http => 'file:///Users/cfhy/data/soft/boost_1_76_0.tar.bz2'|g" ../node_modules/react-native/third-party-podspecs/boost.podspec
#安装三方库
/opt/homebrew/bin/pod repo update --verbose
#/opt/homebrew/bin/pod install
#HERMES_ENGINE_TARBALL_PATH="/Users/cfhy/data/soft/hermes-runtime-darwin-v0.70.8.tar.gz" /opt/homebrew/bin/pod install --verbose
HERMES_ENGINE_TARBALL_PATH="/Users/cfhy/data/soft/react-native-artifacts-0.71.11-hermes-ios-debug.tar.gz"  /opt/homebrew/bin/pod install --verbose

#清除
#xcodebuild clean -workspace ${WORKSPACE}/ios/${PRODUCT}.xcworkspace -scheme ${PRODUCT} -configuration ${CONFIG}


#模拟器编译
#xcodebuild -workspace ${WORKSPACE}/ios/${PRODUCT}.xcworkspace -scheme ${PRODUCT} -configuration debug -sdk iphonesimulator16.2

# ../node_modules/react-native/packager/packager.sh clean

#判断是否更新版本号
if [ ! -n "${MARKETING_VERSION}" ]; then
  echo "IS NULL"
  echo "未修改版本号构建!"
else
  echo "NOT NULL,版本号为: ${MARKETING_VERSION}"
  sed -i '' 's|MARKETING_VERSION = .*;|MARKETING_VERSION = '${MARKETING_VERSION}';|g' cf_rn_base.xcodeproj/project.pbxproj
fi

sh ./release_prod.sh

# 打包签名
xcodebuild -archivePath ${WORKSPACE}/ios/buildDir/${PRODUCT}.xcarchive -workspace ${WORKSPACE}/ios/${PRODUCT}.xcworkspace -sdk ${SDK} -scheme ${PRODUCT} -configuration ${CONFIG} archive

if [ $? -ne 0 ]; then
    echo "=====failed====="
    exit 1
else    
    echo "=====succeed====="
fi

# 导出ipa
xcodebuild -exportArchive -archivePath ${WORKSPACE}/ios/buildDir/${PRODUCT}.xcarchive -exportPath ${WORKSPACE}/ios/${IPADIR} -exportOptionsPlist ${WORKSPACE}/ios/buildDir/${PRODUCT}.xcarchive/Info.plist -allowProvisioningUpdates

if [ $? -ne 0 ]; then
    echo "=====failed====="
    exit 1
else    
    echo "=====succeed====="
fi

echo "\n"
echo "================= jenkins-build end ================="


#判断是否热更
if [ ${Exchange} == "Y" ];then
# 上传热更新服务
echo "----------- 上传热更新服务 --------------"
cd ${WORKSPACE}

#echo "开始任务"
set +x
# 提示 “请在项目目录中运行`pushy login`命令来登录'” 时,打开下面注释进行登录
pushy login  $PUSHY_USERNAME $PUSHY_PASSWORD
if [ $? -eq 0 ];then
     echo "登录成功~"
else
     echo "用户名密码错误!"
     exit 1
fi
set -x

sh ./release_prod.sh

pushy uploadIpa ${WORKSPACE}/ios/${IPADIR}/${PRODUCT}.ipa

if [ $? -ne 0 ]; then
    echo "=====failed====="
    exit 1
else    
    echo "=====succeed====="
fi
else
    echo "原生包构建完成 !"

fi

currentDate=`date '+%Y%m%d%H%M'`

cd ${WORKSPACE}/ios/${IPADIR}/
scp ${PRODUCT}.ipa root@10.3.1.20:/data/jenkins/prod-ios-ipa/hz/${PRODUCT}-${currentDate}.ipa

/Users/cfhy/data/soft/ossutil/ossutil/ossutilmac64 cp -f ${WORKSPACE}/ios/${IPADIR}/${PRODUCT}.ipa oss://cfky-download/ios/hz/
#git clean -xdf

发送消息脚本

import java.util.*;
import java.text.SimpleDateFormat;

//构建结果
def buildResult = manager. getResult()

//构建用户
def buildUser= manager.getEnvVariable("BUILD_USER")

//项目名称
def jobName= manager.getEnvVariable("JOB_NAME")

//构建结果页面
def buildUrl= manager.getEnvVariable("BUILD_URL")

//构建说明
//def buildDes = manager.getEnvVariable("description")
def buildDes = manager.getEnvVariable("Build_Description")

//构建环境
def buildEnv= manager.getEnvVariable("BUILD_ENV")

//构建类型
def buildType= manager.getEnvVariable("CONFIG")

//GIT分支
def gitBranch = manager.getEnvVariable("GIT_BRANCH")

// 应用版本号
apiAppVersion =   manager.getEnvVariable("MARKETING_VERSION")




manager.listener.logger.println("项目名称:"+ jobName)
manager.listener.logger.println("构建分支:"+ gitBranch)
manager.listener.logger.println("构建环境:"+ buildEnv)
manager.listener.logger.println("构建类型:"+ buildType)
manager.listener.logger.println("构建用户:"+ buildUser)
manager.listener.logger.println("构建结果:"+ buildResult)





if(buildResult == "SUCCESS"){
  //解析蒲公英上传返回数据

  //ipa下载地址

  ipaDownloadUrl = "https://www.pgyer.com/"+ manager.getEnvVariable("buildShortcutUrl")

  ossDownloadUrl = "oss地址"
  //ipa二维码
  ipaQrCode =  manager.getEnvVariable("buildQRCodeURL")

  //ipa应用程序包
  ipabuildIdentifier = manager.getEnvVariable("buildIdentifier")

  //ipa 版本号
  ipabuildVersion = manager.getEnvVariable("buildVersion")
  
  
  manager.listener.logger.println("ipa下载地址"+ipaDownloadUrl)  
  manager.listener.logger.println("ipa二维码地址:"+ipaQrCode) 
  manager.listener.logger.println("ipa应用程序包名:"+ipabuildIdentifier)  
  manager.listener.logger.println("ipa版本号:"+ipabuildVersion)  
  manager.listener.logger.println("应用版本号:"+apiAppVersion) 


  

  dingding("iOS打包构建","### [ "+jobName+" ] 构建成功" +
                        "\n\n构建分支:" + gitBranch + 

                        "\n\n构建类型:"+ buildType +

                        "\n\n三端类型:" + "hz" +

                        "\n\n应用版本号:"+ apiAppVersion+

                        "\n\n提审包下载地址:"+ ossDownloadUrl +

                        "\n\n构建日期:" + getNowTime() + "构建 " + 

                        "\n\n构建用户:"+ buildUser +

                        "\n\n查看详情:[项目地址]("+buildUrl+")"

       )

}else if(buildResult == "ABORTED"){
    dingding("prod-ios-hz-rn","### [ prod-ios-hz-rn ] 构建被终止\n" + " " + getNowTime() + " 终止\n\n[查看jenkins任务详情]("+buildUrl+")")
    }else{
      dingding("prod-ios-hz-rn","### [ prod-ios-hz-rn ] 构建失败\n分支:" + gitBranch + "\n\n" + " " + getNowTime() + " 终止\n\n[查看jenkins任务详情]("+buildUrl+")")

      }





//发送钉钉消息

def dingding(p_title,p_text){
  manager.listener.logger.println("--------------------------"+p_title+p_text)
  def json= new groovy.json.JsonBuilder()
  json {
    msgtype "markdown"
    markdown {
      title p_title
      text p_text
    }
    at {
      atMobiles([])
      isAtAll false
    }
  }

   

  manager.listener.logger.println("发送钉钉数据:"+json)
  def connection = new URL("https://oapi.dingtalk.com/robot/send?access_token=xxxx").openConnection()
  connection.setRequestMethod('POST')
  connection.doOutput = true
  connection.setRequestProperty('Content-Type', 'application/json')



  def writer = new OutputStreamWriter(connection.outputStream)
  writer.write(json.toString());
  writer.flush()
  writer.close()
  connection.connect()

  def respText = connection.content.text

  manager.listener.logger.println("钉钉返回结果:"+respText )
}

//获取当前时间
def getNowTime(){
  def str = "";
  SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
  Calendar lastDate = Calendar.getInstance();
  str = sdf.format(lastDate.getTime());
  return str;
}

 

注意事项

  • 从机sh脚本可以独立编译通过,通过主机编译就不可以?

    打开系统钥匙串,解锁 ‘系统’ 钥匙串一次。如果还不行,对登录用户的证书里面 MAC 编译对应的证书访问属性开启 ‘允许所有应用访问此项目’