Merge lp:~robru/cupstream2distro/refactor-publish into lp:cupstream2distro

Proposed by Robert Bruce Park
Status: Merged
Approved by: Robert Bruce Park
Approved revision: no longer in the source branch.
Merged at revision: 1168
Proposed branch: lp:~robru/cupstream2distro/refactor-publish
Merge into: lp:cupstream2distro
Diff against target: 470 lines (+62/-133)
9 files modified
citrain/build.py (+1/-1)
citrain/publisher.py (+2/-23)
citrain/recipes/merge.py (+20/-9)
cupstream2distro/branchhandling.py (+1/-3)
tests/unit/test_branchhandling.py (+3/-1)
tests/unit/test_recipe_merge.py (+27/-16)
tests/unit/test_script_build.py (+1/-18)
tests/unit/test_script_publisher.py (+4/-59)
tests/unit/test_script_revert.py (+3/-3)
To merge this branch: bzr merge lp:~robru/cupstream2distro/refactor-publish
Reviewer Review Type Date Requested Status
PS Jenkins bot continuous-integration Approve
Robert Bruce Park (community) Approve
Review via email: mp+275470@code.launchpad.net

Commit message

Move publisher.push_branches into Merge class.

To post a comment you must log in.
Revision history for this message
Robert Bruce Park (robru) wrote :
review: Approve
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :

PASSED: Continuous integration, rev:1171
http://jenkins.qa.ubuntu.com/job/cu2d-choo-choo-ci/840/
Executed test runs:

Click here to trigger a rebuild:
http://s-jenkins.ubuntu-ci:8080/job/cu2d-choo-choo-ci/840/rebuild

review: Approve (continuous-integration)
1168. By Robert Bruce Park

Move publisher.push_branches into Merge class.

