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