iOS 自动打包 (gitlab + Jenkins + shell + xcode)

总体流程如下表,稍后围绕下表进行分析:


4F0E96B4-9978-4C5E-823A-E656FFACCDE9.png
  1. 实现功能: 从管理平台勾选功能模块, 然后,配置icon,启动图,首次引导图,bundleID ,打包证书, 生成可以安装的二维码.

  2. 背景说明: 项目是有一个主项目, 然后有其他很多项目都是基于此项目开发,其他项目称为 子项目. 主项目包括用户中心, 登录系统,首页动态配置,基础webview.

  3. 为什么要做成一个主项目,然后有很多子项目, 而 不把 (用户中心, 登录系统,首页动态配置, ReactNative 环境) 做成单独的库,然后集成到各个项目中呢?
    由于主工程中appdelegate 做了一些配置 ,如 首次介绍页, log收集,引导图,一些基础的功能, 为了不让子工程 每次都要写这些代码, 于是写了一个主工程, 子工程只是从主工程拉过来代码, 然后,就成为单独的工程,不在会与主工程交互.

  4. 说下主流程.
    4.1 gitlab 创建私有库工程 LNUappProject, 并把开发完成的首次版本上传.(默认包含登录,首页配置,个人中心,和 一个通用webview)
    此主工程 有主工程开发人员维护.
    当用户在后台管理平台创建一个应用, 并打包的导出应用的时候, 走的流程为:


    image.png

    此时, 把创建的命令传给后台, 后台数据库记录此应用信息
    4.2 平台应用信息:


    image.png

此步骤后台记录创建的子应用的信息.
配置证书界面:


image.png

可配置一些三方的key 值,(如有分享, 统计)


image.png

"保存进入下一步" 后, 进入功能配置界面.下面是配置的界面:


image.png

可配置tabbar,首页(可配置首页子模块的实现方式(native,h5,RN)), 轮播图.
可更换tabbar 上的按钮位置.

配置完功能后,进入框架的配置:


image.png

此时点击构建版本:


image.png

填写构建版本的信息.


image.png

点击开始构建:
服务器创建子库, 从主工程拉取代码, 把上面的证书文件拷贝到子库中.把上传的logo 切各个尺寸, 启动图,修改系统中UAPPConfig.plist 文件. 把上述的
通过jekins创建新任务,并分配任务到mac 打包服务器. mac 打包服务器在构建应用的时候选择 通过shell 方式.

image.png

此时 看下执行的shell 脚本的内容: (脚本的大概意思: 解锁钥匙串, 导入证书到钥匙串, 导入ppfile, 修改工程权限,修改appName, bundleid, 获取证书中的公司信息,公司code,构建,导入ipa, 上传ipa 到 s3服务器 ,写的乱, 暂未整理)


image.png
#!/bin/bash -l
export LANG=en_US.UTF-8
security unlock-keychain -p 'xxxx'
ProjectName=$1 echo $ProjectName
if [ -z "$ProjectName" ] then
echo "⼯工程⽂文件夹不不能为空。" exit;
else
echo "传⼊入的⼯工程名字为: {$ProjectName},继续进⾏" fi
workspacePath="/Users/Shared/Jenkins/Home/workspace" projectPath="/Users/Shared/Jenkins/Home/workspace/${ProjectName}"
#sudo chown -R pcsd:pcsd $projectPath if [ ! -d "${projectPath}" ];then
echo "⽂文件夹不不存在"
exit;
else
echo "⽂文件夹存在" fi
cd $workspacePath
sudo chmod -R 777 $workspacePath cd $projectPath
AppConfigPlistPath="${projectPath}/AppConfig.plist"
#1. ==================项⽬目配置=================
#1.1 版本号
version=$(/usr/libexec/PlistBuddy -c "print appversion" ${AppConfigPlistPath}) #1.2 苹果证书对应的Team ID
#developent_team=$(/usr/libexec/PlistBuddy -c "print development_team" $ {AppConfigPlistPath})
#1.3 对应的 proversing_profile ⽂文件
#p_p_specifier=$(/usr/libexec/PlistBuddy -c "print provisioning_profile_specifier" ${AppConfigPlistPath})

