Merge lp:~jelmer/brz/gpg into lp:brz

Proposed by Jelmer Vernooij
Status: Merged
Approved by: Jelmer Vernooij
Approved revision: no longer in the source branch.
Merge reported by: The Breezy Bot
Merged at revision: not available
Proposed branch: lp:~jelmer/brz/gpg
Merge into: lp:brz
Diff against target: 782 lines (+138/-241)
11 files modified
breezy/config.py (+0/-17)
breezy/errors.py (+0/-24)
breezy/gpg.py (+98/-81)
breezy/help_topics/en/configuration.txt (+0/-10)
breezy/tests/blackbox/test_log.py (+5/-2)
breezy/tests/features.py (+3/-1)
breezy/tests/test_commit.py (+1/-5)
breezy/tests/test_config.py (+0/-2)
breezy/tests/test_gpg.py (+24/-97)
breezy/tests/test_merge_directive.py (+0/-2)
doc/en/release-notes/brz-3.0.txt (+7/-0)
To merge this branch: bzr merge lp:~jelmer/brz/gpg
Reviewer Review Type Date Requested Status
Martin Packman Approve
Review via email: mp+326815@code.launchpad.net

This proposal supersedes a proposal from 2017-07-04.

Commit message

Switch to using python-gpg (part of gpgme) rather than deprecated python-gpgme.

Description of the change

Switch to using python-gpg (part of gpgme) rather than deprecated python-gpgme.

Also, switch to using gpgme for signing, while we're at it.

To post a comment you must log in.
Revision history for this message
Martin Packman (gz) wrote :

