Merge lp:~maxb/udd/fix-import-ordering into lp:udd

Proposed by Max Bowsher
Status: Merged
Merged at revision: 426
Proposed branch: lp:~maxb/udd/fix-import-ordering
Merge into: lp:udd
Diff against target: 306 lines (+128/-81)
4 files modified
icommon.py (+71/-35)
import_package.py (+0/-1)
tests/test_import_list.py (+5/-8)
tests/test_package_to_import.py (+52/-37)
To merge this branch: bzr merge lp:~maxb/udd/fix-import-ordering
Reviewer Review Type Date Requested Status
Jelmer Vernooij (community) Approve
Review via email: mp+61115@code.launchpad.net

Description of the change

This branch tidies up and then alters the logic for determining in which order several import operations are performed when there are multiple to do for a package.

This is of particular importance when an import which has been broken for some time, and is then fixed. The old code can produce some spectacularly contorted history, doing things such as importing versions in parallel into the testing and unstable Debian branches.

Even with this change, there's still one other quirk that persists, in terms of history looking different because the importer was temporarily broken: Take qbzr as an example, which has been broken since lucid. As a result, once fixed (by inserting a tag which has gone missing), the imported history has an odd shape, with the maverick, natty and oneiric branches all separately taking the lucid branch as their direct left-hand parent.

To post a comment you must log in.
Revision history for this message
Jelmer Vernooij (jelmer) wrote :

Thanks, I think these changes make a lot of sense.

What about, in addition to testing coming after experimental/sid, things entering testing before stable/lenny/etch/etc ?

This seems like fairly trivial code; it would be nice to have a few more tests (and perhaps factor out compare_versions) ?

review: Needs Fixing
Revision history for this message
James Westby (james-w) wrote :

Hi,

Importing all Debian before any Ubuntu would prevent recording merges
if Debian were to merge from Ubuntu. It's a rare case, so ok to sacrifice
if this is needed to make other things work, but if we can preserve
it it would be good.

Thanks,

James

Revision history for this message
Max Bowsher (maxb) wrote :

@ james_w:

> Importing all Debian before any Ubuntu would prevent recording merges
> if Debian were to merge from Ubuntu. It's a rare case, so ok to sacrifice
> if this is needed to make other things work, but if we can preserve
> it it would be good.

That particular element of the ordering is pre-existing before my changes (though this branch adds the comment explaining it). I don't think it will be possible to remove it without significantly rewriting the ordering algorithm to either be based on a topological sort involving introspection of each version's debian/changelog, or to be a simple sort on the timestamp of the publishing records fetched from Launchpad.

Revision history for this message
Max Bowsher (maxb) wrote :

@ jelmer:

> What about, in addition to testing coming after experimental/sid, things
> entering testing before stable/lenny/etch/etc ?

We talked IRL on this and decided there is probably no issue here - because usually nothing should be entering testing other than through unstable, so will be imported first there. Other possibilities are packages entering via *-proposed-updates, but that's OK too, because the UDD importer does not track *-proposed-updates, and versions entering a series via *-proposed-updates do not migrate to other series.

> This seems like fairly trivial code; it would be nice to have a few more tests
> (and perhaps factor out compare_versions) ?

A few more tests wouldn't be hard, I'll make some.

MP marked WIP pending addition of more tests.

Revision history for this message
Max Bowsher (maxb) wrote :

Additional revisions 429-434 pushed with miscellaneous cleanups, one small fix, and then some tests.

Revision history for this message
Jelmer Vernooij (jelmer) wrote :

On Tue, 2011-05-17 at 13:35 +0000, Max Bowsher wrote:
> Additional revisions 429-434 pushed with miscellaneous cleanups, one small fix, and then some tests.

  review approve

Thanks for fixing this.

As discussed IRL, it might be possible to get away with actually
comparing versions here first, distros later (if the versions are equal)
and just having a special case for -backports (always sorting them
later, I guess).

Cheers,

