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)

2015-06-22

Automatic version numbering with Mercurial hook

Assuming you put your version number setting in a version.py file as VERSION = NNN, you can add the following to the project .hg/hgrc to automatically increment the version number upon each commit. The version number is actually the local revision number before the commit plus 1, although not exactly robust but is generally increasingly upward. Any conflict should be resolved manually.
# .hg/hgrc
[hooks]
pre-commit.version = echo VERSION = $((`hg id -n | tr -d +` + 1)) > ./path/version.py
We use Mercurial pre-commit hook to update the version number in the file version.py. When the hook is run, the current directory is set to the repo's root directory. We get the local revision number with "hg id -n", remove the trailing "+" if any, and then add one to it as the version number. Please note that the hook is pre-commit not precommit. Both hooks exist, but if you use the precommit hook, you will not get what you want. See this post for details.
EDITED 2015-07-06: See Part 2 for a different approach.