Merge lp:~parthm/bzr/549310-mandatory-whoami into lp:bzr

Proposed by Parth Malwankar on 2010-04-27
Status: Merged
Approved by: Parth Malwankar on 2010-05-05
Approved revision: 5197
Merged at revision: 5213
Proposed branch: lp:~parthm/bzr/549310-mandatory-whoami
Merge into: lp:bzr
Diff against target: 316 lines (+98/-83)
11 files modified
NEWS (+5/-0)
bzrlib/config.py (+9/-80)
bzrlib/errors.py (+7/-0)
bzrlib/lockdir.py (+2/-2)
bzrlib/osutils.py (+13/-0)
bzrlib/tests/__init__.py (+1/-1)
bzrlib/tests/blackbox/test_commit.py (+12/-0)
bzrlib/tests/blackbox/test_init.py (+14/-0)
bzrlib/tests/blackbox/test_shared_repository.py (+15/-0)
bzrlib/tests/blackbox/test_whoami.py (+9/-0)
bzrlib/tests/test_osutils.py (+11/-0)
To merge this branch: bzr merge lp:~parthm/bzr/549310-mandatory-whoami
Reviewer Review Type Date Requested Status
Martin Pool 2010-04-29 Approve on 2010-05-05
Andrew Bennetts 2010-04-27 Needs Fixing on 2010-04-28
bzr-core 2010-04-28 Pending
Review via email: mp+24244@code.launchpad.net

Commit Message

