Merge branch 'sg/travis-osx-brew-breakage-workaround'
[git] / contrib / hooks / multimail / migrate-mailhook-config
1 #! /usr/bin/env python
2
3 """Migrate a post-receive-email configuration to be usable with git_multimail.py.
4
5 See README.migrate-from-post-receive-email for more information.
6
7 """
8
9 import sys
10 import optparse
11
12 from git_multimail import CommandError
13 from git_multimail import Config
14 from git_multimail import read_output
15
16
17 OLD_NAMES = [
18     'mailinglist',
19     'announcelist',
20     'envelopesender',
21     'emailprefix',
22     'showrev',
23     'emailmaxlines',
24     'diffopts',
25     'scancommitforcc',
26     ]
27
28 NEW_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
46 INFO = """\
47
48 SUCCESS!
49
50 Your post-receive-email configuration has been converted to
51 git-multimail format.  Please see README and
52 README.migrate-from-post-receive-email to learn about other
53 git-multimail configuration possibilities.
54
55 For example, git-multimail has the following new options with no
56 equivalent in post-receive-email.  You might want to read about them
57 to see if they would be useful in your situation:
58
59 """
60
61
62 def _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
72 def _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
86 def 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
100 def 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
128 def 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
157 def 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
237 def 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
274 main(sys.argv[1:])