如何通过使用 git 提交号作为 xcode 内部版本号来避免无限循环

Posted

技术标签:

【中文标题】如何通过使用 git 提交号作为 xcode 内部版本号来避免无限循环【英文标题】:How to avoid infinite loop by using git commit number as xcode build number 【发布时间】:2016-02-15 10:44:49 【问题描述】:

我最近使用一个脚本来增加与 git 提交相关的项目的内部版本号,我遇到了一个无限循环。

我构建,然后内部版本号被更改,然后我提交,因为 info.plist 被更改,那么内部版本号将在我下次构建时更改,所以我必须再次提交,因为 info.plist 被更改,那你就知道会发生什么了。

有什么办法可以避免这种情况吗?还是有更好的方法来自动增加内部版本号?

ps。因为我和别人一起做这个项目,所以每次构建时手动更改编号或颠簸它很难维护编号。

【问题讨论】:

我使用了 similar to this question 的东西,但不是增加一个数字,而是计算自项目以 git rev-list HEAD --count 开始以来的提交次数。 另见blog.jaredsinclair.com/post/97193356620/… @GuillaumeAlgis:我确实使用git rev-list HEAD --count。 :( 我链接的文章中的“技巧”是永远不要在 git 已知的任何文件中写入提交计数。在创建和签署.ipa 之前,计数在最后一分钟写入 Info.plist 在最终构建目录中(不是您的源目录)。 这能回答你的问题吗? ***.com/q/35407266/2998 【参考方案1】:

是的,这是一个经典问题。我使用了类似的技术,但我没有将内部版本号与 git 提交相关联,而是检测与 version 文件(一个简单文件)的修改时间相关的源树中文件的更改保存版本和内部版本号)。如果有变化,我会修改内部版本号。

这里是我使用的 python 脚本。您应该在您的 Xcode 项目中使其成为一个单独的目标,然后使其他目标依赖于该目标。它将根据需要使用新版本/内部版本号更新您的 .plist 文件。它假定源代码树位于版本文件的位置之下:

#!/usr/bin/env python
#
# Bump build number in Info.plist files if a source file have changed.
#
# usage: bump_buildnum.py buildnum.ver Info.plist [ ... Info.plist ]
#
# andy@***foe.com, 2014.
#

import sys, os, subprocess, re

def read_verfile(name):
    version = None
    build = None
    verfile = open(name, "r")
    for line in verfile:
        match = re.match(r"^version\s+(\S+)", line)
        if match:
            version = match.group(1).rstrip()
        match = re.match(r"^build\s+(\S+)", line)
        if match:
            build = int(match.group(1).rstrip())
    verfile.close()
    return (version, build)

def write_verfile(name, version, build):
    verfile = open(name, "w")
    verfile.write("version 0\n".format(version))
    verfile.write("build 0\n".format(build))
    verfile.close()
    return True

def set_plist_version(plistname, version, build):
    if not os.path.exists(plistname):
        print("0 does not exist".format(plistname))
        return False

    plistbuddy = '/usr/libexec/Plistbuddy'
    if not os.path.exists(plistbuddy):
        print("0 does not exist".format(plistbuddy))
        return False

    cmdline = [plistbuddy,
        "-c", "Set CFBundleShortVersionString 0".format(version),
        "-c", "Set CFBundleVersion 0".format(build),
        plistname]
    if subprocess.call(cmdline) != 0:
        print("Failed to update 0".format(plistname))
        return False

    print("Updated 0 with v1 (2)".format(plistname, version, build))
    return True

def should_bump(vername, dirname):
    verstat = os.stat(vername)
    allnames = []
    for dirname, dirnames, filenames in os.walk(dirname):
        for filename in filenames:
            allnames.append(os.path.join(dirname, filename))

    for filename in allnames:
        filestat = os.stat(filename)
        if filestat.st_mtime > verstat.st_mtime:
            print("0 is newer than 1".format(filename, vername))
            return True

    return False

def upver(vername):
    (version, build) = read_verfile(vername)
    if version == None or build == None:
        print("Failed to read version/build from 0".format(vername))
        return False

    # Bump the version number if any files in the same directory as the version file
    # have changed, including sub-directories.
    srcdir = os.path.dirname(vername)
    bump = should_bump(vername, srcdir)

    if bump:
        build += 1
        print("Incremented to build 0".format(build))
        write_verfile(vername, version, build)
        print("Written 0".format(vername))
    else:
        print("Staying at build 0".format(build))

    return (version, build)

if __name__ == "__main__":
    if os.environ.has_key('ACTION') and os.environ['ACTION'] == 'clean':
        print("0: Not running while cleaning".format(sys.argv[0]))
        sys.exit(0)

    if len(sys.argv) < 3:
        print("Usage: 0 buildnum.ver Info.plist [... Info.plist]".format(sys.argv[0]))
        sys.exit(1)
    vername = sys.argv[1]

    (version, build) = upver(vername)
    if version == None or build == None:
        sys.exit(2)

    for i in range(2, len(sys.argv)):
        plistname = sys.argv[i]
        set_plist_version(plistname, version, build)        

    sys.exit(0)

【讨论】:

【参考方案2】:

我使用的方法是在包含版本号的文件中放置一个占位符,然后让构建服务器在创建可分发包之前本地将该占位符替换为正确的版本号。存储库中的 info.plist 版本不会更改;它总是有占位符。

【讨论】:

【参考方案3】:

我用纯 bash 制作了 @***foe 的答案版本。这假定在根项目目录中放置了一个名为 versions 的文件。

#!/bin/bash

beginswith()  case $2 in "$1"*) true;; *) false;; esac; 

shopt -s dotglob

versionModifiedAt=$(date -r versions +%s)
needsUpdate=false

processEntry() 
  if [[ "$1" == "versions" ]]; then return; fi
  if [[ "$1" == ".git" ]];     then return; fi
  if beginswith ".git/" "$1";  then return; fi

  if [ $(date -r "$1" +%s) -gt "$versionModifiedAt" ]; then
    needsUpdate=true
  fi


# Top level
for entry in *; do
  processEntry "$entry"
  if [[ $needsUpdate == true ]]; then
    break;
  fi
done

# Nested
if [[ $needsUpdate != true ]]; then
  for entry in **/*; do
    processEntry "$entry"
    if [[ $needsUpdate == true ]]; then
      break;
    fi
  done
fi

if [[ $needsUpdate != true ]]; then
  echo "No update needed"
  exit 0
fi
echo "Updating version and build info"


buildPlist=$PROJECT_DIR/$INFOPLIST_FILE

# Increment build number
buildNumber=$(/usr/libexec/PlistBuddy -c "Print CFBundleVersion" "$buildPlist")
buildNumber=$((0x$buildNumber))
buildNumber=$(($buildNumber + 1))
buildNumber=$(printf "%04X" $buildNumber)
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $buildNumber" "$buildPlist"

echo -e "$git_release_version#*v\n$buildNumber" > versions

【讨论】:

以上是关于如何通过使用 git 提交号作为 xcode 内部版本号来避免无限循环的主要内容,如果未能解决你的问题,请参考以下文章

sh 基于git commit自动更改Xcode项目内部版本号

如何利用git 号作为版本号 c++

Xcode 中 Git 的配置与使用

内部版本号不是由 fastlane 设置的,而是由 Xcode 设置的

提交显示为由不同的用户 Xcode 完成

增加内部版本号的更好方法?