Merge lp:~robru/cupstream2distro/fix-missing-commit-check into lp:cupstream2distro

Proposed by Robert Bruce Park
Status: Merged
Approved by: Robert Bruce Park
Approved revision: 1308
Merged at revision: 1296
Proposed branch: lp:~robru/cupstream2distro/fix-missing-commit-check
Merge into: lp:cupstream2distro
Diff against target: 321 lines (+89/-62)
5 files modified
citrain/recipes/merge.py (+6/-7)
cupstream2distro/branchhandling.py (+32/-15)
tests/strings.py (+22/-21)
tests/unit/test_branchhandling.py (+25/-10)
tests/unit/test_recipe_merge.py (+4/-9)
To merge this branch: bzr merge lp:~robru/cupstream2distro/fix-missing-commit-check
Reviewer Review Type Date Requested Status
PS Jenkins bot continuous-integration Approve
Robert Bruce Park (community) Approve
Review via email: mp+279534@code.launchpad.net

Commit message

Do three-way diffing in order to ignore false positive trunk commits.

To post a comment you must log in.
Revision history for this message
Robert Bruce Park (robru) wrote :

Looks good in staging.

review: Approve
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :

FAILED: Continuous integration, rev:1306
http://jenkins.qa.ubuntu.com/job/cu2d-choo-choo-ci/940/
Executed test runs:

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

review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :

FAILED: Continuous integration, rev:1307
http://jenkins.qa.ubuntu.com/job/cu2d-choo-choo-ci/941/
Executed test runs:

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

review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :

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

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

