Merge lp:~robru/cupstream2distro/moar-bileto into lp:cupstream2distro
- moar-bileto
- Merge into trunk
Proposed by
Robert Bruce Park
Status: | Merged |
---|---|
Approved by: | Robert Bruce Park |
Approved revision: | 1124 |
Merged at revision: | 1079 |
Proposed branch: | lp:~robru/cupstream2distro/moar-bileto |
Merge into: | lp:cupstream2distro |
Diff against target: |
456 lines (+104/-141) 7 files modified
citrain/build.py (+2/-4) citrain/prepare_silo.py (+0/-25) citrain/recipes/base.py (+1/-0) cupstream2distro/silomanager.py (+34/-18) tests/unit/test_script_build.py (+1/-5) tests/unit/test_script_prepare_silo.py (+0/-78) tests/unit/test_silomanager.py (+66/-11) |
To merge this branch: | bzr merge lp:~robru/cupstream2distro/moar-bileto |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Robert Bruce Park (community) | Approve | ||
PS Jenkins bot | continuous-integration | Approve | |
Review via email: mp+270258@code.launchpad.net |
Commit message
Convert source_archive and source_series to Bileto.
Description of the change
To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : | # |
review:
Approve
(continuous-integration)
- 1124. By Robert Bruce Park
-
Print silo summary early during build.
Revision history for this message
Robert Bruce Park (robru) wrote : | # |
Looks good in staging.
review:
Approve
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-09-05 03:54:05 +0000 |
3 | +++ citrain/build.py 2015-09-06 00:07:30 +0000 |
4 | @@ -60,7 +60,6 @@ |
5 | from citrain.recipes.sourcesync import SourceSync |
6 | from citrain.recipes.binarysync import BinarySync |
7 | from cupstream2distro.silomanager import SiloState |
8 | -from cupstream2distro.launchpadmanager import lp |
9 | from cupstream2distro.errors import BuildError, CITrainError |
10 | from cupstream2distro.utils import ( |
11 | env, |
12 | @@ -124,9 +123,8 @@ |
13 | Source: self.names & set(self.silo_state.sources), |
14 | } |
15 | if self.silo_state.source_archive: |
16 | - Sync.from_archive = lp.load(self.silo_state.source_archive) |
17 | - Sync.from_series = lp.get_series( |
18 | - self.silo_state.source_series, dest=Sync.from_archive) |
19 | + Sync.from_archive = self.silo_state.source_archive |
20 | + Sync.from_series = self.silo_state.source_series |
21 | Merge.all_mps = self.silo_state.mps |
22 | |
23 | def instantiate_build_objects(self): |
24 | |
25 | === modified file 'citrain/prepare_silo.py' |
26 | --- citrain/prepare_silo.py 2015-09-05 19:41:20 +0000 |
27 | +++ citrain/prepare_silo.py 2015-09-06 00:07:30 +0000 |
28 | @@ -45,10 +45,8 @@ |
29 | |
30 | from cupstream2distro.settings import ( |
31 | BILETO_GET, |
32 | - LANDING_SCHEME, |
33 | SILO_NAME_LIST, |
34 | SILOS_DIR, |
35 | - STABLE_OVERLAY_PPA, |
36 | ) |
37 | from cupstream2distro.utils import ( |
38 | SILO_DIR, |
39 | @@ -133,32 +131,9 @@ |
40 | silo_state = SiloState.new_blank( |
41 | siloname=env.SILONAME, |
42 | requestid=env.REQUEST_ID) |
43 | - if env.SYNC_REQUEST: |
44 | - prepare_sync(silo_state) |
45 | return silo_state |
46 | |
47 | |
48 | -def prepare_sync(silo_state): |
49 | - """Process sync request.""" |
50 | - archive, comma, series = env.SYNC_REQUEST.partition(',') |
51 | - source_series = series or env.SERIES |
52 | - if archive.startswith('ppa:'): |
53 | - source_archive = lp.get_ppa(archive.split(':')[1].strip('~')) |
54 | - elif archive.isdigit(): |
55 | - siloname = '{}/landing-{:03d}'.format(env.DISTRIBUTION, int(archive)) |
56 | - source_archive = lp.get_ppa(LANDING_SCHEME.format(siloname)) |
57 | - source_state = SiloState(siloname) |
58 | - source_series = source_state.series.name |
59 | - elif archive == 'stable-overlay': |
60 | - source_archive = lp.get_ppa(STABLE_OVERLAY_PPA) |
61 | - else: |
62 | - source_archive = lp.distributions[archive].main_archive |
63 | - logging.info('Syncing from {} {}.'.format( |
64 | - source_series, source_archive.web_link)) |
65 | - silo_state.source_archive = source_archive.self_link |
66 | - silo_state.source_series = source_series |
67 | - |
68 | - |
69 | def check_merge_status(merge): |
70 | """Raise PrepError if merge is in wrong state.""" |
71 | try: |
72 | |
73 | === modified file 'citrain/recipes/base.py' |
74 | --- citrain/recipes/base.py 2015-09-03 18:50:16 +0000 |
75 | +++ citrain/recipes/base.py 2015-09-06 00:07:30 +0000 |
76 | @@ -168,6 +168,7 @@ |
77 | silo_state.set_empty() |
78 | silo_state.set_preparing() |
79 | silo_state.save_config() |
80 | + logging.info(silo_state.summarize()) |
81 | |
82 | def clean_phase(self): |
83 | """Delete files leftover from previous builds.""" |
84 | |
85 | === modified file 'cupstream2distro/silomanager.py' |
86 | --- cupstream2distro/silomanager.py 2015-09-05 20:33:19 +0000 |
87 | +++ cupstream2distro/silomanager.py 2015-09-06 00:07:30 +0000 |
88 | @@ -41,9 +41,9 @@ |
89 | LANDING_SCHEME, |
90 | SILO_NAME_LIST, |
91 | SILOS_DIR, |
92 | + STABLE_OVERLAY_PPA, |
93 | ) |
94 | from cupstream2distro.utils import ( |
95 | - SILO_DIR, |
96 | env, |
97 | memoize, |
98 | log_value_of, |
99 | @@ -176,7 +176,7 @@ |
100 | @property |
101 | def request_id_file(self): |
102 | """Identify the file that contains the requestid.""" |
103 | - return SILO_DIR('request_id') |
104 | + return join(SILOS_DIR, self.siloname, 'request_id') |
105 | |
106 | @staticmethod |
107 | def find_first_available(distribution='ubuntu'): |
108 | @@ -463,7 +463,7 @@ |
109 | @property |
110 | def step_file(self): |
111 | """Define filename indicating silo has been published.""" |
112 | - return SILO_DIR('step') |
113 | + return join(SILOS_DIR, self.siloname, 'step') |
114 | |
115 | @property |
116 | def step(self): |
117 | @@ -537,9 +537,10 @@ |
118 | return self._series.split('+')[-1] |
119 | |
120 | @property |
121 | + @memoize |
122 | def sources(self): |
123 | """Identify the manual source packages configured for this silo.""" |
124 | - archive = self.sync_request.split(',')[0] |
125 | + archive = self.sync_request.partition(',')[0] |
126 | sources = splitter(self._sources) |
127 | if not sources and archive.isdigit(): |
128 | return self.load_source_sync_sources(int(archive)) |
129 | @@ -549,7 +550,7 @@ |
130 | def load_source_sync_sources(self, silonum): |
131 | """Check what packages are configured in a different silo.""" |
132 | self.info('Inferring source names from silo {}.'.format(silonum)) |
133 | - return SiloState( |
134 | + return self.get_source_state( |
135 | '{}/landing-{:03d}'.format(self._distribution, silonum) |
136 | ).all_projects |
137 | |
138 | @@ -574,24 +575,39 @@ |
139 | return lp.load('{}/{}'.format(self._distribution, series)) |
140 | |
141 | @property |
142 | + @memoize |
143 | def source_archive(self): |
144 | - """Identify the archive we are synching from.""" |
145 | - return self._config.get('source_archive') |
146 | - |
147 | - @source_archive.setter |
148 | - def source_archive(self, value): |
149 | - """Record the archive we are syncing from.""" |
150 | - self._config['source_archive'] = value |
151 | + """Identify the archive we are syncing from.""" |
152 | + archive = self._sync_request.partition(',')[0] |
153 | + if archive.startswith('ppa:'): |
154 | + return lp.get_ppa(archive.split(':')[1].strip('~')) |
155 | + elif archive.isdigit(): |
156 | + siloname = '{}/landing-{:03d}'.format( |
157 | + self._distribution, int(archive)) |
158 | + return lp.get_ppa(LANDING_SCHEME.format(siloname)) |
159 | + elif archive == 'stable-overlay': |
160 | + return lp.get_ppa(STABLE_OVERLAY_PPA) |
161 | + elif archive: |
162 | + return lp.distributions[archive].main_archive |
163 | |
164 | @property |
165 | + @memoize |
166 | def source_series(self): |
167 | """Identify the distro series we are syncing from.""" |
168 | - return self._config.get('source_series') |
169 | + archive, comma, series = self._sync_request.partition(',') |
170 | + if archive.isdigit(): |
171 | + siloname = '{}/landing-{:03d}'.format( |
172 | + self._distribution, int(archive)) |
173 | + return self.get_source_state(siloname).series |
174 | + elif archive: |
175 | + return lp.load('{}/{}'.format( |
176 | + self.source_archive.distribution.name, |
177 | + series or self._series)) |
178 | |
179 | - @source_series.setter |
180 | - def source_series(self, value): |
181 | - """Record the distro series we are syncing from.""" |
182 | - self._config['source_series'] = value |
183 | + @memoize |
184 | + def get_source_state(self, siloname): |
185 | + """Fetch SiloState object representing some other silo.""" |
186 | + return SiloState(siloname) |
187 | |
188 | def append_mp(self, source_package, mp_link): |
189 | """Record a new MP URL that this silo will build.""" |
190 | @@ -604,7 +620,7 @@ |
191 | logging.error(message) |
192 | if env.SILONAME: |
193 | silo_state = SiloState(env.SILONAME) |
194 | - silo_state.set_config_status(-1, message) |
195 | + silo_state.set_config_status(message) |
196 | silo_state.save_config() |
197 | |
198 | |
199 | |
200 | === modified file 'tests/unit/test_script_build.py' |
201 | --- tests/unit/test_script_build.py 2015-09-05 03:54:05 +0000 |
202 | +++ tests/unit/test_script_build.py 2015-09-06 00:07:30 +0000 |
203 | @@ -112,13 +112,9 @@ |
204 | self.script.SourceSync: set(['b']), |
205 | }) |
206 | self.assertEqual(self.script.Merge.all_mps, silo_state.mps) |
207 | - self.assertEqual(self.script.lp.get_series.mock_calls, [ |
208 | - call(silo_state.source_series, |
209 | - dest=self.script.SourceSync.from_archive), |
210 | - ]) |
211 | self.assertEqual( |
212 | self.script.SourceSync.from_series, |
213 | - self.script.lp.get_series.return_value) |
214 | + silo_state.source_series) |
215 | |
216 | def test_buildmanager_instantiate_build_objects(self): |
217 | """Create all build objects.""" |
218 | |
219 | === modified file 'tests/unit/test_script_prepare_silo.py' |
220 | --- tests/unit/test_script_prepare_silo.py 2015-09-05 19:16:06 +0000 |
221 | +++ tests/unit/test_script_prepare_silo.py 2015-09-06 00:07:30 +0000 |
222 | @@ -23,7 +23,6 @@ |
223 | from cupstream2distro import project |
224 | from cupstream2distro.utils import env, os_path_join_safe |
225 | from cupstream2distro.errors import PrepError |
226 | -from cupstream2distro.settings import STABLE_OVERLAY_PPA |
227 | |
228 | from tests.unit import CITrainScriptTestCase |
229 | |
230 | @@ -169,87 +168,10 @@ |
231 | env.DISTRIBUTION = 'ubuntu' |
232 | env.SERIES = 'wily' |
233 | self.script.lp.distributions = dict(ubuntu=Mock()) |
234 | - self.script.prepare_sync = Mock() |
235 | self.script.create_new_state() |
236 | self.script.SiloState.new_blank.assert_called_once_with( |
237 | siloname='ubuntu/landing-123', |
238 | requestid='42') |
239 | - self.script.prepare_sync.assert_called_once_with( |
240 | - self.script.SiloState.new_blank.return_value) |
241 | - |
242 | - def test_prepare_sync_ppa(self): |
243 | - """Configure syncs correctly from a PPA.""" |
244 | - silo_state = Mock() |
245 | - env.DISTRIBUTION = 'ubuntu' |
246 | - env.SERIES = 'wily' |
247 | - env.SOURCES = 'foo, bar, baz' |
248 | - env.SYNC_REQUEST = 'ppa:some/ubuntu/place,vivid' |
249 | - self.script.prepare_sync(silo_state) |
250 | - self.script.lp.get_ppa.assert_called_once_with('some/ubuntu/place') |
251 | - self.assertEqual(silo_state.source_series, 'vivid') |
252 | - self.assertEqual( |
253 | - silo_state.source_archive, |
254 | - self.script.lp.get_ppa.return_value.self_link) |
255 | - |
256 | - def test_prepare_sync_silo(self): |
257 | - """Configure syncs correctly from a silo.""" |
258 | - silo_state = Mock() |
259 | - env.DISTRIBUTION = 'ubuntu' |
260 | - env.SERIES = 'wily' |
261 | - env.SOURCES = 'foo, bar, baz' |
262 | - env.SYNC_REQUEST = '8' |
263 | - self.script.SiloState.return_value.series.name = 'utopic' |
264 | - self.script.prepare_sync(silo_state) |
265 | - self.script.lp.get_ppa.assert_called_once_with( |
266 | - 'ci-train-staging-area/ubuntu/landing-008') |
267 | - self.assertEqual(silo_state.source_series, 'utopic') |
268 | - self.assertEqual( |
269 | - silo_state.source_archive, |
270 | - self.script.lp.get_ppa.return_value.self_link) |
271 | - |
272 | - def test_prepare_sync_silo_sources(self): |
273 | - """Configure syncs correctly from a silo.""" |
274 | - silo_state = Mock() |
275 | - env.DISTRIBUTION = 'ubuntu' |
276 | - env.SERIES = 'wily' |
277 | - env.SOURCES = '' |
278 | - env.SYNC_REQUEST = '8' |
279 | - self.script.SiloState.return_value.series.name = 'vivid' |
280 | - self.script.prepare_sync(silo_state) |
281 | - self.script.lp.get_ppa.assert_called_once_with( |
282 | - 'ci-train-staging-area/ubuntu/landing-008') |
283 | - self.assertEqual(silo_state.source_series, 'vivid') |
284 | - self.assertEqual( |
285 | - silo_state.source_archive, |
286 | - self.script.lp.get_ppa.return_value.self_link) |
287 | - |
288 | - def test_prepare_sync_overlay(self): |
289 | - """Configure syncs correctly from the overlay.""" |
290 | - silo_state = Mock() |
291 | - env.DISTRIBUTION = 'ubuntu' |
292 | - env.SERIES = 'wily' |
293 | - env.SOURCES = 'foo, bar, baz' |
294 | - env.SYNC_REQUEST = 'stable-overlay' |
295 | - self.script.prepare_sync(silo_state) |
296 | - self.script.lp.get_ppa.assert_called_once_with(STABLE_OVERLAY_PPA) |
297 | - self.assertEqual(silo_state.source_series, 'wily') |
298 | - self.assertEqual( |
299 | - silo_state.source_archive, |
300 | - self.script.lp.get_ppa.return_value.self_link) |
301 | - |
302 | - def test_prepare_sync_main(self): |
303 | - """Configure syncs correctly from the main archive.""" |
304 | - silo_state = Mock() |
305 | - env.DISTRIBUTION = 'ubuntu' |
306 | - env.SERIES = 'wily' |
307 | - env.SOURCES = 'foo, bar, baz' |
308 | - env.SYNC_REQUEST = 'ubuntu,vivid' |
309 | - ubuntu = Mock() |
310 | - self.script.lp.distributions = dict(ubuntu=ubuntu) |
311 | - self.script.prepare_sync(silo_state) |
312 | - self.assertEqual(silo_state.source_series, 'vivid') |
313 | - self.assertEqual( |
314 | - silo_state.source_archive, ubuntu.main_archive.self_link) |
315 | |
316 | def test_check_merge_status(self): |
317 | """Ensure that merges are in acceptable states.""" |
318 | |
319 | === modified file 'tests/unit/test_silomanager.py' |
320 | --- tests/unit/test_silomanager.py 2015-09-05 20:18:00 +0000 |
321 | +++ tests/unit/test_silomanager.py 2015-09-06 00:07:30 +0000 |
322 | @@ -86,15 +86,74 @@ |
323 | silomanager.SILO_NAME_LIST = dict( |
324 | ubuntu=['ubuntu/landing-00{}'.format(x) for x in range(0, 10)]) |
325 | |
326 | + def test_silomanager_refuse_SILO_DIR(self): |
327 | + """Prevent badness from baddening.""" |
328 | + self.assertFalse( |
329 | + hasattr(silomanager, 'SILO_DIR'), |
330 | + 'SILO_DIR() cannot be trusted inside SiloState class.') |
331 | + |
332 | def test_silostate_requestid(self): |
333 | """Ensure requestid property behaves sensible.""" |
334 | - func = 'cupstream2distro.silomanager.SILO_DIR' |
335 | + func = 'cupstream2distro.silomanager.join' |
336 | with patch(func, lambda *ingore: self.tempdir + '/step'): |
337 | self.state.requestid = '42' |
338 | self.assertEqual(self.state._config['requestid'], '42') |
339 | self.state._config['requestid'] = None |
340 | self.assertEqual(self.state.requestid, '42') |
341 | |
342 | + @patch('cupstream2distro.silomanager.lp') |
343 | + def test_silostate_source_archive_ppa(self, lp_mock): |
344 | + """Ensure source_archive property has correct return values.""" |
345 | + lp_mock.get_ppa.return_value.distribution.name = 'ubuntu' |
346 | + self.state._bileto.update(sync_request='ppa:team/ubuntu/ppa,vivid') |
347 | + self.assertEqual( |
348 | + self.state.source_archive, lp_mock.get_ppa.return_value) |
349 | + lp_mock.get_ppa.assert_called_once_with('team/ubuntu/ppa') |
350 | + self.assertEqual(self.state.source_series, lp_mock.load.return_value) |
351 | + lp_mock.load.assert_called_once_with('ubuntu/vivid') |
352 | + |
353 | + @patch('cupstream2distro.silomanager.lp') |
354 | + @patch('cupstream2distro.silomanager.SiloState') |
355 | + def test_silostate_source_archive_digit(self, ss_mock, lp_mock): |
356 | + """Ensure source_archive property has correct return values.""" |
357 | + ss_mock.return_value.series = 'zip' |
358 | + self.state._bileto.update(sync_request='12', distribution='blah') |
359 | + self.assertEqual( |
360 | + self.state.source_archive, lp_mock.get_ppa.return_value) |
361 | + lp_mock.get_ppa.assert_called_once_with( |
362 | + 'ci-train-staging-area/blah/landing-012') |
363 | + self.assertEqual(self.state.source_series, 'zip') |
364 | + |
365 | + @patch('cupstream2distro.silomanager.lp') |
366 | + def test_silostate_source_archive_stable(self, lp_mock): |
367 | + """Ensure source_archive property has correct return values.""" |
368 | + lp_mock.get_ppa.return_value.distribution.name = 'ubuntu' |
369 | + self.state._bileto.update(sync_request='stable-overlay,vivid') |
370 | + self.assertEqual( |
371 | + self.state.source_archive, lp_mock.get_ppa.return_value) |
372 | + lp_mock.get_ppa.assert_called_once_with( |
373 | + 'ci-train-staging-area/ubuntu/stable-phone-overlay') |
374 | + self.assertEqual(self.state.source_series, lp_mock.load.return_value) |
375 | + lp_mock.load.assert_called_once_with('ubuntu/vivid') |
376 | + |
377 | + @patch('cupstream2distro.silomanager.lp') |
378 | + def test_silostate_source_archive_other(self, lp_mock): |
379 | + """Ensure source_archive property has correct return values.""" |
380 | + rtm = Mock() |
381 | + lp_mock.distributions = {'ubuntu-rtm': rtm} |
382 | + rtm.main_archive.distribution.name = 'ubuntu-rtm' |
383 | + self.state._bileto.update(sync_request='ubuntu-rtm,14.09') |
384 | + self.assertEqual(self.state.source_archive, rtm.main_archive) |
385 | + self.assertEqual(self.state.source_series, lp_mock.load.return_value) |
386 | + lp_mock.load.assert_called_once_with('ubuntu-rtm/14.09') |
387 | + |
388 | + @patch('cupstream2distro.silomanager.lp') |
389 | + def test_silostate_source_archive_none(self, lp_mock): |
390 | + """Ensure source_archive property has correct return values.""" |
391 | + self.state._bileto.update(sync_request='') |
392 | + self.assertIsNone(self.state.source_archive) |
393 | + self.assertIsNone(self.state.source_series) |
394 | + |
395 | def test_mark_packages_dirty(self): |
396 | """Create dirty package marker files.""" |
397 | names = ['bleep', 'blorp'] |
398 | @@ -250,7 +309,7 @@ |
399 | |
400 | def test_silostate_new_blank(self): |
401 | """Create a new SiloState instance for a fresh silo assignment.""" |
402 | - methodname = 'cupstream2distro.silomanager.SILO_DIR' |
403 | + methodname = 'cupstream2distro.silomanager.join' |
404 | with patch(methodname, lambda *ignore: self.tempdir + '/request_id'): |
405 | silo_state = SiloState.new_blank( |
406 | siloname='ubuntu/landing-789', |
407 | @@ -620,7 +679,7 @@ |
408 | |
409 | def test_silostate_step_setters(self): |
410 | """Set various silo steps.""" |
411 | - func = 'cupstream2distro.silomanager.SILO_DIR' |
412 | + func = 'cupstream2distro.silomanager.join' |
413 | with patch(func, lambda *ingore: self.tempdir + '/step'): |
414 | self.state.set_empty() |
415 | self.assertEqual(self.state._config['global']['step'], 0) |
416 | @@ -686,14 +745,10 @@ |
417 | """Set various state properties.""" |
418 | self.state.mps = 'flibbyjib' |
419 | self.assertEqual(self.state._config['mps'], 'flibbyjib') |
420 | - self.state.source_archive = 'ubuntu' |
421 | - self.assertEqual(self.state._config['source_archive'], 'ubuntu') |
422 | - self.state.source_series = 'vivid' |
423 | - self.assertEqual(self.state._config['source_series'], 'vivid') |
424 | |
425 | def test_silostate_set_ready(self): |
426 | """Set ready to build status.""" |
427 | - func = 'cupstream2distro.silomanager.SILO_DIR' |
428 | + func = 'cupstream2distro.silomanager.join' |
429 | with patch(func, lambda *ingore: self.tempdir + '/step'): |
430 | status_mock = self.state.set_config_status = Mock() |
431 | self.state.set_ready() |
432 | @@ -702,7 +757,7 @@ |
433 | |
434 | def test_silostate_set_reconfigure_failed(self): |
435 | """Set reconfigure failed status.""" |
436 | - func = 'cupstream2distro.silomanager.SILO_DIR' |
437 | + func = 'cupstream2distro.silomanager.join' |
438 | with patch(func, lambda *ingore: self.tempdir + '/step'): |
439 | status_mock = self.state.set_config_status = Mock() |
440 | self.state.set_reconfigure_failed('You did a bad thing.') |
441 | @@ -742,7 +797,7 @@ |
442 | silomanager.save_error_message('Whoa, errors galore!') |
443 | silostate.assert_called_once_with('my/fake/silo') |
444 | silostate().set_config_status.assert_called_once_with( |
445 | - -1, 'Whoa, errors galore!') |
446 | + 'Whoa, errors galore!') |
447 | silostate().save_config.assert_called_once_with() |
448 | |
449 | @patch('cupstream2distro.silomanager.SiloState') |
450 | @@ -778,5 +833,5 @@ |
451 | silomanager.cleanup_on_exception(None, Exception('It broke'), None) |
452 | silostate.assert_called_once_with('ubuntu/landing-negative-one') |
453 | silostate().set_config_status.assert_called_once_with( |
454 | - -1, 'Uncaught exception: Exception: It broke') |
455 | + 'Uncaught exception: Exception: It broke') |
456 | silostate().save_config.assert_called_once_with() |
PASSED: Continuous integration, rev:1123 jenkins. qa.ubuntu. com/job/ cu2d-choo- choo-ci/ 763/
http://
Executed test runs:
Click here to trigger a rebuild: s-jenkins. ubuntu- ci:8080/ job/cu2d- choo-choo- ci/763/ rebuild
http://