缘起:公司最近推出大企业定制化的App产品,客户企业可以定制App名称、logo、文案及工程里的参数等,项目经理在禅道里上传图片素材,填写文案和参数,点击构建即可远程触发Jenkins服务器构建iOS工程,将构建好的ipa包上传服务器,并通知禅道构建结果。我负责iOS构建的Jenkins服务器的搭建。
关于Jenkins服务的搭建网上已经有大量的文章介绍了,本文主要介绍一些实用的操作Xcode工程的shell指令和Jenkins插件
常用操作指令
- 文件删除、创建路径
if [ -d dirPath ];then
rm -rf dirPath/*
else
mkdir dirPath
fi
- 文件查找、替换
find(选项)(参数)
find命令可以高效地替换工程中的图片,遍历客户提供的图片素材,利用find指令自动地在工程里的指定文件夹下替换和素材同名的图片
#指定工程里的图片路径
Find_Path="${WORKSPACE}/Resources/images/Configurable.xcassets ${WORKSPACE}/Resources/images/Proj.xcassets"
#客户提供的图片素材
Images_Path="${Project_Build_resource_path}/images_iOS"
cd ${Images_Path}
for itemFile in `ls`; do
cd ${Images_Path}/$itemFile
find ${Find_Path} -iname "${itemFile}.*" -exec cp -r ${Images_Path}/${itemFile}/* {} \;
done
- 文件下载、get请求
wget
命令用来从指定的URL下载文件
下载文件:
wget http://192.20.183.143/data/project/ios.zip
发送get请求
wget "http://sample.com/recFile?result=构建成功"
- 文件上传
curl
是一个利用URL规则在命令行下工作的文件传输工具,是一款很强大的http命令行工具(功能比wget更强大)
上传文件到ftp服务器
#ftp服务器(两个都需要上传) 用户名 密码
ftp_domain="ftp://10.247.10.75/mixed"
ftp_user="ftpmixyun"
ftp_pw="ftpmixyun"
#ftp上传ipa的路径,如:ftp://10.247.10.75/wens/iOS/beta/
ftp_upload_path="${projectcode}/iOS/beta"
#ftp创建上传路径
curl -u ${ftp_user}:${ftp_pw} --ftp-create-dirs "${ftp_domain}/${ftp_upload_path}/"
#ftp上传ipa
curl -u ${ftp_user}:${ftp_pw} -T ${IPAPath} "${ftp_domain}/${ftp_upload_path}/"
- 压缩文件解压缩
unzip -o ./image_ios.zip
- 读取属性值,替换普通文件里的值
sed
是处理、编辑文本文件的指令,功能非常强大
读取属性(例. properties文件)
读取属性值
appname=`sed '/^APPNAME=/!d;s/.*=//' ios.properties`
替换工程里的参数
# Config_project:工程里的 userConfig.m文件
# APPNAME_placeHolder:userConfig.m文件里的占位值
sed -i '' "s/APPNAME_placeHolder/$appname/g" $Config_project
- plist文件的增、删、改、查
从PlistBuddy
的名字就可以看出,它是plist的小伙伴。PlistBuddy
是Mac自带的专门解析plist的小工具
替换info.plist里的App名称
/usr/libexec/plistbuddy -c "Set :CFBundleDisplayName ‘${appname}’” ${WORKSPACE}/info.plist
读取info.plist里的版本
bundleShortVersion=$(/usr/libexec/PlistBuddy -c "print CFBundleShortVersionString" ${WORKSPACE}/info.plist)
在info.plist里增加QQ分享AppKey
/usr/libexec/plistbuddy -c "Add CFBundleURLTypes:0:CFBundleURLSchemes:1 string QQ${QQ_APPKEY}" ${WORKSPACE}/info.plist
- 导入iOS证书
Security
是Mac系统中钥匙串和安全模块的命令行管理工具
导入发布证书
security unlock-keychain -p $mac_password /Users/${mac_user}/Library/Keychains/login.keychain
security list-keychains -s /Users/${mac_user}/Library/Keychains/login.keychain
security import $p12_distribution -k /Users/${mac_user}/Library/Keychains/login.keychain -P $Distribustion_PW -T /usr/bin/codesign
security find-identity -p codesigning /Users/${mac_user}/Library/Keychains/login.keychain
Ps:目前用脚本导入证书后电脑钥匙串里有该证书,但是第一次构建时依然提醒要输入电脑密码,这个问题未解决 —_—
- 解析iOS描述文件
# 获取描述文件.mobileprovision的详细信息,包括名称、ID、有效期等等
PROFILE_DATA=$(security cms -D -i ${pp_main})
# 解析AppGroup名称
APPGroup=$(/usr/libexec/PlistBuddy -c 'Print :Entitlements:com.apple.security.application-groups:0' /dev/stdin <<< $PROFILE_DATA)
# 解析TeamId
TeamId =$(/usr/libexec/PlistBuddy -c 'Print :ApplicationIdentifierPrefix:0' /dev/stdin <<< $PROFILE_DATA)
# .mobileprovision的名字
pp_name=$(/usr/libexec/PlistBuddy -c 'Print :Name' /dev/stdin <<< $PROFILE_DATA)
# .mobileprovision的UUID
pp_UUID=$(/usr/libexec/PlistBuddy -c 'Print :UUID' /dev/stdin <<< $PROFILE_DATA)
# .mobileprovision的AppID,即bundleId
CODE_SIGN_IDENTITY=$(/usr/libexec/PlistBuddy -c 'Print :Entitlements:application-identifier' /dev/stdin <<< $PROFILE_DATA)
pp_bundleId=${CODE_SIGN_IDENTITY#*.}
- xcodebuild编译(企业分发类型In-House)
一般项目可以用Jenkins的插件Xcode integration xcode来打包。
由于我们的app的target有多个extension,因此有多个描述文件,这个插件无法配置,采用纯脚本的方式
#选择Xcode版本
sudo xcode-select -s /Applications/Xcode9.0.app/Contents/Developer
## 打包
archivePath=${WORKSPACE}/build/${projectcode}-iphoneos/kdweibo.xcarchive
xcodebuild -scheme ${projectname} -workspace ${projectname}.xcworkspace -configuration Release archive -archivePath ${archivePath} DEVELOPMENT_TEAM=${TeamId}
企业分发类型的项目导出 ipa 包时需要一个enterprise.plist文件
enterprise="${Project_Build_resource_path}/enterprise-${TeamId}-${projectcode}.plist"
#设置enterprise.plist
/usr/libexec/plistbuddy -c "Set teamID ${TeamId}" ${enterprise}
/usr/libexec/plistbuddy -c "Add provisioningProfiles:${pp_main_bundleId} string ${pp_main_name}" ${enterprise}
/usr/libexec/plistbuddy -c "Add provisioningProfiles:${pp_extension_bundleId} string ${pp_extension_name}" ${enterprise}
/usr/libexec/plistbuddy -c "Add provisioningProfiles:${pp_widget_bundleId} string ${pp_widget_name}" ${enterprise}
/usr/libexec/plistbuddy -c "Add provisioningProfiles:${pp_message_bundleId} string ${pp_message_name}" ${enterprise}
/usr/libexec/plistbuddy -c "Add provisioningProfiles:${pp_call_bundleId} string ${pp_call_name}" ${enterprise}
导出ipa包
/usr/bin/xcodebuild -exportArchive -archivePath ${archivePath} -exportPath ${exportPath} -exportOptionsPlist ${enterprise}
- 比较版本号
currentVer="8.8.6"
targetVer="9.9.9"
if [ "$(printf '%s\n' "$currentVer" "$targetVer" | sort -V | head -n1)" = "$targetVer" ]; then
echo "Greater than or equal to $targetVer"
else
echo "Less than $targetVer"
fi
备注:表达式$(printf '%s\n' "$a” "$a” | sort -V | head -n1)
返回$a
和$b
中较小的一个值
实用Jenkins插件
Jenkins默认的构建历史列表只有构建号和日期:
当我们有大量的构建任务,并且有不同的项目,我们就很难从这个构建历史列表中找出某一个项目的某次构建任务了,下面两个插件可以自定义构建任务的标题,并且为构建任务添加描述,效果如下:
-
build-name-setter 自定义任务名称
description setter plugin 任务描述
这个插件的原理是从构建日志中用正则找出想要在构建描述里显示的值,因此需要首先在构建脚本的最后面写上
#build信息(用于在构建历史列表里显示)
description="[description]分支:${definedBranch}, 版本:${bundleShortVersion}, 构建号:${buildno}"
然后“增加构建步骤”——Set build description
由于description里需要换行,因此要设置Jenkins支持html格式
点击 Manage Jenkins 进入Jenkins管理配置界面:
然后在进入 Configure Global Security 配置界面:
修改Markup Formatter 中默认的Plain text ==> Safe HTML
保存配置即可
- Email Extension 自定义邮件通知
- PostBuildScript 构建结束后执行脚本
- Hudson Post build task 构建完后分析日志并执行脚本
- Xcode integration xcode项目傻瓜式构建