Jelmer

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'icommon.py'
2--- icommon.py 2011-04-28 11:41:36 +0000
3+++ icommon.py 2011-05-17 13:32:07 +0000
4@@ -230,6 +230,8 @@
5
6
7 class PackageToImport(object):
8+ """This object is analogous to (in Soyuz terminology) a source package
9+ publication."""
10
11 def __init__(self, name, version, distro, release, pocket, component="",
12 on_lp=True, url=None):
13@@ -248,6 +250,11 @@
14 self.distro, self.release, self.pocket, self.component,
15 self.url))
16
17+ def __str__(self):
18+ return "PackageToImport(%s, %s, %s, %s, %s, %s)" % (self.name,
19+ self.version, self.distro, self.release, self.pocket,
20+ self.component)
21+
22 def same_as(self, other):
23 return (self.name == other.name
24 and self.version == other.version
25@@ -282,10 +289,69 @@
26 self.url = files_url + "%s_%s.dsc" % (self.name, version_str)
27 return self.url
28
29- def __str__(self):
30- return "PackageToImport(%s, %s, %s, %s, %s, %s)" % (self.name,
31- self.version, self.distro, self.release, self.pocket,
32- self.component)
33+ @staticmethod
34+ def import_order_comparator(a, b):
35+ """This comparator aims to sort prospective import jobs such that
36+ package versions are imported after versions they were derived from,
37+ and publications that are likely to be copied into other archives,
38+ series or pockets are imported before imports to the likely
39+ destinations, such that the source imported version is available to be
40+ pulled."""
41+ # Debian versions should be imported before Ubuntu versions, so
42+ # that any Debian version an Ubuntu version may be based on is
43+ # already imported when the Ubuntu version is imported.
44+ if a.distro != b.distro:
45+ if a.distro == "debian":
46+ return -1
47+ return 1
48+ # From here on, a.distro == b.distro
49+ if a.distro == "ubuntu":
50+ # Ubuntu backports should be imported after all other Ubuntu
51+ # pockets, so that the version the backport is based on is
52+ # imported before the backport itself.
53+ if a.pocket == "backports" and b.pocket != "backports":
54+ return 1
55+ if a.pocket != "backports" and b.pocket == "backports":
56+ return -1
57+ # Within Ubuntu, release series should be imported in ascending
58+ # order, so that a package version is imported into the first
59+ # series in which it is present.
60+ if a.release != b.release:
61+ a_release = distro_releases[a.distro].index(a.release)
62+ b_release = distro_releases[b.distro].index(b.release)
63+ if a_release < b_release:
64+ return -1
65+ return 1
66+ # Within a single Ubuntu release series, import pockets such
67+ # that any pocket that can contain versions based on versions
68+ # in another pocket, is imported after that pocket.
69+ if a.pocket != b.pocket:
70+ a_pocket = distro_pockets[a.distro].index(a.pocket)
71+ b_pocket = distro_pockets[b.distro].index(b.pocket)
72+ if a_pocket < b_pocket:
73+ return -1
74+ return 1
75+ if a.distro == "debian":
76+ # Debian sid/experimental should be imported before any other
77+ # codename, to get the proper ancestry.
78+ if a.release in ("sid", "experimental") and b.release not in ("sid", "experimental"):
79+ return -1
80+ if a.release not in ("sid", "experimental") and b.release in ("sid", "experimental"):
81+ return 1
82+ # Other than that special case, import in ascending order.
83+ if a.release != b.release:
84+ a_release = distro_releases[a.distro].index(a.release)
85+ b_release = distro_releases[b.distro].index(b.release)
86+ if a_release < b_release:
87+ return -1
88+ return 1
89+ # Within a single distro, release, and pocket, import in version
90+ # order.
91+ if a.version < b.version:
92+ return -1
93+ if a.version > b.version:
94+ return 1
95+ return 0
96
97
98 class ImportList(object):
99@@ -297,37 +363,7 @@
100 return '%s(%s)' % (self.__class__.__name__, self.plist)
101
102 def sort(self):
103- def compare_versions(a, b):
104- if a.distro == "ubuntu" and a.distro == b.distro:
105- if a.pocket == "backports" and b.pocket != "backports":
106- return -1
107- if a.pocket != "backports" and b.pocket == "backports":
108- return 1
109- if a.distro != b.distro:
110- if a.distro == "debian":
111- return 1
112- return -1
113- if a.release != b.release:
114- a_release = distro_releases[a.distro].index(a.release)
115- b_release = distro_releases[b.distro].index(b.release)
116- if a_release < b_release:
117- return 1
118- return -1
119- if a.pocket != b.pocket:
120- a_pocket = distro_pockets[a.distro].index(a.pocket)
121- b_pocket = distro_pockets[b.distro].index(b.pocket)
122- if a_pocket < b_pocket:
123- return 1
124- return -1
125- if a.version < b.version:
126- return 1
127- if a.version > b.version:
128- return -1
129- return 0
130- self.plist.sort(cmp=compare_versions)
131-
132- def reverse(self):
133- self.plist.reverse()
134+ self.plist.sort(cmp=PackageToImport.import_order_comparator)
135
136 def add_if_needed(self, newp):
137 for oldp in self.plist:
138
139=== modified file 'import_package.py'
140--- import_package.py 2011-03-28 09:33:45 +0000
141+++ import_package.py 2011-05-17 13:32:07 +0000
142@@ -1014,7 +1014,6 @@
143 trace.mutter("finding all versions of %s" % (package,))
144 versions = get_versions(package, extra_debian=extra_debian)
145 assert len(versions.plist) > 0
146- versions.reverse()
147 trace.mutter("found %d versions: %s"
148 % (len(versions.plist), versions))
149 possible_transports = []
150
151=== modified file 'tests/test_import_list.py'
152--- tests/test_import_list.py 2011-03-24 18:52:48 +0000
153+++ tests/test_import_list.py 2011-05-17 13:32:07 +0000
154@@ -22,17 +22,14 @@
155 "jaunty, Release, ), PackageToImport(pkg, 1.0-1, "
156 "ubuntu, karmic, Release, )]", str(list))
157
158- def test_reverse(self):
159- list = self.make_simple()
160- self.assertEqual([self.pkg1, self.pkg2], list.plist)
161- list.reverse()
162- self.assertEqual([self.pkg2, self.pkg1], list.plist)
163-
164 def test_sort(self):
165 list = self.make_simple()
166- self.assertEqual([self.pkg1, self.pkg2], list.plist)
167+ # make_simple's list is already sorted, reverse it to prove that
168+ # sorting will restore the original order:
169+ list.plist.reverse()
170+ self.assertEqual([self.pkg2, self.pkg1], list.plist)
171 list.sort()
172- self.assertEqual([self.pkg2, self.pkg1], list.plist)
173+ self.assertEqual([self.pkg1, self.pkg2], list.plist)
174
175 def test_add_if_needed(self):
176 list = self.make_simple()
177
178=== modified file 'tests/test_package_to_import.py'
179--- tests/test_package_to_import.py 2011-03-24 18:39:55 +0000
180+++ tests/test_package_to_import.py 2011-05-17 13:32:07 +0000
181@@ -7,38 +7,30 @@
182
183
184 import icommon
185+from icommon import PackageToImport as PTI
186+V = changelog.Version
187
188
189 class PackageToImportTests(tests.TestCase):
190
191 def test_same_as_same(self):
192- a = icommon.PackageToImport("a", changelog.Version("1"),
193- "debian", "sid", "release", component="main",
194- on_lp=True, url=None)
195- b = icommon.PackageToImport("a", changelog.Version("1"),
196- "debian", "sid", "release", component="contrib")
197- c = icommon.PackageToImport("a", changelog.Version("1"),
198- "debian", "sid", "release", on_lp=False)
199- d = icommon.PackageToImport("a", changelog.Version("1"),
200- "debian", "sid", "release", url="a")
201+ a = PTI("a", V("1"), "debian", "sid", "release",
202+ component="main", on_lp=True, url=None)
203+ b = PTI("a", V("1"), "debian", "sid", "release", component="contrib")
204+ c = PTI("a", V("1"), "debian", "sid", "release", on_lp=False)
205+ d = PTI("a", V("1"), "debian", "sid", "release", url="a")
206 self.assertTrue(a.same_as(b))
207 self.assertTrue(a.same_as(c))
208 self.assertTrue(a.same_as(d))
209
210 def test_same_as_different(self):
211- a = icommon.PackageToImport("a", changelog.Version("1"),
212- "debian", "sid", "release", component="main",
213- on_lp=True, url=None)
214- b = icommon.PackageToImport("b", changelog.Version("1"),
215- "debian", "sid", "release")
216- c = icommon.PackageToImport("a", changelog.Version("1.1"),
217- "debian", "sid", "release")
218- d = icommon.PackageToImport("a", changelog.Version("1"),
219- "ubuntu", "sid", "release")
220- e = icommon.PackageToImport("a", changelog.Version("1"),
221- "debian", "squeeze", "release")
222- f = icommon.PackageToImport("a", changelog.Version("1"),
223- "debian", "sid", "security")
224+ a = PTI("a", V("1"), "debian", "sid", "release",
225+ component="main", on_lp=True, url=None)
226+ b = PTI("b", V("1"), "debian", "sid", "release")
227+ c = PTI("a", V("1.1"), "debian", "sid", "release")
228+ d = PTI("a", V("1"), "ubuntu", "sid", "release")
229+ e = PTI("a", V("1"), "debian", "squeeze", "release")
230+ f = PTI("a", V("1"), "debian", "sid", "security")
231 self.assertFalse(a.same_as(b))
232 self.assertFalse(a.same_as(c))
233 self.assertFalse(a.same_as(d))
234@@ -46,34 +38,57 @@
235 self.assertFalse(a.same_as(f))
236
237 def test_get_url(self):
238- a = icommon.PackageToImport("a", changelog.Version("1"),
239- "debian", "sid", "release", url="url")
240+ a = PTI("a", V("1"), "debian", "sid", "release", url="url")
241 self.assertEqual("url", a.get_url())
242- b = icommon.PackageToImport("a", changelog.Version("1"),
243- "debian", "sid", "release", component="non-free", on_lp=False)
244+ b = PTI("a", V("1"), "debian", "sid", "release",
245+ component="non-free", on_lp=False)
246 self.assertEqual(icommon.urllib_debian_base_url
247 + "pool/non-free/a/a/a_1.dsc", b.get_url())
248- c = icommon.PackageToImport("libfoo", changelog.Version("1:1.1-1"),
249- "debian", "squeeze", "release", component="contrib", on_lp=False)
250+ c = PTI("libfoo", V("1:1.1-1"), "debian", "squeeze", "release",
251+ component="contrib", on_lp=False)
252 self.assertEqual(icommon.urllib_debian_base_url
253 + "pool/contrib/libf/libfoo/libfoo_1.1-1.dsc", c.get_url())
254- c = icommon.PackageToImport("libfoo", changelog.Version("1:1.1-1"),
255- "debian", "squeeze", "release", component="contrib",
256- on_lp=True)
257+ c = PTI("libfoo", V("1:1.1-1"), "debian", "squeeze", "release",
258+ component="contrib", on_lp=True)
259 self.assertEqual(icommon.urllib_launchpad_base_url
260 + "debian/squeeze/+source/libfoo/1:1.1-1/+files/libfoo_1.1-1.dsc",
261 c.get_url())
262- d = icommon.PackageToImport("libfoo", changelog.Version("1:1.1-1"),
263- "ubuntu", "lucid", "security")
264+ d = PTI("libfoo", V("1:1.1-1"), "ubuntu", "lucid", "security")
265 self.assertEqual(icommon.urllib_launchpad_base_url
266 + "ubuntu/lucid/+source/libfoo/1:1.1-1/+files/libfoo_1.1-1.dsc",
267 d.get_url())
268
269 def test_str(self):
270- a = icommon.PackageToImport("a", changelog.Version("1"),
271- "debian", "sid", "release", component="main",
272- on_lp=True, url=None)
273+ a = PTI("a", V("1"), "debian", "sid", "release",
274+ component="main", on_lp=True, url=None)
275 self.assertEqual("PackageToImport(a, 1, debian, sid, release, main)",
276 str(a))
277
278-
279+ def assertOrdering(self, a, b):
280+ self.assertTrue(PTI.import_order_comparator(a, b) < 0)
281+
282+ def test_comparison(self):
283+ # Distro is the most-significant factor, with all of Ubuntu coming
284+ # after all of Debian, irregardless of releases and versions:
285+ self.assertOrdering(PTI("a", V("999999"), "debian", "sid", "release"),
286+ PTI("a", V("1"), "ubuntu", "warty", "release"))
287+
288+ # Within a distro, release is the next most significant:
289+ self.assertOrdering(PTI("a", V("1"), "debian", "lenny", "release"),
290+ PTI("a", V("1"), "debian", "squeeze", "release"))
291+
292+ # Except for Ubuntu backports, which are a special case, imported after
293+ # all other Ubuntu imports in the same per-package run:
294+ self.assertOrdering(PTI("a", V("1"), "ubuntu", "natty", "release"),
295+ PTI("a", V("1~hardy1"), "ubuntu", "hardy",
296+ "backports"))
297+
298+ # Within Ubuntu, pockets matter:
299+ self.assertOrdering(PTI("a", V("1"), "ubuntu", "natty", "release"),
300+ PTI("a", V("1"), "ubuntu", "natty", "security"))
301+ self.assertOrdering(PTI("a", V("1"), "ubuntu", "natty", "security"),
302+ PTI("a", V("1"), "ubuntu", "natty", "proposed"))
303+ self.assertOrdering(PTI("a", V("1"), "ubuntu", "natty", "proposed"),
304+ PTI("a", V("1"), "ubuntu", "natty", "updates"))
305+ self.assertOrdering(PTI("a", V("1"), "ubuntu", "natty", "updates"),
306+ PTI("a", V("1"), "ubuntu", "natty", "backports"))

Subscribers

People subscribed via source and target branches