bzr does not guess whoami anymore. (parthm, #549310)

Description of the Change

=== Fixed Bug #549310 ===

This proposal make setting username mandatory either using 'bzr whoami', EMAIL or BZR_EMAIL.
bzr does not try to guess username as login@host as thats not very reliable.

Behavior change is that init, init-repo, commit, whoami throw error suggesting the use of 'bzr whoami'.
Explicit checks are added to init and init-repo to ensure that username is avoid creation of branch/repo directory by failing early. For making tests work, EMAIL has be set to <email address hidden> in the test environment.

E.g.
sandbox@parthm-laptop:~$ /home/parthm/src/bzr.dev/549310-mandatory-whoami/bzr --no-plugins init-repo foo
bzr: ERROR: Unable to determine your name.
Please, set your name with the 'whoami' command.
E.g. bzr whoami "Your Name <email address hidden>"
sandbox@parthm-laptop:~$
sandbox@parthm-laptop:~$
sandbox@parthm-laptop:~$ /home/parthm/src/bzr.dev/549310-mandatory-whoami/bzr --no-plugins init foo
bzr: ERROR: Unable to determine your name.
Please, set your name with the 'whoami' command.
E.g. bzr whoami "Your Name <email address hidden>"
sandbox@parthm-laptop:~$
sandbox@parthm-laptop:~$ ls
examples.desktop tmp
sandbox@parthm-laptop:~$ /home/parthm/src/bzr.dev/549310-mandatory-whoami/bzr --no-plugins whoami
bzr: ERROR: Unable to determine your name.
Please, set your name with the 'whoami' command.
E.g. bzr whoami "Your Name <email address hidden>"
sandbox@parthm-laptop:~$

To post a comment you must log in.
Andrew Bennetts (spiv) wrote :

It will be very nice to have this bug fixed.

However, I don't think we should require whoami to be set to be able to use init-repo or init. Especially for init-repo there are valid use cases for it that don't require an identity to be set, e.g. mirroring a set of branches.

review: Needs Fixing
Parth Malwankar (parthm) wrote :
Download full text (3.7 KiB)

> It will be very nice to have this bug fixed.
>
> However, I don't think we should require whoami to be set to be able to use
> init-repo or init. Especially for init-repo there are valid use cases for it
> that don't require an identity to be set, e.g. mirroring a set of branches.

I was myself curious as to where/why init/init-repo need whoami to
be set. This patch merely ensures that they fail early before creating
a directory on disk.

So, it seems this info is used by lockdir to write it to lock file
so bzr know who has locked the dir. I suppose it maybe better to
keep whoami as mandatory in such a situation. Thoughts?

Following are the interesting parts of the stack trace without the
fail early change.

Traceback (most recent call last):
  File "/storage/parth/src/bzr.dev/549310-mandatory-whoami/bzrlib/commands.py", line 907, in exception_to_return_code
    return the_callable(*args, **kwargs)
  File "/storage/parth/src/bzr.dev/549310-mandatory-whoami/bzrlib/commands.py", line 1111, in run_bzr
    ret = run(*run_argv)
  File "/storage/parth/src/bzr.dev/549310-mandatory-whoami/bzrlib/commands.py", line 685, in run_argv_aliases
    return self.run(**all_cmd_args)
  File "/storage/parth/src/bzr.dev/549310-mandatory-whoami/bzrlib/commands.py", line 700, in run
    return self._operation.run_simple(*args, **kwargs)
  File "/storage/parth/src/bzr.dev/549310-mandatory-whoami/bzrlib/cleanup.py", line 122, in run_simple
    self.cleanups, self.func, *args, **kwargs)
  File "/storage/parth/src/bzr.dev/549310-mandatory-whoami/bzrlib/cleanup.py", line 156, in _do_with_cleanups
    result = func(*args, **kwargs)
  File "/storage/parth/src/bzr.dev/549310-mandatory-whoami/bzrlib/builtins.py", line 1761, in run
    possible_transports=[to_transport])
  File "/storage/parth/src/bzr.dev/549310-mandatory-whoami/bzrlib/bzrdir.py", line 539, in create_branch_convenience
    bzrdir = BzrDir.create(base, format, possible_transports)
  File "/storage/parth/src/bzr.dev/549310-mandatory-whoami/bzrlib/bzrdir.py", line 315, in create
    return format.initialize_on_transport(t)
  File "/storage/parth/src/bzr.dev/549310-mandatory-whoami/bzrlib/bzrdir.py", line 1961, in initialize_on_transport
    return self._initialize_on_transport_vfs(transport)
  File "/storage/parth/src/bzr.dev/549310-mandatory-whoami/bzrlib/bzrdir.py", line 2098, in _initialize_on_transport_vfs
    control_files.lock_write()
  File "/storage/parth/src/bzr.dev/549310-mandatory-whoami/bzrlib/lockable_files.py", line 193, in lock_write
    token_from_lock = self._lock.lock_write(token=token)
  File "/storage/parth/src/bzr.dev/549310-mandatory-whoami/bzrlib/lockdir.py", line 600, in lock_write
    return self.wait_lock()
  File "/storage/parth/src/bzr.dev/549310-mandatory-whoami/bzrlib/lockdir.py", line 521, in wait_lock
    return self.attempt_lock()
  File "/storage/parth/src/bzr.dev/549310-mandatory-whoami/bzrlib/lockdir.py", line 482, in attempt_lock
    result = self._attempt_lock()
  File "/storage/parth/src/bzr.dev/549310-mandatory-whoami/bzrlib/lockdir.py", line 224, in _attempt_lock
    tmpname = self._create_pending_dir()
  File "/storage/parth/src/bzr.dev/549310-mandatory-whoami/bzr...

Read more...

Andrew Bennetts (spiv) wrote :

Parth Malwankar wrote:
> > It will be very nice to have this bug fixed.
> >
> > However, I don't think we should require whoami to be set to be able to use
> > init-repo or init. Especially for init-repo there are valid use cases for it
> > that don't require an identity to be set, e.g. mirroring a set of branches.
>
> I was myself curious as to where/why init/init-repo need whoami to
> be set. This patch merely ensures that they fail early before creating
> a directory on disk.
>
> So, it seems this info is used by lockdir to write it to lock file
> so bzr know who has locked the dir. I suppose it maybe better to
> keep whoami as mandatory in such a situation. Thoughts?
>
> Following are the interesting parts of the stack trace without the
> fail early change.

Ah!

I think for lockdirs it doesn't matter so much what the name is. It's
not a name that will be recorded permanently in the revision history,
it's just an indication for a concurrent user of that repo/branch/tree
of who has got the lock.

