Shell 自动化打包

一、利用 Shell 脚本将 .app 自动转换为 .ipa

  1. 在项目 command + b 编译完成生成后缀名为 .app 的文件,选择工程的 Products 文件夹,展开选择编译生成的 .app 文件,右键–>Show in Finder,即可找到 .app 文件;
  2. 将 convert-ipa.sh 和 .app 后缀名文件放在同一个文件夹中;
  3. 将 convert-ipa.sh 拖入终端,回车即可;
  4. 转换完成的 ipa 文件在 IPAFolder 文件夹中。

转换为 ipa 的 Shell 脚本如下:

#!/bin/sh

# 脚本目录和xxx.app要在同一个目录,如果想要配置,请填入包含后缀名的全称
g_app_name=""

# 在当前目录下找到 .app 文件
AutoFindAppFolder() {
	for p_file in ./*; do
		#判断是否为文件夹
		if [ -d "$p_file" ]; then
			file_name=$(basename "$p_file")
			#判断后缀名称是否是app
			file_last=${file_name##*.}
            if [ "$file_last" = "app" ]; then
                g_app_name="$file_name"
            fi
		fi
	done
}

# 打包ipa
AppToIpa() {
    app_file_name=$1
    # 先删除里面当前的IPAFolder文件夹
    rm -rf IPAFolder
    # 再创建IPAFolder文件夹
    mkdir IPAFolder
    # 在文件夹里面创建Payload文件夹
    mkdir IPAFolder/Payload
    # 将当前目录下的App_Name.app复制到Payload里面
    cp -r "$app_file_name" IPAFolder/Payload/"$app_file_name"
    # 进入IPAFolder文件夹
    cd IPAFolder
    # 压缩多个目录zip FileName.zip 目录1 目录2 目录3....
    file_head=${app_file_name%.*}
    zip -r "$file_head.ipa" Payload
    echo "*** ipa 文件已经创建到 IPAFolder 文件夹下 ***"
}

cd "$(dirname "$0")" || exit 0

# 如果未设置,则自动获取一次
if [ -z "$g_app_name" ]; then
  AutoFindAppFolder
fi
# 如果自动获取仍旧为空,提示
if [ -z "$g_app_name" ]; then
  echo "*** 当前脚本文件夹下找不到后缀名 .app 的文件 ***"
else
  # 转换
  AppToIpa "$g_app_name"
fi

二、利用 Shell 脚本自动打包

主要 Shell 脚本如下,完整脚本请至 Git 下载(文章末尾有下载链接):

  1. 将 auto-archive.sh 脚本复制到文件后缀名 .xcodeproj 所在项目目录下;
  2. 将 auto-archive.sh 拖入终端,回车即可;
  3. 如果自动打包失败,请打开 auto-archive.sh 配置项目信息;
  4. 如果项目目录无 ExportOptions.plist 文件,请参考新建。
#!/bin/sh
# 打包脚本, 将此脚本放在后缀名为xcodeproj的文件同级目录下即可

#工程名称,注意:ExportOptions.plist文件必须放到"工程名.xcodeproj"同级目录下,然后在终端中输入"cd 当前工程名.xcodeproj所在文件路径",在拖入此Shell脚本即可。
g_project_name=""

#打包模式 Debug/Release
g_development_mode="Release"

#scheme名
g_scheme_name=""

#plist文件所在路径,里面需要包含Bundle ID和描述文件名称,注意:ExportOptions.plist文件必须放到"工程名.xcodeproj"同级目录下
g_export_options="ExportOptions.plist"

#导出.ipa文件所在路径,默认在桌面的 auto-ipa 文件夹下
g_ipa_path="~/Desktop/auto-ipa"

#导出 ipa 文件的时间
g_current_time=""

# 找到xcodeproj文件或者xcworkspace文件
FindProjFile() {
	for file in ./*; do
		#判断是否为文件夹
		if [ -d "$file" ]; then
			file_name=$(basename "$file")
			#判断后缀名称
			file_last=${file_name##*.}
            if [ "$file_last" = "xcodeproj" ]; then
                g_project_name=$file_name
            fi
            if [ "$file_last" = "xcworkspace" ]; then
                g_project_name=$file_name
                break
            fi
		fi
	done
}

# 自动找到scheme
# list_proj=$(xcodebuild -list -project "$file_proj")
# list_scheme=${list_proj##*:}
# 使用awk去除换行,使用sed去除空格
# g_scheme_name=$(echo "$list_scheme" | awk '{printf "%s",$1}'| sed "s/ //g")
FindScheme() {
    temp_project_name=$1
    file_proj=${temp_project_name%.*}.xcodeproj
    if [ -d "$file_proj" ]; then
        proj_setting=$(xcodebuild -showBuildSettings)
        s_scheme=$(echo "$proj_setting" | grep TARGET_NAME)
        g_scheme_name=${s_scheme#*= }
    fi
}

# 创建ExportOptions.plist 文件
CreateExportOptionsPlist() {
    export_plist=$1
    proj_setting=$(xcodebuild -showBuildSettings)
    s_identifier=$(echo "$proj_setting" | grep PRODUCT_BUNDLE_IDENTIFIER)
    k_id=${s_identifier#*= }
    s_provision=$(echo "$proj_setting" | grep PROVISIONING_PROFILE_SPECIFIER)
    k_pr=${s_provision#*= }
    s_team_id=$(echo "$proj_setting" | grep DEVELOPMENT_TEAM)
    k_te=${s_team_id#*= }

    # 添加team id
    /usr/libexec/PlistBuddy -c "Add :teamID string $k_te" "$export_plist"
    # 添加描述文件
    /usr/libexec/PlistBuddy -c "Add :provisioningProfiles dict" "$export_plist"
    # 添加value值,
    /usr/libexec/PlistBuddy -c "Add :provisioningProfiles:$k_id string $k_pr" "$export_plist"

    echo "*** teamID:$k_te"
    echo "*** provisioningProfiles:$k_id:$k_pr"
}

# 导出ipa
ArchiveIpa() {
    proj_name=$1
    proj_export_plist=$2
    proj_scheme=$3
    proj_ipa_path=$4
    # 获取时间 如:20190613-092619
    g_current_time="$(date +%Y%m%d-%H%M%S)"
    ipa_folder="$proj_ipa_path/$proj_scheme$g_current_time"

    xcodebuild -exportArchive -archivePath build/"$proj_name".xcarchive \
	-exportPath "$ipa_folder" \
    -destination generic/platform=ios \
	-exportOptionsPlist "$proj_export_plist" \
    -allowProvisioningUpdates \
	-quiet || exit

    # 删除build包
    if [ -d build ]; then
        rm -rf build -r
    fi
}

# 编译项目
BuildProj() {
    proj_name=$1
    proj_mode=$2
    proj_scheme=$3
    proj_export_plist=$4
    proj_ipa_path=$5

    echo "*** 正在清理工程 ***"
    xcodebuild clean  -project "$proj_name".xcodeproj \
    -scheme "$proj_scheme" \
    -configuration "$proj_mode" -quiet || exit
    echo "*** 清理完成     ***"

    echo "*** 正在编译 For $proj_mode"
    xcodebuild \
    archive -project "$proj_name".xcodeproj \
    -scheme "$proj_scheme" \
    -configuration "$proj_mode" \
    -archivePath build/"$proj_name".xcarchive \
    -destination generic/platform=ios -quiet || exit
    echo "*** 编译完成     ***"

    # 导出ipa
    echo "*** 正在导出ipa  ***"
    ArchiveIpa "$proj_name" "$proj_export_plist" "$proj_scheme" "$proj_ipa_path"
    echo "*** ipa 已导出到目录:$proj_ipa_path"
}

BuildWorkspace() {
    proj_name=$1
    proj_mode=$2
    proj_scheme=$3
    proj_export_plist=$4
    proj_ipa_path=$5

    echo "*** 正在清理工程 ***"
    xcodebuild clean  -workspace "$proj_name".xcworkspace \
    -scheme "$proj_scheme" \
    -configuration "$proj_mode" -quiet || exit
    echo "*** 清理完成     ***"

    echo "*** 正在编译 For $proj_mode"
    xcodebuild \
    archive -workspace "$proj_name".xcworkspace \
    -scheme "$proj_scheme" \
    -configuration "$proj_mode" \
    -archivePath build/"$proj_name".xcarchive \
    -destination generic/platform=ios -quiet || exit
    echo "*** 编译完成     ***"

     # 导出ipa
    echo "*** 正在导出ipa  ***"
    ArchiveIpa "$proj_name" "$proj_export_plist" "$proj_scheme" "$proj_ipa_path"
    echo "*** ipa 已导出到目录:$proj_ipa_path"
}

# 程序主方法
Main() {
    # 自动获取项目名称
    if [ -z "$g_project_name" ]; then
        FindProjFile
    fi

    # 自动获取scheme
    if [ -z "$g_scheme_name" ]; then
        FindScheme "$g_project_name"
    fi

    if [ -z "$g_project_name" ] || [ -z "$g_scheme_name" ]; then
        echo "*** 提示:项目名称或 scheme 获取失败。 ***"
        exit 0
    fi

    if [ ! -f "$g_export_options" ]; then
        echo "*** 未发现ExportOptions.plist文件,自动创建中... ***"
        CreateExportOptionsPlist "$g_export_options"
    fi

    echo "*** 项目配置信息如下: ***"
    echo "*** 工程名称:$g_project_name"
    echo "*** 打包模式:$g_development_mode"
    echo "*** scheme:$g_scheme_name"
    echo "*** 导出配置:$g_export_options"
    echo "*** 导出路径:$g_ipa_path"
    echo "*** 项目配置完毕 ***"

    # 获取名称和后缀
    file_head=${g_project_name%.*}
    file_last=${g_project_name##*.}
    if [ "$file_last" = "xcworkspace" ]; then
        BuildWorkspace "$file_head" "$g_development_mode" "$g_scheme_name" "$g_export_options" "$g_ipa_path"
    else
        BuildProj "$file_head" "$g_development_mode" "$g_scheme_name" "$g_export_options" "$g_ipa_path" 
    fi

    # 绝对路径
    user_path=$(cd ~ && pwd)
    abs_ipa_folder=$(echo ${g_ipa_path/\~/"$user_path"})

    ipa_path="$abs_ipa_folder/$proj_scheme$g_current_time/$g_scheme_name.ipa"
    if [ -e "$ipa_path" ]; then
        echo "*** ipa 路径:$ipa_path"
    else
        echo "*** 创建 ipa 文件失败 ***"
    fi

    echo "*** 打包完成 ***"
}

cd "$(dirname "$0")" || exit 0

# 判断有无传递参数,Python脚本中传递有参数
if [ -z "$1" ]; then
    Main
fi


三、Shell 脚本完整版链接

自动打包脚本和 .app 自动转换为 .ipa 的脚本下载链接下载链接

如果您觉得有所帮助,请在GitHub dev-shell上赏个Star ⭐️,您的鼓励是我前进的动力。