1
# Copyright (C) 2005 by Canonical Ltd
2
# Authors: Robert Collins <robert.collins@canonical.com>
4
# This program is free software; you can redistribute it and/or modify
5
# it under the terms of the GNU General Public License as published by
6
# the Free Software Foundation; either version 2 of the License, or
7
# (at your option) any later version.
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
# GNU General Public License for more details.
14
# You should have received a copy of the GNU General Public License
15
# along with this program; if not, write to the Free Software
16
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
"""Configuration that affects the behaviour of Bazaar.
20
Currently this configuration resides in ~/.bazaar/bazaar.conf
21
and ~/.bazaar/branches.conf, which is written to by bzr.
23
In bazaar.conf the following options may be set:
25
editor=name-of-program
26
email=Your Name <your@email.address>
27
check_signatures=require|ignore|check-available(default)
28
create_signatures=always|never|when-required(default)
29
gpg_signing_command=name-of-program
31
in branches.conf, you specify the url of a branch and options for it.
32
Wildcards may be used - * and ? as normal in shell completion. Options
33
set in both bazaar.conf and branches.conf are overriden by the branches.conf
35
[/home/robertc/source]
36
recurse=False|True(default)
38
check_signatures= as abive
39
create_signatures= as above.
41
explanation of options
42
----------------------
43
editor - this option sets the pop up editor to use during commits.
44
email - this option sets the user id bzr will use when committing.
45
check_signatures - this option controls whether bzr will require good gpg
46
signatures, ignore them, or check them if they are
48
create_signatures - this option controls whether bzr will always create
49
gpg signatures, never create them, or create them if the
50
branch is configured to require them.
51
NB: This option is planned, but not implemented yet.
58
from fnmatch import fnmatch
62
import bzrlib.errors as errors
63
import bzrlib.util.configobj.configobj as configobj
64
from StringIO import StringIO
71
class ConfigObj(configobj.ConfigObj):
73
def get_bool(self, section, key):
74
val = self[section][key].lower()
75
if val in ('1', 'yes', 'true', 'on'):
77
elif val in ('0', 'no', 'false', 'off'):
80
raise ValueError("Value %r is not boolean" % val)
82
def get_value(self, section, name):
83
# Try [] for the old DEFAULT section.
84
if section == "DEFAULT":
89
return self[section][name]
93
"""A configuration policy - what username, editor, gpg needs etc."""
96
"""Get the users pop up editor."""
97
raise NotImplementedError
99
def _get_signature_checking(self):
100
"""Template method to override signature checking policy."""
102
def _get_user_option(self, option_name):
103
"""Template method to provide a user option."""
106
def get_user_option(self, option_name):
107
"""Get a generic option - no special process, no default."""
108
return self._get_user_option(option_name)
110
def gpg_signing_command(self):
111
"""What program should be used to sign signatures?"""
112
result = self._gpg_signing_command()
117
def _gpg_signing_command(self):
118
"""See gpg_signing_command()."""
122
super(Config, self).__init__()
124
def post_commit(self):
125
"""An ordered list of python functions to call.
127
Each function takes branch, rev_id as parameters.
129
return self._post_commit()
131
def _post_commit(self):
132
"""See Config.post_commit."""
135
def user_email(self):
136
"""Return just the email component of a username."""
137
return extract_email_address(self.username())
140
"""Return email-style username.
142
Something similar to 'Martin Pool <mbp@sourcefrog.net>'
144
$BZREMAIL can be set to override this, then
145
the concrete policy type is checked, and finally
147
If none is found, a reasonable default is (hopefully)
150
TODO: Check it's reasonably well-formed.
152
v = os.environ.get('BZREMAIL')
154
return v.decode(bzrlib.user_encoding)
156
v = self._get_user_id()
160
v = os.environ.get('EMAIL')
162
return v.decode(bzrlib.user_encoding)
164
name, email = _auto_user_id()
166
return '%s <%s>' % (name, email)
170
def signature_checking(self):
171
"""What is the current policy for signature checking?."""
172
policy = self._get_signature_checking()
173
if policy is not None:
175
return CHECK_IF_POSSIBLE
177
def signature_needed(self):
178
"""Is a signature needed when committing ?."""
179
policy = self._get_signature_checking()
180
if policy == CHECK_ALWAYS:
185
class IniBasedConfig(Config):
186
"""A configuration policy that draws from ini files."""
188
def _get_parser(self, file=None):
189
if self._parser is not None:
192
input = self._get_filename()
196
self._parser = ConfigObj(input)
197
except configobj.ConfigObjError, e:
198
raise errors.ParseConfigError(e.errors, e.config.filename)
201
def _get_section(self):
202
"""Override this to define the section used by the config."""
205
def _get_signature_checking(self):
206
"""See Config._get_signature_checking."""
207
policy = self._get_user_option('check_signatures')
209
return self._string_to_signature_policy(policy)
211
def _get_user_id(self):
212
"""Get the user id from the 'email' key in the current section."""
213
return self._get_user_option('email')
215
def _get_user_option(self, option_name):
216
"""See Config._get_user_option."""
218
return self._get_parser().get_value(self._get_section(),
223
def _gpg_signing_command(self):
224
"""See Config.gpg_signing_command."""
225
return self._get_user_option('gpg_signing_command')
227
def __init__(self, get_filename):
228
super(IniBasedConfig, self).__init__()
229
self._get_filename = get_filename
232
def _post_commit(self):
233
"""See Config.post_commit."""
234
return self._get_user_option('post_commit')
236
def _string_to_signature_policy(self, signature_string):
237
"""Convert a string to a signing policy."""
238
if signature_string.lower() == 'check-available':
239
return CHECK_IF_POSSIBLE
240
if signature_string.lower() == 'ignore':
242
if signature_string.lower() == 'require':
244
raise errors.BzrError("Invalid signatures policy '%s'"
248
class GlobalConfig(IniBasedConfig):
249
"""The configuration that should be used for a specific location."""
251
def get_editor(self):
252
return self._get_user_option('editor')
255
super(GlobalConfig, self).__init__(config_filename)
258
class LocationConfig(IniBasedConfig):
259
"""A configuration object that gives the policy for a location."""
261
def __init__(self, location):
262
super(LocationConfig, self).__init__(branches_config_filename)
263
self._global_config = None
264
self.location = location
266
def _get_global_config(self):
267
if self._global_config is None:
268
self._global_config = GlobalConfig()
269
return self._global_config
271
def _get_section(self):
272
"""Get the section we should look in for config items.
274
Returns None if none exists.
275
TODO: perhaps return a NullSection that thunks through to the
278
sections = self._get_parser()
279
location_names = self.location.split('/')
280
if self.location.endswith('/'):
281
del location_names[-1]
283
for section in sections:
284
section_names = section.split('/')
285
if section.endswith('/'):
286
del section_names[-1]
287
names = zip(location_names, section_names)
290
if not fnmatch(name[0], name[1]):
295
# so, for the common prefix they matched.
296
# if section is longer, no match.
297
if len(section_names) > len(location_names):
299
# if path is longer, and recurse is not true, no match
300
if len(section_names) < len(location_names):
302
if not self._get_parser().get_bool(section, 'recurse'):
306
matches.append((len(section_names), section))
309
matches.sort(reverse=True)
312
def _gpg_signing_command(self):
313
"""See Config.gpg_signing_command."""
314
command = super(LocationConfig, self)._gpg_signing_command()
315
if command is not None:
317
return self._get_global_config()._gpg_signing_command()
319
def _get_user_id(self):
320
user_id = super(LocationConfig, self)._get_user_id()
321
if user_id is not None:
323
return self._get_global_config()._get_user_id()
325
def _get_user_option(self, option_name):
326
"""See Config._get_user_option."""
327
option_value = super(LocationConfig,
328
self)._get_user_option(option_name)
329
if option_value is not None:
331
return self._get_global_config()._get_user_option(option_name)
333
def _get_signature_checking(self):
334
"""See Config._get_signature_checking."""
335
check = super(LocationConfig, self)._get_signature_checking()
336
if check is not None:
338
return self._get_global_config()._get_signature_checking()
340
def _post_commit(self):
341
"""See Config.post_commit."""
342
hook = self._get_user_option('post_commit')
345
return self._get_global_config()._post_commit()
347
def set_user_option(self, option, value):
348
"""Save option and its value in the configuration."""
349
# FIXME: RBC 20051029 This should refresh the parser and also take a
350
# file lock on branches.conf.
351
if not os.path.isdir(os.path.dirname(self._get_filename())):
352
os.mkdir(os.path.dirname(self._get_filename()))
353
location = self.location
354
if location.endswith('/'):
355
location = location[:-1]
356
if (not location in self._get_parser() and
357
not location + '/' in self._get_parser()):
358
self._get_parser()[location]={}
359
elif location + '/' in self._get_parser():
360
location = location + '/'
361
self._get_parser()[location][option]=value
362
self._get_parser().write()
365
class BranchConfig(Config):
366
"""A configuration object giving the policy for a branch."""
368
def _get_location_config(self):
369
if self._location_config is None:
370
self._location_config = LocationConfig(self.branch.base)
371
return self._location_config
373
def _get_user_id(self):
374
"""Return the full user id for the branch.
376
e.g. "John Hacker <jhacker@foo.org>"
377
This is looked up in the email controlfile for the branch.
380
return (self.branch.controlfile("email", "r")
382
.decode(bzrlib.user_encoding)
384
except errors.NoSuchFile, e:
387
return self._get_location_config()._get_user_id()
389
def _get_signature_checking(self):
390
"""See Config._get_signature_checking."""
391
return self._get_location_config()._get_signature_checking()
393
def _get_user_option(self, option_name):
394
"""See Config._get_user_option."""
395
return self._get_location_config()._get_user_option(option_name)
397
def _gpg_signing_command(self):
398
"""See Config.gpg_signing_command."""
399
return self._get_location_config()._gpg_signing_command()
401
def __init__(self, branch):
402
super(BranchConfig, self).__init__()
403
self._location_config = None
406
def _post_commit(self):
407
"""See Config.post_commit."""
408
return self._get_location_config()._post_commit()
412
"""Return per-user configuration directory.
414
By default this is ~/.bazaar/
416
TODO: Global option --config-dir to override this.
418
base = os.environ.get('BZR_HOME', None)
419
if sys.platform == 'win32':
421
base = os.environ.get('APPDATA', None)
423
base = os.environ.get('HOME', None)
425
raise BzrError('You must have one of BZR_HOME, APPDATA, or HOME set')
426
return os.path.join(base, 'bazaar', '2.0')
428
# cygwin, linux, and darwin all have a $HOME directory
430
base = os.path.expanduser("~")
431
return os.path.join(base, ".bazaar")
434
def config_filename():
435
"""Return per-user configuration ini file filename."""
436
return os.path.join(config_dir(), 'bazaar.conf')
439
def branches_config_filename():
440
"""Return per-user configuration ini file filename."""
441
return os.path.join(config_dir(), 'branches.conf')
445
"""Calculate automatic user identification.
447
Returns (realname, email).
449
Only used when none is set in the environment or the id file.
451
This previously used the FQDN as the default domain, but that can
452
be very slow on machines where DNS is broken. So now we simply
457
# XXX: Any good way to get real user name on win32?
462
w = pwd.getpwuid(uid)
463
gecos = w.pw_gecos.decode(bzrlib.user_encoding)
464
username = w.pw_name.decode(bzrlib.user_encoding)
465
comma = gecos.find(',')
469
realname = gecos[:comma]
475
realname = username = getpass.getuser().decode(bzrlib.user_encoding)
477
return realname, (username + '@' + socket.gethostname())
480
def extract_email_address(e):
481
"""Return just the address part of an email string.
483
That is just the user@domain part, nothing else.
484
This part is required to contain only ascii characters.
485
If it can't be extracted, raises an error.
487
>>> extract_email_address('Jane Tester <jane@test.com>')
490
m = re.search(r'[\w+.-]+@[\w+.-]+', e)
492
raise errors.BzrError("%r doesn't seem to contain "
493
"a reasonable email address" % e)
496
class TreeConfig(object):
497
"""Branch configuration data associated with its contents, not location"""
498
def __init__(self, branch):
501
def _get_config(self):
503
obj = ConfigObj(self.branch.controlfile('branch.conf',
506
except errors.NoSuchFile:
510
def get_option(self, name, section=None, default=None):
511
self.branch.lock_read()
513
obj = self._get_config()
515
if section is not None:
524
def set_option(self, value, name, section=None):
525
"""Set a per-branch configuration option"""
526
self.branch.lock_write()
528
cfg_obj = self._get_config()
533
obj = cfg_obj[section]
535
cfg_obj[section] = {}
536
obj = cfg_obj[section]
538
cfg_obj.encode('UTF-8')
539
out_file = StringIO(''.join([l+'\n' for l in cfg_obj.write()]))
541
self.branch.put_controlfile('branch.conf', out_file, encode=False)