Changeset 4e6dc64 in rtems-source-builder


Ignore:
Timestamp:
Apr 21, 2021, 9:58:09 PM (3 months ago)
Author:
Alex White <alex.white@…>
Branches:
master
Children:
9c33ea0
Parents:
774b5fc
git-author:
Alex White <alex.white@…> (04/21/21 21:58:09)
git-committer:
Joel Sherrill <joel@…> (05/26/21 21:34:36)
Message:

sb: Merge mailer changes from rtems-tools

This adds the improved mailer.py script from rtems-tools.

Closes #4388

Location:
source-builder/sb
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • source-builder/sb/mailer.py

    r774b5fc r4e6dc64  
    11#
    22# RTEMS Tools Project (http://www.rtems.org/)
    3 # Copyright 2013 Chris Johns (chrisj@rtems.org)
     3# Copyright 2013-2016 Chris Johns (chrisj@rtems.org)
     4# Copyright (C) 2021 On-Line Applications Research Corporation (OAR)
    45# All rights reserved.
    56#
    67# This file is part of the RTEMS Tools package in 'rtems-tools'.
    78#
    8 # Permission to use, copy, modify, and/or distribute this software for any
    9 # purpose with or without fee is hereby granted, provided that the above
    10 # copyright notice and this permission notice appear in all copies.
    11 #
    12 # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
    13 # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
    14 # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
    15 # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
    16 # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
    17 # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
    18 # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     9# Redistribution and use in source and binary forms, with or without
     10# modification, are permitted provided that the following conditions are met:
     11#
     12# 1. Redistributions of source code must retain the above copyright notice,
     13# this list of conditions and the following disclaimer.
     14#
     15# 2. Redistributions in binary form must reproduce the above copyright notice,
     16# this list of conditions and the following disclaimer in the documentation
     17# and/or other materials provided with the distribution.
     18#
     19# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
     20# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     21# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     22# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
     23# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     24# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     25# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     26# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     27# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     28# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     29# POSSIBILITY OF SUCH DAMAGE.
     30#
    1931
    2032#
     
    2941
    3042from . import error
     43from . import execute
    3144from . import options
    3245from . import path
    3346
     47_options = {
     48    '--mail'         : 'Send email report or results.',
     49    '--use-gitconfig': 'Use mail configuration from git config.',
     50    '--mail-to'      : 'Email address to send the email to.',
     51    '--mail-from'    : 'Email address the report is from.',
     52    '--smtp-host'    : 'SMTP host to send via.',
     53    '--smtp-port'    : 'SMTP port to send via.',
     54    '--smtp-user'    : 'User for SMTP authentication.',
     55    '--smtp-password': 'Password for SMTP authentication.'
     56}
     57
    3458def append_options(opts):
    35     opts['--mail'] = 'Send email report or results.'
    36     opts['--smtp-host'] = 'SMTP host to send via.'
    37     opts['--mail-to'] = 'Email address to send the email too.'
    38     opts['--mail-from'] = 'Email address the report is from.'
     59    for o in _options:
     60        opts[o] = _options[o]
     61
     62def add_arguments(argsp):
     63    argsp.add_argument('--mail', help = _options['--mail'], action = 'store_true')
     64    argsp.add_argument('--use-gitconfig', help = _options['--use-gitconfig'], action = 'store_true')
     65    no_add = ['--mail', '--use-gitconfig']
     66    for o in [opt for opt in list(_options) if opt not in no_add]:
     67        argsp.add_argument(o, help = _options[o], type = str)
    3968
    4069class mail:
    4170    def __init__(self, opts):
    4271        self.opts = opts
     72        self.gitconfig_lines = None
     73        if opts.find_arg('--use-gitconfig') is not None:
     74            # Read the output of `git config --list` instead of reading the
     75            # .gitconfig file directly because Python 2 ConfigParser does not
     76            # accept tabs at the beginning of lines.
     77            e = execute.capture_execution()
     78            exit_code, proc, output = e.open('git config --list', shell=True)
     79            if exit_code == 0:
     80                self.gitconfig_lines = output.split(os.linesep)
     81
     82    def _args_are_macros(self):
     83        return isinstance(self.opts, options.command_line)
     84
     85    def _get_arg(self, arg):
     86        if self._args_are_macros():
     87            value = self.opts.find_arg(arg)
     88            if value is not None:
     89                value = self.opts.find_arg(arg)[1]
     90        else:
     91            if arg.startswith('--'):
     92                arg = arg[2:]
     93            arg = arg.replace('-', '_')
     94            if arg in vars(self.opts):
     95                value = vars(self.opts)[arg]
     96            else:
     97                value = None
     98        return value
     99
     100    def _get_from_gitconfig(self, variable_name):
     101        if self.gitconfig_lines is None:
     102            return None
     103
     104        for line in self.gitconfig_lines:
     105            if line.startswith(variable_name):
     106                ls = line.split('=')
     107                if len(ls) >= 2:
     108                    return ls[1]
    43109
    44110    def from_address(self):
     
    53119            return l.strip()
    54120
    55         addr = self.opts.get_arg('--mail-from')
     121        addr = self._get_arg('--mail-from')
    56122        if addr is not None:
    57             return addr[1]
     123            return addr
     124        addr = self._get_from_gitconfig('user.email')
     125        if addr is not None:
     126            name = self._get_from_gitconfig('user.name')
     127            if name is not None:
     128                addr = '%s <%s>' % (name, addr)
     129            return addr
    58130        mailrc = None
    59131        if 'MAILRC' in os.environ:
     
    64136            # set from="Joe Blow <joe@blow.org>"
    65137            try:
    66                 mrc = open(mailrc, 'r')
    67                 lines = mrc.readlines()
    68                 mrc.close()
     138                with open(mailrc, 'r') as mrc:
     139                    lines = mrc.readlines()
    69140            except IOError as err:
    70141                raise error.general('error reading: %s' % (mailrc))
     
    77148            if addr is not None:
    78149                return addr
    79         addr = self.opts.defaults.get_value('%{_sbgit_mail}')
     150        if self._args_are_macros():
     151            addr = self.opts.defaults.get_value('%{_sbgit_mail}')
     152        else:
     153            raise error.general('no valid from address for mail')
    80154        return addr
    81155
    82156    def smtp_host(self):
    83         host = self.opts.get_arg('--smtp-host')
     157        host = self._get_arg('--smtp-host')
    84158        if host is not None:
    85             return host[1]
    86         host = self.opts.defaults.get_value('%{_mail_smtp_host}')
     159            return host
     160        host = self._get_from_gitconfig('sendemail.smtpserver')
     161        if host is not None:
     162            return host
     163        if self._args_are_macros():
     164            host = self.opts.defaults.get_value('%{_mail_smtp_host}')
    87165        if host is not None:
    88166            return host
    89167        return 'localhost'
     168
     169    def smtp_port(self):
     170        port = self._get_arg('--smtp-port')
     171        if port is not None:
     172            return port
     173        port = self._get_from_gitconfig('sendemail.smtpserverport')
     174        if port is not None:
     175            return port
     176        if self._args_are_macros():
     177            port = self.opts.defaults.get_value('%{_mail_smtp_port}')
     178        return port
     179
     180    def smtp_user(self):
     181        user = self._get_arg('--smtp-user')
     182        if user is not None:
     183            return user
     184        user = self._get_from_gitconfig('sendemail.smtpuser')
     185        return user
     186
     187    def smtp_password(self):
     188        password = self._get_arg('--smtp-password')
     189        if password is not None:
     190            return password
     191        password = self._get_from_gitconfig('sendemail.smtppass')
     192        return password
    90193
    91194    def send(self, to_addr, subject, body):
     
    93196        msg = "From: %s\r\nTo: %s\r\nSubject: %s\r\n\r\n" % \
    94197            (from_addr, to_addr, subject) + body
    95         if type(to_addr) is str:
    96             to_addr = to_addr.split(',')
    97         if type(to_addr) is not list:
    98             raise error.general('invalid to_addr type')
     198        port = self.smtp_port()
     199
    99200        try:
    100             s = smtplib.SMTP(self.smtp_host())
    101             s.sendmail(from_addr, to_addr, msg)
     201            s = smtplib.SMTP(self.smtp_host(), port, timeout=10)
     202
     203            password = self.smtp_password()
     204            # If a password is provided, assume that authentication is required.
     205            if password is not None:
     206                user = self.smtp_user()
     207                if user is None:
     208                    user = from_addr
     209                s.starttls()
     210                s.login(user, password)
     211
     212            s.sendmail(from_addr, [to_addr], msg)
    102213        except smtplib.SMTPException as se:
    103214            raise error.general('sending mail: %s' % (str(se)))
     
    105216            raise error.general('sending mail: %s' % (str(se)))
    106217
     218    def send_file_as_body(self, to_addr, subject, name, intro = None):
     219        try:
     220            with open(name, 'r') as f:
     221                body = f.readlines()
     222        except IOError as err:
     223            raise error.general('error reading mail body: %s' % (name))
     224        if intro is not None:
     225            body = intro + body
     226        self.send(to_addr, from_addr, body)
     227
    107228if __name__ == '__main__':
    108229    import sys
     230    from . import macros
    109231    optargs = {}
     232    rtdir = 'source-builder'
     233    defaults = '%s/defaults.mc' % (rtdir)
    110234    append_options(optargs)
    111     opts = options.load(sys.argv, optargs = optargs, defaults = 'defaults.mc')
     235    opts = options.command_line(base_path = '.',
     236                                argv = sys.argv,
     237                                optargs = optargs,
     238                                defaults = macros.macros(name = defaults, rtdir = rtdir),
     239                                command_path = '.')
     240    options.load(opts)
    112241    m = mail(opts)
    113242    print('From: %s' % (m.from_address()))
    114243    print('SMTP Host: %s' % (m.smtp_host()))
    115     m.send(m.from_address(), 'Test mailer.py', 'This is a test')
     244    if '--mail' in sys.argv:
     245        m.send(m.from_address(), 'Test mailer.py', 'This is a test')
  • source-builder/sb/options.py

    r774b5fc r4e6dc64  
    518518        return self.parse_args(arg)
    519519
     520    def find_arg(self, arg):
     521        if self.optargs is None or arg not in self.optargs:
     522            raise error.internal('bad arg: %s' % (arg))
     523        for a in self.args:
     524            sa = a.split('=')
     525            if sa[0].startswith(arg):
     526                return sa
     527        return None
     528
    520529    def with_arg(self, label, default = 'not-found'):
    521530        # the default if there is no option for without.
     
    583592
    584593    def info(self):
    585         s = ' Command Line: %s%s' % (' '.join(self.argv), os.linesep)
     594        # Filter potentially sensitive mail options out.
     595        filtered_args = [
     596            arg for arg in self.argv
     597            if all(
     598                smtp_opt not in arg
     599                for smtp_opt in [
     600                    '--smtp-host',
     601                    '--mail-to',
     602                    '--mail-from',
     603                    '--smtp-user',
     604                    '--smtp-password',
     605                    '--smtp-port'
     606                ]
     607            )
     608        ]
     609        s = ' Command Line: %s%s' % (' '.join(filtered_args), os.linesep)
    586610        s += ' Python: %s' % (sys.version.replace('\n', ''))
    587611        return s
  • source-builder/sb/setbuilder.py

    r774b5fc r4e6dc64  
    696696                     'reports': [],
    697697                     'failure': None }
     698            # Request this now to generate any errors.
     699            smtp_host = mail['mail'].smtp_host()
    698700            to_addr = opts.get_arg('--mail-to')
    699701            if to_addr is not None:
Note: See TracChangeset for help on using the changeset viewer.