Merge ~cjwatson/launchpad:built-using-model into launchpad:master
- Git
- lp:~cjwatson/launchpad
- built-using-model
- Merge into master
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | Colin Watson | ||||
Approved revision: | 4f4c8809bc92c7ef495d8281b0fbac9cd2d7bc92 | ||||
Merge reported by: | Otto Co-Pilot | ||||
Merged at revision: | not available | ||||
Proposed branch: | ~cjwatson/launchpad:built-using-model | ||||
Merge into: | launchpad:master | ||||
Diff against target: |
1100 lines (+678/-24) 18 files modified
database/schema/security.cfg (+2/-0) lib/lp/archiveuploader/nascentuploadfile.py (+8/-1) lib/lp/archiveuploader/tests/test_nascentuploadfile.py (+29/-1) lib/lp/registry/model/distroseries.py (+21/-1) lib/lp/soyuz/configure.zcml (+14/-0) lib/lp/soyuz/doc/distroarchseriesbinarypackage.txt (+2/-0) lib/lp/soyuz/enums.py (+14/-1) lib/lp/soyuz/interfaces/binarypackagebuild.py (+3/-3) lib/lp/soyuz/interfaces/binarypackagerelease.py (+6/-1) lib/lp/soyuz/interfaces/binarysourcereference.py (+86/-0) lib/lp/soyuz/model/binarypackagebuild.py (+13/-4) lib/lp/soyuz/model/binarypackagerelease.py (+15/-1) lib/lp/soyuz/model/binarysourcereference.py (+149/-0) lib/lp/soyuz/scripts/gina/handlers.py (+16/-1) lib/lp/soyuz/scripts/gina/packages.py (+10/-1) lib/lp/soyuz/scripts/tests/test_gina.py (+73/-4) lib/lp/soyuz/tests/test_binarysourcereference.py (+208/-0) lib/lp/soyuz/tests/test_publishing.py (+9/-5) |
||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Tom Wardill (community) | Approve | ||
William Grant | direction | Approve | |
Review via email: mp+381234@code.launchpad.net |
Commit message
Parse Built-Using fields from uploaded binaries
Description of the change
These are parsed into a form that we'll later be able to use in the dominator, with the goal of keeping source publications that are referenced by Built-Using fields in active binary publications.
The most complicated bit is working out which source package releases a given Built-Using field refers to, since there are edge cases where just the name and version can be ambiguous. We do the best we can by following the archive dependencies of the build and finding sources that it could plausibly have used.
Unresolvable entries in Built-Using, including ones that refer to deleted source publications, will result in binary uploads being rejected.
Database MP: https:/
William Grant (wgrant) : | # |
- de9e46b... by Colin Watson
-
Explicitly check "build.
current_ component is None" This can happen in tests, especially of gina, and the resulting crash is
a bit confusing, so let's raise a clearer exception in this case. - fde0f10... by Colin Watson
-
Preserve original Built-Using fields for publication
We aren't sure whether apt will get confused if the exact serialisation
(e.g. ordering) of Built-Using in Packages differs from that in the
package's control file, so let's preserve it rather than trying to
reconstruct it. - 0692e86... by Colin Watson
-
Don't consider SPPH.status when creating BSRs
We don't normally treat SUPERSEDED differently from DELETED in this sort
of situation, and at that point it isn't really worth checking the
status at all. - a4163e3... by Colin Watson
-
Add more vertical whitespace
- 7ca1f32... by Colin Watson
-
Limit Built-Using references to the same archive and series
Allowing cross-archive references makes the dominator's job harder, as
well as permitting non-consensual pinning of sources to the Published
state in foreign archives. Forbidding such references may be confusing
in some cases, but seems likely to be less bad than the alternative. - 8983670... by Colin Watson
-
Refactor createFromRelat
ionship using Archive. getPublishedSou rces Now that we're only considering a single archive, this is equivalent and
much clearer. - e9bb481... by Colin Watson
-
Add BinarySourceRef
erence. createFromSourc ePackageRelease s This makes some tests (especially in branches further up this stack)
more convenient.
- 6fd4090... by Colin Watson
-
Clarify "unfortunate apt behaviour"
- 4f4c880... by Colin Watson
-
Give IBinaryPackageR
elease. built_using_ references a value_type
Colin Watson (cjwatson) wrote : | # |
I've clarified this.
Tom Wardill (twom) wrote : | # |
The model and implementation for this seem to make sense. I'm not entirely fluent in apt behaviours, but this LGTM :).
Preview Diff
1 | diff --git a/database/schema/security.cfg b/database/schema/security.cfg |
2 | index a0cf410..5ead0b8 100644 |
3 | --- a/database/schema/security.cfg |
4 | +++ b/database/schema/security.cfg |
5 | @@ -1213,6 +1213,7 @@ public.binarypackagefile = SELECT, INSERT, UPDATE |
6 | public.binarypackagename = SELECT, INSERT, UPDATE |
7 | public.binarypackagepublishinghistory = SELECT, INSERT, UPDATE, DELETE |
8 | public.binarypackagerelease = SELECT, INSERT, UPDATE |
9 | +public.binarysourcereference = SELECT, INSERT |
10 | public.branch = SELECT, INSERT, UPDATE |
11 | public.bug = SELECT, INSERT, UPDATE |
12 | public.bugactivity = SELECT, INSERT, UPDATE |
13 | @@ -1385,6 +1386,7 @@ public.binarypackagefile = SELECT, INSERT |
14 | public.binarypackagename = SELECT, INSERT |
15 | public.binarypackagepublishinghistory = SELECT |
16 | public.binarypackagerelease = SELECT, INSERT |
17 | +public.binarysourcereference = SELECT, INSERT |
18 | public.bug = SELECT, UPDATE |
19 | public.bugactivity = SELECT, INSERT |
20 | public.bugaffectsperson = SELECT, INSERT, UPDATE, DELETE |
21 | diff --git a/lib/lp/archiveuploader/nascentuploadfile.py b/lib/lp/archiveuploader/nascentuploadfile.py |
22 | index aa0ec95..f28e43e 100644 |
23 | --- a/lib/lp/archiveuploader/nascentuploadfile.py |
24 | +++ b/lib/lp/archiveuploader/nascentuploadfile.py |
25 | @@ -1,4 +1,4 @@ |
26 | -# Copyright 2009-2019 Canonical Ltd. This software is licensed under the |
27 | +# Copyright 2009-2020 Canonical Ltd. This software is licensed under the |
28 | # GNU Affero General Public License version 3 (see the file LICENSE). |
29 | |
30 | """Specific models for uploaded files""" |
31 | @@ -445,6 +445,12 @@ class BaseBinaryUploadFile(PackageUploadFile): |
32 | "Provides", |
33 | "Pre-Depends", |
34 | "Enhances", |
35 | + # Note that we intentionally don't include Built-Using here; |
36 | + # although we parse it, we want to preserve its original form to |
37 | + # make sure apt doesn't decide that it needs to keep re-upgrading |
38 | + # the package to the same version because the metadata looks |
39 | + # slightly out of sync. This is most easily done by adding it to |
40 | + # user_defined_fields. |
41 | "Essential", |
42 | "Description", |
43 | "Installed-Size", |
44 | @@ -920,6 +926,7 @@ class BaseBinaryUploadFile(PackageUploadFile): |
45 | pre_depends=encoded.get('Pre-Depends', ''), |
46 | enhances=encoded.get('Enhances', ''), |
47 | breaks=encoded.get('Breaks', ''), |
48 | + built_using=encoded.get('Built-Using', ''), |
49 | homepage=encoded.get('Homepage'), |
50 | essential=is_essential, |
51 | installedsize=installedsize, |
52 | diff --git a/lib/lp/archiveuploader/tests/test_nascentuploadfile.py b/lib/lp/archiveuploader/tests/test_nascentuploadfile.py |
53 | index 54841a9..ff63636 100644 |
54 | --- a/lib/lp/archiveuploader/tests/test_nascentuploadfile.py |
55 | +++ b/lib/lp/archiveuploader/tests/test_nascentuploadfile.py |
56 | @@ -1,4 +1,4 @@ |
57 | -# Copyright 2010-2019 Canonical Ltd. This software is licensed under the |
58 | +# Copyright 2010-2020 Canonical Ltd. This software is licensed under the |
59 | # GNU Affero General Public License version 3 (see the file LICENSE). |
60 | |
61 | """Test NascentUploadFile functionality.""" |
62 | @@ -26,6 +26,8 @@ from testtools.matchers import ( |
63 | MatchesAny, |
64 | MatchesListwise, |
65 | MatchesRegex, |
66 | + MatchesSetwise, |
67 | + MatchesStructure, |
68 | ) |
69 | |
70 | from lp.archiveuploader.changesfile import ChangesFile |
71 | @@ -43,6 +45,7 @@ from lp.services.compat import lzma |
72 | from lp.services.log.logger import BufferLogger |
73 | from lp.services.osutils import write_file |
74 | from lp.soyuz.enums import ( |
75 | + BinarySourceReferenceType, |
76 | PackagePublishingStatus, |
77 | PackageUploadCustomFormat, |
78 | ) |
79 | @@ -573,6 +576,31 @@ class DebBinaryUploadFileTests(PackageUploadFileTestCase): |
80 | [u"RandomData", u"Foo\nbar\nbla\n"], |
81 | ], bpr.user_defined_fields) |
82 | |
83 | + def test_built_using(self): |
84 | + # storeInDatabase parses Built-Using into BinarySourceReference |
85 | + # rows, and also adds the unparsed contents to user_defined_fields. |
86 | + uploadfile = self.createDebBinaryUploadFile( |
87 | + "foo_0.42_i386.deb", "main/python", "unknown", "mypkg", "0.42", |
88 | + None) |
89 | + control = self.getBaseControl() |
90 | + control["Built-Using"] = b"bar (= 0.1)" |
91 | + uploadfile.parseControl(control) |
92 | + build = self.factory.makeBinaryPackageBuild() |
93 | + spph = self.factory.makeSourcePackagePublishingHistory( |
94 | + archive=build.archive, distroseries=build.distro_series, |
95 | + pocket=build.pocket, sourcepackagename="bar", version="0.1") |
96 | + bpr = uploadfile.storeInDatabase(build) |
97 | + self.assertThat( |
98 | + bpr.built_using_references, |
99 | + MatchesSetwise( |
100 | + MatchesStructure.byEquality( |
101 | + binary_package_release=bpr, |
102 | + source_package_release=spph.sourcepackagerelease, |
103 | + reference_type=BinarySourceReferenceType.BUILT_USING, |
104 | + ))) |
105 | + self.assertEqual( |
106 | + [[u"Built-Using", u"bar (= 0.1)"]], bpr.user_defined_fields) |
107 | + |
108 | def test_homepage(self): |
109 | # storeInDatabase stores homepage field. |
110 | uploadfile = self.createDebBinaryUploadFile( |
111 | diff --git a/lib/lp/registry/model/distroseries.py b/lib/lp/registry/model/distroseries.py |
112 | index 51dbeea..fefc8cd 100644 |
113 | --- a/lib/lp/registry/model/distroseries.py |
114 | +++ b/lib/lp/registry/model/distroseries.py |
115 | @@ -1,4 +1,4 @@ |
116 | -# Copyright 2009-2019 Canonical Ltd. This software is licensed under the |
117 | +# Copyright 2009-2020 Canonical Ltd. This software is licensed under the |
118 | # GNU Affero General Public License version 3 (see the file LICENSE). |
119 | |
120 | """Database classes for a distribution series.""" |
121 | @@ -124,6 +124,7 @@ from lp.services.propertycache import ( |
122 | from lp.services.worlddata.model.language import Language |
123 | from lp.soyuz.enums import ( |
124 | ArchivePurpose, |
125 | + BinarySourceReferenceType, |
126 | IndexCompressionType, |
127 | PackagePublishingStatus, |
128 | PackageUploadStatus, |
129 | @@ -1138,6 +1139,9 @@ class DistroSeries(SQLBase, BugTargetBase, HasSpecificationsMixin, |
130 | |
131 | def getBinaryPackagePublishing(self, archtag, pocket, component, archive): |
132 | """See `IDistroSeries`.""" |
133 | + # Circular import. |
134 | + from lp.soyuz.model.binarysourcereference import BinarySourceReference |
135 | + |
136 | bpphs = Store.of(self).find( |
137 | BinaryPackagePublishingHistory, |
138 | DistroArchSeries.distroseries == self, |
139 | @@ -1161,6 +1165,22 @@ class DistroSeries(SQLBase, BugTargetBase, HasSpecificationsMixin, |
140 | bpbs = load_related(BinaryPackageBuild, bprs, ["buildID"]) |
141 | sprs = load_related( |
142 | SourcePackageRelease, bpbs, ["source_package_release_id"]) |
143 | + |
144 | + built_using_bsrs = load_referencing( |
145 | + BinarySourceReference, bprs, ["binary_package_release_id"], |
146 | + extra_conditions=[ |
147 | + BinarySourceReference.reference_type == |
148 | + BinarySourceReferenceType.BUILT_USING, |
149 | + ]) |
150 | + # Make sure this is initialised for all BPRs, as some may not |
151 | + # have any BinarySourceReferences. |
152 | + built_using_bsr_map = {bpr: [] for bpr in bprs} |
153 | + for bsr in built_using_bsrs: |
154 | + built_using_bsr_map[bsr.binary_package_release].append(bsr) |
155 | + for bpr, bsrs in built_using_bsr_map.items(): |
156 | + get_property_cache(bpr).built_using_references = sorted( |
157 | + bsrs, key=attrgetter("id")) |
158 | + |
159 | bpfs = load_referencing( |
160 | BinaryPackageFile, bprs, ["binarypackagereleaseID"]) |
161 | file_map = collections.defaultdict(list) |
162 | diff --git a/lib/lp/soyuz/configure.zcml b/lib/lp/soyuz/configure.zcml |
163 | index 89c9d83..2e97958 100644 |
164 | --- a/lib/lp/soyuz/configure.zcml |
165 | +++ b/lib/lp/soyuz/configure.zcml |
166 | @@ -70,6 +70,20 @@ |
167 | set_attributes="changelog"/> |
168 | </class> |
169 | |
170 | + <!-- BinarySourceReference --> |
171 | + |
172 | + <class |
173 | + class="lp.soyuz.model.binarysourcereference.BinarySourceReference"> |
174 | + <allow |
175 | + interface="lp.soyuz.interfaces.binarysourcereference.IBinarySourceReference"/> |
176 | + </class> |
177 | + <securedutility |
178 | + class="lp.soyuz.model.binarysourcereference.BinarySourceReferenceSet" |
179 | + provides="lp.soyuz.interfaces.binarysourcereference.IBinarySourceReferenceSet"> |
180 | + <allow |
181 | + interface="lp.soyuz.interfaces.binarysourcereference.IBinarySourceReferenceSet"/> |
182 | + </securedutility> |
183 | + |
184 | <!-- SourcePackagePublishingHistory --> |
185 | |
186 | <class |
187 | diff --git a/lib/lp/soyuz/doc/distroarchseriesbinarypackage.txt b/lib/lp/soyuz/doc/distroarchseriesbinarypackage.txt |
188 | index 03d690a..c759bbf 100644 |
189 | --- a/lib/lp/soyuz/doc/distroarchseriesbinarypackage.txt |
190 | +++ b/lib/lp/soyuz/doc/distroarchseriesbinarypackage.txt |
191 | @@ -62,6 +62,7 @@ needs to be removed. |
192 | ... pre_depends=None, |
193 | ... enhances=None, |
194 | ... breaks=None, |
195 | + ... built_using=None, |
196 | ... essential=False, |
197 | ... installedsize=0, |
198 | ... architecturespecific=False, |
199 | @@ -106,6 +107,7 @@ needs to be removed. |
200 | ... pre_depends=None, |
201 | ... enhances=None, |
202 | ... breaks=None, |
203 | + ... built_using=None, |
204 | ... essential=False, |
205 | ... installedsize=0, |
206 | ... architecturespecific=False, |
207 | diff --git a/lib/lp/soyuz/enums.py b/lib/lp/soyuz/enums.py |
208 | index b8dafa8..641df6b 100644 |
209 | --- a/lib/lp/soyuz/enums.py |
210 | +++ b/lib/lp/soyuz/enums.py |
211 | @@ -1,4 +1,4 @@ |
212 | -# Copyright 2010-2019 Canonical Ltd. This software is licensed under the |
213 | +# Copyright 2010-2020 Canonical Ltd. This software is licensed under the |
214 | # GNU Affero General Public License version 3 (see the file LICENSE). |
215 | |
216 | """Enumerations used in the lp/soyuz modules.""" |
217 | @@ -13,6 +13,7 @@ __all__ = [ |
218 | 'archive_suffixes', |
219 | 'BinaryPackageFileType', |
220 | 'BinaryPackageFormat', |
221 | + 'BinarySourceReferenceType', |
222 | 'DistroArchSeriesFilterSense', |
223 | 'IndexCompressionType', |
224 | 'PackageCopyPolicy', |
225 | @@ -613,3 +614,15 @@ class DistroArchSeriesFilterSense(DBEnumeratedType): |
226 | |
227 | Packages in this package set are excluded from the distro arch series. |
228 | """) |
229 | + |
230 | + |
231 | +class BinarySourceReferenceType(DBEnumeratedType): |
232 | + """The type of a reference from a binary package to a source package.""" |
233 | + |
234 | + BUILT_USING = DBItem(1, """ |
235 | + Built-Using |
236 | + |
237 | + The referencing binary package incorporates part of the referenced |
238 | + source package, and so the referenced source package needs to remain |
239 | + in the archive for as long as the referencing binary package does. |
240 | + """) |
241 | diff --git a/lib/lp/soyuz/interfaces/binarypackagebuild.py b/lib/lp/soyuz/interfaces/binarypackagebuild.py |
242 | index 8546a85..d811c6c 100644 |
243 | --- a/lib/lp/soyuz/interfaces/binarypackagebuild.py |
244 | +++ b/lib/lp/soyuz/interfaces/binarypackagebuild.py |
245 | @@ -1,4 +1,4 @@ |
246 | -# Copyright 2009-2017 Canonical Ltd. This software is licensed under the |
247 | +# Copyright 2009-2020 Canonical Ltd. This software is licensed under the |
248 | # GNU Affero General Public License version 3 (see the file LICENSE). |
249 | |
250 | """BinaryPackageBuild interfaces.""" |
251 | @@ -179,8 +179,8 @@ class IBinaryPackageBuildView(IPackageBuild): |
252 | component, section, priority, installedsize, architecturespecific, |
253 | shlibdeps=None, depends=None, recommends=None, suggests=None, |
254 | conflicts=None, replaces=None, provides=None, pre_depends=None, |
255 | - enhances=None, breaks=None, essential=False, debug_package=None, |
256 | - user_defined_fields=None, homepage=None): |
257 | + enhances=None, breaks=None, built_using=None, essential=False, |
258 | + debug_package=None, user_defined_fields=None, homepage=None): |
259 | """Create and return a `BinaryPackageRelease`. |
260 | |
261 | The binarypackagerelease will be attached to this specific build. |
262 | diff --git a/lib/lp/soyuz/interfaces/binarypackagerelease.py b/lib/lp/soyuz/interfaces/binarypackagerelease.py |
263 | index 32fe93c..c69a23d 100644 |
264 | --- a/lib/lp/soyuz/interfaces/binarypackagerelease.py |
265 | +++ b/lib/lp/soyuz/interfaces/binarypackagerelease.py |
266 | @@ -1,4 +1,4 @@ |
267 | -# Copyright 2009-2012 Canonical Ltd. This software is licensed under the |
268 | +# Copyright 2009-2020 Canonical Ltd. This software is licensed under the |
269 | # GNU Affero General Public License version 3 (see the file LICENSE). |
270 | |
271 | """Binary package release interfaces.""" |
272 | @@ -61,6 +61,11 @@ class IBinaryPackageRelease(Interface): |
273 | pre_depends = TextLine(required=False) |
274 | enhances = TextLine(required=False) |
275 | breaks = TextLine(required=False) |
276 | + built_using_references = List( |
277 | + title=_("Sequence of Built-Using references."), |
278 | + # Really IBinarySourceReference. |
279 | + value_type=Reference(schema=Interface), |
280 | + required=True) |
281 | essential = Bool(required=False) |
282 | installedsize = Int(required=False) |
283 | architecturespecific = Bool(required=True) |
284 | diff --git a/lib/lp/soyuz/interfaces/binarysourcereference.py b/lib/lp/soyuz/interfaces/binarysourcereference.py |
285 | new file mode 100644 |
286 | index 0000000..dc21765 |
287 | --- /dev/null |
288 | +++ b/lib/lp/soyuz/interfaces/binarysourcereference.py |
289 | @@ -0,0 +1,86 @@ |
290 | +# Copyright 2020 Canonical Ltd. This software is licensed under the |
291 | +# GNU Affero General Public License version 3 (see the file LICENSE). |
292 | + |
293 | +"""Interface for references from binary packages to source packages.""" |
294 | + |
295 | +from __future__ import absolute_import, print_function, unicode_literals |
296 | + |
297 | +__metaclass__ = type |
298 | +__all__ = [ |
299 | + 'IBinarySourceReference', |
300 | + 'IBinarySourceReferenceSet', |
301 | + 'UnparsableBuiltUsing', |
302 | + ] |
303 | + |
304 | +from lazr.restful.fields import Reference |
305 | +from zope.interface import Interface |
306 | +from zope.schema import ( |
307 | + Choice, |
308 | + Int, |
309 | + ) |
310 | + |
311 | +from lp import _ |
312 | +from lp.soyuz.enums import BinarySourceReferenceType |
313 | +from lp.soyuz.interfaces.binarypackagerelease import IBinaryPackageRelease |
314 | +from lp.soyuz.interfaces.sourcepackagerelease import ISourcePackageRelease |
315 | + |
316 | + |
317 | +class UnparsableBuiltUsing(Exception): |
318 | + """A Built-Using field could not be parsed.""" |
319 | + |
320 | + |
321 | +class IBinarySourceReference(Interface): |
322 | + """A reference from a binary package to a source package.""" |
323 | + |
324 | + id = Int(title=_("ID")) |
325 | + |
326 | + binary_package_release = Reference( |
327 | + IBinaryPackageRelease, |
328 | + title=_("The referencing binary package release."), |
329 | + required=True, readonly=True) |
330 | + source_package_release = Reference( |
331 | + ISourcePackageRelease, |
332 | + title=_("The referenced source package release."), |
333 | + required=True, readonly=True) |
334 | + reference_type = Choice( |
335 | + title=_("The type of the reference."), |
336 | + vocabulary=BinarySourceReferenceType, |
337 | + required=True, readonly=True) |
338 | + |
339 | + |
340 | +class IBinarySourceReferenceSet(Interface): |
341 | + """A set of references from binary packages to source packages.""" |
342 | + |
343 | + def createFromRelationship(bpr, relationship, reference_type): |
344 | + """Create references from a text relationship field. |
345 | + |
346 | + :param bpr: The `IBinaryPackageRelease` from which new references |
347 | + should be created. |
348 | + :param relationship: A text relationship field containing one or |
349 | + more source package relations in the usual Debian encoding (e.g. |
350 | + "source1 (= 1.0), source2 (= 2.0)"). |
351 | + :param reference_type: The `BinarySourceReferenceType` of references |
352 | + to create. |
353 | + :return: A list of new `IBinarySourceReference`s. |
354 | + """ |
355 | + |
356 | + def createFromSourcePackageReleases(bpr, sprs, reference_type): |
357 | + """Create references from a sequence of source package releases. |
358 | + |
359 | + This is a convenience method for use in tests. |
360 | + |
361 | + :param bpr: The `IBinaryPackageRelease` from which new references |
362 | + should be created. |
363 | + :param sprs: A sequence of `ISourcePackageRelease`s. |
364 | + :param reference_type: The `BinarySourceReferenceType` of references |
365 | + to create. |
366 | + :return: A list of new `IBinarySourceReference`s. |
367 | + """ |
368 | + |
369 | + def findByBinaryPackageRelease(bpr, reference_type): |
370 | + """Find references from a given binary package release. |
371 | + |
372 | + :param bpr: An `IBinaryPackageRelease` to search for. |
373 | + :param reference_type: A `BinarySourceReferenceType` to search for. |
374 | + :return: A `ResultSet` of matching `IBinarySourceReference`s. |
375 | + """ |
376 | diff --git a/lib/lp/soyuz/model/binarypackagebuild.py b/lib/lp/soyuz/model/binarypackagebuild.py |
377 | index ae4f1de..470a8f2 100644 |
378 | --- a/lib/lp/soyuz/model/binarypackagebuild.py |
379 | +++ b/lib/lp/soyuz/model/binarypackagebuild.py |
380 | @@ -1,4 +1,4 @@ |
381 | -# Copyright 2009-2019 Canonical Ltd. This software is licensed under the |
382 | +# Copyright 2009-2020 Canonical Ltd. This software is licensed under the |
383 | # GNU Affero General Public License version 3 (see the file LICENSE). |
384 | |
385 | __metaclass__ = type |
386 | @@ -91,6 +91,7 @@ from lp.services.macaroons.model import MacaroonIssuerBase |
387 | from lp.soyuz.adapters.buildarch import determine_architectures_to_build |
388 | from lp.soyuz.enums import ( |
389 | ArchivePurpose, |
390 | + BinarySourceReferenceType, |
391 | PackagePublishingStatus, |
392 | ) |
393 | from lp.soyuz.interfaces.archive import ( |
394 | @@ -104,6 +105,9 @@ from lp.soyuz.interfaces.binarypackagebuild import ( |
395 | IBinaryPackageBuildSet, |
396 | UnparsableDependencies, |
397 | ) |
398 | +from lp.soyuz.interfaces.binarysourcereference import ( |
399 | + IBinarySourceReferenceSet, |
400 | + ) |
401 | from lp.soyuz.interfaces.distroarchseries import IDistroArchSeries |
402 | from lp.soyuz.interfaces.packageset import IPackagesetSet |
403 | from lp.soyuz.mail.binarypackagebuild import BinaryPackageBuildMailer |
404 | @@ -662,10 +666,11 @@ class BinaryPackageBuild(PackageBuildMixin, SQLBase): |
405 | binpackageformat, component, section, priority, installedsize, |
406 | architecturespecific, shlibdeps=None, depends=None, recommends=None, |
407 | suggests=None, conflicts=None, replaces=None, provides=None, |
408 | - pre_depends=None, enhances=None, breaks=None, essential=False, |
409 | - debug_package=None, user_defined_fields=None, homepage=None): |
410 | + pre_depends=None, enhances=None, breaks=None, built_using=None, |
411 | + essential=False, debug_package=None, user_defined_fields=None, |
412 | + homepage=None): |
413 | """See IBuild.""" |
414 | - return BinaryPackageRelease( |
415 | + bpr = BinaryPackageRelease( |
416 | build=self, binarypackagename=binarypackagename, version=version, |
417 | summary=summary, description=description, |
418 | binpackageformat=binpackageformat, |
419 | @@ -677,6 +682,10 @@ class BinaryPackageBuild(PackageBuildMixin, SQLBase): |
420 | architecturespecific=architecturespecific, |
421 | debug_package=debug_package, |
422 | user_defined_fields=user_defined_fields, homepage=homepage) |
423 | + if built_using: |
424 | + getUtility(IBinarySourceReferenceSet).createFromRelationship( |
425 | + bpr, built_using, BinarySourceReferenceType.BUILT_USING) |
426 | + return bpr |
427 | |
428 | def estimateDuration(self): |
429 | """See `IPackageBuild`.""" |
430 | diff --git a/lib/lp/soyuz/model/binarypackagerelease.py b/lib/lp/soyuz/model/binarypackagerelease.py |
431 | index ad4f514..3458b70 100644 |
432 | --- a/lib/lp/soyuz/model/binarypackagerelease.py |
433 | +++ b/lib/lp/soyuz/model/binarypackagerelease.py |
434 | @@ -1,4 +1,4 @@ |
435 | -# Copyright 2009-2012 Canonical Ltd. This software is licensed under the |
436 | +# Copyright 2009-2020 Canonical Ltd. This software is licensed under the |
437 | # GNU Affero General Public License version 3 (see the file LICENSE). |
438 | |
439 | __metaclass__ = type |
440 | @@ -7,6 +7,7 @@ __all__ = [ |
441 | 'BinaryPackageReleaseDownloadCount', |
442 | ] |
443 | |
444 | +from operator import attrgetter |
445 | |
446 | import simplejson |
447 | from sqlobject import ( |
448 | @@ -22,6 +23,7 @@ from storm.locals import ( |
449 | Store, |
450 | Storm, |
451 | ) |
452 | +from zope.component import getUtility |
453 | from zope.interface import implementer |
454 | |
455 | from lp.services.database.constants import UTC_NOW |
456 | @@ -35,12 +37,16 @@ from lp.services.propertycache import ( |
457 | from lp.soyuz.enums import ( |
458 | BinaryPackageFileType, |
459 | BinaryPackageFormat, |
460 | + BinarySourceReferenceType, |
461 | PackagePublishingPriority, |
462 | ) |
463 | from lp.soyuz.interfaces.binarypackagerelease import ( |
464 | IBinaryPackageRelease, |
465 | IBinaryPackageReleaseDownloadCount, |
466 | ) |
467 | +from lp.soyuz.interfaces.binarysourcereference import ( |
468 | + IBinarySourceReferenceSet, |
469 | + ) |
470 | from lp.soyuz.model.files import BinaryPackageFile |
471 | |
472 | |
473 | @@ -89,6 +95,14 @@ class BinaryPackageRelease(SQLBase): |
474 | del kwargs['user_defined_fields'] |
475 | super(BinaryPackageRelease, self).__init__(*args, **kwargs) |
476 | |
477 | + @cachedproperty |
478 | + def built_using_references(self): |
479 | + reference_set = getUtility(IBinarySourceReferenceSet) |
480 | + references = reference_set.findByBinaryPackageRelease( |
481 | + self, BinarySourceReferenceType.BUILT_USING) |
482 | + # Preserving insertion order is good enough. |
483 | + return sorted(references, key=attrgetter('id')) |
484 | + |
485 | @property |
486 | def user_defined_fields(self): |
487 | """See `IBinaryPackageRelease`.""" |
488 | diff --git a/lib/lp/soyuz/model/binarysourcereference.py b/lib/lp/soyuz/model/binarysourcereference.py |
489 | new file mode 100644 |
490 | index 0000000..adcfb6f |
491 | --- /dev/null |
492 | +++ b/lib/lp/soyuz/model/binarysourcereference.py |
493 | @@ -0,0 +1,149 @@ |
494 | +# Copyright 2020 Canonical Ltd. This software is licensed under the |
495 | +# GNU Affero General Public License version 3 (see the file LICENSE). |
496 | + |
497 | +"""References from binary packages to source packages.""" |
498 | + |
499 | +from __future__ import absolute_import, print_function, unicode_literals |
500 | + |
501 | +__metaclass__ = type |
502 | +__all__ = [ |
503 | + 'BinarySourceReference', |
504 | + 'BinarySourceReferenceSet', |
505 | + ] |
506 | + |
507 | +import warnings |
508 | + |
509 | +from debian.deb822 import PkgRelation |
510 | +from storm.locals import ( |
511 | + Int, |
512 | + Reference, |
513 | + ) |
514 | +from zope.interface import implementer |
515 | + |
516 | +from lp.services.database.bulk import create |
517 | +from lp.services.database.enumcol import DBEnum |
518 | +from lp.services.database.interfaces import IStore |
519 | +from lp.services.database.stormbase import StormBase |
520 | +from lp.soyuz.adapters.archivedependencies import pocket_dependencies |
521 | +from lp.soyuz.enums import BinarySourceReferenceType |
522 | +from lp.soyuz.interfaces.binarysourcereference import ( |
523 | + IBinarySourceReference, |
524 | + IBinarySourceReferenceSet, |
525 | + UnparsableBuiltUsing, |
526 | + ) |
527 | + |
528 | + |
529 | +@implementer(IBinarySourceReference) |
530 | +class BinarySourceReference(StormBase): |
531 | + """See `IBinarySourceReference`.""" |
532 | + |
533 | + __storm_table__ = "BinarySourceReference" |
534 | + |
535 | + id = Int(primary=True) |
536 | + |
537 | + binary_package_release_id = Int( |
538 | + name="binary_package_release", allow_none=False) |
539 | + binary_package_release = Reference( |
540 | + binary_package_release_id, "BinaryPackageRelease.id") |
541 | + |
542 | + source_package_release_id = Int( |
543 | + name="source_package_release", allow_none=False) |
544 | + source_package_release = Reference( |
545 | + source_package_release_id, "SourcePackageRelease.id") |
546 | + |
547 | + reference_type = DBEnum(enum=BinarySourceReferenceType, allow_none=False) |
548 | + |
549 | + def __init__(self, binary_package_release, source_package_release, |
550 | + reference_type): |
551 | + """Construct a `BinarySourceReference`.""" |
552 | + super(BinarySourceReference, self).__init__() |
553 | + self.binary_package_release = binary_package_release |
554 | + self.source_package_release = source_package_release |
555 | + self.reference_type = reference_type |
556 | + |
557 | + |
558 | +@implementer(IBinarySourceReferenceSet) |
559 | +class BinarySourceReferenceSet: |
560 | + """See `IBinarySourceReferenceSet`.""" |
561 | + |
562 | + @classmethod |
563 | + def createFromRelationship(cls, bpr, relationship, reference_type): |
564 | + """See `IBinarySourceReferenceSet`.""" |
565 | + if not relationship: |
566 | + return [] |
567 | + |
568 | + try: |
569 | + with warnings.catch_warnings(): |
570 | + warnings.simplefilter("error") |
571 | + parsed_rel = PkgRelation.parse_relations(relationship) |
572 | + except Warning as error: |
573 | + raise UnparsableBuiltUsing( |
574 | + "Invalid Built-Using field; cannot be parsed by deb822: %s" |
575 | + % (error,)) |
576 | + |
577 | + build = bpr.build |
578 | + values = [] |
579 | + for or_rel in parsed_rel: |
580 | + if len(or_rel) != 1: |
581 | + raise UnparsableBuiltUsing( |
582 | + "Alternatives are not allowed in Built-Using field: %s" |
583 | + % (PkgRelation.str([or_rel]),)) |
584 | + rel = or_rel[0] |
585 | + if rel["version"] is None or rel["version"][0] != "=": |
586 | + raise UnparsableBuiltUsing( |
587 | + "Built-Using must contain strict dependencies: %s" |
588 | + % (PkgRelation.str([or_rel]),)) |
589 | + |
590 | + # "source-package-name (= version)" might refer to any of |
591 | + # several SPRs, for example if the same source package was |
592 | + # uploaded to a PPA and then uploaded separately (not copied - |
593 | + # copies reuse the same SPR) to the distribution's primary |
594 | + # archive. We need to disambiguate this and find an actual SPR |
595 | + # so that we can efficiently look up references for a given |
596 | + # source publication. |
597 | + # |
598 | + # However, allowing cross-archive references would make the |
599 | + # dominator's job much harder and have other undesirable |
600 | + # properties, such as being able to pin a source in Published in |
601 | + # a foreign archive just by adding it as a dependency and |
602 | + # declaring a Built-Using relationship on it. |
603 | + # |
604 | + # Therefore, as a safe approximation, try this build's pocket |
605 | + # dependencies within its archive and series. Within this |
606 | + # constraint, a name and version should uniquely identify an |
607 | + # SPR, although we pick the latest by ID just in case that |
608 | + # somehow ends up not being true. |
609 | + closest_spph = build.archive.getPublishedSources( |
610 | + name=rel["name"], version=rel["version"][1], |
611 | + distroseries=build.distro_series, |
612 | + pocket=pocket_dependencies[build.pocket], |
613 | + exact_match=True).first() |
614 | + if closest_spph is None: |
615 | + raise UnparsableBuiltUsing( |
616 | + "Built-Using refers to source package %s (= %s), which is " |
617 | + "not known in %s in %s" % |
618 | + (rel["name"], rel["version"][1], |
619 | + build.distro_series.name, build.archive.reference)) |
620 | + values.append( |
621 | + (bpr.id, closest_spph.sourcepackagereleaseID, reference_type)) |
622 | + |
623 | + return create( |
624 | + (BinarySourceReference.binary_package_release_id, |
625 | + BinarySourceReference.source_package_release_id, |
626 | + BinarySourceReference.reference_type), |
627 | + values, get_objects=True) |
628 | + |
629 | + @classmethod |
630 | + def createFromSourcePackageReleases(cls, bpr, sprs, reference_type): |
631 | + """See `IBinarySourceReferenceSet`.""" |
632 | + relationship = ", ".join( |
633 | + ["%s (= %s)" % (spr.name, spr.version) for spr in sprs]) |
634 | + return cls.createFromRelationship(bpr, relationship, reference_type) |
635 | + |
636 | + @classmethod |
637 | + def findByBinaryPackageRelease(cls, bpr, reference_type): |
638 | + """See `IBinarySourceReferenceSet`.""" |
639 | + return IStore(BinarySourceReference).find( |
640 | + BinarySourceReference, |
641 | + BinarySourceReference.binary_package_release == bpr, |
642 | + BinarySourceReference.reference_type == reference_type) |
643 | diff --git a/lib/lp/soyuz/scripts/gina/handlers.py b/lib/lp/soyuz/scripts/gina/handlers.py |
644 | index 66d2aeb..54539a4 100644 |
645 | --- a/lib/lp/soyuz/scripts/gina/handlers.py |
646 | +++ b/lib/lp/soyuz/scripts/gina/handlers.py |
647 | @@ -1,4 +1,4 @@ |
648 | -# Copyright 2009-2016 Canonical Ltd. This software is licensed under the |
649 | +# Copyright 2009-2020 Canonical Ltd. This software is licensed under the |
650 | # GNU Affero General Public License version 3 (see the file LICENSE). |
651 | |
652 | """Gina db handlers. |
653 | @@ -52,10 +52,15 @@ from lp.services.librarian.interfaces import ILibraryFileAliasSet |
654 | from lp.services.scripts import log |
655 | from lp.soyuz.enums import ( |
656 | BinaryPackageFormat, |
657 | + BinarySourceReferenceType, |
658 | PackagePublishingStatus, |
659 | ) |
660 | from lp.soyuz.interfaces.binarypackagebuild import IBinaryPackageBuildSet |
661 | from lp.soyuz.interfaces.binarypackagename import IBinaryPackageNameSet |
662 | +from lp.soyuz.interfaces.binarysourcereference import ( |
663 | + IBinarySourceReferenceSet, |
664 | + UnparsableBuiltUsing, |
665 | + ) |
666 | from lp.soyuz.interfaces.publishing import ( |
667 | active_publishing_status, |
668 | IPublishingSet, |
669 | @@ -820,6 +825,16 @@ class BinaryPackageHandler: |
670 | installedsize=bin.installed_size, |
671 | architecturespecific=architecturespecific, |
672 | **kwargs) |
673 | + try: |
674 | + getUtility(IBinarySourceReferenceSet).createFromRelationship( |
675 | + binpkg, bin.built_using, BinarySourceReferenceType.BUILT_USING) |
676 | + except UnparsableBuiltUsing: |
677 | + # XXX cjwatson 2020-02-03: It might be nice if we created |
678 | + # BinarySourceReference rows at least for those relations that |
679 | + # can be parsed and resolved to SourcePackageReleases. It's not |
680 | + # worth spending much time on given that we don't use binary |
681 | + # imports much, though. |
682 | + pass |
683 | log.info('Binary Package Release %s (%s) created' % |
684 | (bin_name.name, bin.version)) |
685 | |
686 | diff --git a/lib/lp/soyuz/scripts/gina/packages.py b/lib/lp/soyuz/scripts/gina/packages.py |
687 | index 4126b80..6e2f090 100644 |
688 | --- a/lib/lp/soyuz/scripts/gina/packages.py |
689 | +++ b/lib/lp/soyuz/scripts/gina/packages.py |
690 | @@ -1,4 +1,4 @@ |
691 | -# Copyright 2009-2019 Canonical Ltd. This software is licensed under the |
692 | +# Copyright 2009-2020 Canonical Ltd. This software is licensed under the |
693 | # GNU Affero General Public License version 3 (see the file LICENSE). |
694 | |
695 | """Package information classes. |
696 | @@ -466,6 +466,7 @@ class BinaryPackageData(AbstractPackageData): |
697 | pre_depends = "" |
698 | enhances = "" |
699 | breaks = "" |
700 | + built_using = "" |
701 | essential = False |
702 | |
703 | # Overwritten in do_package, optionally |
704 | @@ -493,6 +494,14 @@ class BinaryPackageData(AbstractPackageData): |
705 | except ValueError: |
706 | raise MissingRequiredArguments("Installed-Size is " |
707 | "not a valid integer: %r" % v) |
708 | + elif k == "Built-Using": |
709 | + self.built_using = v |
710 | + # Preserve the original form of Built-Using to avoid |
711 | + # possible unfortunate apt behaviour. This is most easily |
712 | + # done by adding it to _user_defined as well. |
713 | + if self._user_defined is None: |
714 | + self._user_defined = [] |
715 | + self._user_defined.append([k, v]) |
716 | else: |
717 | self.set_field(k, v) |
718 | |
719 | diff --git a/lib/lp/soyuz/scripts/tests/test_gina.py b/lib/lp/soyuz/scripts/tests/test_gina.py |
720 | index 2653b4f..9574c38 100644 |
721 | --- a/lib/lp/soyuz/scripts/tests/test_gina.py |
722 | +++ b/lib/lp/soyuz/scripts/tests/test_gina.py |
723 | @@ -1,4 +1,4 @@ |
724 | -# Copyright 2009-2019 Canonical Ltd. This software is licensed under the |
725 | +# Copyright 2009-2020 Canonical Ltd. This software is licensed under the |
726 | # GNU Affero General Public License version 3 (see the file LICENSE). |
727 | |
728 | from doctest import DocTestSuite |
729 | @@ -12,6 +12,10 @@ from unittest import TestLoader |
730 | |
731 | import apt_pkg |
732 | from fixtures import EnvironmentVariableFixture |
733 | +from testtools.matchers import ( |
734 | + MatchesSetwise, |
735 | + MatchesStructure, |
736 | + ) |
737 | import transaction |
738 | |
739 | from lp.archiveuploader.tagfiles import parse_tagfile |
740 | @@ -22,7 +26,10 @@ from lp.services.features.testing import FeatureFixture |
741 | from lp.services.log.logger import DevNullLogger |
742 | from lp.services.osutils import write_file |
743 | from lp.services.tarfile_helpers import LaunchpadWriteTarFile |
744 | -from lp.soyuz.enums import PackagePublishingStatus |
745 | +from lp.soyuz.enums import ( |
746 | + BinarySourceReferenceType, |
747 | + PackagePublishingStatus, |
748 | + ) |
749 | from lp.soyuz.scripts.gina import ExecutionError |
750 | from lp.soyuz.scripts.gina.archive import ( |
751 | ArchiveComponentItems, |
752 | @@ -483,9 +490,62 @@ class TestBinaryPackageHandler(TestCaseWithFactory): |
753 | "Summary": "", |
754 | "Priority": "extra", |
755 | "Python-Version": "2.7", |
756 | + "Built-Using": "nonexistent (= 0.1)", |
757 | + } |
758 | + bp_data = BinaryPackageData(**deb_contents) |
759 | + self.assertContentEqual( |
760 | + [["Python-Version", "2.7"], |
761 | + ["Built-Using", "nonexistent (= 0.1)"]], |
762 | + bp_data._user_defined) |
763 | + bp_data.archive_root = archive_root |
764 | + # We don't need a real .deb here. |
765 | + write_file( |
766 | + os.path.join(archive_root, "pool/main/f/foo/foo_1.0-1_amd64.deb"), |
767 | + b"x") |
768 | + bpr = bphandler.createBinaryPackage(bp_data, spr, das, "amd64") |
769 | + self.assertIsNotNone(bpr) |
770 | + self.assertEqual([], bpr.built_using_references) |
771 | + self.assertContentEqual( |
772 | + [["Python-Version", "2.7"], |
773 | + ["Built-Using", "nonexistent (= 0.1)"]], |
774 | + bpr.user_defined_fields) |
775 | + |
776 | + def test_resolvable_built_using(self): |
777 | + das = self.factory.makeDistroArchSeries() |
778 | + archive_root = self.useTempDir() |
779 | + sphandler = SourcePackageHandler( |
780 | + das.distroseries.distribution.name, archive_root, |
781 | + PackagePublishingPocket.RELEASE, None) |
782 | + bphandler = BinaryPackageHandler( |
783 | + sphandler, archive_root, PackagePublishingPocket.RELEASE) |
784 | + spr = self.factory.makeSourcePackagePublishingHistory( |
785 | + distroseries=das.distroseries, |
786 | + component="main").sourcepackagerelease |
787 | + built_using_spph = self.factory.makeSourcePackagePublishingHistory( |
788 | + archive=das.main_archive, distroseries=das.distroseries, |
789 | + pocket=PackagePublishingPocket.RELEASE) |
790 | + built_using_spr = built_using_spph.sourcepackagerelease |
791 | + built_using_relationship = "%s (= %s)" % ( |
792 | + built_using_spr.name, built_using_spr.version) |
793 | + deb_contents = { |
794 | + "Package": "foo", |
795 | + "Installed-Size": "0", |
796 | + "Maintainer": "Foo Bar <foo@canonical.com>", |
797 | + "Section": "misc", |
798 | + "Architecture": "amd64", |
799 | + "Version": "1.0-1", |
800 | + "Filename": "pool/main/f/foo/foo_1.0-1_amd64.deb", |
801 | + "Component": "main", |
802 | + "Size": "0", |
803 | + "MD5sum": "0" * 32, |
804 | + "Description": "", |
805 | + "Summary": "", |
806 | + "Priority": "extra", |
807 | + "Built-Using": built_using_relationship, |
808 | } |
809 | bp_data = BinaryPackageData(**deb_contents) |
810 | - self.assertEqual([["Python-Version", "2.7"]], bp_data._user_defined) |
811 | + self.assertContentEqual( |
812 | + [["Built-Using", built_using_relationship]], bp_data._user_defined) |
813 | bp_data.archive_root = archive_root |
814 | # We don't need a real .deb here. |
815 | write_file( |
816 | @@ -493,7 +553,16 @@ class TestBinaryPackageHandler(TestCaseWithFactory): |
817 | b"x") |
818 | bpr = bphandler.createBinaryPackage(bp_data, spr, das, "amd64") |
819 | self.assertIsNotNone(bpr) |
820 | - self.assertEqual([["Python-Version", "2.7"]], bpr.user_defined_fields) |
821 | + self.assertThat( |
822 | + bpr.built_using_references, |
823 | + MatchesSetwise( |
824 | + MatchesStructure.byEquality( |
825 | + binary_package_release=bpr, |
826 | + source_package_release=built_using_spr, |
827 | + reference_type=BinarySourceReferenceType.BUILT_USING))) |
828 | + self.assertContentEqual( |
829 | + [["Built-Using", built_using_relationship]], |
830 | + bpr.user_defined_fields) |
831 | |
832 | |
833 | class TestBinaryPackagePublisher(TestCaseWithFactory): |
834 | diff --git a/lib/lp/soyuz/tests/test_binarysourcereference.py b/lib/lp/soyuz/tests/test_binarysourcereference.py |
835 | new file mode 100644 |
836 | index 0000000..f682de8 |
837 | --- /dev/null |
838 | +++ b/lib/lp/soyuz/tests/test_binarysourcereference.py |
839 | @@ -0,0 +1,208 @@ |
840 | +# Copyright 2020 Canonical Ltd. This software is licensed under the |
841 | +# GNU Affero General Public License version 3 (see the file LICENSE). |
842 | + |
843 | +"""Test references from binary packages to source packages.""" |
844 | + |
845 | +from __future__ import absolute_import, print_function, unicode_literals |
846 | + |
847 | +__metaclass__ = type |
848 | + |
849 | +import re |
850 | + |
851 | +from testtools.matchers import ( |
852 | + MatchesSetwise, |
853 | + MatchesStructure, |
854 | + ) |
855 | +from testtools.testcase import ExpectedException |
856 | +from zope.component import getUtility |
857 | + |
858 | +from lp.registry.interfaces.pocket import PackagePublishingPocket |
859 | +from lp.soyuz.enums import ( |
860 | + ArchivePurpose, |
861 | + BinarySourceReferenceType, |
862 | + ) |
863 | +from lp.soyuz.interfaces.binarysourcereference import ( |
864 | + IBinarySourceReferenceSet, |
865 | + UnparsableBuiltUsing, |
866 | + ) |
867 | +from lp.testing import TestCaseWithFactory |
868 | +from lp.testing.layers import DatabaseFunctionalLayer |
869 | + |
870 | + |
871 | +class TestBinarySourceReference(TestCaseWithFactory): |
872 | + |
873 | + layer = DatabaseFunctionalLayer |
874 | + |
875 | + def setUp(self): |
876 | + super(TestBinarySourceReference, self).setUp() |
877 | + self.reference_set = getUtility(IBinarySourceReferenceSet) |
878 | + |
879 | + def test_createFromRelationship_empty(self): |
880 | + bpr = self.factory.makeBinaryPackageRelease() |
881 | + self.assertEqual( |
882 | + [], |
883 | + self.reference_set.createFromRelationship( |
884 | + bpr, "", BinarySourceReferenceType.BUILT_USING)) |
885 | + |
886 | + def test_createFromRelationship_nonsense(self): |
887 | + bpr = self.factory.makeBinaryPackageRelease() |
888 | + expected_message = ( |
889 | + r"Invalid Built-Using field; cannot be parsed by deb822: .*") |
890 | + with ExpectedException(UnparsableBuiltUsing, expected_message): |
891 | + self.reference_set.createFromRelationship( |
892 | + bpr, "nonsense (", BinarySourceReferenceType.BUILT_USING) |
893 | + |
894 | + def test_createFromRelationship_alternatives(self): |
895 | + bpr = self.factory.makeBinaryPackageRelease() |
896 | + expected_message = ( |
897 | + r"Alternatives are not allowed in Built-Using field: " |
898 | + r"foo \(= 1\) \| bar \(= 2\)") |
899 | + with ExpectedException(UnparsableBuiltUsing, expected_message): |
900 | + self.reference_set.createFromRelationship( |
901 | + bpr, "foo (= 1) | bar (= 2)", |
902 | + BinarySourceReferenceType.BUILT_USING) |
903 | + |
904 | + def test_createFromRelationship_no_version(self): |
905 | + bpr = self.factory.makeBinaryPackageRelease() |
906 | + expected_message = r"Built-Using must contain strict dependencies: foo" |
907 | + with ExpectedException(UnparsableBuiltUsing, expected_message): |
908 | + self.reference_set.createFromRelationship( |
909 | + bpr, "foo", BinarySourceReferenceType.BUILT_USING) |
910 | + |
911 | + def test_createFromRelationship_inequality(self): |
912 | + bpr = self.factory.makeBinaryPackageRelease() |
913 | + expected_message = ( |
914 | + r"Built-Using must contain strict dependencies: foo \(>= 1\)") |
915 | + with ExpectedException(UnparsableBuiltUsing, expected_message): |
916 | + self.reference_set.createFromRelationship( |
917 | + bpr, "foo (>= 1)", BinarySourceReferenceType.BUILT_USING) |
918 | + |
919 | + def test_createFromRelationship_unknown_source_package_name(self): |
920 | + bpr = self.factory.makeBinaryPackageRelease() |
921 | + relationship = "nonexistent (= 1)" |
922 | + expected_message = ( |
923 | + r"Built-Using refers to source package %s, which is not known in " |
924 | + r"%s in %s" % ( |
925 | + re.escape(relationship), bpr.build.distro_series.name, |
926 | + bpr.build.archive.reference)) |
927 | + with ExpectedException(UnparsableBuiltUsing, expected_message): |
928 | + self.reference_set.createFromRelationship( |
929 | + bpr, relationship, BinarySourceReferenceType.BUILT_USING) |
930 | + |
931 | + def test_createFromRelationship_unknown_source_package_version(self): |
932 | + bpr = self.factory.makeBinaryPackageRelease() |
933 | + spph = self.factory.makeSourcePackagePublishingHistory( |
934 | + archive=bpr.build.archive, |
935 | + distroseries=bpr.build.distro_series, |
936 | + component=bpr.build.current_component) |
937 | + spr = spph.sourcepackagerelease |
938 | + relationship = "%s (= %s.1)" % (spr.name, spr.version) |
939 | + expected_message = ( |
940 | + r"Built-Using refers to source package %s, which is not known in " |
941 | + r"%s in %s" % ( |
942 | + re.escape(relationship), bpr.build.distro_series.name, |
943 | + bpr.build.archive.reference)) |
944 | + with ExpectedException(UnparsableBuiltUsing, expected_message): |
945 | + self.reference_set.createFromRelationship( |
946 | + bpr, relationship, BinarySourceReferenceType.BUILT_USING) |
947 | + |
948 | + def test_createFromRelationship_simple(self): |
949 | + bpr = self.factory.makeBinaryPackageRelease() |
950 | + spphs = [ |
951 | + self.factory.makeSourcePackagePublishingHistory( |
952 | + archive=bpr.build.archive, |
953 | + distroseries=bpr.build.distro_series, pocket=bpr.build.pocket) |
954 | + for _ in range(3)] |
955 | + sprs = [spph.sourcepackagerelease for spph in spphs] |
956 | + # Create a few more SPPHs with slight mismatches to ensure that |
957 | + # createFromRelationship matches correctly. |
958 | + self.factory.makeSourcePackagePublishingHistory( |
959 | + archive=bpr.build.archive, pocket=bpr.build.pocket, |
960 | + sourcepackagename=sprs[0].name, version=sprs[0].version) |
961 | + self.factory.makeSourcePackagePublishingHistory( |
962 | + archive=bpr.build.archive, distroseries=bpr.build.distro_series, |
963 | + pocket=PackagePublishingPocket.BACKPORTS, |
964 | + sourcepackagename=sprs[0].name, version=sprs[0].version) |
965 | + self.factory.makeSourcePackagePublishingHistory( |
966 | + archive=bpr.build.archive, distroseries=bpr.build.distro_series, |
967 | + pocket=bpr.build.pocket, sourcepackagename=sprs[0].name) |
968 | + self.factory.makeSourcePackagePublishingHistory( |
969 | + archive=bpr.build.archive, distroseries=bpr.build.distro_series, |
970 | + pocket=bpr.build.pocket, version=sprs[0].version) |
971 | + self.factory.makeSourcePackagePublishingHistory() |
972 | + relationship = ( |
973 | + "%s (= %s), %s (= %s)" % |
974 | + (sprs[0].name, sprs[0].version, sprs[1].name, sprs[1].version)) |
975 | + bsrs = self.reference_set.createFromRelationship( |
976 | + bpr, relationship, BinarySourceReferenceType.BUILT_USING) |
977 | + self.assertThat(bsrs, MatchesSetwise(*( |
978 | + MatchesStructure.byEquality( |
979 | + binary_package_release=bpr, |
980 | + source_package_release=spr, |
981 | + reference_type=BinarySourceReferenceType.BUILT_USING) |
982 | + for spr in sprs[:2]))) |
983 | + |
984 | + def test_createFromRelationship_foreign_archive(self): |
985 | + # createFromRelationship only considers SPRs found in the same |
986 | + # archive as the build. |
987 | + archive = self.factory.makeArchive(purpose=ArchivePurpose.PPA) |
988 | + build = self.factory.makeBinaryPackageBuild(archive=archive) |
989 | + bpr = self.factory.makeBinaryPackageRelease(build=build) |
990 | + spph = self.factory.makeSourcePackagePublishingHistory( |
991 | + archive=build.distro_series.main_archive, |
992 | + distroseries=build.distro_series, pocket=build.pocket) |
993 | + spr = spph.sourcepackagerelease |
994 | + relationship = "%s (= %s)" % (spr.name, spr.version) |
995 | + expected_message = ( |
996 | + r"Built-Using refers to source package %s, which is not known in " |
997 | + r"%s in %s" % ( |
998 | + re.escape(relationship), build.distro_series.name, |
999 | + build.archive.reference)) |
1000 | + with ExpectedException(UnparsableBuiltUsing, expected_message): |
1001 | + self.reference_set.createFromRelationship( |
1002 | + bpr, relationship, BinarySourceReferenceType.BUILT_USING) |
1003 | + |
1004 | + def test_findByBinaryPackageRelease_empty(self): |
1005 | + bpr = self.factory.makeBinaryPackageRelease() |
1006 | + self.assertContentEqual( |
1007 | + [], |
1008 | + self.reference_set.findByBinaryPackageRelease( |
1009 | + bpr, BinarySourceReferenceType.BUILT_USING)) |
1010 | + |
1011 | + def test_findByBinaryPackageRelease(self): |
1012 | + bprs = [self.factory.makeBinaryPackageRelease() for _ in range(2)] |
1013 | + all_sprs = [] |
1014 | + for bpr in bprs: |
1015 | + spphs = [ |
1016 | + self.factory.makeSourcePackagePublishingHistory( |
1017 | + archive=bpr.build.archive, |
1018 | + distroseries=bpr.build.distro_series, |
1019 | + pocket=bpr.build.pocket) |
1020 | + for _ in range(2)] |
1021 | + sprs = [spph.sourcepackagerelease for spph in spphs] |
1022 | + all_sprs.extend(sprs) |
1023 | + self.reference_set.createFromSourcePackageReleases( |
1024 | + bpr, sprs, BinarySourceReferenceType.BUILT_USING) |
1025 | + other_bpr = self.factory.makeBinaryPackageRelease() |
1026 | + self.assertThat( |
1027 | + self.reference_set.findByBinaryPackageRelease( |
1028 | + bprs[0], BinarySourceReferenceType.BUILT_USING), |
1029 | + MatchesSetwise(*( |
1030 | + MatchesStructure.byEquality( |
1031 | + binary_package_release=bprs[0], |
1032 | + source_package_release=spr, |
1033 | + reference_type=BinarySourceReferenceType.BUILT_USING) |
1034 | + for spr in all_sprs[:2]))) |
1035 | + self.assertThat( |
1036 | + self.reference_set.findByBinaryPackageRelease( |
1037 | + bprs[1], BinarySourceReferenceType.BUILT_USING), |
1038 | + MatchesSetwise(*( |
1039 | + MatchesStructure.byEquality( |
1040 | + binary_package_release=bprs[1], |
1041 | + source_package_release=spr, |
1042 | + reference_type=BinarySourceReferenceType.BUILT_USING) |
1043 | + for spr in all_sprs[2:]))) |
1044 | + self.assertContentEqual( |
1045 | + [], |
1046 | + self.reference_set.findByBinaryPackageRelease( |
1047 | + other_bpr, BinarySourceReferenceType.BUILT_USING)) |
1048 | diff --git a/lib/lp/soyuz/tests/test_publishing.py b/lib/lp/soyuz/tests/test_publishing.py |
1049 | index e65003d..cd7d1ea 100644 |
1050 | --- a/lib/lp/soyuz/tests/test_publishing.py |
1051 | +++ b/lib/lp/soyuz/tests/test_publishing.py |
1052 | @@ -306,7 +306,8 @@ class SoyuzTestPublisher: |
1053 | shlibdep=None, depends=None, recommends=None, |
1054 | suggests=None, conflicts=None, replaces=None, |
1055 | provides=None, pre_depends=None, enhances=None, |
1056 | - breaks=None, filecontent=b'bbbiiinnnaaarrryyy', |
1057 | + breaks=None, built_using=None, |
1058 | + filecontent=b'bbbiiinnnaaarrryyy', |
1059 | changes_file_content=b"Fake: fake changes file", |
1060 | status=PackagePublishingStatus.PENDING, |
1061 | pocket=PackagePublishingPocket.RELEASE, |
1062 | @@ -353,7 +354,8 @@ class SoyuzTestPublisher: |
1063 | build, binaryname + '-dbgsym', filecontent, summary, |
1064 | description, shlibdep, depends, recommends, suggests, |
1065 | conflicts, replaces, provides, pre_depends, enhances, |
1066 | - breaks, BinaryPackageFormat.DDEB, version=version) |
1067 | + breaks, built_using, BinaryPackageFormat.DDEB, |
1068 | + version=version) |
1069 | pub_binaries += self.publishBinaryInArchive( |
1070 | binarypackagerelease_ddeb, archive, status, |
1071 | pocket, scheduleddeletiondate, dateremoved, |
1072 | @@ -364,7 +366,7 @@ class SoyuzTestPublisher: |
1073 | binarypackagerelease = self.uploadBinaryForBuild( |
1074 | build, binaryname, filecontent, summary, description, |
1075 | shlibdep, depends, recommends, suggests, conflicts, replaces, |
1076 | - provides, pre_depends, enhances, breaks, format, |
1077 | + provides, pre_depends, enhances, breaks, built_using, format, |
1078 | binarypackagerelease_ddeb, version=version, |
1079 | user_defined_fields=user_defined_fields) |
1080 | pub_binaries += self.publishBinaryInArchive( |
1081 | @@ -387,8 +389,9 @@ class SoyuzTestPublisher: |
1082 | summary="summary", description="description", shlibdep=None, |
1083 | depends=None, recommends=None, suggests=None, conflicts=None, |
1084 | replaces=None, provides=None, pre_depends=None, enhances=None, |
1085 | - breaks=None, format=BinaryPackageFormat.DEB, debug_package=None, |
1086 | - user_defined_fields=None, homepage=None, version=None): |
1087 | + breaks=None, built_using=None, format=BinaryPackageFormat.DEB, |
1088 | + debug_package=None, user_defined_fields=None, homepage=None, |
1089 | + version=None): |
1090 | """Return the corresponding `BinaryPackageRelease`.""" |
1091 | sourcepackagerelease = build.source_package_release |
1092 | distroarchseries = build.distro_arch_series |
1093 | @@ -418,6 +421,7 @@ class SoyuzTestPublisher: |
1094 | pre_depends=pre_depends, |
1095 | enhances=enhances, |
1096 | breaks=breaks, |
1097 | + built_using=built_using, |
1098 | essential=False, |
1099 | installedsize=100, |
1100 | architecturespecific=architecturespecific, |
What is the possible 'unfortunate apt behaviour'?