如何根据GitHub提交记录更新Xcode的CFBundleVersion?原SVN脚本迁移Git后失效求助
解决Xcode中通过Git提交记录自动更新版本号的问题
你之前靠SVN的Ruby脚本在Xcode的Run Script Build Phase里自动维护CFBundleVersion和CFBundleShortVersionString,现在要切换到Git(GitHub)但自己写的脚本跑不起来?别担心,我帮你分析问题并给出可靠的解决方案——毕竟直接用正则怼XML格式的plist很容易踩坑,我们还是用更稳妥的plist解析库来处理,和你原来的SVN脚本逻辑保持一致,降低适配成本。
先说说你原有脚本的问题
你的Git脚本主要有几个坑:
- 直接修改源Info.plist:这会把构建生成的版本号提交到源码仓库,污染原始配置,正确的做法应该修改构建产物里的plist,不影响源码
- 正则匹配XML结构不可靠:Xcode生成的plist可能有不同的缩进、换行格式,正则很容易匹配失败,导致版本号更新失效
- 缺少错误处理:比如Git命令执行失败(当前目录不是Git仓库、没有标签)时,脚本直接崩溃,没有友好提示
- 版本号逻辑有漏洞:如果仓库没有任何Git标签,
git describe会报错,你的脚本没处理这种情况
修正后的Git版本脚本
我基于你原来的SVN脚本逻辑,改写了适配Git的版本,保留了plist解析库的用法,更稳定可靠:
#!/usr/bin/ruby require "rubygems" require "plist" # 必须从Xcode的Run Script Phase运行 unless ENV['XCODE_VERSION_ACTUAL'] puts "error: 必须在Xcode的Run Script Build Phase中运行此脚本" exit false end # 获取Git相关版本信息 # 优先用git describe --tags获取标签+提交数,没有标签则用提交哈希前7位 revision_info = `git describe --tags --abbrev=7 2>/dev/null`.chomp if $?.success? # 匹配格式:v1.2.3-5-gabcdefg 或者 1.2.3 if revision_info =~ /^v?(\d+\.\d+)(?:\.(\d+))?(?:-(\d+))?-g?([0-9a-f]+)?/ major_minor = $1 patch = $2 || "0" commit_count = $3 || "0" commit_hash = $4 || "" # 组装版本号:比如1.2.3.5(标签1.2.3之后有5次提交) short_version = "#{major_minor}.#{patch}.#{commit_count}" # 如果是Release配置,确保没有未提交的修改 if ENV['CONFIGURATION'] == 'Release' uncommitted_changes = `git status --porcelain`.chomp unless uncommitted_changes.empty? puts "error: Release配置下不能有未提交的修改,请先提交或 stash 更改" exit false end end else puts "warning: Git标签格式不符合x.y.z规范,使用提交哈希作为版本" short_version = `git rev-parse --short HEAD`.chomp end else # 没有标签的情况,用提交哈希前7位作为版本号 puts "warning: 未找到Git标签,使用提交哈希作为版本" short_version = `git rev-parse --short HEAD`.chomp end # 获取构建后的plist路径(和你原来的SVN脚本逻辑一致) plist_file = "#{ENV['BUILT_PRODUCTS_DIR']}/#{ENV['INFOPLIST_PATH']}" # 转成XML格式才能用plist库解析 `/usr/bin/plutil -convert xml1 "#{plist_file}"` # 解析plist文件 unless plist_data = Plist::parse_xml(plist_file) puts "error: 无法解析plist文件 #{plist_file}" exit false end # 更新CFBundleShortVersionString(显示给用户的版本号) plist_data["CFBundleShortVersionString"] = short_version # 更新CFBundleVersion(内部版本号,用UTC时间戳,和你原来的逻辑一致) plist_data["CFBundleVersion"] = Time.now.utc.strftime("%Y%m%d%H") # 保存修改后的plist,再转成binary格式 plist_data.save_plist(plist_file) `/usr/bin/plutil -convert binary1 "#{plist_file}"` # 输出日志,方便调试 puts "已更新版本号:" puts "CFBundleShortVersionString = #{plist_data["CFBundleShortVersionString"]}" puts "CFBundleVersion = #{plist_data["CFBundleVersion"]}"
关键细节说明
- Git版本逻辑:
- 优先用
git describe --tags获取标签信息,比如你打了v1.2.3的标签,之后有5次提交,会生成1.2.3.5的版本号 - 如果没有标签,会用提交哈希的前7位作为版本号
- Release配置下会检查未提交修改,避免脏构建
- 优先用
- 操作构建后的plist:不会修改源码目录里的Info.plist,只修改Xcode构建产物中的文件,避免污染源码仓库
- 可靠的plist解析:用官方推荐的plist库操作,不会因为XML格式变化导致匹配失败
- 错误处理:每一步都做了失败检查,出错时会输出明确的错误信息,方便调试
使用步骤
- 替换你原来的SVN脚本:在Xcode的Target -> Build Phases -> Run Script中,把原来的Ruby脚本替换成上面的内容
- 确保你的Git仓库有符合规范的标签(比如
v1.0.0或者1.0.0),这样脚本能生成更友好的版本号 - 测试构建:先做一次Debug构建,看Xcode的Build Log里有没有版本号更新的日志,确认正常工作
内容的提问来源于stack exchange,提问作者Shallow
相关产品推荐
相关产品推荐

