#!/usr/bin/env bash set -euo pipefail # 修复 Xcode Organizer 显示 “Generic Xcode Archive” 的问题: # - 某些情况下 xcodebuild 生成的 .xcarchive/Info.plist 缺少 ApplicationProperties # - Organizer 无法识别归档中的主 App(即使 Products/Applications/*.app 存在) # # 说明: # - 该脚本的核心作用是让 Organizer 能识别归档里的主 App,从而出现“分发/上传 TestFlight”入口。 # - 这类问题通常发生在命令行/CI 归档(xcodebuild archive)或某些自定义归档流程中, # 导致 .xcarchive/Info.plist 缺少/不完整。 # # 用法: # ./scripts/fix-xcarchive-header.sh "/path/to/xxx.xcarchive" ARCHIVE_PATH="${1:-}" if [[ -z "$ARCHIVE_PATH" ]]; then echo "用法: $0 \"/path/to/xxx.xcarchive\"" >&2 exit 2 fi if [[ ! -d "$ARCHIVE_PATH" ]]; then echo "错误:找不到归档目录:$ARCHIVE_PATH" >&2 exit 2 fi ARCHIVE_INFO_PLIST="$ARCHIVE_PATH/Info.plist" if [[ ! -f "$ARCHIVE_INFO_PLIST" ]]; then echo "错误:找不到归档 Info.plist:$ARCHIVE_INFO_PLIST" >&2 exit 2 fi # 归档名:尽量从路径推导,避免依赖 Xcode 环境变量 archive_basename="$(/usr/bin/basename "$ARCHIVE_PATH")" archive_name="${archive_basename%.xcarchive}" # 取第一个 App(归档里通常只有一个主 App) APP_PLIST="$(/usr/bin/find "$ARCHIVE_PATH/Products/Applications" -maxdepth 2 -name Info.plist -path "*.app/Info.plist" 2>/dev/null | /usr/bin/head -n 1 || true)" if [[ -z "$APP_PLIST" ]]; then echo "错误:归档中未找到 Products/Applications/*.app/Info.plist(请先确保归档产出包含 .app)" >&2 exit 2 fi APP_DIR="$(/usr/bin/dirname "$APP_PLIST")" APP_NAME="$(/usr/bin/basename "$APP_DIR")" # 例如 DearMama.app APP_REL_PATH="Applications/$APP_NAME" bundle_id="$(/usr/bin/plutil -extract CFBundleIdentifier raw -o - "$APP_PLIST" 2>/dev/null || true)" short_version="$(/usr/bin/plutil -extract CFBundleShortVersionString raw -o - "$APP_PLIST" 2>/dev/null || true)" build_version="$(/usr/bin/plutil -extract CFBundleVersion raw -o - "$APP_PLIST" 2>/dev/null || true)" display_name="$(/usr/bin/plutil -extract CFBundleDisplayName raw -o - "$APP_PLIST" 2>/dev/null || true)" bundle_name="$(/usr/bin/plutil -extract CFBundleName raw -o - "$APP_PLIST" 2>/dev/null || true)" # SchemeName 在 Organizer 中会用到,但在某些归档流程里会缺失 scheme_name="${SCHEME_NAME:-}" if [[ -z "$scheme_name" ]]; then scheme_name="${archive_name:-}" fi if [[ -z "$bundle_id" || -z "$short_version" || -z "$build_version" ]]; then echo "错误:无法从 App Info.plist 读取 bundle/version/build:$APP_PLIST" >&2 exit 2 fi # 解析 embedded.mobileprovision(若存在) profile_name="" profile_uuid="" team_id="" provision_path="$APP_DIR/embedded.mobileprovision" if [[ -f "$provision_path" ]]; then decoded="$(/usr/bin/security cms -D -i "$provision_path" 2>/dev/null || true)" if [[ -n "$decoded" ]]; then # 使用 plutil 从 xml 中提取字段 profile_name="$(printf "%s" "$decoded" | /usr/bin/plutil -extract Name raw -o - - 2>/dev/null || true)" profile_uuid="$(printf "%s" "$decoded" | /usr/bin/plutil -extract UUID raw -o - - 2>/dev/null || true)" team_id="$(printf "%s" "$decoded" | /usr/bin/plutil -extract TeamIdentifier.0 raw -o - - 2>/dev/null || true)" fi fi # 备份一份,防止误操作 cp -f "$ARCHIVE_INFO_PLIST" "$ARCHIVE_INFO_PLIST.bak" # 修复归档根字段,避免 Organizer 仍然把它当 Generic Archive # 参考:标准 .xcarchive/Info.plist 通常包含 Name / SchemeName / ArchiveVersion / CreationDate 等。 # 我们只在缺失时补齐,尽量不改动归档的其他内容。 if ! /usr/bin/plutil -extract Name xml1 -o - "$ARCHIVE_INFO_PLIST" >/dev/null 2>&1; then /usr/bin/plutil -insert Name -string "${archive_name:-${display_name:-${bundle_name:-}}}" "$ARCHIVE_INFO_PLIST" 2>/dev/null || true fi if ! /usr/bin/plutil -extract SchemeName xml1 -o - "$ARCHIVE_INFO_PLIST" >/dev/null 2>&1; then /usr/bin/plutil -insert SchemeName -string "${scheme_name:-${archive_name:-}}" "$ARCHIVE_INFO_PLIST" 2>/dev/null || true fi # 如果已有 ApplicationProperties,直接更新关键字段即可 if /usr/bin/plutil -extract ApplicationProperties xml1 -o - "$ARCHIVE_INFO_PLIST" >/dev/null 2>&1; then /usr/bin/plutil -replace ApplicationProperties.ApplicationPath -string "$APP_REL_PATH" "$ARCHIVE_INFO_PLIST" /usr/bin/plutil -replace ApplicationProperties.CFBundleIdentifier -string "$bundle_id" "$ARCHIVE_INFO_PLIST" /usr/bin/plutil -replace ApplicationProperties.CFBundleShortVersionString -string "$short_version" "$ARCHIVE_INFO_PLIST" /usr/bin/plutil -replace ApplicationProperties.CFBundleVersion -string "$build_version" "$ARCHIVE_INFO_PLIST" else # 新增 ApplicationProperties(注意:plutil 的空字典/数组类型是 -dictionary / -array) /usr/bin/plutil -insert ApplicationProperties -dictionary "$ARCHIVE_INFO_PLIST" /usr/bin/plutil -insert ApplicationProperties.ApplicationPath -string "$APP_REL_PATH" "$ARCHIVE_INFO_PLIST" /usr/bin/plutil -insert ApplicationProperties.CFBundleIdentifier -string "$bundle_id" "$ARCHIVE_INFO_PLIST" /usr/bin/plutil -insert ApplicationProperties.CFBundleShortVersionString -string "$short_version" "$ARCHIVE_INFO_PLIST" /usr/bin/plutil -insert ApplicationProperties.CFBundleVersion -string "$build_version" "$ARCHIVE_INFO_PLIST" /usr/bin/plutil -insert ApplicationProperties.Architectures -array "$ARCHIVE_INFO_PLIST" /usr/bin/plutil -insert ApplicationProperties.Architectures.0 -string "arm64" "$ARCHIVE_INFO_PLIST" fi # 可选字段:Provisioning Profile 信息(不保证一定存在) if [[ -n "$profile_name" ]]; then /usr/bin/plutil -replace ApplicationProperties.ProvisioningProfileName -string "$profile_name" "$ARCHIVE_INFO_PLIST" 2>/dev/null || \ /usr/bin/plutil -insert ApplicationProperties.ProvisioningProfileName -string "$profile_name" "$ARCHIVE_INFO_PLIST" fi if [[ -n "$profile_uuid" ]]; then /usr/bin/plutil -replace ApplicationProperties.ProvisioningProfileUUID -string "$profile_uuid" "$ARCHIVE_INFO_PLIST" 2>/dev/null || \ /usr/bin/plutil -insert ApplicationProperties.ProvisioningProfileUUID -string "$profile_uuid" "$ARCHIVE_INFO_PLIST" fi if [[ -n "$team_id" ]]; then /usr/bin/plutil -replace ApplicationProperties.Team -string "$team_id" "$ARCHIVE_INFO_PLIST" 2>/dev/null || \ /usr/bin/plutil -insert ApplicationProperties.Team -string "$team_id" "$ARCHIVE_INFO_PLIST" fi echo "已修复归档 header:$ARCHIVE_INFO_PLIST" echo "主 App:$APP_REL_PATH" echo "Bundle:$bundle_id" echo "Version/Build:$short_version/$build_version" echo "Name/SchemeName:${archive_name:-} / ${scheme_name:-}" # 轻量自检:确保关键字段存在(不强制失败,避免中断归档) if ! /usr/bin/plutil -extract ApplicationProperties.ApplicationPath xml1 -o - "$ARCHIVE_INFO_PLIST" >/dev/null 2>&1; then echo "警告:归档 Info.plist 仍缺少 ApplicationProperties.ApplicationPath,Organizer 可能仍显示 Generic Archive" >&2 fi