#!/usr/bin/env python2.4

import os
import os.path
import re
import stat
import sys
import tarfile

import distutils.core

checkout_dir = "checkout/myproject"
tmp_dir = "tmp/myproject"
tmp_log_dir = os.path.join(tmp_dir, "logs")
build_dir_frag = "build"
build_dir = checkout_dir + "/" + build_dir_frag
dist_dir_frag = "dist"
dist_dir = checkout_dir + "/" + dist_dir_frag
xslt_dir = "xslt"
deploy_dir = "web/myproject/builds"


tar_pattern = re.compile(r"^.*-\d+\.\d+\.\d+\.(\d+)\.tar\.gz$")
def get_tarname(revision):
    """
    Return the name of the distribution tar file for the supplied revision.
    If no such file exists, raise an exception.
    """
    tarname = None
    for filename in os.listdir(dist_dir):
        matches = tar_pattern.match(filename)
        if matches == None:
            continue
        if matches.group(1) != revision:
            continue
        return filename
    raise IOError

def unpack_tar(filename, dest):
    """
    Unpack a tar file to a destination directory. Return the name of the
    first item in the tar file.
    """
    name = None
    tar = tarfile.open(filename)
    for info in tar:
        if name == None:
            name = info.name
        tar.extract(info, tmp_dir)
    tar.close()
    return name

def try_remove_file(filename):
    """
    Try to remove the supplied file, but ignore any errors.
    """
    try:
        os.remove(filename)
    except OSError:
        pass

def remove_files_pattern(dir, pattern):
    """
    Remove files that match a regular expression from the supplied directory.
    """
    regexp = re.compile(pattern)
    for file in os.listdir(dir):
        if regexp.match(file):
            fullname = dir + "/" + file
            os.remove(fullname)

def remove_recursively(dir):
    """
    Remove a directory and files contained within.
    """
    for filename in os.listdir(dir):
        full_name = os.path.join(dir, filename)
        mode = os.stat(full_name).st_mode
        if stat.S_ISDIR(mode):
            remove_recursively(full_name)
        else:
            os.remove(full_name)
    os.rmdir(dir)

def copy_file(inname, outname):
    """
    Copy a file.
    """
    infile = open(inname, "rb")
    outfile = open(outname, "wb")
    for line in infile:
        outfile.write(line)
    outfile.close()
    infile.close()

def clean():
    """
    Clean the Python installation from old files.
    """
    try_remove_file(os.path.join(checkout_dir, "version.py"))
    try_remove_file(os.path.join(checkout_dir, "version.pyc"))
    try_remove_file(os.path.join(checkout_dir, "MANIFEST"))

    remove_files_pattern(tmp_log_dir, r".*\.xml$")
    remove_files_pattern(dist_dir, r".*\.tar\.gz$")

    for filename in os.listdir(tmp_dir):
        if filename[:7] == "mifgen-":
            remove_recursively(os.path.join(tmp_dir, filename))

def update():
    """
    Update the subversion repository.
    """
    olddir = os.getcwd()
    os.chdir(checkout_dir)
    os.system("svn up")
    os.chdir(olddir)

def build_dist(revision):
    """
    Build the distribution files.
    """
    oldpath = os.getcwd()
    os.chdir(checkout_dir)

    versionfile = open("version.py", "w")
    print >> versionfile, "revision = '%s'" % revision
    versionfile.close()

    sys.path += ["."]
    distutils.core.run_setup("setup.py", ["sdist", "--dist-dir", dist_dir_frag])
    sys.path = sys.path[:-1]

    os.chdir(oldpath)

def test(revision):
    xunit_dir_frag = build_dir_frag + "/xunit-reports"
    acceptance_dir_frag = build_dir_frag + "/acceptance-reports"
    acceptance_xslt = xslt_dir + "/acceptance-wrap.xsl"

    # unpack

    filename = get_tarname(revision)
    dir_name = unpack_tar(os.path.join(dist_dir, filename), tmp_dir)
    unpacked_dir = os.path.join(tmp_dir, dir_name)

    # run tests

    python_cmd = "PYTHONPATH=. python2.4"

    olddir = os.getcwd()
    os.chdir(unpacked_dir)
    os.makedirs("xunit")
    os.makedirs("acceptance")
    status = os.system(python_cmd + " test/myproject/xmlrunner.py xunit")
    if status != 0:
        sys.exit(1)
    os.system(python_cmd + " test/acceptance/xmlrunner.py acceptance")
    
    os.chdir(olddir)

    # handle reports

    xunit_src_xml = \
        os.path.join(unpacked_dir, "xunit", "TEST-alltests.AllTests.xml")
    xunit_dst_xml = \
        os.path.join(tmp_log_dir, "xunit.xml")
    copy_file(xunit_src_xml, xunit_dst_xml)

    acceptance_src_xml = os.path.join(unpacked_dir, "acceptance",
            "TEST-alltests.AllTests.xml")
    acceptance_dst_xml = os.path.join(tmp_log_dir, "acceptance.xml")
    os.system("xsltproc %s %s >%s" % \
        (acceptance_xslt, acceptance_src_xml, acceptance_dst_xml))

def deploy(revision):
    """
    Copy distribution files into web directory.
    """
    deployfile = open(os.path.join(tmp_log_dir, "deployment.xml"), "w")
    print >> deployfile, '<?xml version="1.0"?>'
    print >> deployfile, '<deployment>'
    tarname = get_tarname(revision)
    copy_file(os.path.join(dist_dir, tarname),
        os.path.join(deploy_dir, tarname))
    print >> deployfile, '  <filename>%s</filename>' % tarname
    print >> deployfile, '</deployment>'
    deployfile.close()

if __name__ == "__main__":
    if len(sys.argv) != 2:
        print >> sys.stderr, "Usage: %s REVISION" % sys.argv[0]
        sys.exit(1)
    revision = sys.argv[1].split(".")[-1]

    clean()
    update()
    build_dist(revision)
    test(revision)
    deploy(revision)