Changes look good mostly, a couple of comments inline.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'breezy/config.py'
2--- breezy/config.py 2017-06-15 01:07:35 +0000
3+++ breezy/config.py 2017-07-04 21:30:36 +0000
4@@ -27,7 +27,6 @@
5 email=Your Name <your@email.address>
6 check_signatures=require|ignore|check-available(default)
7 create_signatures=always|never|when-required(default)
8-gpg_signing_command=name-of-program
9 log_format=name-of-format
10 validate_signatures_in_log=true|false(default)
11 acceptable_keys=pattern1,pattern2
12@@ -798,10 +797,6 @@
13 else:
14 return None
15
16- def _gpg_signing_command(self):
17- """See Config.gpg_signing_command."""
18- return self._get_user_option('gpg_signing_command')
19-
20 def _log_format(self):
21 """See Config.log_format."""
22 return self._get_user_option('log_format')
23@@ -1314,10 +1309,6 @@
24 def remove_user_option(self, option_name, section_name=None):
25 self._get_branch_data_config().remove_option(option_name, section_name)
26
27- def _gpg_signing_command(self):
28- """See Config.gpg_signing_command."""
29- return self._get_safe_value('_gpg_signing_command')
30-
31 def _post_commit(self):
32 """See Config.post_commit."""
33 return self._get_safe_value('_post_commit')
34@@ -2706,14 +2697,6 @@
35 Option('email', override_from_env=['BRZ_EMAIL'], default=default_email,
36 help='The users identity'))
37 option_registry.register(
38- Option('gpg_signing_command',
39- default='gpg',
40- help="""\
41-Program to use use for creating signatures.
42-
43-This should support at least the -u and --clearsign options.
44-"""))
45-option_registry.register(
46 Option('gpg_signing_key',
47 default=None,
48 help="""\
49
50=== modified file 'breezy/errors.py'
51--- breezy/errors.py 2017-06-10 18:39:27 +0000
52+++ breezy/errors.py 2017-07-04 21:30:36 +0000
53@@ -1771,22 +1771,6 @@
54 self.username = username
55
56
57-class SigningFailed(BzrError):
58-
59- _fmt = 'Failed to GPG sign data with command "%(command_line)s"'
60-
61- def __init__(self, command_line):
62- BzrError.__init__(self, command_line=command_line)
63-
64-
65-class SignatureVerificationFailed(BzrError):
66-
67- _fmt = 'Failed to verify GPG signature data with error "%(error)s"'
68-
69- def __init__(self, error):
70- BzrError.__init__(self, error=error)
71-
72-
73 class DependencyNotPresent(BzrError):
74
75 _fmt = 'Unable to import library "%(library)s": %(error)s'
76@@ -1795,14 +1779,6 @@
77 BzrError.__init__(self, library=library, error=error)
78
79
80-class GpgmeNotInstalled(DependencyNotPresent):
81-
82- _fmt = 'python-gpgme is not installed, it is needed to verify signatures'
83-
84- def __init__(self, error):
85- DependencyNotPresent.__init__(self, 'gpgme', error)
86-
87-
88 class WorkingTreeNotRevision(BzrError):
89
90 _fmt = ("The working tree for %(basedir)s has changed since"
91
92=== modified file 'breezy/gpg.py'
93--- breezy/gpg.py 2017-06-05 20:48:31 +0000
94+++ breezy/gpg.py 2017-07-04 21:30:36 +0000
95@@ -29,7 +29,6 @@
96
97 from breezy import (
98 config,
99- errors,
100 trace,
101 ui,
102 )
103@@ -39,6 +38,9 @@
104 )
105 """)
106
107+from . import (
108+ errors,
109+ )
110 from .sixish import (
111 BytesIO,
112 )
113@@ -51,6 +53,30 @@
114 SIGNATURE_EXPIRED = 4
115
116
117+class GpgNotInstalled(errors.DependencyNotPresent):
118+
119+ _fmt = 'python-gpg is not installed, it is needed to verify signatures'
120+
121+ def __init__(self, error):
122+ errors.DependencyNotPresent.__init__(self, 'gpg', error)
123+
124+
125+class SigningFailed(errors.BzrError):
126+
127+ _fmt = 'Failed to GPG sign data: "%(error)s"'
128+
129+ def __init__(self, error):
130+ errors.BzrError.__init__(self, error=error)
131+
132+
133+class SignatureVerificationFailed(errors.BzrError):
134+
135+ _fmt = 'Failed to verify GPG signature data with error "%(error)s"'
136+
137+ def __init__(self, error):
138+ errors.BzrError.__init__(self, error=error)
139+
140+
141 def bulk_verify_signatures(repository, revids, strategy,
142 process_events_callback=None):
143 """Do verifications on a set of revisions
144@@ -101,10 +127,10 @@
145 """Real strategies take a configuration."""
146
147 def sign(self, content):
148- raise errors.SigningFailed('Signing is disabled.')
149+ raise SigningFailed('Signing is disabled.')
150
151 def verify(self, content, testament):
152- raise errors.SignatureVerificationFailed('Signature verification is \
153+ raise SignatureVerificationFailed('Signature verification is \
154 disabled.')
155
156 def set_acceptable_keys(self, command_line_input):
157@@ -162,11 +188,32 @@
158 def __init__(self, config_stack):
159 self._config_stack = config_stack
160 try:
161- import gpgme
162- self.context = gpgme.Context()
163+ import gpg
164+ self.context = gpg.Context()
165 except ImportError as error:
166 pass # can't use verify()
167
168+ self.context.signers = self._get_signing_keys()
169+
170+ def _get_signing_keys(self):
171+ import gpg
172+ keyname = self._config_stack.get('gpg_signing_key')
173+ if keyname:
174+ try:
175+ return [self.context.get_key(keyname)]
176+ except gpg.errors.KeyNotFound:
177+ pass
178+
179+ if keyname is None or keyname == 'default':
180+ # 'default' or not setting gpg_signing_key at all means we should
181+ # use the user email address
182+ keyname = config.extract_email_address(self._config_stack.get('email'))
183+ possible_keys = self.context.keylist(keyname, secret=True)
184+ try:
185+ return [next(possible_keys)]
186+ except StopIteration:
187+ return []
188+
189 @staticmethod
190 def verify_signatures_available():
191 """
192@@ -175,55 +222,24 @@
193 :return: boolean if this strategy can verify signatures
194 """
195 try:
196- import gpgme
197+ import gpg
198 return True
199 except ImportError as error:
200 return False
201
202- def _command_line(self):
203- key = self._config_stack.get('gpg_signing_key')
204- if key is None or key == 'default':
205- # 'default' or not setting gpg_signing_key at all means we should
206- # use the user email address
207- key = config.extract_email_address(self._config_stack.get('email'))
208- return [self._config_stack.get('gpg_signing_command'), '--clearsign',
209- '-u', key]
210-
211 def sign(self, content):
212+ import gpg
213 if isinstance(content, unicode):
214 raise errors.BzrBadParameterUnicode('content')
215- ui.ui_factory.clear_term()
216
217- preexec_fn = _set_gpg_tty
218- if sys.platform == 'win32':
219- # Win32 doesn't support preexec_fn, but wouldn't support TTY anyway.
220- preexec_fn = None
221+ plain_text = gpg.Data(content)
222 try:
223- process = subprocess.Popen(self._command_line(),
224- stdin=subprocess.PIPE,
225- stdout=subprocess.PIPE,
226- preexec_fn=preexec_fn)
227- try:
228- result = process.communicate(content)[0]
229- if process.returncode is None:
230- process.wait()
231- if process.returncode != 0:
232- raise errors.SigningFailed(self._command_line())
233- return result
234- except OSError as e:
235- if e.errno == errno.EPIPE:
236- raise errors.SigningFailed(self._command_line())
237- else:
238- raise
239- except ValueError:
240- # bad subprocess parameters, should never happen.
241- raise
242- except OSError as e:
243- if e.errno == errno.ENOENT:
244- # gpg is not installed
245- raise errors.SigningFailed(self._command_line())
246- else:
247- raise
248+ output, result = self.context.sign(
249+ plain_text, mode=gpg.constants.sig.mode.CLEAR)
250+ except gpg.errors.GPGMEError as error:
251+ raise SigningFailed(str(error))
252+
253+ return output
254
255 def verify(self, content, testament):
256 """Check content has a valid signature.
257@@ -234,74 +250,75 @@
258 :return: SIGNATURE_VALID or a failed SIGNATURE_ value, key uid if valid
259 """
260 try:
261- import gpgme
262+ import gpg
263 except ImportError as error:
264- raise errors.GpgmeNotInstalled(error)
265+ raise errors.GpgNotInstalled(error)
266
267- signature = BytesIO(content)
268- plain_output = BytesIO()
269+ signature = gpg.Data(content)
270+ sink = gpg.Data()
271 try:
272- result = self.context.verify(signature, None, plain_output)
273- except gpgme.GpgmeError as error:
274- raise errors.SignatureVerificationFailed(error[2])
275+ plain_output, result = self.context.verify(signature)
276+ except gpg.errors.BadSignatures as error:
277+ signatures = error.result.signatures
278+ fingerprint = signatures[0].fpr
279+ if signatures[0].summary & gpg.constants.SIGSUM_KEY_EXPIRED:
280+ expires = self.context.get_key(signatures[0].fpr).subkeys[0].expires
281+ if expires > signatures[0].timestamp:
282+ # The expired key was not expired at time of signing.
283+ # test_verify_expired_but_valid()
284+ return SIGNATURE_EXPIRED, fingerprint[-8:]
285+ else:
286+ # I can't work out how to create a test where the signature
287+ # was expired at the time of signing.
288+ return SIGNATURE_NOT_VALID, None
289+
290+ # GPG does not know this key.
291+ # test_verify_unknown_key()
292+ if signatures[0].summary & gpg.constants.SIGSUM_KEY_MISSING:
293+ return SIGNATURE_KEY_MISSING, fingerprint[-8:]
294+
295+ return SIGNATURE_NOT_VALID, None
296+ except gpg.errors.GPGMEError as error:
297+ raise SignatureVerificationFailed(error[2])
298
299 # No result if input is invalid.
300 # test_verify_invalid()
301- if len(result) == 0:
302+ if len(result.signatures) == 0:
303 return SIGNATURE_NOT_VALID, None
304 # User has specified a list of acceptable keys, check our result is in
305 # it. test_verify_unacceptable_key()
306- fingerprint = result[0].fpr
307+ fingerprint = result.signatures[0].fpr
308 if self.acceptable_keys is not None:
309 if not fingerprint in self.acceptable_keys:
310 return SIGNATURE_KEY_MISSING, fingerprint[-8:]
311 # Check the signature actually matches the testament.
312 # test_verify_bad_testament()
313- if testament != plain_output.getvalue():
314+ if testament != plain_output:
315 return SIGNATURE_NOT_VALID, None
316- # Yay gpgme set the valid bit.
317+ # Yay gpg set the valid bit.
318 # Can't write a test for this one as you can't set a key to be
319- # trusted using gpgme.
320- if result[0].summary & gpgme.SIGSUM_VALID:
321+ # trusted using gpg.
322+ if result.signatures[0].summary & gpg.constants.SIGSUM_VALID:
323 key = self.context.get_key(fingerprint)
324 name = key.uids[0].name
325 email = key.uids[0].email
326 return SIGNATURE_VALID, name + " <" + email + ">"
327 # Sigsum_red indicates a problem, unfortunatly I have not been able
328 # to write any tests which actually set this.
329- if result[0].summary & gpgme.SIGSUM_RED:
330+ if result.signatures[0].summary & gpg.constants.SIGSUM_RED:
331 return SIGNATURE_NOT_VALID, None
332- # GPG does not know this key.
333- # test_verify_unknown_key()
334- if result[0].summary & gpgme.SIGSUM_KEY_MISSING:
335- return SIGNATURE_KEY_MISSING, fingerprint[-8:]
336 # Summary isn't set if sig is valid but key is untrusted but if user
337 # has explicity set the key as acceptable we can validate it.
338- if result[0].summary == 0 and self.acceptable_keys is not None:
339+ if result.signatures[0].summary == 0 and self.acceptable_keys is not None:
340 if fingerprint in self.acceptable_keys:
341 # test_verify_untrusted_but_accepted()
342 return SIGNATURE_VALID, None
343 # test_verify_valid_but_untrusted()
344- if result[0].summary == 0 and self.acceptable_keys is None:
345- return SIGNATURE_NOT_VALID, None
346- if result[0].summary & gpgme.SIGSUM_KEY_EXPIRED:
347- expires = self.context.get_key(result[0].fpr).subkeys[0].expires
348- if expires > result[0].timestamp:
349- # The expired key was not expired at time of signing.
350- # test_verify_expired_but_valid()
351- return SIGNATURE_EXPIRED, fingerprint[-8:]
352- else:
353- # I can't work out how to create a test where the signature
354- # was expired at the time of signing.
355- return SIGNATURE_NOT_VALID, None
356- # A signature from a revoked key gets this.
357- # test_verify_revoked_signature()
358- if ((result[0].summary & gpgme.SIGSUM_SYS_ERROR
359- or result[0].status.strerror == 'Certificate revoked')):
360+ if result.signatures[0].summary == 0 and self.acceptable_keys is None:
361 return SIGNATURE_NOT_VALID, None
362 # Other error types such as revoked keys should (I think) be caught by
363 # SIGSUM_RED so anything else means something is buggy.
364- raise errors.SignatureVerificationFailed(
365+ raise SignatureVerificationFailed(
366 "Unknown GnuPG key verification result")
367
368 def set_acceptable_keys(self, command_line_input):
369
370=== modified file 'breezy/help_topics/en/configuration.txt'
371--- breezy/help_topics/en/configuration.txt 2017-06-12 23:25:04 +0000
372+++ breezy/help_topics/en/configuration.txt 2017-07-04 21:30:36 +0000
373@@ -498,16 +498,6 @@
374 This section only applies to the branch at this directory and not
375 branches below it.
376
377-gpg_signing_command
378-~~~~~~~~~~~~~~~~~~~
379-
380-(Default: "gpg"). Which program should be used to sign and check revisions.
381-For example::
382-
383- gpg_signing_command = /usr/bin/gnpg
384-
385-The specified command must accept the options "--clearsign" and "-u <email>".
386-
387 bzr_remote_path
388 ~~~~~~~~~~~~~~~
389
390
391=== modified file 'breezy/tests/blackbox/test_log.py'
392--- breezy/tests/blackbox/test_log.py 2017-06-22 01:52:28 +0000
393+++ breezy/tests/blackbox/test_log.py 2017-07-04 21:30:36 +0000
394@@ -17,6 +17,9 @@
395
396 """Black-box tests for brz log."""
397
398+from __future__ import absolute_import
399+
400+
401 import os
402
403 from breezy import (
404@@ -474,7 +477,7 @@
405 class TestLogSignatures(TestLog):
406
407 def test_log_with_signatures(self):
408- self.requireFeature(features.gpgme)
409+ self.requireFeature(features.gpg)
410
411 tree = self.make_linear_branch(format='dirstate-tags')
412
413@@ -482,7 +485,7 @@
414 self.assertTrue('signature: no signature' in log)
415
416 def test_log_without_signatures(self):
417- self.requireFeature(features.gpgme)
418+ self.requireFeature(features.gpg)
419
420 tree = self.make_linear_branch(format='dirstate-tags')
421
422
423=== modified file 'breezy/tests/features.py'
424--- breezy/tests/features.py 2017-06-17 12:58:29 +0000
425+++ breezy/tests/features.py 2017-07-04 21:30:36 +0000
426@@ -17,6 +17,8 @@
427 """A collection of commonly used 'Features' to optionally run tests.
428 """
429
430+from __future__ import absolute_import
431+
432 import os
433 import subprocess
434 import stat
435@@ -373,7 +375,7 @@
436 not_running_as_root = _NotRunningAsRoot()
437
438 apport = ModuleAvailableFeature('apport')
439-gpgme = ModuleAvailableFeature('gpgme')
440+gpg = ModuleAvailableFeature('gpg')
441 lzma = ModuleAvailableFeature('lzma')
442 meliae = ModuleAvailableFeature('meliae.scanner')
443 paramiko = ModuleAvailableFeature('paramiko')
444
445=== modified file 'breezy/tests/test_commit.py'
446--- breezy/tests/test_commit.py 2017-06-18 22:23:02 +0000
447+++ breezy/tests/test_commit.py 2017-07-04 21:30:36 +0000
448@@ -33,7 +33,6 @@
449 from ..errors import (
450 PointlessCommit,
451 BzrError,
452- SigningFailed,
453 LockContention,
454 )
455 from . import (
456@@ -53,7 +52,6 @@
457
458 def __init__(self):
459 super(MustSignConfig, self).__init__('''
460-gpg_signing_command=cat -
461 create_signatures=always
462 ''')
463
464@@ -431,7 +429,6 @@
465 # monkey patch gpg signing mechanism
466 breezy.gpg.GPGStrategy = breezy.gpg.LoopbackGPGStrategy
467 conf = config.MemoryStack('''
468-gpg_signing_command=cat -
469 create_signatures=always
470 ''')
471 commit.Commit(config_stack=conf).commit(
472@@ -457,10 +454,9 @@
473 # monkey patch gpg signing mechanism
474 breezy.gpg.GPGStrategy = breezy.gpg.DisabledGPGStrategy
475 conf = config.MemoryStack('''
476-gpg_signing_command=cat -
477 create_signatures=always
478 ''')
479- self.assertRaises(SigningFailed,
480+ self.assertRaises(breezy.gpg.SigningFailed,
481 commit.Commit(config_stack=conf).commit,
482 message="base",
483 allow_pointless=True,
484
485=== modified file 'breezy/tests/test_config.py'
486--- breezy/tests/test_config.py 2017-06-12 23:25:04 +0000
487+++ breezy/tests/test_config.py 2017-07-04 21:30:36 +0000
488@@ -176,7 +176,6 @@
489 email=Erik B\u00e5gfors <erik@bagfors.nu>
490 editor=vim
491 change_editor=vimdiff -of @new_path @old_path
492-gpg_signing_command=gnome-gpg
493 gpg_signing_key=DD4D5088
494 log_format=short
495 validate_signatures_in_log=true
496@@ -232,7 +231,6 @@
497 # test trailing / matching with no children
498 [/a/]
499 check_signatures=check-available
500-gpg_signing_command=false
501 gpg_signing_key=default
502 user_local_option=local
503 # test trailing / matching
504
505=== modified file 'breezy/tests/test_gpg.py'
506--- breezy/tests/test_gpg.py 2017-05-22 00:56:52 +0000
507+++ breezy/tests/test_gpg.py 2017-07-04 21:30:36 +0000
508@@ -43,90 +43,17 @@
509 if content is None:
510 content = '''
511 gpg_signing_key=amy@example.com
512-gpg_signing_command=false'''
513+'''
514 super(FakeConfig, self).__init__(content)
515
516
517-class TestCommandLine(tests.TestCase):
518-
519- def setUp(self):
520- super(TestCommandLine, self).setUp()
521- self.my_gpg = gpg.GPGStrategy(FakeConfig())
522-
523- def test_signing_command_line(self):
524- self.assertEqual(['false', '--clearsign', '-u', 'amy@example.com'],
525- self.my_gpg._command_line())
526-
527- def test_signing_command_line_from_default(self):
528- # Using 'default' for gpg_signing_key will use the mail part of 'email'
529- my_gpg = gpg.GPGStrategy(FakeConfig('''
530-email=Amy <amy@example.com>
531-gpg_signing_key=default
532-gpg_signing_command=false'''))
533- self.assertEqual(['false', '--clearsign', '-u', 'amy@example.com'],
534- my_gpg._command_line())
535-
536- def test_signing_command_line_from_email(self):
537- # Not setting gpg_signing_key will use the mail part of 'email'
538- my_gpg = gpg.GPGStrategy(FakeConfig('''
539-email=Amy <amy@example.com>
540-gpg_signing_command=false'''))
541- self.assertEqual(['false', '--clearsign', '-u', 'amy@example.com'],
542- my_gpg._command_line())
543-
544- def test_checks_return_code(self):
545- # This test needs a unix like platform - one with 'false' to run.
546- # if you have one, please make this work :)
547- self.assertRaises(errors.SigningFailed, self.my_gpg.sign, 'content')
548-
549- def assertProduces(self, content):
550- # This needs a 'cat' command or similar to work.
551- if sys.platform == 'win32':
552- # Windows doesn't come with cat, and we don't require it
553- # so lets try using python instead.
554- # But stupid windows and line-ending conversions.
555- # It is too much work to make sys.stdout be in binary mode.
556- # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/65443
557- self.my_gpg._command_line = lambda:[sys.executable, '-c',
558- 'import sys; sys.stdout.write(sys.stdin.read())']
559- new_content = content.replace('\n', '\r\n')
560-
561- self.assertEqual(new_content, self.my_gpg.sign(content))
562- else:
563- self.my_gpg._command_line = lambda:['cat', '-']
564- self.assertEqual(content, self.my_gpg.sign(content))
565-
566- def test_returns_output(self):
567- content = "some content\nwith newlines\n"
568- self.assertProduces(content)
569-
570- def test_clears_progress(self):
571- content = "some content\nwith newlines\n"
572- old_clear_term = ui.ui_factory.clear_term
573- clear_term_called = []
574- def clear_term():
575- old_clear_term()
576- clear_term_called.append(True)
577- ui.ui_factory.clear_term = clear_term
578- try:
579- self.assertProduces(content)
580- finally:
581- ui.ui_factory.clear_term = old_clear_term
582- self.assertEqual([True], clear_term_called)
583-
584- def test_aborts_on_unicode(self):
585- """You can't sign Unicode text; it must be encoded first."""
586- self.assertRaises(errors.BzrBadParameterUnicode,
587- self.assertProduces, u'foo')
588-
589-
590 class TestVerify(TestCase):
591
592 def import_keys(self):
593- import gpgme
594- context = gpgme.Context()
595+ import gpg
596+ context = gpg.Context()
597
598- key = BytesIO(b"""-----BEGIN PGP PUBLIC KEY BLOCK-----
599+ key = gpg.Data(b"""-----BEGIN PGP PUBLIC KEY BLOCK-----
600 Version: GnuPG v1.4.11 (GNU/Linux)
601
602 mQENBE343IgBCADwzPW7kmKb2bjB+UU+1ER/ABMZspvtoZMPusUw7bk6coXHF/0W
603@@ -158,7 +85,7 @@
604 -----END PGP PUBLIC KEY BLOCK-----
605 """)
606
607- secret_key = BytesIO(b"""-----BEGIN PGP PRIVATE KEY BLOCK-----
608+ secret_key = gpg.Data(b"""-----BEGIN PGP PRIVATE KEY BLOCK-----
609 Version: GnuPG v1.4.11 (GNU/Linux)
610
611 lQOYBE343IgBCADwzPW7kmKb2bjB+UU+1ER/ABMZspvtoZMPusUw7bk6coXHF/0W
612@@ -217,7 +144,7 @@
613 -----END PGP PRIVATE KEY BLOCK-----
614 """)
615
616- revoked_key = BytesIO(b"""-----BEGIN PGP PUBLIC KEY BLOCK-----
617+ revoked_key = gpg.Data(b"""-----BEGIN PGP PUBLIC KEY BLOCK-----
618 Version: GnuPG v1.4.11 (GNU/Linux)
619
620 mI0ETjlW5gEEAOb/6P+TVM59E897wRtatxys2BhsHCXM4T7xjIiANfDwejDdifqh
621@@ -242,7 +169,7 @@
622 -----END PGP PUBLIC KEY BLOCK-----
623 """)
624
625- expired_key = BytesIO(b"""-----BEGIN PGP PUBLIC KEY BLOCK-----
626+ expired_key = gpg.Data(b"""-----BEGIN PGP PUBLIC KEY BLOCK-----
627 Version: GnuPG v1.4.11 (GNU/Linux)
628
629 mI0ETjZ6PAEEALkR4GcFQidCCxV7pgQwQd5MZua0YO2l92fVqHX+PhnZ6egCLKdD
630@@ -263,14 +190,14 @@
631 =p0gt
632 -----END PGP PUBLIC KEY BLOCK-----
633 """)
634- context.import_(key)
635- context.import_(secret_key)
636- context.import_(revoked_key)
637- context.import_(expired_key)
638+ context.op_import(key)
639+ context.op_import(secret_key)
640+ context.op_import(revoked_key)
641+ context.op_import(expired_key)
642
643 def test_verify_untrusted_but_accepted(self):
644 #untrusted by gpg but listed as acceptable_keys by user
645- self.requireFeature(features.gpgme)
646+ self.requireFeature(features.gpg)
647 self.import_keys()
648
649 content = """-----BEGIN PGP SIGNED MESSAGE-----
650@@ -301,7 +228,7 @@
651 plain))
652
653 def test_verify_unacceptable_key(self):
654- self.requireFeature(features.gpgme)
655+ self.requireFeature(features.gpg)
656 self.import_keys()
657
658 content = """-----BEGIN PGP SIGNED MESSAGE-----
659@@ -332,7 +259,7 @@
660 my_gpg.verify(content, plain))
661
662 def test_verify_valid_but_untrusted(self):
663- self.requireFeature(features.gpgme)
664+ self.requireFeature(features.gpg)
665 self.import_keys()
666
667 content = """-----BEGIN PGP SIGNED MESSAGE-----
668@@ -362,7 +289,7 @@
669 plain))
670
671 def test_verify_bad_testament(self):
672- self.requireFeature(features.gpgme)
673+ self.requireFeature(features.gpg)
674 self.import_keys()
675
676 content = """-----BEGIN PGP SIGNED MESSAGE-----
677@@ -394,7 +321,7 @@
678
679
680 def test_verify_revoked_signature(self):
681- self.requireFeature(features.gpgme)
682+ self.requireFeature(features.gpg)
683 self.import_keys()
684
685 content = """-----BEGIN PGP SIGNED MESSAGE-----
686@@ -418,7 +345,7 @@
687 plain))
688
689 def test_verify_invalid(self):
690- self.requireFeature(features.gpgme)
691+ self.requireFeature(features.gpg)
692 self.import_keys()
693 content = """-----BEGIN PGP SIGNED MESSAGE-----
694 Hash: SHA1
695@@ -443,7 +370,7 @@
696 my_gpg.verify(content, plain))
697
698 def test_verify_expired_but_valid(self):
699- self.requireFeature(features.gpgme)
700+ self.requireFeature(features.gpg)
701 self.import_keys()
702 content = """-----BEGIN PGP SIGNED MESSAGE-----
703 Hash: SHA1
704@@ -470,7 +397,7 @@
705 my_gpg.verify(content, plain))
706
707 def test_verify_unknown_key(self):
708- self.requireFeature(features.gpgme)
709+ self.requireFeature(features.gpg)
710 self.import_keys()
711 content = """-----BEGIN PGP SIGNED MESSAGE-----
712 Hash: SHA1
713@@ -494,7 +421,7 @@
714 my_gpg.verify(content, plain))
715
716 def test_set_acceptable_keys(self):
717- self.requireFeature(features.gpgme)
718+ self.requireFeature(features.gpg)
719 self.import_keys()
720 my_gpg = gpg.GPGStrategy(FakeConfig())
721 my_gpg.set_acceptable_keys("bazaar@example.com")
722@@ -502,7 +429,7 @@
723 [u'B5DEED5FCB15DAE6ECEF919587681B1EE3080E45'])
724
725 def test_set_acceptable_keys_from_config(self):
726- self.requireFeature(features.gpgme)
727+ self.requireFeature(features.gpg)
728 self.import_keys()
729 my_gpg = gpg.GPGStrategy(FakeConfig(
730 'acceptable_keys=bazaar@example.com'))
731@@ -511,7 +438,7 @@
732 [u'B5DEED5FCB15DAE6ECEF919587681B1EE3080E45'])
733
734 def test_set_acceptable_keys_unknown(self):
735- self.requireFeature(features.gpgme)
736+ self.requireFeature(features.gpg)
737 my_gpg = gpg.GPGStrategy(FakeConfig())
738 self.notes = []
739 def note(*args):
740@@ -526,10 +453,10 @@
741 class TestDisabled(TestCase):
742
743 def test_sign(self):
744- self.assertRaises(errors.SigningFailed,
745+ self.assertRaises(gpg.SigningFailed,
746 gpg.DisabledGPGStrategy(None).sign, 'content')
747
748 def test_verify(self):
749- self.assertRaises(errors.SignatureVerificationFailed,
750+ self.assertRaises(gpg.SignatureVerificationFailed,
751 gpg.DisabledGPGStrategy(None).verify, 'content',
752 'testament')
753
754=== modified file 'breezy/tests/test_merge_directive.py'
755--- breezy/tests/test_merge_directive.py 2017-06-10 00:17:06 +0000
756+++ breezy/tests/test_merge_directive.py 2017-07-04 21:30:36 +0000
757@@ -464,8 +464,6 @@
758 class FakeBranch(object):
759 def get_config_stack(self):
760 return self
761- def gpg_signing_command(self):
762- return 'loopback'
763 md = self.make_merge_directive('example:', 'sha', time, timezone,
764 'http://example.com', source_branch="http://example.org",
765 patch='booga', patch_type='diff')
766
767=== modified file 'doc/en/release-notes/brz-3.0.txt'
768--- doc/en/release-notes/brz-3.0.txt 2017-06-22 01:52:28 +0000
769+++ doc/en/release-notes/brz-3.0.txt 2017-07-04 21:30:36 +0000
770@@ -56,6 +56,13 @@
771 This simplifies ``brz init --help``.
772 (Neil Martinsen-Burrell, #330494)
773
774+ * ``python-gpg`` is now used for checking GPG signatures rather than
775+ ``python-gpgme``. (Jelmer Vernooij, #1702308)
776+
777+ * ``python-gpg`` is now used for signing commits, rather than shelling
778+ out to the gnupg command. The ``gpg_signing_command`` option has been
779+ removed. (Jelmer Vernooij, #847388)
780+
781 New Features
782 ************
783

Subscribers

People subscribed via source and target branches