Ideally I think that for lockdirs:

  * if the whoami is set, use that
  * if the whoami isn't set, then the best-guess of the old
    _auto_user_id code is good enough — that will almost certainly tell
    whoever is looking at the lock file enough to figure out who created
    the lock.

-Andrew.

Martin Pool (mbp) wrote :

On 28 April 2010 12:48, Andrew Bennetts <email address hidden> wrote:

> I think for lockdirs it doesn't matter so much what the name is.  It's
> not a name that will be recorded permanently in the revision history,
> it's just an indication for a concurrent user of that repo/branch/tree
> of who has got the lock.

+1

>
> Ideally I think that for lockdirs:
>
>  * if the whoami is set, use that
>  * if the whoami isn't set, then the best-guess of the old
>    _auto_user_id code is good enough — that will almost certainly tell
>    whoever is looking at the lock file enough to figure out who created
>    the lock.

even just putting the local username with no special tricks would be
enough. It's pretty transient.

--
Martin <http://launchpad.net/~mbp/>

5193. By Parth Malwankar on 2010-04-28

lockdir no long mandates whoami but uses unicode version of getuser

Robert Collins (lifeless) wrote :

Well, lockdirs are exposed over the network, so if we have any host
info that is better than nothing.

Martin Pool (mbp) wrote :

On 28 April 2010 16:45, Robert Collins <email address hidden> wrote:
> Well, lockdirs are exposed over the network, so if we have any host
> info that is better than nothing.

I'm pretty sure the process hostname is already included in a separate field.

--
Martin <http://launchpad.net/~mbp/>

5194. By Parth Malwankar on 2010-04-28

moved getuser_unicode to osutils.

5195. By Parth Malwankar on 2010-04-28

added tests for getuser_unicode

Parth Malwankar (parthm) wrote :

lockdir updated to use unicode version of getpass.getuser().
Added utility function osutils.getuser_unicode() for this.
init/init-repo now work even when whoami is not set.
Added errors.NoWhoami.

Parth Malwankar (parthm) wrote :

The review comments here are closed. Maybe someone can give this a second look.

Martin Pool (mbp) wrote :

That looks good to me. Perhaps you could add comments to the tests for init and init-repo explaining why they're even tested, because it's a bit surprising they would ever fail.

review: Approve
5196. By Parth Malwankar on 2010-05-05

added comment to init/init-repo pass tests for lacking whoami.

5197. By Parth Malwankar on 2010-05-05

merged in changes from trunk.

Parth Malwankar (parthm) wrote :

> That looks good to me. Perhaps you could add comments to the tests for init
> and init-repo explaining why they're even tested, because it's a bit
> surprising they would ever fail.

Done. Thanks for the review.

All open comments on this are closed, so if another reviewer could give it a look and there is nothing more to do on this patch I could try to land it.

Martin Pool (mbp) wrote :

I think this is sufficiently reviewed and you can land at your leisure.

