contrib / hooks / multimail / migrate-mailhook-configon commit Merge branch 'jk/proto-v2-hidden-refs-fix' (d3b0178)
   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:
 118        t, e, traceback = sys.exc_info()
 119        if e.retcode == 1:
 120            # This means that no settings were found.
 121            return True
 122        else:
 123            raise
 124    else:
 125        return False
 126
 127
 128def remove_section_if_empty(section):
 129    """If the specified configuration section is empty, delete it."""
 130
 131    try:
 132        empty = is_section_empty(section, local=True)
 133    except CommandError:
 134        # Older versions of git do not support the --local option, so
 135        # if the first attempt fails, try without --local.
 136        try:
 137            empty = is_section_empty(section, local=False)
 138        except CommandError:
 139            sys.stderr.write(
 140                '\nINFO: If configuration section "%s.*" is empty, you might want '
 141                'to delete it.\n\n'
 142                % (section,)
 143                )
 144            return
 145
 146    if empty:
 147        sys.stderr.write('...removing section "%s.*"\n' % (section,))
 148        read_output(['git', 'config', '--remove-section', section])
 149    else:
 150        sys.stderr.write(
 151            '\nINFO: Configuration section "%s.*" still has contents.  '
 152            'It will not be deleted.\n\n'
 153            % (section,)
 154            )
 155
 156
 157def migrate_config(strict=False, retain=False, overwrite=False):
 158    old = Config('hooks')
 159    new = Config('multimailhook')
 160    if not _check_old_config_exists(old):
 161        sys.exit(
 162            'Your repository has no post-receive-email configuration.  '
 163            'Nothing to do.'
 164            )
 165    if not _check_new_config_clear(new):
 166        if overwrite:
 167            sys.stderr.write('\nWARNING: Erasing the above values...\n\n')
 168            erase_values(new, NEW_NAMES)
 169        else:
 170            sys.exit(
 171                '\nERROR: Refusing to overwrite existing values.  Use the --overwrite\n'
 172                'option to continue anyway.'
 173                )
 174
 175    name = 'showrev'
 176    if name in old:
 177        msg = 'git-multimail does not support "%s.%s"' % (old.section, name,)
 178        if strict:
 179            sys.exit(
 180                'ERROR: %s.\n'
 181                'Please unset that value then try again, or run without --strict.'
 182                % (msg,)
 183                )
 184        else:
 185            sys.stderr.write('\nWARNING: %s (ignoring).\n\n' % (msg,))
 186
 187    for name in ['mailinglist', 'announcelist']:
 188        if name in old:
 189            sys.stderr.write(
 190                '...copying "%s.%s" to "%s.%s"\n' % (old.section, name, new.section, name)
 191                )
 192            old_recipients = old.get_all(name, default=None)
 193            old_recipients = ', '.join(o.strip() for o in old_recipients)
 194            new.set_recipients(name, old_recipients)
 195
 196    if strict:
 197        sys.stderr.write(
 198            '...setting "%s.commitlist" to the empty string\n' % (new.section,)
 199            )
 200        new.set_recipients('commitlist', '')
 201        sys.stderr.write(
 202            '...setting "%s.announceshortlog" to "true"\n' % (new.section,)
 203            )
 204        new.set('announceshortlog', 'true')
 205
 206    for name in ['envelopesender', 'emailmaxlines', 'diffopts', 'scancommitforcc']:
 207        if name in old:
 208            sys.stderr.write(
 209                '...copying "%s.%s" to "%s.%s"\n' % (old.section, name, new.section, name)
 210                )
 211            new.set(name, old.get(name))
 212
 213    name = 'emailprefix'
 214    if name in old:
 215        sys.stderr.write(
 216            '...copying "%s.%s" to "%s.%s"\n' % (old.section, name, new.section, name)
 217            )
 218        new.set(name, old.get(name))
 219    elif strict:
 220        sys.stderr.write(
 221            '...setting "%s.%s" to "[SCM]" to preserve old subject lines\n'
 222            % (new.section, name)
 223            )
 224        new.set(name, '[SCM]')
 225
 226    if not retain:
 227        erase_values(old, OLD_NAMES)
 228        remove_section_if_empty(old.section)
 229
 230    sys.stderr.write(INFO)
 231    for name in NEW_NAMES:
 232        if name not in OLD_NAMES:
 233            sys.stderr.write('    "%s.%s"\n' % (new.section, name,))
 234    sys.stderr.write('\n')
 235
 236
 237def main(args):
 238    parser = optparse.OptionParser(
 239        description=__doc__,
 240        usage='%prog [OPTIONS]',
 241        )
 242
 243    parser.add_option(
 244        '--strict', action='store_true', default=False,
 245        help=(
 246            'Slavishly configure git-multimail as closely as possible to '
 247            'the post-receive-email configuration.  Default is to turn '
 248            'on some new features that have no equivalent in post-receive-email.'
 249            ),
 250        )
 251    parser.add_option(
 252        '--retain', action='store_true', default=False,
 253        help=(
 254            'Retain the post-receive-email configuration values.  '
 255            'Default is to delete them after the new values are set.'
 256            ),
 257        )
 258    parser.add_option(
 259        '--overwrite', action='store_true', default=False,
 260        help=(
 261            'Overwrite any existing git-multimail configuration settings.  '
 262            'Default is to abort if such settings already exist.'
 263            ),
 264        )
 265
 266    (options, args) = parser.parse_args(args)
 267
 268    if args:
 269        parser.error('Unexpected arguments: %s' % (' '.join(args),))
 270
 271    migrate_config(strict=options.strict, retain=options.retain, overwrite=options.overwrite)
 272
 273
 274main(sys.argv[1:])