#codesign_identity=$(/usr/libexec/PlistBuddy -c "print code_sign_identity" $ {AppConfigPlistPath})
#应⽤用名称
app_name=$(/usr/libexec/PlistBuddy -c "print app_name" $ {AppConfigPlistPath})
#变异环境
build_env=$(/usr/libexec/PlistBuddy -c "print build_even" ${AppConfigPlistPath}) #bundleid
bundleid=$(/usr/libexec/PlistBuddy -c "print bundle_id" ${AppConfigPlistPath}) #证书的密码
code_sign_pwd=$(/usr/libexec/PlistBuddy -c "print code_sign_pwd" $ {AppConfigPlistPath})
#ipa 包路路径
ipa_package_path=$(/usr/libexec/PlistBuddy -c "print ipa_package_path" $ {AppConfigPlistPath})
ipaName=$(/usr/libexec/PlistBuddy -c "print ipaName" ${AppConfigPlistPath})
echo "pwd: ${code_sign_pwd}"
#导⼊入证书
sudo security unlock-keychain -p 'xxxx' /Users/pcsd/Library/ Keychains/login.keychain-db
sudo security import './LNDEV01.p12' -k /Users/pcsd/Library/Keychains/ login.keychain-db -P "${code_sign_pwd}" -A -T /usr/bin/codesign
sudo security unlock-keychain -p 'xxxx' /Users/Shared/Jenkins/ Library/Keychains/login.keychain-db
sudo security import './LNDEV01.p12' -k /Users/Shared/Jenkins/Library/ Keychains/login.keychain-db -P "${code_sign_pwd}" -A -T /usr/bin/codesign #导⼊入配置⽂文件
cd $projectPath
echo "==================${projectPath}=========================" #mobileprovision-read -f "./LNUAPPDEV.mobileprovision" -o UUID mobileprovision_plist="${projectPath}/LNUAPPDEV.plist"
echo $mobileprovision_plist
#吧配置⽂文件转成plist ⽂文件
mobileprovisions=`find "${projectPath}" -name "*.mobileprovision"`
for mp in $mobileprovisions
do
sudo security cms -D -i "${mp}" > $mobileprovision_plist ppUUID=`/usr/libexec/PlistBuddy -c "Print UUID" $mobileprovision_plist`

p_p_specifier=`/usr/libexec/PlistBuddy -c "Print Name" $mobileprovision_plist`
echo "********${ppUUID}*********"
echo ${mp}
mv "${mp}" "${ppUUID}.mobileprovision"
sleep 1
sudo cp "${ppUUID}.mobileprovision" "/Users/pcsd/Library/MobileDevice/ Provisioning Profiles"
#rm $mobileprovision_plist
mv "${ppUUID}.mobileprovision" "${mp}"
done
p12s=`find "./" -name "*.p12"`
for p12 in $p12s
do
openssl pkcs12 -in $p12 -out ${projectPath}/temp.pem -nodes -password pass: $code_sign_pwd
code_sign_identity_inner=`strings temp.pem | grep "friendlyName: iP"`
echo $code_sign_identity_inner code_sign_identity_inner=${code_sign_identity_inner:18}
echo $code_sign_identity_inner
CODE_SIGN_IDENTITY=$code_sign_identity_inner teamid=`strings temp.pem | grep "/OU="` sub="OU="
subIndex="${teamid#*OU=}" teamid_result="${subIndex:0:10}" DEVELOPMENT_TEAM=$teamid_result
done
#echo $version
#echo $developent_team
#echo $p_p_specifier
#echo $version
#证书及描述⽂文件 #CODE_SIGN_IDENTITY=$codesign_identity #DEVELOPMENT_TEAM=$developent_team PROVISIONING_PROFILE_SPECIFIER=$p_p_specifier
cd $workspacePath

sudo chmod -R 777 $workspacePath
PROJECT_NAME="LNUniversalApplication" APP_NAME=$PROJECT_NAME APP_SCHEME=$PROJECT_NAME APP_SPACENAME="${PROJECT_NAME}.xcworkspace"
cd ${projectPath}/
#更更新 pod
#pod setup
#pod install --verbose --no-repo-update
# info.plist路路径
project_infoplist_path="./${APP_NAME}/Info.plist" #result_path=${workspacePath}/build/${build_env}_$(date +%Y-%m- %d_%H_%M) result_path=${workspacePath}/build/${build_env}_${ipa_package_path}
#修改exportOptions.plist ⽂文件 exportoptions_infoplist_path="./ExportOptions.plist"
exportteamid=$DEVELOPMENT_TEAM /usr/libexec/PlistBuddy -c "Set :teamID ${exportteamid}" $ {exportoptions_infoplist_path}
exportoption_bundleid=$bundleid
/usr/libexec/PlistBuddy -c "delete :provisioningProfiles " $ {exportoptions_infoplist_path}
/usr/libexec/PlistBuddy -c "add :provisioningProfiles dict" $ {exportoptions_infoplist_path}
/usr/libexec/PlistBuddy -c "add :provisioningProfiles:${exportoption_bundleid} string ${ppUUID}" ${exportoptions_infoplist_path}
#修改⼯工程中的 info.plist ⽂文件 #1. 修改版本
bundleShortVersion=$version
/usr/libexec/PlistBuddy -c "Set :CFBundleShortVersionString $ {bundleShortVersion}" ${project_infoplist_path}
#修改项⽬目名称
bundleDisplayName=$app_name
/usr/libexec/PlistBuddy -c "Set :CFBundleDisplayName ${bundleDisplayName}" ${project_infoplist_path}

