Merge lp:~abentley/bzr/send-hookage into lp:~bzr/bzr/trunk-old
- send-hookage
- Merge into trunk-old
Proposed by
Aaron Bentley
Status: | Merged |
---|---|
Merged at revision: | not available |
Proposed branch: | lp:~abentley/bzr/send-hookage |
Merge into: | lp:~bzr/bzr/trunk-old |
Diff against target: | 289 lines |
To merge this branch: | bzr merge lp:~abentley/bzr/send-hookage |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Robert Collins (community) | Approve | ||
Review via email: mp+6436@code.launchpad.net |
Commit message
Description of the change
To post a comment you must log in.
Revision history for this message
Aaron Bentley (abentley) wrote : | # |
Revision history for this message
Robert Collins (lifeless) wrote : | # |
review approve
review:
Approve
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'NEWS' | |||
2 | --- NEWS 2009-05-11 06:44:30 +0000 | |||
3 | +++ NEWS 2009-05-11 20:35:15 +0000 | |||
4 | @@ -150,6 +150,9 @@ | |||
5 | 150 | broken. This is mainly for testing that lock/unlock are | 150 | broken. This is mainly for testing that lock/unlock are |
6 | 151 | balanced in tests. (Vincent Ladeuil) | 151 | balanced in tests. (Vincent Ladeuil) |
7 | 152 | 152 | ||
8 | 153 | * New MergeDirective hook 'merge_request_body' allows hooks to supply or | ||
9 | 154 | alter a body for the message produced by ``bzr send``. | ||
10 | 155 | |||
11 | 153 | * New smart server verb ``BzrDir.initialize_ex`` which implements a | 156 | * New smart server verb ``BzrDir.initialize_ex`` which implements a |
12 | 154 | refactoring to the core of clone allowing less round trips on new | 157 | refactoring to the core of clone allowing less round trips on new |
13 | 155 | branches. (Robert Collins) | 158 | branches. (Robert Collins) |
14 | 156 | 159 | ||
15 | === modified file 'bzrlib/builtins.py' | |||
16 | --- bzrlib/builtins.py 2009-05-07 05:08:46 +0000 | |||
17 | +++ bzrlib/builtins.py 2009-05-11 20:35:15 +0000 | |||
18 | @@ -4866,13 +4866,7 @@ | |||
19 | 4866 | def _run(self, submit_branch, revision, public_branch, remember, format, | 4866 | def _run(self, submit_branch, revision, public_branch, remember, format, |
20 | 4867 | no_bundle, no_patch, output, from_, mail_to, message, body): | 4867 | no_bundle, no_patch, output, from_, mail_to, message, body): |
21 | 4868 | from bzrlib.revision import NULL_REVISION | 4868 | from bzrlib.revision import NULL_REVISION |
29 | 4869 | branch = Branch.open_containing(from_)[0] | 4869 | tree, branch = bzrdir.BzrDir.open_containing_tree_or_branch(from_)[:2] |
23 | 4870 | if output is None: | ||
24 | 4871 | outfile = cStringIO.StringIO() | ||
25 | 4872 | elif output == '-': | ||
26 | 4873 | outfile = self.outf | ||
27 | 4874 | else: | ||
28 | 4875 | outfile = open(output, 'wb') | ||
30 | 4876 | # we may need to write data into branch's repository to calculate | 4870 | # we may need to write data into branch's repository to calculate |
31 | 4877 | # the data to send. | 4871 | # the data to send. |
32 | 4878 | branch.lock_write() | 4872 | branch.lock_write() |
33 | @@ -4959,21 +4953,20 @@ | |||
34 | 4959 | public_branch=public_branch, patch_type=patch_type, | 4953 | public_branch=public_branch, patch_type=patch_type, |
35 | 4960 | message=message) | 4954 | message=message) |
36 | 4961 | 4955 | ||
37 | 4962 | outfile.writelines(directive.to_lines()) | ||
38 | 4963 | if output is None: | 4956 | if output is None: |
42 | 4964 | subject = '[MERGE] ' | 4957 | directive.compose_merge_request(mail_client, mail_to, body, |
43 | 4965 | if message is not None: | 4958 | branch, tree) |
44 | 4966 | subject += message | 4959 | else: |
45 | 4960 | if output == '-': | ||
46 | 4961 | outfile = self.outf | ||
47 | 4967 | else: | 4962 | else: |
54 | 4968 | revision = branch.repository.get_revision(revision_id) | 4963 | outfile = open(output, 'wb') |
55 | 4969 | subject += revision.get_summary() | 4964 | try: |
56 | 4970 | basename = directive.get_disk_name(branch) | 4965 | outfile.writelines(directive.to_lines()) |
57 | 4971 | mail_client.compose_merge_request(mail_to, subject, | 4966 | finally: |
58 | 4972 | outfile.getvalue(), | 4967 | if outfile is not self.outf: |
59 | 4973 | basename, body) | 4968 | outfile.close() |
60 | 4974 | finally: | 4969 | finally: |
61 | 4975 | if output != '-': | ||
62 | 4976 | outfile.close() | ||
63 | 4977 | branch.unlock() | 4970 | branch.unlock() |
64 | 4978 | 4971 | ||
65 | 4979 | 4972 | ||
66 | 4980 | 4973 | ||
67 | === modified file 'bzrlib/hooks.py' | |||
68 | --- bzrlib/hooks.py 2009-04-29 15:01:40 +0000 | |||
69 | +++ bzrlib/hooks.py 2009-05-11 20:35:15 +0000 | |||
70 | @@ -56,6 +56,9 @@ | |||
71 | 56 | known_hooks.register_lazy( | 56 | known_hooks.register_lazy( |
72 | 57 | ('bzrlib.version_info_formats.format_rio', 'RioVersionInfoBuilder.hooks'), | 57 | ('bzrlib.version_info_formats.format_rio', 'RioVersionInfoBuilder.hooks'), |
73 | 58 | 'bzrlib.version_info_formats.format_rio', 'RioVersionInfoBuilderHooks') | 58 | 'bzrlib.version_info_formats.format_rio', 'RioVersionInfoBuilderHooks') |
74 | 59 | known_hooks.register_lazy( | ||
75 | 60 | ('bzrlib.merge_directive', '_BaseMergeDirective.hooks'), | ||
76 | 61 | 'bzrlib.merge_directive', 'MergeDirectiveHooks') | ||
77 | 59 | 62 | ||
78 | 60 | 63 | ||
79 | 61 | def known_hooks_key_to_object((module_name, member_name)): | 64 | def known_hooks_key_to_object((module_name, member_name)): |
80 | @@ -178,8 +181,8 @@ | |||
81 | 178 | :ivar introduced: A version tuple specifying what version the hook was | 181 | :ivar introduced: A version tuple specifying what version the hook was |
82 | 179 | introduced in. None indicates an unknown version. | 182 | introduced in. None indicates an unknown version. |
83 | 180 | :ivar deprecated: A version tuple specifying what version the hook was | 183 | :ivar deprecated: A version tuple specifying what version the hook was |
86 | 181 | deprecated or superceded in. None indicates that the hook is not | 184 | deprecated or superseded in. None indicates that the hook is not |
87 | 182 | superceded or deprecated. If the hook is superceded then the doc | 185 | superseded or deprecated. If the hook is superseded then the doc |
88 | 183 | should describe the recommended replacement hook to register for. | 186 | should describe the recommended replacement hook to register for. |
89 | 184 | :ivar doc: The docs for using the hook. | 187 | :ivar doc: The docs for using the hook. |
90 | 185 | """ | 188 | """ |
91 | 186 | 189 | ||
92 | === modified file 'bzrlib/merge_directive.py' | |||
93 | --- bzrlib/merge_directive.py 2009-05-06 05:36:28 +0000 | |||
94 | +++ bzrlib/merge_directive.py 2009-05-11 20:35:15 +0000 | |||
95 | @@ -23,11 +23,13 @@ | |||
96 | 23 | diff, | 23 | diff, |
97 | 24 | errors, | 24 | errors, |
98 | 25 | gpg, | 25 | gpg, |
99 | 26 | hooks, | ||
100 | 26 | registry, | 27 | registry, |
101 | 27 | revision as _mod_revision, | 28 | revision as _mod_revision, |
102 | 28 | rio, | 29 | rio, |
103 | 29 | testament, | 30 | testament, |
104 | 30 | timestamp, | 31 | timestamp, |
105 | 32 | trace, | ||
106 | 31 | ) | 33 | ) |
107 | 32 | from bzrlib.bundle import ( | 34 | from bzrlib.bundle import ( |
108 | 33 | serializer as bundle_serializer, | 35 | serializer as bundle_serializer, |
109 | @@ -35,8 +37,37 @@ | |||
110 | 35 | from bzrlib.email_message import EmailMessage | 37 | from bzrlib.email_message import EmailMessage |
111 | 36 | 38 | ||
112 | 37 | 39 | ||
113 | 40 | class MergeRequestBodyParams(object): | ||
114 | 41 | """Parameter object for the merge_request_body hook.""" | ||
115 | 42 | |||
116 | 43 | def __init__(self, body, orig_body, directive, to, basename, subject, | ||
117 | 44 | branch, tree=None): | ||
118 | 45 | self.body = body | ||
119 | 46 | self.orig_body = orig_body | ||
120 | 47 | self.directive = directive | ||
121 | 48 | self.branch = branch | ||
122 | 49 | self.tree = tree | ||
123 | 50 | self.to = to | ||
124 | 51 | self.basename = basename | ||
125 | 52 | self.subject = subject | ||
126 | 53 | |||
127 | 54 | |||
128 | 55 | class MergeDirectiveHooks(hooks.Hooks): | ||
129 | 56 | """Hooks for MergeDirective classes.""" | ||
130 | 57 | |||
131 | 58 | def __init__(self): | ||
132 | 59 | hooks.Hooks.__init__(self) | ||
133 | 60 | self.create_hook(hooks.HookPoint('merge_request_body', | ||
134 | 61 | "Called with a MergeRequestBodyParams when a body is needed for" | ||
135 | 62 | " a merge request. Callbacks must return a body. If more" | ||
136 | 63 | " than one callback is registered, the output of one callback is" | ||
137 | 64 | " provided to the next.", (1, 15, 0), False)) | ||
138 | 65 | |||
139 | 66 | |||
140 | 38 | class _BaseMergeDirective(object): | 67 | class _BaseMergeDirective(object): |
141 | 39 | 68 | ||
142 | 69 | hooks = MergeDirectiveHooks() | ||
143 | 70 | |||
144 | 40 | def __init__(self, revision_id, testament_sha1, time, timezone, | 71 | def __init__(self, revision_id, testament_sha1, time, timezone, |
145 | 41 | target_branch, patch=None, source_branch=None, message=None, | 72 | target_branch, patch=None, source_branch=None, message=None, |
146 | 42 | bundle=None): | 73 | bundle=None): |
147 | @@ -240,6 +271,37 @@ | |||
148 | 240 | target_repo.fetch(source_branch.repository, self.revision_id) | 271 | target_repo.fetch(source_branch.repository, self.revision_id) |
149 | 241 | return self.revision_id | 272 | return self.revision_id |
150 | 242 | 273 | ||
151 | 274 | def compose_merge_request(self, mail_client, to, body, branch, tree=None): | ||
152 | 275 | """Compose a request to merge this directive. | ||
153 | 276 | |||
154 | 277 | :param mail_client: The mail client to use for composing this request. | ||
155 | 278 | :param to: The address to compose the request to. | ||
156 | 279 | :param branch: The Branch that was used to produce this directive. | ||
157 | 280 | :param tree: The Tree (if any) for the Branch used to produce this | ||
158 | 281 | directive. | ||
159 | 282 | """ | ||
160 | 283 | basename = self.get_disk_name(branch) | ||
161 | 284 | subject = '[MERGE] ' | ||
162 | 285 | if self.message is not None: | ||
163 | 286 | subject += self.message | ||
164 | 287 | else: | ||
165 | 288 | revision = branch.repository.get_revision(self.revision_id) | ||
166 | 289 | subject += revision.get_summary() | ||
167 | 290 | if getattr(mail_client, 'supports_body', False): | ||
168 | 291 | orig_body = body | ||
169 | 292 | for hook in self.hooks['merge_request_body']: | ||
170 | 293 | params = MergeRequestBodyParams(body, orig_body, self, | ||
171 | 294 | to, basename, subject, branch, | ||
172 | 295 | tree) | ||
173 | 296 | body = hook(params) | ||
174 | 297 | elif len(self.hooks['merge_request_body']) > 0: | ||
175 | 298 | trace.warning('Cannot run merge_request_body hooks because mail' | ||
176 | 299 | ' client %s does not support message bodies.', | ||
177 | 300 | mail_client.__class__.__name__) | ||
178 | 301 | mail_client.compose_merge_request(to, subject, | ||
179 | 302 | ''.join(self.to_lines()), | ||
180 | 303 | basename, body) | ||
181 | 304 | |||
182 | 243 | 305 | ||
183 | 244 | class MergeDirective(_BaseMergeDirective): | 306 | class MergeDirective(_BaseMergeDirective): |
184 | 245 | 307 | ||
185 | 246 | 308 | ||
186 | === modified file 'bzrlib/tests/test_merge_directive.py' | |||
187 | --- bzrlib/tests/test_merge_directive.py 2009-03-23 14:59:43 +0000 | |||
188 | +++ bzrlib/tests/test_merge_directive.py 2009-05-11 20:35:15 +0000 | |||
189 | @@ -19,8 +19,10 @@ | |||
190 | 19 | from bzrlib import ( | 19 | from bzrlib import ( |
191 | 20 | errors, | 20 | errors, |
192 | 21 | gpg, | 21 | gpg, |
193 | 22 | mail_client, | ||
194 | 22 | merge_directive, | 23 | merge_directive, |
195 | 23 | tests, | 24 | tests, |
196 | 25 | trace, | ||
197 | 24 | ) | 26 | ) |
198 | 25 | 27 | ||
199 | 26 | 28 | ||
200 | @@ -697,3 +699,89 @@ | |||
201 | 697 | self.assertEqual('booga', md.patch) | 699 | self.assertEqual('booga', md.patch) |
202 | 698 | self.assertEqual('diff', md.patch_type) | 700 | self.assertEqual('diff', md.patch_type) |
203 | 699 | self.assertEqual('Hi mom!', md.message) | 701 | self.assertEqual('Hi mom!', md.message) |
204 | 702 | |||
205 | 703 | |||
206 | 704 | class TestHook(object): | ||
207 | 705 | """Hook callback for test purposes.""" | ||
208 | 706 | |||
209 | 707 | def __init__(self, result=None): | ||
210 | 708 | self.calls = [] | ||
211 | 709 | self.result = result | ||
212 | 710 | |||
213 | 711 | def __call__(self, params): | ||
214 | 712 | self.calls.append(params) | ||
215 | 713 | return self.result | ||
216 | 714 | |||
217 | 715 | |||
218 | 716 | class HookMailClient(mail_client.MailClient): | ||
219 | 717 | """Mail client for testing hooks.""" | ||
220 | 718 | |||
221 | 719 | def __init__(self, config): | ||
222 | 720 | self.body = None | ||
223 | 721 | self.config = config | ||
224 | 722 | |||
225 | 723 | def compose(self, prompt, to, subject, attachment, mime_subtype, | ||
226 | 724 | extension, basename=None, body=None): | ||
227 | 725 | self.body = body | ||
228 | 726 | |||
229 | 727 | |||
230 | 728 | class TestBodyHook(tests.TestCaseWithTransport): | ||
231 | 729 | |||
232 | 730 | def compose_with_hooks(self, test_hooks, supports_body=True): | ||
233 | 731 | client = HookMailClient({}) | ||
234 | 732 | client.supports_body = supports_body | ||
235 | 733 | for test_hook in test_hooks: | ||
236 | 734 | merge_directive.MergeDirective.hooks.install_named_hook( | ||
237 | 735 | 'merge_request_body', test_hook, 'test') | ||
238 | 736 | tree = self.make_branch_and_tree('foo') | ||
239 | 737 | tree.commit('foo') | ||
240 | 738 | directive = merge_directive.MergeDirective2( | ||
241 | 739 | tree.branch.last_revision(), 'sha', 0, 0, 'sha', | ||
242 | 740 | source_branch=tree.branch.base, | ||
243 | 741 | base_revision_id=tree.branch.last_revision(), | ||
244 | 742 | message='This code rox') | ||
245 | 743 | directive.compose_merge_request(client, 'jrandom@example.com', | ||
246 | 744 | None, tree.branch) | ||
247 | 745 | return client, directive | ||
248 | 746 | |||
249 | 747 | def test_no_supports_body(self): | ||
250 | 748 | test_hook = TestHook('foo') | ||
251 | 749 | old_warn = trace.warning | ||
252 | 750 | warnings = [] | ||
253 | 751 | def warn(*args): | ||
254 | 752 | warnings.append(args) | ||
255 | 753 | trace.warning = warn | ||
256 | 754 | try: | ||
257 | 755 | client, directive = self.compose_with_hooks([test_hook], | ||
258 | 756 | supports_body=False) | ||
259 | 757 | finally: | ||
260 | 758 | trace.warning = old_warn | ||
261 | 759 | self.assertEqual(0, len(test_hook.calls)) | ||
262 | 760 | self.assertEqual(('Cannot run merge_request_body hooks because mail' | ||
263 | 761 | ' client %s does not support message bodies.', | ||
264 | 762 | 'HookMailClient'), warnings[0]) | ||
265 | 763 | |||
266 | 764 | def test_body_hook(self): | ||
267 | 765 | test_hook = TestHook('foo') | ||
268 | 766 | client, directive = self.compose_with_hooks([test_hook]) | ||
269 | 767 | self.assertEqual(1, len(test_hook.calls)) | ||
270 | 768 | self.assertEqual('foo', client.body) | ||
271 | 769 | params = test_hook.calls[0] | ||
272 | 770 | self.assertIsInstance(params, | ||
273 | 771 | merge_directive.MergeRequestBodyParams) | ||
274 | 772 | self.assertIs(None, params.body) | ||
275 | 773 | self.assertIs(None, params.orig_body) | ||
276 | 774 | self.assertEqual('jrandom@example.com', params.to) | ||
277 | 775 | self.assertEqual('[MERGE] This code rox', params.subject) | ||
278 | 776 | self.assertEqual(directive, params.directive) | ||
279 | 777 | self.assertEqual('foo-1', params.basename) | ||
280 | 778 | |||
281 | 779 | def test_body_hook_chaining(self): | ||
282 | 780 | test_hook1 = TestHook('foo') | ||
283 | 781 | test_hook2 = TestHook('bar') | ||
284 | 782 | client = self.compose_with_hooks([test_hook1, test_hook2])[0] | ||
285 | 783 | self.assertEqual(None, test_hook1.calls[0].body) | ||
286 | 784 | self.assertEqual(None, test_hook1.calls[0].orig_body) | ||
287 | 785 | self.assertEqual('foo', test_hook2.calls[0].body) | ||
288 | 786 | self.assertEqual(None, test_hook2.calls[0].orig_body) | ||
289 | 787 | self.assertEqual('bar', client.body) |
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
Hi all,
This patch adds a new MergeDirective hook to calculate the message body.
This helps projects with a specific review template, like Launchpad, to
ensure the template is considered for all reviews.
It does this by moving the mail_client- specific functionality of compose_ merge_request method.
cmd_send into the new MergeDirective.
There is a similarly-named method on MailClient, but this receives a
MergeDirective that has already been serialized, and for the hook, live
objects are more useful.
As well as the Branch and directive, this method also takes an optional
Tree parameter. This will typically be the WorkingTree that the user
ran 'bzr send' against. This may be useful for hooks that want to
examine the working tree to generate the body. Launchpad, for example,
runs 'lint' against files which have changed.
Aaron enigmail. mozdev. org
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.9 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://
iEYEARECAAYFAko Ih8cACgkQ0F+ nu1YWqI1KPACeNr cy/bMN+ CqdgEjOnA5rNJKl UUgZRGqMM8NJlMP B0
eGQAn1hIvbNAj7P
=biQr
-----END PGP SIGNATURE-----