contrib / hooks / multimail / migrate-mailhook-configon commit Merge branch 'ad/commit-have-m-option' (0709261)
   1#! /usr/bin/env python
   2
   3"""Migrate a post-receive-email configuration to be usable with git_multimail.py.
   4
   5See README.migrate-from-post-receive-email for more information.
   6
   7"""
   8
   9import sys
  10import optparse
  11
  12from git_multimail import CommandError
  13from git_multimail import Config
  14from git_multimail import read_output
  15
  16
  17OLD_NAMES = [
  18    'mailinglist',
  19    'announcelist',
  20    'envelopesender',
  21    'emailprefix',
  22    'showrev',
  23    'emailmaxlines',
  24    'diffopts',
  25    'scancommitforcc',
  26    ]
  27
  28NEW_NAMES = [
  29    'environment',
  30    'reponame',
  31    'mailinglist',
  32    'refchangelist',
  33    'commitlist',
  34    'announcelist',
  35    'announceshortlog',
  36    'envelopesender',
  37    'administrator',
  38    'emailprefix',
  39    'emailmaxlines',
  40    'diffopts',
  41    'emaildomain',
  42    'scancommitforcc',
  43    ]
  44
  45
  46INFO = """\
  47
  48SUCCESS!
  49
  50Your post-receive-email configuration has been converted to
  51git-multimail format.  Please see README and
  52README.migrate-from-post-receive-email to learn about other
  53git-multimail configuration possibilities.
  54
  55For example, git-multimail has the following new options with no
  56equivalent in post-receive-email.  You might want to read about them
  57to see if they would be useful in your situation:
  58
  59"""
  60
  61
  62def _check_old_config_exists(old):
  63    """Check that at least one old configuration value is set."""
  64
  65    for name in OLD_NAMES:
  66        if name in old:
  67            return True
  68
  69    return False
  70
  71
  72def _check_new_config_clear(new):
  73    """Check that none of the new configuration names are set."""
  74
  75    retval = True
  76    for name in NEW_NAMES:
  77        if name in new:
  78            if retval:
  79                sys.stderr.write('INFO: The following configuration values already exist:\n\n')
  80            sys.stderr.write('    "%s.%s"\n' % (new.section, name))
  81            retval = False
  82
  83    return retval
  84
  85
  86def erase_values(config, names):
  87    for name in names:
  88        if name in config:
  89            try:
  90                sys.stderr.write('...unsetting "%s.%s"\n' % (config.section, name))
  91                config.unset_all(name)
  92            except CommandError:
  93                sys.stderr.write(
  94                    '\nWARNING: could not unset "%s.%s".  '
  95                    'Perhaps it is not set at the --local level?\n\n'
  96                    % (config.section, name)
  97                    )
  98
  99
 100def is_section_empty(section, local):
 101    """Return True iff the specified configuration section is empty.
 102
 103    Iff local is True, use the --local option when invoking 'git
 104    config'."""
 105
 106    if local:
 107        local_option = ['--local']
 108    else:
 109        local_option = []
 110
 111    try:
 112        read_output(
 113            ['git', 'config']
 114            + local_option
 115            + ['--get-regexp', '^%s\.' % (section,)]
 116            )
 117    except CommandError, e:
 118        if e.retcode == 1:
 119            # This means that no settings were found.
 120            return True
 121        else:
 122            raise
 123    else:
 124        return False
 125
 126
 127def remove_section_if_empty(section):
 128    """If the specified configuration section is empty, delete it."""
 129
 130    try:
 131        empty = is_section_empty(section, local=True)
 132    except CommandError:
 133        # Older versions of git do not support the --local option, so
 134        # if the first attempt fails, try without --local.
 135        try:
 136            empty = is_section_empty(section, local=False)
 137        except CommandError:
 138            sys.stderr.write(
 139                '\nINFO: If configuration section "%s.*" is empty, you might want '
 140                'to delete it.\n\n'
 141                % (section,)
 142                )
 143            return
 144
 145    if empty:
 146        sys.stderr.write('...removing section "%s.*"\n' % (section,))
 147        read_output(['git', 'config', '--remove-section', section])
 148    else:
 149        sys.stderr.write(
 150            '\nINFO: Configuration section "%s.*" still has contents.  '
 151            'It will not be deleted.\n\n'
 152            % (section,)
 153            )
 154
 155
 156def migrate_config(strict=False, retain=False, overwrite=False):
 157    old = Config('hooks')
 158    new = Config('multimailhook')
 159    if not _check_old_config_exists(old):
 160        sys.exit(
 161            'Your repository has no post-receive-email configuration.  '
 162            'Nothing to do.'
 163            )
 164    if not _check_new_config_clear(new):
 165        if overwrite:
 166            sys.stderr.write('\nWARNING: Erasing the above values...\n\n')
 167            erase_values(new, NEW_NAMES)
 168        else:
 169            sys.exit(
 170                '\nERROR: Refusing to overwrite existing values.  Use the --overwrite\n'
 171                'option to continue anyway.'
 172                )
 173
 174    name = 'showrev'
 175    if name in old:
 176        msg = 'git-multimail does not support "%s.%s"' % (old.section, name,)
 177        if strict:
 178            sys.exit(
 179                'ERROR: %s.\n'
 180                'Please unset that value then try again, or run without --strict.'
 181                % (msg,)
 182                )
 183        else:
 184            sys.stderr.write('\nWARNING: %s (ignoring).\n\n' % (msg,))
 185
 186    for name in ['mailinglist', 'announcelist']:
 187        if name in old:
 188            sys.stderr.write(
 189                '...copying "%s.%s" to "%s.%s"\n' % (old.section, name, new.section, name)
 190                )
 191            new.set_recipients(name, old.get_recipients(name))
 192
 193    if strict:
 194        sys.stderr.write(
 195            '...setting "%s.commitlist" to the empty string\n' % (new.section,)
 196            )
 197        new.set_recipients('commitlist', '')
 198        sys.stderr.write(
 199            '...setting "%s.announceshortlog" to "true"\n' % (new.section,)
 200            )
 201        new.set('announceshortlog', 'true')
 202
 203    for name in ['envelopesender', 'emailmaxlines', 'diffopts', 'scancommitforcc']:
 204        if name in old:
 205            sys.stderr.write(
 206                '...copying "%s.%s" to "%s.%s"\n' % (old.section, name, new.section, name)
 207                )
 208            new.set(name, old.get(name))
 209
 210    name = 'emailprefix'
 211    if name in old:
 212        sys.stderr.write(
 213            '...copying "%s.%s" to "%s.%s"\n' % (old.section, name, new.section, name)
 214            )
 215        new.set(name, old.get(name))
 216    elif strict:
 217        sys.stderr.write(
 218            '...setting "%s.%s" to "[SCM]" to preserve old subject lines\n'
 219            % (new.section, name)
 220            )
 221        new.set(name, '[SCM]')
 222
 223    if not retain:
 224        erase_values(old, OLD_NAMES)
 225        remove_section_if_empty(old.section)
 226
 227    sys.stderr.write(INFO)
 228    for name in NEW_NAMES:
 229        if name not in OLD_NAMES:
 230            sys.stderr.write('    "%s.%s"\n' % (new.section, name,))
 231    sys.stderr.write('\n')
 232
 233
 234def main(args):
 235    parser = optparse.OptionParser(
 236        description=__doc__,
 237        usage='%prog [OPTIONS]',
 238        )
 239
 240    parser.add_option(
 241        '--strict', action='store_true', default=False,
 242        help=(
 243            'Slavishly configure git-multimail as closely as possible to '
 244            'the post-receive-email configuration.  Default is to turn '
 245            'on some new features that have no equivalent in post-receive-email.'
 246            ),
 247        )
 248    parser.add_option(
 249        '--retain', action='store_true', default=False,
 250        help=(
 251            'Retain the post-receive-email configuration values.  '
 252            'Default is to delete them after the new values are set.'
 253            ),
 254        )
 255    parser.add_option(
 256        '--overwrite', action='store_true', default=False,
 257        help=(
 258            'Overwrite any existing git-multimail configuration settings.  '
 259            'Default is to abort if such settings already exist.'
 260            ),
 261        )
 262
 263    (options, args) = parser.parse_args(args)
 264
 265    if args:
 266        parser.error('Unexpected arguments: %s' % (' '.join(args),))
 267
 268    migrate_config(strict=options.strict, retain=options.retain, overwrite=options.overwrite)
 269
 270
 271main(sys.argv[1:])