2015-06-29

Automatic version numbering with Mercurial hook - Part 2

The pre-commit hook commands in the last post only works in Linux or Mac. I need to run it in Windows as well. Instead of converting it to MS-DOS commands, I found it easier to write an external python script as the hook handler, as it will work in Windows, Linux and Mac.
Step 1 - Modify the hook to run an external script.
# .hg/hgrc

[hooks]
pre-commit.version = python:./script/hghooks.py:increment_version
Step 2 - Create a python module with the handler function.
It basically uses the subprocess module to get the local revision number, strip any ending newline or plus sign, add one to it, and then write it out to the version.py file. Note that we obtain the repo root folder from the repo object passed into the hook handler, so that we don't have to hardcode the path to the version file.
# ./script/hghooks.py

import os.path
import subprocess

def increment_version(*args, **kw):
    repo = kw['repo']
    version = int(subprocess.check_output(["hg", "id", "-n"]).strip().strip("+")) + 1
    setting = "VERSION = %s" % version
    file = os.path.join(repo.root, "dms/dms/version.py") 
    with open(file, "w") as fp:
        fp.write(setting)
    print(setting)
Using an external script makes it not only cross-platform, but also version control friendly because it is not stored in the .hg folder!

EDITED 2015-07-09 In order to take care of multiple heads, the increment_version() is revised as follow:

# ./script/hghooks.py

import os.path
import subprocess

def increment_version(*args, **kw):
    """hg id -n can returns:
    145      # no uncomitted changes
    145+     # with uncomitted changes
    146+145+ # multi-heads"
    The strip() and split() will take care of all the above cases.
    """
    repo = kw['repo']
    version = int(subprocess.check_output(["hg", "id", "-n"]).strip().split("+")[0]) + 1
    setting = "VERSION = %s" % version
    file = os.path.join(repo.root, "pms/pms/version.py") 
    with open(file, "w") as fp:
        fp.write(setting)
    print(setting)
EDITED 2015-07-15
The script is further enhanced by removing the hard-coded file path to the Mercurial project config file, and add a test to make sure something has changed indeed before incrementing the version number. The final version of the script is listed below, and is also available in Bitbucket.
import os.path
import subprocess

def increment_version(ui, repo, **kw):
    """
    An in-process Mercurial hook to increment a version setting.

    FILE .hg/hgrc

    [hooks]
    pre-commit.version = python:path/to/hghooks.py:increment_version

    [hook_params]
    version.file = path/to/version.py

    ENDFILE

    hg id -n can returns:
        145      # no uncomitted changes
        145+     # with uncomitted changes
        146+145+ # multi-heads"
        The strip() and split() will take care of all the above cases.
    """
    has_changes = subprocess.check_output(["hg", "stat"])
    if has_changes:
        version = int(subprocess.check_output(["hg", "id", "-n"]).strip().split("+")[0]) + 1
        setting = "VERSION = %s" % version
        path = ui.config('hook_params', 'version.file', default='version.py', untrusted=False)
        if not os.path.isabs(path):
            path = os.path.join(repo.root, path) 
        with open(path, "w") as fp:
            fp.write(setting)
        print(setting)

No comments: