Merge lp:~maxb/udd/fix-import-ordering into lp:udd
- fix-import-ordering
- Merge into import-scripts
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 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Jelmer Vernooij (community) | Approve | ||
Review via email: mp+61115@code.launchpad.net |
Commit message
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.
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
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.
Max Bowsher (maxb) wrote : | # |
@ jelmer:
> What about, in addition to testing coming after experimental/sid, things
> entering testing before stable/
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.
Max Bowsher (maxb) wrote : | # |
Additional revisions 429-434 pushed with miscellaneous cleanups, one small fix, and then some tests.
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
Preview Diff
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")) |
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) ?