#! /usr/bin/python

# Basic instructions
#
# 1. Save patch email to file [patch.email]
#
# 2. Go to the svn directory to which you want to apply the patch.
# 
# 3. Run "selinux-maint split patch.email". This will run vi on the
# logmsg (pulled out of the email) to allow you to add anything (ack
# messages). When you quit vi the current directory will have files
# called "patch" and "logmsg".
#
# 4. Run "selinux-maint apply" (optionally with a strip level as
# the last argument). This will do a dry run of applying the patch
# showing the results and ask if you want to apply the patch. If you
# say yes it will apply the patch and attempt to detect file adds (by
# comparing svn status and the output of patch). If it finds adds it
# will ask if you want to add each file.
#
# 5. Run "selinux-maint commit" to commit that patch with the log
# message.
#
# 6. Repeat 4 and 5 as often as necessary for a set of patch emails.
#
# 7. Run "selinux-maint rev packagename" where packagename is
# something like "libsepol". This will prompt for the new version
# number (showing the current), update VERSION, add a Changelog entry
# with the version and date, and vi the changelog for you to add
# entries.
#
# 8. Run "selinux-maint commit" again to commit the revision change
# (rev adds a simple log message - I just fixed this as my last
# checkin had the wrong log message).

import sys
import subprocess
import shutil
import os
import os.path
import datetime

dir = "/tmp/selinux-maint/"

def usage():
    print "selinux-maint [command] [options]"
    print ""
    print "commands:"
    print "\tsplit patch-email: split patch-email into a patch and log message"
    print "\tapply [patch-level]: apply the patch and logmsg with optional level"
    print "\tcommit username: commit the changes"
    print "\trev package: update the version number and changelog of package"
    print "\tmerge reva:revb source-branch: merge changes to the current branch"

def create_tmpdir():
    try:
        os.mkdir(dir)
    except OSError:
        if not os.path.isdir(dir):
            print "path %s exists and is not a directory" % dir
            sys.exit(1)

def split_email(args):
    # Get an absolute path for the patch email since we are going to
    # change the working directory
    patch_path = os.path.abspath(args[0])

    create_tmpdir()
    prevdir = os.getcwd()
    os.chdir(dir)

    infd = open(patch_path)
    outfd = open("info", "w")
    retcode = subprocess.call(["git-mailinfo", "msg", "patch"], stdin=infd,
                              stdout=outfd)
    if retcode != 0:
        sys.exit(1)

    msgfd = open("logmsg", "w")
    retcode = subprocess.call(["cat", "info", "msg"], stdout=msgfd)

    msgfd.close()

    retcode = subprocess.call(["vi", "logmsg"])

    shutil.copyfile("logmsg", prevdir + "/logmsg")
    shutil.copyfile("patch", prevdir + "/patch")

def apply(args):
    if len(args) >= 1:
        patch_level = "-p%d" % int(args[0])
    else:
        patch_level = "-p1"

    if len(args) == 2:
        patch_name = "../patch"
        patch_dir = args[1]
    else:
        patch_name = "patch"
        patch_dir = None

    print "Test applying patch:"
    if patch_dir:
        os.chdir(patch_dir)
        
    patchfd = open(patch_name)
    retcode = subprocess.call(["patch", patch_level, "--dry-run", "-l"], stdin=patchfd)
    resp = raw_input("apply [y/n]: ")
    if resp != "y":
        sys.exit(0)

    patchfd = open(patch_name)
    patch_output = subprocess.Popen(["patch", patch_level, "-l"], stdin=patchfd,
                                    stdout=subprocess.PIPE).communicate()[0]

    status_output = subprocess.Popen(["svn", "status"], stdout=subprocess.PIPE).communicate()[0]


    # Detect adds
    unknown_files = []
    for status_line in status_output.split("\n"):
        try:
            status, fname = status_line.split()
        except ValueError:
            continue
        if status == "?":
            unknown_files.append(fname)

    added_files = []
    for patch_line in patch_output.split("\n"):
        try:
            patched_fname = patch_line.split(" ")[2]
        except:
            continue
        if patched_fname in unknown_files:
            added_files.append(patched_fname)

    for fname in added_files:
        input = raw_input("add file %s [y/n]: " % fname)
        if input == "y":
            subprocess.call(["svn", "add", fname])
        
def commit(args):
    if len(args) == 1:
        retcode = subprocess.call(["svn", "commit", "--username", args[0], "-F", "logmsg"])
    else:
        retcode = subprocess.call(["svn", "commit", "-F", "logmsg"])


def rev(args):
    if len(args) != 1:
        print "you must provide a package name"
        usage()
        sys.exit(1)
    package = args[0]

    ver_fd = open("%s/VERSION" % package, "r")
    cur = ver_fd.read()
    cur = cur.split("\n")[0]
    ver_fd.close()
    input = raw_input("new version [current is %s]: " % cur)
    new_fd = open("%s/VERSION.new" % package, "w")
    new_fd.write(input + "\n")
    new_fd.close()
    shutil.copyfile("%s/VERSION.new" % package, "%s/VERSION" % package)

    old_changelog = "%s/ChangeLog" % package
    new_changelog = "%s/ChangeLog.new" % package

    n = open(new_changelog, "w")

    entry = "%s %s\n" % (input, str(datetime.date.today()))
    n.write(entry)
    n.write("\t*\n\n")
    o = open(old_changelog)
    n.write(o.read())
    n.close()
    o.close()

    subprocess.call(["vi", new_changelog])
    shutil.copyfile(new_changelog, old_changelog)

    logmsg = open("logmsg", "w")
    logmsg.write("updated %s to version %s\n" % (package, input))

def merge(args):
    if len(args) != 2:
        print "you must provide a revision pair and source branch"
        usage()
        sys.exit(1)

    rev = args[0]
    branch = args[1]

    if branch == "trunk":
        url = "https://selinux.svn.sourceforge.net/svnroot/selinux/trunk"
    elif branch == "stable":
        url = "https://selinux.svn.sourceforge.net/svnroot/selinux/branches/stable/1_0"
    else:
        url = "https://selinux.svn.sourceforge.net/svnroot/selinux/branches/%s" % branch

    subprocess.call(["svn", "diff", "-r%s" % rev, url])
    input = raw_input("apply these changes [y/n]? ")
    if input != "y":
        sys.exit(0)

    subprocess.call(["svn", "merge", "-r%s" % rev, url])

    logmsg = open("logmsg", "w")
    logmsg.write("applied r%s from %s\n" % (rev, branch))
    

def main():
    if len(sys.argv) < 2:
        usage()
        sys.exit(1)

    command = sys.argv[1]
    if command == "split":
        split_email(sys.argv[2:])
    elif command == "apply":
        apply(sys.argv[2:])
    elif command == "commit":
        commit(sys.argv[2:])
    elif command == "rev":
        rev(sys.argv[2:])
    elif command == "merge":
        merge(sys.argv[2:])
    else:
        usage()

main()