--
Martin <http://launchpad.net/~mbp/>

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'NEWS'
2--- NEWS 2010-05-05 13:25:24 +0000
3+++ NEWS 2010-05-05 14:13:28 +0000
4@@ -13,6 +13,11 @@
5 Compatibility Breaks
6 ********************
7
8+* ``bzr`` does not try to guess the username as ``username@hostname``
9+ and requires it to be explictly set. This can be set using ``bzr
10+ whoami``.
11+ (Parth Malwankar, #549310)
12+
13 New Features
14 ************
15
16
17=== modified file 'bzrlib/config.py'
18--- bzrlib/config.py 2010-04-24 09:08:27 +0000
19+++ bzrlib/config.py 2010-05-05 14:13:28 +0000
20@@ -257,8 +257,7 @@
21
22 Something similar to 'Martin Pool <mbp@sourcefrog.net>'
23
24- $BZR_EMAIL can be set to override this (as well as the
25- deprecated $BZREMAIL), then
26+ $BZR_EMAIL can be set to override this, then
27 the concrete policy type is checked, and finally
28 $EMAIL is examined.
29 If none is found, a reasonable default is (hopefully)
30@@ -278,11 +277,14 @@
31 if v:
32 return v.decode(osutils.get_user_encoding())
33
34- name, email = _auto_user_id()
35- if name:
36- return '%s <%s>' % (name, email)
37- else:
38- return email
39+ raise errors.NoWhoami()
40+
41+ def ensure_username(self):
42+ """Raise BzrCommandError if username is not set.
43+
44+ This method relies on the username() function raising the error.
45+ """
46+ self.username()
47
48 def signature_checking(self):
49 """What is the current policy for signature checking?."""
50@@ -899,79 +901,6 @@
51 return os.path.expanduser('~/.cache')
52
53
54-def _auto_user_id():
55- """Calculate automatic user identification.
56-
57- Returns (realname, email).
58-
59- Only used when none is set in the environment or the id file.
60-
61- This previously used the FQDN as the default domain, but that can
62- be very slow on machines where DNS is broken. So now we simply
63- use the hostname.
64- """
65- import socket
66-
67- if sys.platform == 'win32':
68- name = win32utils.get_user_name_unicode()
69- if name is None:
70- raise errors.BzrError("Cannot autodetect user name.\n"
71- "Please, set your name with command like:\n"
72- 'bzr whoami "Your Name <name@domain.com>"')
73- host = win32utils.get_host_name_unicode()
74- if host is None:
75- host = socket.gethostname()
76- return name, (name + '@' + host)
77-
78- try:
79- import pwd
80- uid = os.getuid()
81- try:
82- w = pwd.getpwuid(uid)
83- except KeyError:
84- raise errors.BzrCommandError('Unable to determine your name. '
85- 'Please use "bzr whoami" to set it.')
86-
87- # we try utf-8 first, because on many variants (like Linux),
88- # /etc/passwd "should" be in utf-8, and because it's unlikely to give
89- # false positives. (many users will have their user encoding set to
90- # latin-1, which cannot raise UnicodeError.)
91- try:
92- gecos = w.pw_gecos.decode('utf-8')
93- encoding = 'utf-8'
94- except UnicodeError:
95- try:
96- encoding = osutils.get_user_encoding()
97- gecos = w.pw_gecos.decode(encoding)
98- except UnicodeError:
99- raise errors.BzrCommandError('Unable to determine your name. '
100- 'Use "bzr whoami" to set it.')
101- try:
102- username = w.pw_name.decode(encoding)
103- except UnicodeError:
104- raise errors.BzrCommandError('Unable to determine your name. '
105- 'Use "bzr whoami" to set it.')
106-
107- comma = gecos.find(',')
108- if comma == -1:
109- realname = gecos
110- else:
111- realname = gecos[:comma]
112- if not realname:
113- realname = username
114-
115- except ImportError:
116- import getpass
117- try:
118- user_encoding = osutils.get_user_encoding()
119- realname = username = getpass.getuser().decode(user_encoding)
120- except UnicodeDecodeError:
121- raise errors.BzrError("Can't decode username as %s." % \
122- user_encoding)
123-
124- return realname, (username + '@' + socket.gethostname())
125-
126-
127 def parse_username(username):
128 """Parse e-mail username and return a (name, address) tuple."""
129 match = re.match(r'(.*?)\s*<?([\w+.-]+@[\w+.-]+)>?', username)
130
131=== modified file 'bzrlib/errors.py'
132--- bzrlib/errors.py 2010-04-21 11:27:04 +0000
133+++ bzrlib/errors.py 2010-05-05 14:13:28 +0000
134@@ -3134,3 +3134,10 @@
135 def __init__(self, bzrdir):
136 self.bzrdir = bzrdir
137
138+class NoWhoami(BzrError):
139+
140+ _fmt = ('Unable to determine your name.\n'
141+ "Please, set your name with the 'whoami' command.\n"
142+ 'E.g. bzr whoami "Your Name <name@example.com>"')
143+
144+
145
146=== modified file 'bzrlib/lockdir.py'
147--- bzrlib/lockdir.py 2010-03-03 22:59:21 +0000
148+++ bzrlib/lockdir.py 2010-05-05 14:13:28 +0000
149@@ -447,9 +447,9 @@
150 # XXX: is creating this here inefficient?
151 config = bzrlib.config.GlobalConfig()
152 try:
153- user = config.user_email()
154- except errors.NoEmailInUsername:
155 user = config.username()
156+ except errors.NoWhoami:
157+ user = osutils.getuser_unicode()
158 s = rio.Stanza(hostname=get_host_name(),
159 pid=str(os.getpid()),
160 start_time=str(int(time.time())),
161
162=== modified file 'bzrlib/osutils.py'
163--- bzrlib/osutils.py 2010-05-03 06:25:46 +0000
164+++ bzrlib/osutils.py 2010-05-05 14:13:28 +0000
165@@ -26,6 +26,7 @@
166 lazy_import(globals(), """
167 from datetime import datetime
168 import errno
169+import getpass
170 from ntpath import (abspath as _nt_abspath,
171 join as _nt_join,
172 normpath as _nt_normpath,
173@@ -2302,3 +2303,15 @@
174 return os.fdopen(os.open(filename, flags), mode, bufsize)
175 else:
176 open_file = open
177+
178+
179+def getuser_unicode():
180+ """Return the username as unicode.
181+ """
182+ try:
183+ user_encoding = get_user_encoding()
184+ username = getpass.getuser().decode(user_encoding)
185+ except UnicodeDecodeError:
186+ raise errors.BzrError("Can't decode username as %s." % \
187+ user_encoding)
188+ return username
189
190=== modified file 'bzrlib/tests/__init__.py'
191--- bzrlib/tests/__init__.py 2010-05-05 02:35:02 +0000
192+++ bzrlib/tests/__init__.py 2010-05-05 14:13:28 +0000
193@@ -1524,7 +1524,7 @@
194 'EDITOR': None,
195 'BZR_EMAIL': None,
196 'BZREMAIL': None, # may still be present in the environment
197- 'EMAIL': None,
198+ 'EMAIL': 'jrandom@example.com', # set EMAIL as bzr does not guess
199 'BZR_PROGRESS_BAR': None,
200 'BZR_LOG': None,
201 'BZR_PLUGIN_PATH': None,
202
203=== modified file 'bzrlib/tests/blackbox/test_commit.py'
204--- bzrlib/tests/blackbox/test_commit.py 2010-04-07 22:22:27 +0000
205+++ bzrlib/tests/blackbox/test_commit.py 2010-05-05 14:13:28 +0000
206@@ -710,3 +710,15 @@
207 out, err = self.run_bzr_error(["empty commit message"],
208 "commit tree/hello.txt", stdin="n\n")
209 self.assertEqual(expected, tree.last_revision())
210+
211+ def test_commit_without_username(self):
212+ """Ensure commit error if username is not set.
213+ """
214+ self.run_bzr(['init', 'foo'])
215+ os.chdir('foo')
216+ open('foo.txt', 'w').write('hello')
217+ self.run_bzr(['add'])
218+ osutils.set_or_unset_env('EMAIL', None)
219+ osutils.set_or_unset_env('BZR_EMAIL', None)
220+ out, err = self.run_bzr(['commit', '-m', 'initial'], 3)
221+ self.assertContainsRe(err, 'Unable to determine your name')
222
223=== modified file 'bzrlib/tests/blackbox/test_init.py'
224--- bzrlib/tests/blackbox/test_init.py 2010-04-23 07:15:23 +0000
225+++ bzrlib/tests/blackbox/test_init.py 2010-05-05 14:13:28 +0000
226@@ -202,3 +202,17 @@
227 self.assertEqual(True, branch._get_append_revisions_only())
228 self.run_bzr_error(['cannot be set to append-revisions-only'],
229 'init --append-revisions-only --knit knit')
230+
231+ def test_init_without_username(self):
232+ """Ensure init works if username is not set.
233+ """
234+ # bzr makes user specified whoami mandatory for operations
235+ # like commit as whoami is recorded. init however is not so final
236+ # and uses whoami only in a lock file. Without whoami the login name
237+ # is used. This test is to ensure that init passes even when whoami
238+ # is not available.
239+ osutils.set_or_unset_env('EMAIL', None)
240+ osutils.set_or_unset_env('BZR_EMAIL', None)
241+ out, err = self.run_bzr(['init', 'foo'])
242+ self.assertEqual(err, '')
243+ self.assertTrue(os.path.exists('foo'))
244
245=== modified file 'bzrlib/tests/blackbox/test_shared_repository.py'
246--- bzrlib/tests/blackbox/test_shared_repository.py 2010-03-25 18:02:45 +0000
247+++ bzrlib/tests/blackbox/test_shared_repository.py 2010-05-05 14:13:28 +0000
248@@ -18,6 +18,7 @@
249
250 import os
251
252+from bzrlib import osutils
253 from bzrlib.bzrdir import BzrDir, BzrDirMetaFormat1
254 import bzrlib.errors as errors
255 from bzrlib.tests import TestCaseInTempDir
256@@ -145,3 +146,17 @@
257 self.assertLength(0, calls)
258 self.run_bzr("init-repository a")
259 self.assertLength(1, calls)
260+
261+ def test_init_repo_without_username(self):
262+ """Ensure init-repo works if username is not set.
263+ """
264+ # bzr makes user specified whoami mandatory for operations
265+ # like commit as whoami is recorded. init-repo however is not so final
266+ # and uses whoami only in a lock file. Without whoami the login name
267+ # is used. This test is to ensure that init-repo passes even when whoami
268+ # is not available.
269+ osutils.set_or_unset_env('EMAIL', None)
270+ osutils.set_or_unset_env('BZR_EMAIL', None)
271+ out, err = self.run_bzr(['init-repo', 'foo'])
272+ self.assertEqual(err, '')
273+ self.assertTrue(os.path.exists('foo'))
274
275=== modified file 'bzrlib/tests/blackbox/test_whoami.py'
276--- bzrlib/tests/blackbox/test_whoami.py 2009-03-23 14:59:43 +0000
277+++ bzrlib/tests/blackbox/test_whoami.py 2010-05-05 14:13:28 +0000
278@@ -20,6 +20,7 @@
279 import os
280
281 import bzrlib
282+from bzrlib import osutils
283 from bzrlib.branch import Branch
284 from bzrlib.tests.blackbox import ExternalBase
285
286@@ -111,3 +112,11 @@
287 self.assertEquals('"Branch Identity" does not seem to contain an '
288 'email address. This is allowed, but not '
289 'recommended.\n', display)
290+
291+ def test_whoami_not_set(self):
292+ """Ensure whoami error if username is not set.
293+ """
294+ osutils.set_or_unset_env('EMAIL', None)
295+ osutils.set_or_unset_env('BZR_EMAIL', None)
296+ out, err = self.run_bzr(['whoami'], 3)
297+ self.assertContainsRe(err, 'Unable to determine your name')
298
299=== modified file 'bzrlib/tests/test_osutils.py'
300--- bzrlib/tests/test_osutils.py 2010-04-27 07:52:08 +0000
301+++ bzrlib/tests/test_osutils.py 2010-05-05 14:13:28 +0000
302@@ -2018,3 +2018,14 @@
303 self.assertEquals(self.path, 'test_file')
304 self.assertEquals(self.uid, s.st_uid)
305 self.assertEquals(self.gid, s.st_gid)
306+
307+class TestGetuserUnicode(tests.TestCase):
308+
309+ def test_ascii_user(self):
310+ osutils.set_or_unset_env('LOGNAME', 'jrandom')
311+ self.assertEqual(u'jrandom', osutils.getuser_unicode())
312+
313+ def test_unicode_user(self):
314+ ue = osutils.get_user_encoding()
315+ osutils.set_or_unset_env('LOGNAME', u'jrandom\xb6'.encode(ue))
316+ self.assertEqual(u'jrandom\xb6', osutils.getuser_unicode())