#! /usr/bin/env python
"""Bundle a package as a signed firmware for installation/redistribution
"""
import tempfile, subprocess, os
from optparse import OptionParser

DEFAULT_EXCLUDES = [
    '.svn',
    '.bzr',
    '.git',
]

def pack( root_dir, excludes=None ):
    """Bundle directory into a firmware file...
    
    * root_dir -- directory to be packed into the firmware, the 
        :func:`os.path.basename` of this directory will be the name used 
        in the created bundle filename and the directory installed into 
        the target on client machines
    * excludes -- patterns to exclude from the firmware image (passed to
        :command:`tar` during packing)
    
    returns absolute filename for generated .tar.gz firmware 
    (created in a temporary directory)
    """
    temp_dir = tempfile.mkdtemp(prefix='fussy-',suffix='-pack')
    firmware_name = os.path.basename( root_dir )
    if firmware_name == 'current':
        raise RuntimeError( 'Cannot create a firmware named "current"')
    tar_name = firmware_name+ '.tar.gz'
    tar_file = os.path.join( temp_dir, tar_name )
    command = [
        'tar','-zcf',
        tar_file,
    ] + [
        '--exclude=%s'%(x,) 
        for x in ((excludes or []) + DEFAULT_EXCLUDES)
    ] + [os.path.basename(root_dir)]
    subprocess.check_call( command, cwd=os.path.dirname( os.path.abspath(root_dir) ) )
    return tar_file

def sign( tar_file, key=None,encrypt_for=None ):
    """Sign an existing tar.gz-file with the default key
    
    * tar_file -- tar.gz file to sign (and/or encrypt)
    * encrypt_for -- if non-null, string identifier for which to encrypt
    """
    gpg_file = tar_file + '.gpg'
    command = [ 'gpg' ]
    if encrypt_for:
        command += ['-se', '-r', encrypt_for]
    else:
        command += ['--sign']
    if key:
        command += ['-u',key]
    command.append( tar_file )
    subprocess.check_call( command )
    assert os.path.exists( gpg_file )
    os.remove( tar_file )
    return gpg_file
    

def get_options():
    """Produces the OptionParser for :func:main"""
    parser = OptionParser()
    parser.add_option( 
        '-x','--exclude', 
        dest='exclude', 
        action="append",
        type="string",
        help="Paths/patterns to exclude from the archive",
    )
    parser.add_option(
        '-r','--root',
        dest = 'root',
        default='firmware',
        type="string",
        help="The directory to be packed for distribution, should be a versioned/unique name, for instance including a human-readable timestamp",
    )
    parser.add_option(
        '-u','--unsigned',
        dest='signed',
        action="store_false",
        default=True,
        help="If specified, do not do signing, just produced an unsigned tar.gz file (intended to allow for signing on a separate workstation)",
    )
    add_encryption_options( parser )
    return parser
def add_encryption_options( parser ):
    parser.add_option(
        '-k','--key',
        dest='key',
        default = None,
        help = 'If specified, use this key id to perform the signing (passed to :cmd:gpg -u option), otherwise uses default identity',
    )
    parser.add_option(
        '-e','--encrypt-for',
        dest = 'encrypt',
        action="store",
        type="string",
        help="The name of the key for which to encrypt (otherwise just sign)",
    )
    return parser
    
def main():
    """Main function for the packing script"""
    options,args = get_options().parse_args()
    tar_file = pack( options.root, options.exclude )
    if options.signed:
        gpg_file = sign( tar_file, key=options.key, encrypt_for=options.encrypt )
        print(gpg_file) 
    else:
        print(tar_file)
    return 0

def get_sign_options():
    parser = OptionParser()
    parser.add_option(
        '-f','--filename',
        dest='filename',
        default = None,
        help = 'The full path to the tar.gz file we will sign',
    )
    add_encryption_options( parser )
    return parser

def sign_main():
    """Main function that *just* signs a tar-file"""
    options,args = get_sign_options().parse_args()
    if not options.filename and args:
        options.filename = args[0]
    gpg_file = sign( options.filename, key=options.key, encrypt_for=options.encrypt )
    print(gpg_file)
    return 0
    