#修改bundle ID
bundleID=$bundleid
/usr/libexec/PlistBuddy -c "Set :CFBundleIdentifier ${bundleID}" $ {project_infoplist_path}
#修改PRODUCT_BUNDLE_IDENTIFIER
sed -i '' s/com.lenovo.lnuapp/$bundleID/g $projectPath/ LNUniversalApplication.xcodeproj/project.pbxproj
sleep 2
#构建路路径设置
mkdir -p "${result_path}"
setting_out=${result_path}/build_setting.txt
xcodebuild -showBuildSettings -workspace "${APP_SPACENAME}" -scheme "$ {APP_NAME}" -configuration Debug > ${setting_out}
build_path=`echo $(grep -w "CONFIGURATION_BUILD_DIR" ${setting_out} | awk -F= '{print $2}')`
echo "======build_path: ${build_path}======" archive_path=${build_path}/${APP_NAME}.xcarchive
echo "======开始构建======"
security unlock-keychain -p 'xxxx' &
xcodebuild clean archive -workspace "${APP_SPACENAME}" \ -scheme "${APP_NAME}" \
-archivePath ${archive_path} \
-configuration Debug \ CODE_SIGN_IDENTITY="${CODE_SIGN_IDENTITY}" \ DEVELOPMENT_TEAM="${DEVELOPMENT_TEAM}" \ PROVISIONING_PROFILE_SPECIFIER="$ {PROVISIONING_PROFILE_SPECIFIER}"
echo "======检查是否构建成功======" if [ -d "${archive_path}" ]; then
echo "构建成功......" else
echo -e " 构建失败,请修正后重新运⾏行行!" rm -rf ${result_path}
exit 1
fi ipa_path=${result_path}

ExportOptionsPlist=${projectPath}/ExportOptions.plist #XCODE8 需要使⽤用系统的ruby
which rvm > /dev/null
if [[ $? -eq 0 ]]; then
echo "RVM detected, forcing to use system ruby"
[ -s "$HOME/.rvm/scripts/rvm" ] && . "$HOME/.rvm/scripts/rvm" rvm use system
fi
xcodebuild -exportArchive \
-archivePath ${archive_path} \ -exportOptionsPlist ${ExportOptionsPlist} \ -exportPath ${ipa_path}
echo "======检查是否成功导出ipa======" ipa_temp_path=${ipa_path}/"LNUniversalApplication".ipa
echo "--------------------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------------------- -----------------------------------------------"
if [ -f "${ipa_temp_path}" ]; then
echo "导出ipa成功......"
#ipa重命名
mv "${ipa_temp_path}" "${ipa_path}/${ipaName}.ipa"
else
echo -e "\033[31m 导出ipa失败...... \033[0m"
exit 1
fi
echo "======共耗时${SECONDS}秒======"
#curl http://xxx.xxx.xxx.xxx:8082/fileNamagement/upload -F "file=@$ {ipa_temp_path}"
aws s3 cp "${ipa_path}/${ipaName}.ipa" s3://uapp-test/ --grants read=uri=http:// acs.amazonaws.com/groups/global/AllUsers


可能遇到的问题:

  • 权限问题,由于 安装 jenkins,电脑默认创建了 一个jenkins 用户 , 通过终端来操作,和通过浏览器执行脚本,所需权限不同,
  • jenkins 用户去操作钥匙串中的证书有权限问题.
  • Sudo 免密操作.由于需要给文件更大权限,读写权限,所以脚本中使用sudo 命令要免密.
  • 修改文件 /磁盘/etc/sudoers 文件中免密的配置. 由于这个文件是只读只读无权限的文件,所以要修改该文件权限.开启mac 系统的超级管理员.
    ** 开启超级管理员方法: (系统偏好设置/登录选项/网络账户服务器:(加入) / 打开目录使用工具/在状态栏找到 “编辑” ,下拉找到启用Root 用户)
    ** 切换到root 用户,修改sudoers 文件中内容.
  • Ssh 免密信任配置.(A机器如果想免密登录B机器,B机器需要有A机器生成的RSA_public key 放到 B机器的.ssh/authorized_keys 文件中.)
  • 读取p12 文件中公司信息,公司ID (首先要把p12证书文件转换成pem 文件,然后把pem 文件转换成plist 文集,查看公司信息 )
  • 读取mobileprovision 文件中team 信息,和 UUID,修改mobileprovision 文件名为UUID,然后倒入到本地 pcsd/mobileDevice 中
  • Ssh 免密配置失败. 使用expect -c spawn su - jenkins …. 脚本来模拟登录.
  • 电脑启动自动和服务器连接

主流程目录:


image.png

你可能感兴趣的:(iOS 自动打包 (gitlab + Jenkins + shell + xcode))