!/bin/bash
export LANG=en_US.UTF-8
报错后退出执行
set -o errexit
要区分 scheme_name
UAT 开发+测试
Prd ECDH服务端新公钥
APPSTORE 上线的target
要区分 导出method
ad-hoc
release-testing
app-store-connect
要区分 上传 (加上加固 - 默认上传)
http://****:7979/app/ios
sit(test)|dev(开发联调提供给服务端开发使用) 测试环境
|uat| 正式提测(是测试人员拿包自行上传)
preprod 预生产(app人员上传)
pro (不使用)
production (不上传)
appstore/testflight (加上加固 - 默认上传)
脚本放在工程目录下
原 导出的包放在桌面/IPA/scheme_name/
新 导出的包放在桌面/xcodeBulid/scheme_name
最新加固工具放在
桌面/bulid/shield/doc
桌面/bulid/shield/SDK
桌面/bulid/shield/Shielder
旧版本归档
桌面/bulid/旧版本加固工具/Shield_iOS7.0.1.140843
定义 display_help 函数
display_help() {
echo ""
echo -e "\033[32m🍏欢迎使用iOS打包脚本, 请按照指引使用🍏\033[0m"
echo -e " 脚本名:\033[32mxcodebuildscript.sh\033[0m"
echo -e " 模式:\033[32m iOS打包主脚本\033[0m"
echo ""
echo -e "\033[33m 使用:\033[0m"
echo -e "\033[33m [脚本文件] [选项] [选项]\033[0m (使用打包脚本并使用对应配置)"
echo ""
echo -e "\033[33m 方式一:\033[31m[key] [value]\033[0m 选项:\033[0m"
echo -e " -t, --target【选项】指定项目的scheme名称(也就是工程的target名称)后面加【选项】"
echo -e " uat对应UAT"
echo -e " prd对应Prd"
echo -e " appstore对应APPSTORE"
echo ""
echo -e " -m, --method【选项】打包方式 后面加【选项】"
echo -e " dev对应release-testing"
echo -e " dis对应app-store-connect"
echo ""
echo -e "\033[33m 示例:\033[0m"
echo -e " 脚本目录/xcodebuild_only_modify.sh -t uat -m dev"
echo ""
echo -e " 脚本目录/xcodebuild_only_modify.sh -t appstore -m dis"
echo ""
echo -e " 脚本目录/xcodebuild_only_modify.sh -target appstore -method dis"
echo ""
echo ""
echo -e "\033[33m 方式二:\033[31m[value]\033[0m 选项:\033[0m"
echo -e " -uat, \033[33muat对应UAT\033[0m"
echo -e " -prd, \033[33mprd对应Prd\033[0m"
echo -e " -appstore, \033[33mappstore对应APPSTORE\033[0m"
echo ""
echo -e " -dev, -adhoc, \033[33m打测试安装包,adhoc模式\033[0m"
echo -e " -dis, -appstore, \033[33m打生产/testflight包,appstore模式\033[0m"
echo ""
echo -e "\033[33m 示例:\033[0m"
echo ""
echo -e " 脚本目录/xcodebuild_only_modify.sh -prd -dis"
echo ""
echo -e " 脚本目录/xcodebuild_only_modify.sh -appstore -dis"
echo ""
echo -e "\033[32m如不输入参数,则会在后续过程中输入选择\033[0m"
echo ""
echo ""
}
if [[ "1" == "-help" ]]; then
display_help
exit 0
fi
echo -e "\033[32m =================================================== \033[0m"
echo -e "\033[32m ============= 开始执行 iOS打包脚本 ============= \033[0m"
echo -e "\033[32m =================================================== \033[0m"
是否编译工作空间 (例:若是用Cocopods管理的.xcworkspace项目,赋值true;用Xcode默认创建的.xcodeproj,赋值false)
is_workspace="true"
.xcworkspace的名字,如果is_workspace为true,则必须填。否则可不填
workspace_name="*****"
.xcodeproj的名字,如果is_workspace为false,则必须填。否则可不填
project_name="*****"
指定要打包编译的方式 : Release,Debug。一般用Release。必填
build_configuration="Release"
签名方式,目前****都是手动管理 manual/automatic
signing_style="manual"
team_id="9595***569"
项目的scheme名称(也就是工程的target名称)
scheme_name=""
导出ipa方式
旧方式分别为: development, ad-hoc, app-store, enterprise,
新方式为:release-testing,
method=""
描述文件
mobileprovision_name=""
bundleid
bundle_identifier=""
function input_error() {
echo -e "\033[31m❗️❗️❗️脚本格式错误, 请使用 [-h] 或 [-help]查看使用方法 \033[0m"
echo ""
display_help
exit 1
}
基于参数配置target、描述文件、bundleid
function set_targetname() {
if [[ "1" == "prd" ]]; then
scheme_name="Prd"
elif [[ "$1" == "appstore" ]]; then
scheme_name="APPSTORE"
else
input_error
fi
}
基于参数配置导出方法
function set_method() {
if [[ "$1" == "dev" ]]; then
method="release-testing"
if [[ "$scheme_name" == "UAT" ]]; then
mobileprovision_name="***.uat.adhoc"
bundle_identifier="***.uat"
elif [[ "$scheme_name" == "Prd" ]]; then
mobileprovision_name="***.uat.adhoc"
bundle_identifier="***.uat"
elif [[ "$scheme_name" == "APPSTORE" ]]; then
mobileprovision_name="***.prod.adhoc"
bundle_identifier="***.prod"
else
input_error
fi
elif [[ "$1" == "dis" ]]; then
method="app-store-connect"
if [[ "$scheme_name" == "UAT" ]]; then
mobileprovision_name="***.uat.dis"
bundle_identifier="***.uat"
elif [[ "$scheme_name" == "Prd" ]]; then
mobileprovision_name="***.uat.dis"
bundle_identifier="***.uat"
elif [[ "$scheme_name" == "APPSTORE" ]]; then
mobileprovision_name="***.prod.dis"
bundle_identifier="***.prod"
else
input_error
fi
else
input_error
fi
}
选择target
function choose_target() {
if [ "$scheme_name" = "" ]; then
echo -e "\033[35m 请选择打包target:\033[0m"
echo -e "\033[33m 1)UAT"
echo -e " 2)Prd"
echo -e " 3)APPSTORE"
echo -e "\033[0m"
read choice
case $choice in
1) set_targetname "uat" ;;
2) set_targetname "prd" ;;
3) set_targetname "appstore" ;;
*) {
echo -e "\033[31m 无效输入,重新选择 \033[0m"
echo -e ""
choose_target
} ;;
esac
fi
}
选择method,打包的方式
function choose_method() {
if [ "$method" = "" ]; then
echo -e "\033[35m 请选择打包的方式method:\033[0m"
echo -e "\033[33m 1)release-testing"
echo -e " 2)app-store-connect"
echo -e "\033[0m"
read choice
case $choice in
1) set_method "dev" ;;
2) set_method "dis" ;;
*)
echo -e "\033[31m 无效输入,重新选择 \033[0m"
choose_method
;;
esac
fi
}
判断参数key是否存在
function print_msg() {
if [ "(printf "%-20s = %s\n" "
2" = "" ]; then
echo -e "\033[33m1" """")\033[0m"
else
echo -e "\033[33m1")\033[0m\033[31m
2")\033[0m"
fi
}
if [[ "1" == "--target" ]]; then
set_targetname "3" == "-m" || "
4"
else
input_error
fi
elif [[ "1" == "-prd" || "
1"
set_targetname "2" == "-dev" || "
2"
set_method "1" == "" ]]; then
echo -e ""
echo -e "\033[35m 未输入配置,需手动选择配置 \033[0m"
echo -e ""
choose_target
choose_method
else
input_error
fi
echo -e ""
echo -e "\033[32m ============= 脚本配置参数检查 ============= \033[0m"
print_msg is_workspace workspace_name
print_msg project_name scheme_name
print_msg build_configuration bundle_identifier
print_msg mobileprovision_name method
=======================脚本的一些固定参数定义(无特殊情况不用修改)======================
获取当前脚本所在目录
script_dir="(dirname "$0")" && pwd)"
工程根目录
project_dir=$script_dir
进入项目工程目录
cd ${project_dir}
读取旧的version版本号 原脚本代码中需要修改buildVerison 但不是改的xcodeproject文件中,而是改的plist文件
oldBuildNumber=
infoPlist_path")
echo "打包build号:$oldBuildNumber"
打包版本号
versions=project_dir/
scheme_name" -showBuildSettings | grep MARKETING_VERSION | awk '{print
versions"
读取旧的版本号
oldBuildNumber=project_dir/
scheme_name" -showBuildSettings | grep CURRENT_PROJECT_VERSION | awk '{print
oldBuildNumber"
获取当前设备当前用户的根路径
adm_path=$HOME
时间
now_date=$(date '+%Y-%m-%d_%H-%M-%S')
指定输出导出文件夹路径
package_path="$adm_path/Desktop/XcodeBuild"
例如~/Desktop/XcodeBuild/****/UAT/ad-hoc/2024-10-30_10-44-18
export_path="project_name/
method/$now_date"
判断是否为目录,创建空目录
if [ ! -d "export_path"
fi
指定输出.xcarchive归档文件路径
export_archive_path="scheme_name.xcarchive"
指定输出ipa文件夹路径
export_ipa_path="scheme_name"_v"
method""
指定导出ipa包需要用到的plist配置文件的路径
export_options_plist_path="$export_path/ExportOptions.plist"
echo ""
echo -e "\033[32m ============= infoPlist修改参数检查(buildVersion修改) ============= \033[0m"
echo ""
查找info.plist 文件的路径(得到的是相对路径)
infoPlist_path_relative={scheme_name}*.plist" -print)
info.plist 的绝对路径
infoPlist_path_absolute={infoPlist_path_relative:1}
info.plist 的路径
infoPlist_path=$infoPlist_path_absolute
新build 版本号
buildNumber=$(date '+%Y%m%d%H%M')
指定输出ipa名称 未加固
ipa_name=""{versions}_${buildNumber}_noshield"
已加固
shield_ipa_name=""{versions}_${buildNumber}_shield"
dSYM路径
package_dsym_path="scheme_name.app.dSYM"
dSYM导出路径
new_dsym_path="ipa_name.dSYM"
if [ ! "{buildNumber}" "
{buildNumber}"
else
echo "旧buildVersion存在={buildNumber}" "
{buildNumber}"
fi
echo ""
echo -e "\033[32m ============= 脚本固定参数检查 ============= \033[0m"
echo ""
print_msg project_dir {now_date}
print_msg export_path {export_archive_path}
print_msg export_ipa_path {export_options_plist_path}
print_msg ipa_name ${ipa_name}
=======================自动打包部分(无特殊情况不用修改)======================
echo ""
echo -e "\033[32m ============= 开始构建项目 ============= \033[0m"
echo ""
指定输出文件目录不存在则创建
if [ -d "export_path
else
mkdir -pv $export_path
fi
判断编译的项目类型是workspace还是project
if {workspace_name}.xcworkspace
-scheme {build_configuration}
-destination 'generic/platform=iOS'
-quiet || exit # -quiet只有warn和error才会输出
xcodebuild archive -allowProvisioningUpdates \
-workspace ${workspace_name}.xcworkspace \
-scheme ${scheme_name} \
-configuration ${build_configuration} \
-archivePath ${export_archive_path} \
-destination 'generic/platform=iOS' \
-quiet || exit # -quiet只有warn和error才会输出
else
# 编译前清理工程
xcodebuild clean -project {scheme_name}
-configuration ${build_configuration} -destination 'generic/platform=iOS'
xcodebuild archive -project ${project_name}.xcodeproj \
-scheme ${scheme_name} \
-configuration ${build_configuration} \
-archivePath ${export_archive_path} -destination 'generic/platform=iOS'
fi
检查是否构建成功
xcarchive 实际是一个文件夹不是一个文件所以使用 -d 判断
if [ -d "$export_archive_path" ]; then
echo -e "\033[32m项目构建成功 🚀 🚀 🚀 \033[0m"
else
echo -e "\033[31m项目构建失败 😢 😢 😢 \033[0m"
exit 1
fi
echo ""
echo -e "\033[32m =============== 正在导出ipa: "${export_ipa_path}" =============== \033[0m"
echo ""
先删除export_options_plist文件
if [ -f "{export_options_plist_path}文件存在,进行删除"
rm -f $export_options_plist_path
fi
根据参数生成export_options_plist文件
/usr/libexec/PlistBuddy -c "Add :destination string export" "export_options_plist_path
/usr/libexec/PlistBuddy -c "Add :method String export_options_plist_path
/usr/libexec/PlistBuddy -c "Add :signingStyle string export_options_plist_path"
/usr/libexec/PlistBuddy -c "Add :stripSwiftSymbols bool true" "export_options_plist_path"
/usr/libexec/PlistBuddy -c "Add :teamID string export_options_plist_path"
/usr/libexec/PlistBuddy -c "Add :provisioningProfiles dict" {bundle_identifier} String
export_options_plist_path
xcodebuild -exportArchive
-archivePath {export_ipa_path}
-exportOptionsPlist ${export_options_plist_path}
-allowProvisioningUpdates -destination 'generic/platform=iOS'
检查ipa文件是否存在
if [ -f "scheme_name.ipa" ]; then
echo -e "\033[32mexportArchive ipa包成功,准备进行重命名\033[0m"
else
echo -e "\033[31mexportArchive ipa包失败 😢 😢 😢 \033[0m"
exit 1
fi
修改ipa文件名称
mv scheme_name.ipa
ipa_name.ipa
检查文件是否存在
if [ -f "ipa_name.ipa" ]; then
echo -e "\033[32m导出 export_ipa_path
else
echo -e "\033[31m导出 ${ipa_name}.ipa 包失败 😢 😢 😢 \033[0m"
exit 1
fi
开始加固
echo -e "\033[32m ==============开始执行加固命令================== \033[0m"
echo ""
shield_tool_path=""package_path"/shield/Shielder/config-release-template.xml"
java -jar shield_config_path
ipa_name.ipa --out
shield_ipa_name.ipa
echo ""
echo -e "\033[32m ==============加固成功!================== \033[0m"
echo ""
输出打包总用时
echo -e "\033[32m 🚀🚀🚀打包脚本执行总用时为 ${SECONDS} 秒 \033[0m"
选择上传到哪个环境
function choose_uploadToQR() {
echo "\033[32m 请选择上传环境:\033[0m"
echo "\033[33m 1)上传 dev 环境二维码"
echo " 2)上传 uat 环境二维码"
echo " 3)上传 preprod 环境二维码"
echo " *)不上传\033[0m"
read choice
case $choice in
1) system="dev" ;;
2) system="uat" ;;
3) system="preprod" ;;
*)
echo "\033[31m 无效选择 \033[0m"
exit 1
;;
esac
}
上传到二维码
uploadToQR() {
echo -e "\033[32m =============== 执行app上传到内部平台脚本 =============== \033[0m"
osType="ios"
appName="***_*****"
bundleId=$bundle_identifier
versionCode=$1 # Assume the first argument to the function is the versionCode
ipa_name=$2 # Assume the second argument is the ipa name
system=$3 # Assume the third argument is the environment (dev/sit/uat/preprod/pro/production)
# Derived variables
app_file_name="$ipa_name.ipa"
UPLOAD_URL="http://192.168.1.19:7979/app/ios"
APP_FILE_PATH="$export_ipa_path/$ipa_name.ipa"
print_msg "Uploading package" $app_file_name
print_msg "bundleId" $app_file_name
print_msg "Environment" $system
print_msg "Version number" $versionCode
print_msg "Local package path" $APP_FILE_PATH
upload_response=$(curl -X POST "$UPLOAD_URL" \
-F "osType=$osType" \
-F "appName=$appName" \
-F "system=$system" \
-F "versionCode=$versionCode" \
-F "bundleId=$bundleId" \
-F "fileName=@$APP_FILE_PATH")
# Extract and print the link
link=$(echo $upload_response | sed -n 's/.*<p>\(https*:\/\/[^<]*\)<\/p>.*/\1/p')
echo -e "\033[32miOS ipa file upload result:\033[0m\033[31m$link\033[0m"
}
function uploadToAppstore() {
# app-store-connect,可以选择是否上传Appstore
echo -e "\033[32m =============== 执行app包上传Appstore脚本 =============== \033[0m"
# echo ""
# echo -e "\033[0;35m生产包/testflight,可以选择是否上传Appstore(请输入Y/N): \033[0m"
# read is_upload
# # 判断 is_upload 是否为 "Y" 或 "y"
# if [[ "$is_upload" = "Y" ]] || [[ "$is_upload" = "y" ]]; then
# echo ""
# else
# echo -e "\033[0;32m 不上传包,上传脚本执行完成 \033[0m"
# exit 0
# fi
# upload_app_appstore_ipa文件路径
uas_appFile="$1"
apple_username="****@qq.com"
apple_password="*******"
# 提示用户输入
echo -e "\033[32m =============== 执行app验证脚本 =============== \033[0m"
echo ""
xcrun altool --validate-app --type ios --file "$uas_appFile" --username "$apple_username" --password "$apple_password" --show-progress
echo ""
echo -e "\033[32m =============== 执行app包上传Appstore脚本 =============== \033[0m"
echo ""
xcrun altool --upload-app --type ios --file "$uas_appFile" --username "$apple_username" --password "$apple_password" --show-progress
echo ""
}
ad-hoc 则上次到内部
if [[ "versions
system
elif [[ "$method" == "app-store-connect" ]]; then
echo ""
echo -e "\033[32m =============== 执行导出dSYMs文件 =============== \033[0m"
echo ""
cp -R "$package_dsym_path" "$new_dsym_path"
echo -e "\033[0;32m已提取dSYM文件到ipa目录文件夹:"$new_dsym_path"\033[0m"
echo ""
# 上传ipa到appstore/testflight
uploadToAppstore "$export_ipa_path/$shield_ipa_name.ipa"
fi
exit 1