#!/usr/bin/env python
"""
A script for reporting the results of an invirtibuild.  Supports any
combination of the following reporting mechanisms.

Note that all strings are interpolated with a dictionary containing
the following keys:

build_id, commit, failed_stage, inserted_at, package,
pocket, principal, result, short_commit, traceback, version

== zephyr ==

To configure zephyr, add something like the following to your invirt config:

build:
 hooks:
  post_build:
   zephyr: &post_build_zepyhr
    class: myclass [required]
    instance: myinstance [optional]
    zsig: myzsig [optional]
  failed_build:
   zephyr: *post_build_zephyr

== mail ==

To configure email notifications, add something like the following to your invirt config:

build:
 hooks:
  post_build:
   mail: &post_build_mail
    to: myemail@example.com [required]
    from: myemail@example.com [required]
    subject: My Subject [optional]
  failed_build:
   mail: *post_build_mail

post_build values will be used when this script is invoked as
post-build, while failed_build values will be used when it is invoked
as failed-build.
"""

import optparse
import os
import re
import sys
import textwrap

from email.mime import text

from invirt import common, database, builder
from invirt.config import structs as config

def make_msg(build, values, verbose=True, success=lambda x: x, failure=lambda x: x):
    values = dict(values)
    if not verbose and values['traceback'] is not None:
        # TODO: better heuristic
        values['traceback'] = textwrap.fill('\n'.join(values['traceback'].split('\n')[-2:]))

    if build.succeeded:
        values['result'] = success(values['result'])
        msg = """Build of %(package)s %(version)s in %(pocket)s %(result)s.

Branch %(pocket)s has been advanced to %(short_commit)s.

(Build %(build_id)s was submitted by %(principal)s at %(inserted_at)s.)""" % values
    else:
        values['result'] = failure(values['result'])
        msg = """Build of %(package)s %(version)s in %(pocket)s %(result)s while %(failed_stage)s.

%(traceback)s

(Build %(build_id)s was submitted by %(principal)s at %(inserted_at)s.)""" % values
    return msg

def zephyr_escape(m):
    m = re.sub('@', '@@', m)
    m = re.sub('}', '@(})', m)
    return m

def zephyr_success(m):
    return '}@{@color(green)%s}@{' % zephyr_escape(m)

def zephyr_failure(m):
    return '}@{@color(red)%s}@{' % zephyr_escape(m)

def main():
    parser = optparse.OptionParser('Usage: %prog build_id')
    opts, args = parser.parse_args()
    if len(args) != 1:
        parser.print_help()
        return 1
    prog = os.path.basename(sys.argv[0])

    database.connect()
    build = database.Build.query().get(args[0])
    short_commit = builder.canonicalize_commit(build.package, build.commit, shorten=True)
    values = { 'build_id' : build.build_id,
               'commit' : build.commit,
               'failed_stage' : build.failed_stage,
               'inserted_at' : build.inserted_at,
               'package' : build.package,
               'pocket' : build.pocket,
               'principal' : build.principal,
               'short_commit' : short_commit,
               'traceback' : build.traceback,
               'version' : build.version }
    if build.succeeded:
        values['result'] = 'succeeded'
    else:
        values['result'] = 'failed'

    try:
        if prog == 'post-build':
            hook_config = config.build.hooks.post_build
        elif prog == 'failed-build':
            hook_config = config.build.hooks.failed_build
        else:
            print >>sys.stderr, '{post,failed}-build invoke with unrecognized name %s' % prog
            return 2
    except common.InvirtConfigError:
        print >>sys.stderr, 'No hook configuration found for %s.' % prog
        return 1

    try:
        zephyr_config = hook_config.zephyr
        klass = zephyr_config['class'] % values
    except common.InvirtConfigError:
        print >>sys.stderr, 'No zephyr configuration specified for %s.' % prog
    else:
        msg = '@{%s}' % make_msg(build, values, verbose=False,
                                 success=zephyr_success, failure=zephyr_failure)
        instance = zephyr_config.get('instance', 'build_%(build_id)s') % values
        zsig = zephyr_config.get('zsig', 'XVM Buildbot') % values
        common.captureOutput(['zwrite', '-c', klass, '-i', instance, '-s',
                              zsig, '-d', '-m', msg],
                             stdout=None, stderr=None)

    try:
        mail_config = hook_config.mail
        to = mail_config.to % values
        sender = mail_config['from'] % values
    except common.InvirtConfigError:
        print >>sys.stderr, 'No email configuration specified for %s.' % prog
    else:
        msg = make_msg(build, values)
        email = text.MIMEText(msg)
        email['To'] = to % values
        email['From'] = sender % values
        email['Subject'] = mail_config.get('subject', 'XVM build %(result)s: %(package)s %(version)s in %(pocket)s') % values
        common.captureOutput(['sendmail', '-t'], email.as_string(),
                             stdout=None, stderr=None)
        
if __name__ == '__main__':
    sys.exit(main())