review: Approve (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'citrain/recipes/merge.py'
2--- citrain/recipes/merge.py 2015-12-02 05:35:19 +0000
3+++ citrain/recipes/merge.py 2015-12-04 01:43:03 +0000
4@@ -226,15 +226,14 @@
5 def check_new_commits(self, source=None):
6 """Report if this package needs a rebuild, or not."""
7 count = 0
8- branches = [self.merges[0].target_branch] + [
9+ target = self.merges[0].target_branch
10+ branches = [target] + [
11 branch.source_branch for branch in self.merges]
12 for branch in branches:
13- try:
14- if self.missing_branch_commits(branch.bzr_identity):
15- logging.error('New commits: %s', branch.web_link)
16- count += 1
17- except FileNotFoundError:
18- return 'Ready to build'
19+ if self.missing_branch_commits(
20+ branch.bzr_identity, target.bzr_identity):
21+ logging.error('New commits: %s', branch.web_link)
22+ count += 1
23 if count:
24 return 'Needs rebuild due to new commits'
25 return False
26
27=== modified file 'cupstream2distro/branchhandling.py'
28--- cupstream2distro/branchhandling.py 2015-12-02 05:35:19 +0000
29+++ cupstream2distro/branchhandling.py 2015-12-04 01:43:03 +0000
30@@ -36,8 +36,7 @@
31 )
32
33
34-TRANSLATIONS = {'Launchpad', 'Translations', 'behalf'}
35-ME = {'CI', 'Train', 'Bot'}
36+REVID = 'revision-id: '
37 SEPARATOR = '-' * 60
38 IGNORED = ('Automatic PS uploader', 'Launchpad ')
39 BUG_REGEX = re.compile(r"""
40@@ -61,6 +60,11 @@
41 'No handlers could be found for logger "bzr"'.split()
42 )
43
44+ME = '<bzr-whoami-failed-oh-god-why>'
45+with suppress(FileNotFoundError):
46+ ME = call('bzr whoami')[1]
47+ME = ME[ME.index('<') + 1:ME.index('>')]
48+
49
50 def parse_bzr_log(log):
51 """Generate a list of dicts representing bzr log data."""
52@@ -113,11 +117,11 @@
53
54 def bzr_missing(*args):
55 """Call bzr missing."""
56- cmd = ('bzr', 'missing', '--directory') + args
57+ cmd = ('bzr', 'missing', '--show-ids', '--directory') + args
58 stdout, stderr = call(cmd)[1:]
59 if stderr:
60 if 'Not a branch' in stderr:
61- return 'committer: First build' # Pretend there are new commits
62+ return 'revision-id: very-first' # Pretend there are new commits
63 tokens = set(stderr.split())
64 err = NoStatusError if len(TRANSIENT & tokens) > 6 else BranchError
65 raise err('"bzr missing" failed: {}'.format(stderr))
66@@ -128,12 +132,23 @@
67 def bzr_cat(branch, filename):
68 """Call bzr cat."""
69 assert branch.startswith('lp:')
70- log, err = call(['bzr', 'cat', '-d', branch, filename])[1:]
71+ log, err = call(['bzr', 'cat', '--directory', branch, filename])[1:]
72 if err or not log:
73 raise BranchError('{} unreachable: {}'.format(filename, err))
74 return log
75
76
77+def find_ids_to_ignore(branch, target):
78+ """Ignore trunk commits missing from input branches."""
79+ ignore = set()
80+ if branch != target:
81+ for line in bzr_missing(branch, target, '--theirs-only').splitlines():
82+ if line.startswith(REVID):
83+ ignore.add(line.partition(REVID)[-1])
84+ log_value_of.ignore()
85+ return ignore
86+
87+
88 @ramdisk
89 def get_package_name_from_branch(branch):
90 """Get source package name for a particular branch.
91@@ -260,19 +275,21 @@
92 authors[author].append(msg + format_bugs(bugs))
93 return authors
94
95- def missing_branch_commits(self, branch):
96+ def missing_branch_commits(self, branch, target):
97 """Report any missing commits between our branch & another."""
98- # Ignore commits by me that only I have
99- ignore = TRANSLATIONS | ME
100- commits = 0
101+ ignore = find_ids_to_ignore(branch, target)
102+ ignore_me = True # Ignore commits by me that only I have
103 for line in bzr_missing(self.lp_branch, branch).splitlines():
104 if line.startswith('You are missing '):
105- # Watch commits by me that I am missing (most likely on trunk)
106- ignore = TRANSLATIONS
107- if line.startswith('committer:'):
108- if len(set(line.split()) & ignore) < 3:
109- commits += 1
110- return commits
111+ ignore_me = False # Watch commits by me that I'm missing
112+ if line.startswith(REVID):
113+ if 'launchpad_translations_on_behalf_of_' in line:
114+ continue
115+ if ignore_me and ME in line:
116+ continue
117+ if not set(line.split()) & ignore:
118+ logging.info('Disputed %s', line)
119+ return True
120
121 def get_remote_branch_authors(self, branch):
122 """Identify names of unique authors/committers in a lp branch.
123
124=== modified file 'tests/strings.py'
125--- tests/strings.py 2015-11-26 02:05:01 +0000
126+++ tests/strings.py 2015-12-04 01:43:03 +0000
127@@ -269,43 +269,44 @@
128
129 BZR_MISSING_OURS = """
130 You have 1000000 extra revisions:
131-committer: CI Train Bot <ci-train-bot@canonical.com>
132-committer: CI Train Bot <ci-train-bot@canonical.com>
133-committer: CI Train Bot <ci-train-bot@canonical.com>
134+revision-id: ci-train-bot@canonical.com-yyyymmdd-deadbeef1
135+revision-id: ci-train-bot@canonical.com-yyyymmdd-deadbeef2
136+revision-id: ci-train-bot@canonical.com-yyyymmdd-deadbeef3
137+revision-id: ci-train-bot@canonical.com-yyyymmdd-deadbeef4
138 """
139
140 BZR_MISSING_IGNORED = BZR_MISSING_OURS + """
141 You are missing 5000 revisions:
142-committer: Launchpad Translations on behalf of phablet-team
143-committer: Launchpad Translations on behalf of phablet-team
144-committer: Launchpad Translations on behalf of phablet-team
145+revision-id: launchpad_translations_on_behalf_of_foo-20151202054232-deadbeef1
146+revision-id: launchpad_translations_on_behalf_of_foo-20151202054232-deadbeef2
147+revision-id: launchpad_translations_on_behalf_of_foo-20151202054232-deadbeef3
148 """
149
150 BZR_MISSING_ONE = BZR_MISSING_OURS + """
151 You are missing 10000 revisions:
152-committer: CI Train Bot <ci-train-bot@canonical.com>
153-committer: Launchpad Translations on behalf of phablet-team
154-committer: Launchpad Translations on behalf of phablet-team
155-committer: Launchpad Translations on behalf of phablet-team
156+revision-id: ci-train-bot@canonical.com-yyyymmdd-deadbeef5
157+revision-id: launchpad_translations_on_behalf_of_foo-20151202054232-deadbeef4
158+revision-id: launchpad_translations_on_behalf_of_foo-20151202054232-deadbeef5
159+revision-id: launchpad_translations_on_behalf_of_foo-20151202054232-deadbeef6
160 """
161
162 BZR_MISSING_TWO = BZR_MISSING_OURS + """
163 You are missing 5000 revisions:
164-committer: CI Train Bot <ci-train-bot@canonical.com>
165-committer: Launchpad Translations on behalf of phablet-team
166-committer: Launchpad Translations on behalf of phablet-team
167-committer: Launchpad Translations on behalf of phablet-team
168-committer: Joe Blow deleted this commit from remote branch
169+revision-id: ci-train-bot@canonical.com-yyyymmdd-deadbeef6
170+revision-id: launchpad_translations_on_behalf_of_foo-20151202054232-deadbeef7
171+revision-id: launchpad_translations_on_behalf_of_foo-20151202054232-deadbeef8
172+revision-id: launchpad_translations_on_behalf_of_foo-20151202054232-deadbeef9
173+revision-id: joe-blow@deleted-this-commit-yyyymmdd-deadbeef6
174 """
175
176 BZR_MISSING_THREE = BZR_MISSING_OURS + """
177 You are missing 10000 revisions:
178-committer: CI Train Bot <ci-train-bot@canonical.com>
179-committer: CI Brain Rot on behalf of zombies
180-committer: Lunchpad Translations on behalf of eating
181-committer: Launchpad Translations on behalf of phablet-team
182-committer: Launchpad Translations on behalf of phablet-team
183-committer: Launchpad Translations on behalf of phablet-team
184+revision-id: ci-train-bot@canonical.com-yyyymmdd-deadbeef7
185+revision-id: ci-brain-rot@canonical.com-yyyymmdd-deadbeef6
186+revision-id: lunchpad_translations_on_behalf_of_foo-20151202054232-deadbeef
187+revision-id: launchpad_translations_on_behalf_of_foo-20151202054232-deadbeef10
188+revision-id: launchpad_translations_on_behalf_of_foo-20151202054232-deadbeef11
189+revision-id: launchpad_translations_on_behalf_of_foo-20151202054232-deadbeef12
190 """
191
192 PACKAGING_DIFF_WARNING = """\
193
194=== modified file 'tests/unit/test_branchhandling.py'
195--- tests/unit/test_branchhandling.py 2015-12-02 04:35:10 +0000
196+++ tests/unit/test_branchhandling.py 2015-12-04 01:43:03 +0000
197@@ -27,7 +27,8 @@
198 from cupstream2distro.utils import utf8_open
199 from cupstream2distro.branchhandling import (
200 Branch,
201- get_package_name_from_branch
202+ get_package_name_from_branch,
203+ find_ids_to_ignore,
204 )
205 from cupstream2distro.errors import BranchError, MergeError
206
207@@ -43,13 +44,26 @@
208 self.branch.series = Mock()
209 self.branch.silo_ppa = Mock()
210
211+ @patch('cupstream2distro.branchhandling.bzr_missing')
212+ def test_find_ids_to_ignore(self, missing):
213+ """Return a set of revision ids."""
214+ missing.return_value = ''
215+ self.assertEqual(find_ids_to_ignore('a', 'b'), set())
216+ missing.return_value = s.BZR_MISSING_OURS
217+ self.assertEqual(find_ids_to_ignore('a', 'b'), {
218+ 'ci-train-bot@canonical.com-yyyymmdd-deadbeef1',
219+ 'ci-train-bot@canonical.com-yyyymmdd-deadbeef4',
220+ 'ci-train-bot@canonical.com-yyyymmdd-deadbeef3',
221+ 'ci-train-bot@canonical.com-yyyymmdd-deadbeef2',
222+ })
223+
224 @patch('cupstream2distro.branchhandling.call')
225 def test_get_package_name_from_branch(self, call_mock):
226 """Correctly get the package name from the branch."""
227 call_mock.return_value = (Mock(returncode=0), 'ex (1.0)', '')
228 self.assertEqual(get_package_name_from_branch('lp:example'), 'ex')
229 call_mock.assert_called_once_with(
230- ['bzr', 'cat', '-d', 'lp:example', 'debian/changelog'])
231+ ['bzr', 'cat', '--directory', 'lp:example', 'debian/changelog'])
232
233 @patch('cupstream2distro.branchhandling.call')
234 def test_get_package_name_from_branch_cached(self, call_mock):
235@@ -58,7 +72,7 @@
236 for count in range(10):
237 self.assertEqual(get_package_name_from_branch('lp:example'), 'ex')
238 call_mock.assert_called_once_with( # ONCE
239- ['bzr', 'cat', '-d', 'lp:example', 'debian/changelog'])
240+ ['bzr', 'cat', '--directory', 'lp:example', 'debian/changelog'])
241
242 @patch('cupstream2distro.branchhandling.call')
243 def test_get_package_name_from_branch_failed(self, call_mock):
244@@ -67,7 +81,7 @@
245 with self.assertRaisesRegexp(BranchError, 'unreachable: bark bark'):
246 get_package_name_from_branch('lp:example2')
247 call_mock.assert_called_once_with(
248- ['bzr', 'cat', '-d', 'lp:example2', 'debian/changelog'])
249+ ['bzr', 'cat', '--directory', 'lp:example2', 'debian/changelog'])
250
251 def test_branch_method_names(self):
252 """Ensure all Branch method names contain "branch" for clarity."""
253@@ -404,22 +418,23 @@
254 self.assertEquals(self.branch.get_branch_commits(set()), ({}))
255
256 @patch('cupstream2distro.branchhandling.bzr_missing')
257+ @patch('cupstream2distro.branchhandling.ME', 'ci-train-bot@canonical.com')
258 def test_missing_branch_commits(self, missing):
259 """Ensure we can correctly count the number of missing commits."""
260 missing.return_value = s.BZR_MISSING_IGNORED
261- self.assertEqual(self.branch.missing_branch_commits('lp:foo'), 0)
262+ self.assertIsNone(self.branch.missing_branch_commits('lp:tr', 'lp:tr'))
263 missing.return_value = s.BZR_MISSING_ONE
264- self.assertEqual(self.branch.missing_branch_commits('lp:foo'), 1)
265+ self.assertTrue(self.branch.missing_branch_commits('lp:tr', 'lp:tr'))
266 missing.return_value = s.BZR_MISSING_TWO
267- self.assertEqual(self.branch.missing_branch_commits('lp:foo'), 2)
268+ self.assertTrue(self.branch.missing_branch_commits('lp:tr', 'lp:tr'))
269 missing.return_value = s.BZR_MISSING_THREE
270- self.assertEqual(self.branch.missing_branch_commits('lp:foo'), 3)
271+ self.assertTrue(self.branch.missing_branch_commits('lp:tr', 'lp:tr'))
272
273 @patch('cupstream2distro.branchhandling.call')
274 def test_missing_branch_commits_notabranch(self, call_mock):
275 """Ensure we can correctly count the number of missing commits."""
276 call_mock.return_value = None, '', 'Not a branch: /path/to/foo'
277- self.assertEqual(self.branch.missing_branch_commits('lp:foo'), 1)
278+ self.assertTrue(self.branch.missing_branch_commits('lp:tr', 'lp:tr'))
279
280 @patch('cupstream2distro.branchhandling.call')
281 def test_get_remote_branch_authors(self, call_mock):
282@@ -432,7 +447,7 @@
283 'CI Train Bot <ci-train-bot@canonical.com>',
284 ]))
285 call_mock.assert_called_once_with(
286- ('bzr', 'missing', '--directory', self.tempdir,
287+ ('bzr', 'missing', '--show-ids', '--directory', self.tempdir,
288 'lp:~my/foo/features', '--other'))
289
290 @patch('cupstream2distro.branchhandling.call')
291
292=== modified file 'tests/unit/test_recipe_merge.py'
293--- tests/unit/test_recipe_merge.py 2015-12-02 05:35:19 +0000
294+++ tests/unit/test_recipe_merge.py 2015-12-04 01:43:03 +0000
295@@ -438,13 +438,6 @@
296 merge.missing_branch_commits = Mock(return_value=10)
297 self.assertIn('Needs rebuild', merge.check_new_commits())
298
299- def test_merge_check_new_commits_filenotfound(self):
300- """Report if this package is missing local source."""
301- Merge.all_mps = dict(checkered=[Mock()])
302- merge = Merge('checkered', self.series, self.dest, self.ppa)
303- merge.missing_branch_commits = Mock(side_effect=FileNotFoundError)
304- self.assertEqual('Ready to build', merge.check_new_commits())
305-
306 def test_merge_unbuilt_phase(self):
307 """Block publication if merges have new commits."""
308 mp_obj = Mock()
309@@ -456,8 +449,10 @@
310 'foobaz has new, unbuilt commits.'
311 })
312 self.assertEqual(merge.missing_branch_commits.mock_calls, [
313- call(mp_obj.target_branch.bzr_identity),
314- call(mp_obj.source_branch.bzr_identity),
315+ call(mp_obj.target_branch.bzr_identity,
316+ mp_obj.target_branch.bzr_identity),
317+ call(mp_obj.source_branch.bzr_identity,
318+ mp_obj.target_branch.bzr_identity),
319 ])
320
321 def test_merge_unbuilt_phase_none(self):

Subscribers

People subscribed via source and target branches