Approved by PS Jenkins bot, Robert Bruce Park.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'citrain/build.py'
2--- citrain/build.py 2015-10-20 19:25:31 +0000
3+++ citrain/build.py 2015-10-22 23:24:39 +0000
4@@ -86,7 +86,7 @@
5 manager = buildmanager(silo_state)
6 manager.do('validate')
7 if env.WATCH_ONLY != 'true':
8- manager.do('clean', 'collect', 'build', 'upload')
9+ manager.do('clean', 'collect', 'build', 'upload', 'expose')
10 Manager(silo_state).do('watch', 'diff')
11 silo_state.status = 'Packages built.'
12 except CITrainError as err:
13
14=== modified file 'citrain/publisher.py'
15--- citrain/publisher.py 2015-10-22 18:56:16 +0000
16+++ citrain/publisher.py 2015-10-22 23:24:39 +0000
17@@ -48,21 +48,13 @@
18 from citrain.recipes.manager import Manager
19 from cupstream2distro.version import V
20 from cupstream2distro.archive import Archive
21-from cupstream2distro.branchhandling import Branch
22 from cupstream2distro.packagelist import PackageList
23 from cupstream2distro.errors import CITrainError, PublishError, NoStatusError
24+from cupstream2distro.utils import env, log_value_of, run_script
25+from cupstream2distro.settings import STABLE_OVERLAY_PPA
26 from cupstream2distro.silomanager import SiloState
27 from cupstream2distro.launchpadmanager import lp
28 from cupstream2distro.project import DotProject
29-from cupstream2distro.settings import (
30- STABLE_OVERLAY_PPA,
31-)
32-from cupstream2distro.utils import (
33- SILO_DIR,
34- env,
35- log_value_of,
36- run_script,
37-)
38
39
40 def get_distro_and_series(series):
41@@ -73,18 +65,6 @@
42 env.DISTRO, env.SERIES))
43
44
45-# FIXME: Call this during the build job, so we have access to it sooner.
46-def push_branches(mps):
47- """Push all local branches to Launchpad."""
48- for source in sorted(mps):
49- project_name = mps[source][0].target_branch.project.name
50- branch = SILO_DIR(source)
51- proposed = 'lp:~/{}/{}-{}-{}-proposed'.format(
52- project_name, source, env.DISTRO, env.SERIES)
53- logging.info('Pushing {} to {}'.format(source, proposed))
54- Branch(branch).push_to_branch(proposed, overwrite=True)
55-
56-
57 def find_ppa_sources(ppa, sources, versions):
58 """Populate sources set with all Published and Pending sources in a PPA.
59
60@@ -173,7 +153,6 @@
61 silo_state.status = 'Publishing.'
62 silo_state.save_config()
63 mgr.do('dest_version_check', 'unapproved', 'unbuilt')
64- push_branches(silo_state.mps)
65 initiate_publication(dest, ppa, silo_state.set_package_version_list)
66 silo_state.set_published()
67 except CITrainError as err:
68
69=== modified file 'citrain/recipes/merge.py'
70--- citrain/recipes/merge.py 2015-10-21 03:02:09 +0000
71+++ citrain/recipes/merge.py 2015-10-22 23:24:39 +0000
72@@ -100,7 +100,11 @@
73 merge proposal URLs needed to build that package.
74 """
75 all_mps = {}
76- merges = None
77+
78+ def __init__(self, *args):
79+ """Select only the merges we need from the all_mps dict."""
80+ super().__init__(*args)
81+ self.merges = Merge.all_mps.get(self.name, [])
82
83 def enforce_merge_states(self, acceptable):
84 """Block when merges have bad states."""
85@@ -119,10 +123,9 @@
86 def validate_phase(self):
87 """Sanity check merges."""
88 super().validate_phase()
89- merges = self.merges = Merge.all_mps[self.name]
90 self.enforce_merge_states(BUILDABLE_STATES)
91 if not self.failures:
92- check_merge_targets(merges)
93+ check_merge_targets(self.merges)
94
95 def collect_phase(self):
96 """Commit merges locally.
97@@ -235,27 +238,35 @@
98 authors[BOT_DEBFULLNAME].append('New rebuild forced.')
99 return authors
100
101+ def expose_phase(self):
102+ """Push branches to Launchpad so that they can be inspected."""
103+ project = self.merges[0].target_branch.project.name
104+ branch = '-'.join([
105+ self.name,
106+ self.dest.distribution.name,
107+ self.series.name,
108+ self.silo_ppa.name])
109+ dest = 'lp:~/{}/{}'.format(project, branch)
110+ self.push_to_branch(dest, overwrite=True)
111+
112 def unapproved_phase(self):
113 """Block publication of merges not in approved states."""
114- self.merges = Merge.all_mps[self.name]
115 if env.ALLOW_UNAPPROVED != 'true':
116 self.enforce_merge_states(PUBLISHABLE_STATES)
117
118 def unbuilt_phase(self):
119 """Block publication of merges that have new commits."""
120- merges = Merge.all_mps[self.name]
121- if check_for_unbuilt_revids(self.name, merges):
122+ if check_for_unbuilt_revids(self.name, self.merges):
123 mark_packages_dirty(env.SILONAME, [self.name])
124 self.failures.add(self.name + ' has new, unbuilt commits.')
125
126 def merge_phase(self):
127 """Merge destination trunk back into local branch, if necessary."""
128 self.revert_branch()
129- target = self.target = Merge.all_mps[self.name][0].target_branch
130+ target = self.merges[0].target_branch
131 logging.info('Merging {} to {}.'.format(target.web_link, self.path))
132 self.merge_branch(target.web_link, 'Resync trunk.', unchanged=False)
133
134 def push_phase(self):
135 """Push local branch to destination branch."""
136- logging.info('Pushing {} to {}.'.format(self.name, self.target))
137- self.push_to_branch(self.target.display_name)
138+ self.push_to_branch(self.merges[0].target_branch.display_name)
139
140=== modified file 'cupstream2distro/branchhandling.py'
141--- cupstream2distro/branchhandling.py 2015-09-24 03:45:54 +0000
142+++ cupstream2distro/branchhandling.py 2015-10-22 23:24:39 +0000
143@@ -121,9 +121,6 @@
144 raise BranchError('debian/changelog unreachable: {}'.format(err))
145 return log.split()[0]
146
147- def __init__(self, path):
148- self.path = path
149-
150 def initialize_branch(self):
151 """Create a new bzr branch in self.path."""
152 log_call('bzr init .', cwd=self.path)
153@@ -279,6 +276,7 @@
154 :raises: BranchError if branch is not writable by us.
155 """
156 assert branch.startswith('lp:'), branch
157+ logging.info('Pushing {} to {}.'.format(self.name, branch))
158 command = ['bzr', 'push', branch]
159 if overwrite:
160 command.append('--overwrite')
161
162=== modified file 'tests/unit/test_branchhandling.py'
163--- tests/unit/test_branchhandling.py 2015-09-24 05:23:10 +0000
164+++ tests/unit/test_branchhandling.py 2015-10-22 23:24:39 +0000
165@@ -33,7 +33,9 @@
166 """Test that we can handle a Branch!"""
167 def setUp(self):
168 super().setUp()
169- self.branch = Branch(self.tempdir)
170+ self.branch = Branch()
171+ self.branch.path = self.tempdir
172+ self.branch.name = 'branchy'
173
174 @patch('cupstream2distro.branchhandling.call')
175 def test_get_package_name_from_branch(self, call_mock):
176
177=== modified file 'tests/unit/test_recipe_merge.py'
178--- tests/unit/test_recipe_merge.py 2015-10-21 03:02:09 +0000
179+++ tests/unit/test_recipe_merge.py 2015-10-22 23:24:39 +0000
180@@ -61,9 +61,13 @@
181 super().setUp()
182 self.source = Mock()
183 self.dest = Mock()
184+ self.dest.distribution.name = 'testbuntu'
185 self.series = Mock()
186+ self.series.name = 'xenial'
187 self.ppa = Mock()
188+ self.ppa.name = 'super-testy-fun-ppa'
189 Merge.failures.clear()
190+ Merge.all_mps.clear()
191
192 def order_shuffler(self, expected, shuffles):
193 """Shuffle and re-sort values, then compare against expectations."""
194@@ -179,13 +183,11 @@
195 """Ensure that validate phase blocks bad merges."""
196 merge = Merge('bar', self.series, self.dest, self.ppa)
197 for case in ('Work in progress', 'Needs review', 'Approved'):
198- Merge.all_mps = dict(bar=[Mock(
199- queue_status=case, web_link='example.com')])
200+ merge.merges = [Mock(queue_status=case, web_link='example.com')]
201 merge.validate_phase()
202 self.assertFalse(merge.failures)
203 for case in ('Superceded', 'Rejected', 'Really broken'):
204- Merge.all_mps = dict(bar=[Mock(
205- queue_status=case, web_link='example.com')])
206+ merge.merges = [Mock(queue_status=case, web_link='example.com')]
207 merge.validate_phase()
208 self.assertEqual(merge.failures, {'bar has merges in bad states.'})
209 self.assertEqual(log_mock.mock_calls, [
210@@ -382,19 +384,29 @@
211 merge.collect_package_bugs.return_value, take_whole_commit=False)
212 self.assertEqual(authors, {'CI Train Bot': ['New rebuild forced.']})
213
214+ def test_merge_expose_phase(self):
215+ """Expose the locally merged branches in launchpad."""
216+ project = Mock()
217+ project.name = 'spilly'
218+ merge = Merge('spill', self.series, self.dest, self.ppa)
219+ merge.merges = [Mock(target_branch=Mock(project=project))]
220+ merge.push_to_branch = Mock()
221+ merge.expose_phase()
222+ merge.push_to_branch.assert_called_once_with(
223+ 'lp:~/spilly/spill-testbuntu-xenial-super-testy-fun-ppa',
224+ overwrite=True)
225+
226 @patch('citrain.recipes.merge.logging')
227 def test_merge_unapproved_phase(self, log_mock):
228 """Reject unapproved merges."""
229 env.ALLOW_UNAPPROVED = 'false'
230 merge = Merge('grill', self.series, self.dest, self.ppa)
231 for case in ('Approved', 'Merged'):
232- Merge.all_mps = dict(grill=[Mock(
233- queue_status=case, web_link='example.com')])
234+ merge.merges = [Mock(queue_status=case, web_link='example.com')]
235 merge.unapproved_phase()
236 self.assertFalse(merge.failures)
237 for case in ('Superceded', 'Work in progress', 'Needs review'):
238- Merge.all_mps = dict(grill=[Mock(
239- queue_status=case, web_link='example.com')])
240+ merge.merges = [Mock(queue_status=case, web_link='example.com')]
241 merge.unapproved_phase()
242 self.assertEqual(merge.failures, {'grill has merges in bad states.'})
243 self.assertEqual(log_mock.mock_calls, [
244@@ -414,8 +426,7 @@
245 merge = Merge('grill', self.series, self.dest, self.ppa)
246 merge.unapproved_phase()
247 for case in ('Superceded', 'Work in progress', 'Needs review'):
248- Merge.all_mps = dict(grill=[Mock(
249- queue_status=case, web_link='example.com')])
250+ merge.merges = [Mock(queue_status=case, web_link='example.com')]
251 merge.unapproved_phase()
252 self.assertFalse(merge.failures)
253
254@@ -462,19 +473,19 @@
255 @patch('citrain.recipes.merge.Merge.push_to_branch', Mock())
256 def test_merge_push_phase(self):
257 """Push branches to target."""
258- Merge.all_mps = {'foo-pack': ['a', 'b']}
259+ mp = lambda name: Mock(target_branch=Mock(display_name=name))
260+ Merge.all_mps = {'foo-pack': [mp('a'), mp('b')]}
261 merge = Merge('foo-pack', self.series, self.dest, self.ppa)
262- merge.target = Mock()
263 merge.push_phase()
264- merge.push_to_branch.assert_called_once_with(merge.target.display_name)
265+ merge.push_to_branch.assert_called_once_with('a')
266
267 @patch('citrain.recipes.merge.Merge.push_to_branch', Mock())
268 def test_merge_push_phase_failed(self):
269 """Push branches to target."""
270- Merge.all_mps = {'foo-pack': ['a', 'b']}
271+ mp = lambda name: Mock(target_branch=Mock(display_name=name))
272+ Merge.all_mps = {'foo-pack': [mp('a'), mp('b')]}
273 merge = Merge('foo-pack', self.series, self.dest, self.ppa)
274 merge.push_to_branch.side_effect = BranchError('Failed pushing')
275- merge.target = Mock()
276 with self.assertRaisesRegexp(BranchError, 'Failed pushing'):
277 merge.push_phase()
278- merge.push_to_branch.assert_called_once_with(merge.target.display_name)
279+ merge.push_to_branch.assert_called_once_with('a')
280
281=== modified file 'tests/unit/test_script_build.py'
282--- tests/unit/test_script_build.py 2015-10-21 01:21:43 +0000
283+++ tests/unit/test_script_build.py 2015-10-22 23:24:39 +0000
284@@ -20,10 +20,6 @@
285
286 from tests.unit import CITrainScriptTestCase
287
288-from cupstream2distro.utils import suppress
289-from cupstream2distro.packagemanager import Package
290-from cupstream2distro.branchhandling import Branch
291-from cupstream2distro.archive import Archive
292 from cupstream2distro.errors import BuildError
293
294 # Unused import necessary for code coverage reporting
295@@ -31,19 +27,6 @@
296 build_module
297
298
299-def fake_factory():
300- """Generate a class that has mocked methods but is not itself a mock."""
301- class Fake(object):
302- """Dummy object for attaching mocked methods to."""
303- pass
304- for cls in (Branch, Package, Archive):
305- for name in cls.__dict__:
306- if '__' not in name:
307- with suppress(TypeError, AttributeError):
308- setattr(Fake, name, Mock())
309- return Fake
310-
311-
312 class BuildTestCase(CITrainScriptTestCase):
313 """Test CI Train Build script."""
314 scriptname = 'build.py'
315@@ -144,7 +127,7 @@
316 bman.assert_called_once_with(silo_state)
317 self.assertEqual(bman.return_value.mock_calls, [
318 call.do('validate'),
319- call.do('clean', 'collect', 'build', 'upload'),
320+ call.do('clean', 'collect', 'build', 'upload', 'expose'),
321 ])
322 self.assertEqual(self.script.Manager.return_value.mock_calls, [
323 call.do('watch', 'diff'),
324
325=== modified file 'tests/unit/test_script_publisher.py'
326--- tests/unit/test_script_publisher.py 2015-10-22 18:56:16 +0000
327+++ tests/unit/test_script_publisher.py 2015-10-22 23:24:39 +0000
328@@ -23,29 +23,12 @@
329 from tests.unit import CITrainScriptTestCase
330
331 from cupstream2distro.utils import env
332-from cupstream2distro.errors import PublishError, BranchError, NoStatusError
333+from cupstream2distro.errors import PublishError, NoStatusError
334
335
336 N = '\n'
337
338
339-class MergeFake(object):
340- """Simulate a launchpadlib merge object."""
341- source_branch = Mock(
342- revision_count=500,
343- last_scanned_id='d@c.com-20140922102807-ld3vw6gt3dxsol2g')
344- target_branch = Mock()
345-
346- def __init__(self, status='Approved', name=None):
347- """Set up some fake values."""
348- self.queue_status = status
349- self.web_link = 'example.com/1234'
350- self.target_branch.project.name = 'fooproject'
351- if name:
352- self.self_link = 'api.lp.net/' + name
353- self.web_link = 'lp.net/' + name
354-
355-
356 class PublisherTestCase(CITrainScriptTestCase):
357 """Test the CI Train Publisher."""
358 scriptname = 'publisher.py'
359@@ -71,42 +54,6 @@
360 self.assertEqual(env.DISTRO, 'ubuntu-rtm')
361 self.assertEqual(env.SERIES, '14.09')
362
363- def test_push_branches_success(self):
364- """Ensure that push_branches() can succeed."""
365- env.DISTRO = 'ubuntu'
366- env.SERIES = 'vivid'
367- mps = {
368- 'foo': [MergeFake(name='a'), MergeFake(name='b')],
369- 'bar': [MergeFake(name='c'), MergeFake(name='d')],
370- }
371- self.script.push_branches(mps)
372- branch = self.script.Branch.return_value
373- self.assertEqual(
374- branch.push_to_branch.mock_calls, [
375- call('lp:~/fooproject/bar-ubuntu-vivid-proposed',
376- overwrite=True),
377- call('lp:~/fooproject/foo-ubuntu-vivid-proposed',
378- overwrite=True),
379- ])
380-
381- def test_push_branches_failure(self):
382- """Ensure that push_branches() handles failure."""
383- env.DISTRO = 'ubuntu-rtm'
384- env.SERIES = '14.09'
385- mps = {
386- 'foo': [MergeFake(name='a'), MergeFake(name='b')],
387- 'bar': [MergeFake(name='c'), MergeFake(name='d')],
388- }
389- branch = self.script.Branch.return_value
390- branch.push_to_branch.side_effect = BranchError('Failed to push')
391- with self.assertRaisesRegexp(BranchError, 'Failed to push'):
392- self.script.push_branches(mps)
393- self.assertEqual(
394- branch.push_to_branch.mock_calls, [
395- call('lp:~/fooproject/bar-ubuntu-rtm-14.09-proposed',
396- overwrite=True),
397- ])
398-
399 def test_find_ppa_sources(self):
400 """Use launchpadlib to correctly get the sources from a PPA."""
401 ppa = Mock()
402@@ -298,7 +245,6 @@
403 """Ensure enough of the publisher is mocked out for testing main()."""
404 self.script.Manager = Mock()
405 self.script.get_distro_and_series = Mock()
406- self.script.push_branches = Mock()
407 self.script.initiate_publication = Mock()
408 self.script.SiloState.return_value.series = \
409 'https://api.launchpad.net/devel/ubuntu/vivid'
410@@ -316,7 +262,6 @@
411 call('dest_version_check', 'unapproved', 'unbuilt'),
412 ])
413 self.assertEqual(state.status, 'Publishing.')
414- self.script.push_branches.assert_called_once_with(state.mps)
415 self.script.initiate_publication.assert_called_once_with(
416 dest, ppa, state.set_package_version_list)
417 state.set_published.assert_called_once_with()
418@@ -327,11 +272,11 @@
419 """publisher.main() should handle errors."""
420 self.mock_main()
421 state = self.script.SiloState()
422- self.script.push_branches = Mock(side_effect=PublishError('Whoops'))
423+ self.script.initiate_publication = Mock(
424+ side_effect=PublishError('Whoops'))
425 self.assertEqual(self.script.main(), 1)
426 self.assertEqual(state.status, "Publish failed: Whoops")
427- self.script.push_branches.assert_called_once_with(state.mps)
428- self.assertEqual(self.script.initiate_publication.call_count, 0)
429+ self.assertEqual(state.set_published.call_count, 0)
430 self.script.logging.error.assert_called_once_with('Whoops')
431 self.assertEqual(state.save_config.mock_calls, [call(), call()])
432 self.assertEqual(state.mark_others_dirty.mock_calls, [])
433
434=== modified file 'tests/unit/test_script_revert.py'
435--- tests/unit/test_script_revert.py 2015-09-23 03:43:12 +0000
436+++ tests/unit/test_script_revert.py 2015-10-22 23:24:39 +0000
437@@ -20,7 +20,6 @@
438 from mock import Mock, call
439
440 from tests.unit import CITrainScriptTestCase
441-from tests.unit.test_script_build import fake_factory
442 from cupstream2distro.errors import RevertError
443
444 # Unused import necessary for code coverage reporting
445@@ -34,8 +33,7 @@
446
447 def setUp(self):
448 super().setUp()
449- self.script.Revert.__bases__ = (fake_factory(),)
450- self.revert = self.script.Revert()
451+ self.revert = self.script.Revert(Mock(), Mock(), Mock(), Mock())
452 self.revert.name = 'regress'
453 self.revert.series = Mock(version='15.04')
454 self.revert.dest = Mock()
455@@ -53,6 +51,7 @@
456 revert.delete_old_dsc = Mock()
457 revert.rename_orig_tarball = Mock()
458 revert.initialize_branch = Mock()
459+ revert.get_from_archive = Mock()
460 revert.collect_phase()
461 self.script.sort_by_date.assert_called_once_with(
462 revert.dest, source_name='regress',
463@@ -86,6 +85,7 @@
464 revert.delete_old_dsc = Mock()
465 revert.rename_orig_tarball = Mock()
466 revert.initialize_branch = Mock()
467+ revert.get_from_archive = Mock()
468 revert.collect_phase()
469 self.script.sort_by_date.assert_called_once_with(
470 revert.dest, source_name='regress',

Subscribers

People subscribed via source and target branches