2016年10月25日 星期二

Git 自動版本號

以前公司都是使用SVN,產品都是自已先取一個名字,再加上流水號
然後在 Release Note 上註名 SVN REV (版本號)

前些日子突然想要自已寫一個自動產生版本號的腳本
利用 Git 實作一個簡單的版本管理

網路上有不少作法,要使用 Git 指令也有些差異
我試了幾種後,決定了一個最順手的方式
# [tag]-[rev]-g[hash]
git describe --tags --long --always

Git Tag 使用方式可以參考這一篇
加上--always 可以保證一定會有值產生

Python寫一個腳本
# Coding=UTF-8
"""

@author 小安
@date 2016/10/21
"""
import datetime
from subprocess import check_output

VERSION_FILE = 'version.py'

_Version_FMT = '''# Coding=UTF-8
"""WARNING
This file is automatically generated by GitVersion.py
DO NOT EDIT IT!
"""
PKG_NAME            = '{pkg_name}'

# This file create timestamp
create_time         = '{create_time}'

# Git Version
full_version_string = '{full_version_string}'
__version__         = '{version}'
__rev__             = '{rev}'
__hash_short        = '{hash_short}'

'''


def git_version_parser(full_version_string):
    """
    If tag exist, it will get a string : v0.0.1-rc0-15-g6c11093
    otherwise, get a string : 6c11093 (hash only)

    :param full_version_string:
    :return: (version, rev, hash_short)
    """
    # Default value
    pkg_version = 'v0'
    pkg_rev_commit = 0

    # Parser full_version_string
    tag_version = full_version_string.split('-dirty')[0].split('-')

    # hash
    pkg_hash_short = tag_version.pop() if len(tag_version) == 1 else tag_version.pop().split('g')[1]

    # rev
    if len(tag_version) != 0:
        pkg_rev_commit = tag_version.pop()

    # version
    if len(tag_version) != 0:
        pkg_version = '-'.join(tag_version)

    return pkg_version, pkg_rev_commit, pkg_hash_short


def generate_version(pkg_name, output_filename, dirty=None):
    """[NOTE] I want to update this function to a class or xml format.
    $> git describe --tags --long --always
    It will get a string : v0.0.1-rc0-15-g6c11093

    The means,
    last tag                         : v0.0.1-rc
    number of commits since last tag : 15
    SHA of HEAD                      : 6c11093
    (g is the git's feature)

    :param pkg_name: APP name/Package name
    :param output_filename: 'version.py' for example
    :param dirty: If generate in local/beta, set dirty
    :return:
    """
    cmd_version = 'git describe --tags --long --always'

    if dirty:
        cmd_version += " --dirty"

    # Get git version string
    # [NOTE] In Python3, it will return results in bytes string
    full_version_string = check_output(cmd_version, shell=True).rstrip()
    full_version_string = full_version_string.decode(encoding="utf-8")

    # parser
    pkg_version, pkg_rev_commit, pkg_hash_short = git_version_parser(full_version_string)

    # output, includes create time
    with open(output_filename, 'w') as out_file:
        out_file.write(_Version_FMT.format(pkg_name=pkg_name,
                                           create_time=datetime.datetime.now().isoformat(),
                                           # --- Git Version ---
                                           full_version_string=full_version_string,
                                           version=pkg_version,
                                           rev=pkg_rev_commit,
                                           hash_short=pkg_hash_short, ))

產生出的結果如下
# Coding=UTF-8
"""WARNING
This file is automatically generated by GitVersion.py
DO NOT EDIT IT!
"""
PKG_NAME            = 'APP'

# This file create timestamp
create_time         = '2016-10-25T15:29:26.975873'

# Git Version
full_version_string = 'v0.0.1-rc0-29-g8032766-dirty'
__version__         = 'v0.0.1-rc0'
__rev__             = '29'
__hash_short        = '8032766'

想在程式內引用時
# 記得給一個正確路徑,這裡是輸出version.py
from version import PKG_NAME, full_version_string, create_time

丟到 Release 流程中,就可以保證 release 後的版本號是正確的了

沒有留言:

張貼留言