Merge lp:~cjwatson/launchpad/store-buildinfo into lp:launchpad

Proposed by Colin Watson
Status: Merged
Merged at revision: 18359
Proposed branch: lp:~cjwatson/launchpad/store-buildinfo
Merge into: lp:launchpad
Diff against target: 913 lines (+453/-23)
23 files modified
lib/lp/archiveuploader/buildinfofile.py (+89/-0)
lib/lp/archiveuploader/changesfile.py (+10/-1)
lib/lp/archiveuploader/dscfile.py (+7/-1)
lib/lp/archiveuploader/nascentupload.py (+14/-0)
lib/lp/archiveuploader/tests/__init__.py (+3/-2)
lib/lp/archiveuploader/tests/data/suite/bar_1.0-1_binary_buildinfo/bar_1.0-1_i386.buildinfo (+23/-0)
lib/lp/archiveuploader/tests/data/suite/bar_1.0-1_binary_buildinfo/bar_1.0-1_i386.changes (+29/-0)
lib/lp/archiveuploader/tests/data/suite/bar_1.0-1_buildinfo/bar_1.0-1.dsc (+21/-0)
lib/lp/archiveuploader/tests/data/suite/bar_1.0-1_buildinfo/bar_1.0-1_source.buildinfo (+23/-0)
lib/lp/archiveuploader/tests/data/suite/bar_1.0-1_buildinfo/bar_1.0-1_source.changes (+31/-0)
lib/lp/archiveuploader/tests/test_buildinfofile.py (+92/-0)
lib/lp/archiveuploader/tests/test_changesfile.py (+8/-1)
lib/lp/archiveuploader/tests/test_uploadpolicy.py (+3/-1)
lib/lp/archiveuploader/tests/test_uploadprocessor.py (+42/-0)
lib/lp/archiveuploader/uploadpolicy.py (+5/-2)
lib/lp/archiveuploader/utils.py (+3/-1)
lib/lp/registry/interfaces/distroseries.py (+3/-2)
lib/lp/registry/model/distroseries.py (+4/-3)
lib/lp/soyuz/enums.py (+8/-1)
lib/lp/soyuz/interfaces/binarypackagebuild.py (+14/-2)
lib/lp/soyuz/interfaces/sourcepackagerelease.py (+4/-1)
lib/lp/soyuz/model/binarypackagebuild.py (+15/-4)
lib/lp/soyuz/model/sourcepackagerelease.py (+2/-1)
To merge this branch: bzr merge lp:~cjwatson/launchpad/store-buildinfo
Reviewer Review Type Date Requested Status
William Grant (community) code Approve
Review via email: mp+321263@code.launchpad.net

Commit message

Store uploaded .buildinfo files.

To post a comment you must log in.
Revision history for this message
William Grant (wgrant) :
review: Approve (code)
Revision history for this message
Colin Watson (cjwatson) :

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'lib/lp/archiveuploader/buildinfofile.py'
2--- lib/lp/archiveuploader/buildinfofile.py 1970-01-01 00:00:00 +0000
3+++ lib/lp/archiveuploader/buildinfofile.py 2017-04-19 13:08:31 +0000
4@@ -0,0 +1,89 @@
5+# Copyright 2017 Canonical Ltd. This software is licensed under the
6+# GNU Affero General Public License version 3 (see the file LICENSE).
7+
8+"""Build information files."""
9+
10+__metaclass__ = type
11+
12+__all__ = [
13+ 'BuildInfoFile',
14+ ]
15+
16+from lp.app.errors import NotFoundError
17+from lp.archiveuploader.dscfile import SignableTagFile
18+from lp.archiveuploader.nascentuploadfile import PackageUploadFile
19+from lp.archiveuploader.utils import (
20+ re_isbuildinfo,
21+ re_no_epoch,
22+ UploadError,
23+ )
24+
25+
26+class BuildInfoFile(PackageUploadFile, SignableTagFile):
27+ """Represents an uploaded build information file."""
28+
29+ def __init__(self, filepath, checksums, size, component_and_section,
30+ priority_name, package, version, changes, policy, logger):
31+ super(BuildInfoFile, self).__init__(
32+ filepath, checksums, size, component_and_section, priority_name,
33+ package, version, changes, policy, logger)
34+ self.parse(verify_signature=not policy.unsigned_buildinfo_ok)
35+ arch_match = re_isbuildinfo.match(self.filename)
36+ self.architecture = arch_match.group(3)
37+
38+ @property
39+ def is_sourceful(self):
40+ # XXX cjwatson 2017-03-29: We should get this from the parsed
41+ # Architecture field instead.
42+ return self.architecture == "source"
43+
44+ @property
45+ def is_binaryful(self):
46+ # XXX cjwatson 2017-03-29: We should get this from the parsed
47+ # Architecture field instead.
48+ return self.architecture != "source"
49+
50+ @property
51+ def is_archindep(self):
52+ # XXX cjwatson 2017-03-29: We should get this from the parsed
53+ # Architecture field instead.
54+ return self.architecture == "all"
55+
56+ def verify(self):
57+ """Verify the uploaded buildinfo file.
58+
59+ It returns an iterator over all the encountered errors and warnings.
60+ """
61+ self.logger.debug("Verifying buildinfo file %s" % self.filename)
62+
63+ version_chopped = re_no_epoch.sub('', self.version)
64+ buildinfo_match = re_isbuildinfo.match(self.filename)
65+ filename_version = buildinfo_match.group(2)
66+ if filename_version != version_chopped:
67+ yield UploadError("%s: should be %s according to changes file."
68+ % (filename_version, version_chopped))
69+
70+ def checkBuild(self, build):
71+ """See `PackageUploadFile`."""
72+ try:
73+ das = self.policy.distroseries[self.architecture]
74+ except NotFoundError:
75+ raise UploadError(
76+ "Upload to unknown architecture %s for distroseries %s" %
77+ (self.architecture, self.policy.distroseries))
78+
79+ # Sanity check; raise an error if the build we've been
80+ # told to link to makes no sense.
81+ if (build.pocket != self.policy.pocket or
82+ build.distro_arch_series != das or
83+ build.archive != self.policy.archive):
84+ raise UploadError(
85+ "Attempt to upload buildinfo specifying build %s, where it "
86+ "doesn't fit." % build.id)
87+
88+ def storeInDatabase(self):
89+ """Create and return the corresponding `LibraryFileAlias` reference."""
90+ with open(self.filepath, "rb") as f:
91+ return self.librarian.create(
92+ self.filename, self.size, f, self.content_type,
93+ restricted=self.policy.archive.private)
94
95=== modified file 'lib/lp/archiveuploader/changesfile.py'
96--- lib/lp/archiveuploader/changesfile.py 2015-07-29 01:59:43 +0000
97+++ lib/lp/archiveuploader/changesfile.py 2017-04-19 13:08:31 +0000
98@@ -1,4 +1,4 @@
99-# Copyright 2009 Canonical Ltd. This software is licensed under the
100+# Copyright 2009-2017 Canonical Ltd. This software is licensed under the
101 # GNU Affero General Public License version 3 (see the file LICENSE).
102
103 """ ChangesFile class
104@@ -17,6 +17,7 @@
105
106 import os
107
108+from lp.archiveuploader.buildinfofile import BuildInfoFile
109 from lp.archiveuploader.dscfile import (
110 DSCFile,
111 SignableTagFile,
112@@ -36,6 +37,7 @@
113 parse_and_merge_file_lists,
114 re_changes_file_name,
115 re_isadeb,
116+ re_isbuildinfo,
117 re_issource,
118 rfc822_encode_address,
119 UploadError,
120@@ -75,6 +77,7 @@
121 }
122
123 dsc = None
124+ buildinfo = None
125 maintainer = None
126 changed_by = None
127 filename_archtag = None
128@@ -209,6 +212,8 @@
129
130 if cls == DSCFile:
131 self.dsc = file_instance
132+ elif cls == BuildInfoFile:
133+ self.buildinfo = file_instance
134 except UploadError as error:
135 yield error
136 else:
137@@ -366,6 +371,7 @@
138 """Determine the name and PackageUploadFile subclass for the filename."""
139 source_match = re_issource.match(filename)
140 binary_match = re_isadeb.match(filename)
141+ buildinfo_match = re_isbuildinfo.match(filename)
142 if source_match:
143 package = source_match.group(1)
144 if (determine_source_file_type(filename) ==
145@@ -380,6 +386,9 @@
146 BinaryPackageFileType.DDEB: DdebBinaryUploadFile,
147 BinaryPackageFileType.UDEB: UdebBinaryUploadFile,
148 }[determine_binary_file_type(filename)]
149+ elif buildinfo_match:
150+ package = buildinfo_match.group(1)
151+ cls = BuildInfoFile
152 else:
153 raise CannotDetermineFileTypeError(
154 "Could not determine the type of %r" % filename)
155
156=== modified file 'lib/lp/archiveuploader/dscfile.py'
157--- lib/lp/archiveuploader/dscfile.py 2016-06-01 01:59:32 +0000
158+++ lib/lp/archiveuploader/dscfile.py 2017-04-19 13:08:31 +0000
159@@ -1,4 +1,4 @@
160-# Copyright 2009-2016 Canonical Ltd. This software is licensed under the
161+# Copyright 2009-2017 Canonical Ltd. This software is licensed under the
162 # GNU Affero General Public License version 3 (see the file LICENSE).
163
164 """ DSCFile and related.
165@@ -671,6 +671,11 @@
166 user_defined_fields = self.extractUserDefinedFields([
167 (field, encoded[field]) for field in self._dict.iterkeys()])
168
169+ if self.changes.buildinfo is not None:
170+ buildinfo_lfa = self.changes.buildinfo.storeInDatabase()
171+ else:
172+ buildinfo_lfa = None
173+
174 release = self.policy.distroseries.createUploadedSourcePackageRelease(
175 sourcepackagename=source_name,
176 version=self.dsc_version,
177@@ -698,6 +703,7 @@
178 copyright=encoded.get('copyright'),
179 # dateuploaded by default is UTC:now in the database
180 user_defined_fields=user_defined_fields,
181+ buildinfo=buildinfo_lfa,
182 )
183
184 # SourcePackageFiles should contain also the DSC
185
186=== modified file 'lib/lp/archiveuploader/nascentupload.py'
187--- lib/lp/archiveuploader/nascentupload.py 2015-12-30 23:34:34 +0000
188+++ lib/lp/archiveuploader/nascentupload.py 2017-04-19 13:08:31 +0000
189@@ -24,6 +24,7 @@
190 from zope.component import getUtility
191
192 from lp.app.errors import NotFoundError
193+from lp.archiveuploader.buildinfofile import BuildInfoFile
194 from lp.archiveuploader.changesfile import ChangesFile
195 from lp.archiveuploader.dscfile import DSCFile
196 from lp.archiveuploader.nascentuploadfile import (
197@@ -267,6 +268,15 @@
198 files_archdep or not uploaded_file.is_archindep)
199 elif isinstance(uploaded_file, SourceUploadFile):
200 files_sourceful = True
201+ elif isinstance(uploaded_file, BuildInfoFile):
202+ files_sourceful = (
203+ files_sourceful or uploaded_file.is_sourceful)
204+ if uploaded_file.is_binaryful:
205+ files_binaryful = files_binaryful or True
206+ files_archindep = (
207+ files_archindep or uploaded_file.is_archindep)
208+ files_archdep = (
209+ files_archdep or not uploaded_file.is_archindep)
210 else:
211 # This is already caught in ChangesFile.__init__
212 raise AssertionError("Unknown uploaded file type.")
213@@ -857,6 +867,10 @@
214 assert self.queue_root.pocket == bpf_build.pocket, (
215 "Binary was not build for the claimed pocket.")
216 binary_package_file.storeInDatabase(bpf_build)
217+ if self.changes.buildinfo is not None:
218+ self.changes.buildinfo.checkBuild(bpf_build)
219+ bpf_build.addBuildInfo(
220+ self.changes.buildinfo.storeInDatabase())
221 processed_builds.append(bpf_build)
222
223 # Store the related builds after verifying they were built
224
225=== modified file 'lib/lp/archiveuploader/tests/__init__.py'
226--- lib/lp/archiveuploader/tests/__init__.py 2014-08-13 07:49:59 +0000
227+++ lib/lp/archiveuploader/tests/__init__.py 2017-04-19 13:08:31 +0000
228@@ -1,4 +1,4 @@
229-# Copyright 2009 Canonical Ltd. This software is licensed under the
230+# Copyright 2009-2017 Canonical Ltd. This software is licensed under the
231 # GNU Affero General Public License version 3 (see the file LICENSE).
232
233 """Tests for the archive uploader."""
234@@ -81,8 +81,9 @@
235
236 def __init__(self):
237 AbstractUploadPolicy.__init__(self)
238- # We require the changes to be signed but not the dsc
239+ # We require the changes to be signed but not the dsc or buildinfo
240 self.unsigned_dsc_ok = True
241+ self.unsigned_buildinfo_ok = True
242
243 def validateUploadType(self, upload):
244 """We accept uploads of any type."""
245
246=== added directory 'lib/lp/archiveuploader/tests/data/suite/bar_1.0-1_binary_buildinfo'
247=== added file 'lib/lp/archiveuploader/tests/data/suite/bar_1.0-1_binary_buildinfo/bar_1.0-1_i386.buildinfo'
248--- lib/lp/archiveuploader/tests/data/suite/bar_1.0-1_binary_buildinfo/bar_1.0-1_i386.buildinfo 1970-01-01 00:00:00 +0000
249+++ lib/lp/archiveuploader/tests/data/suite/bar_1.0-1_binary_buildinfo/bar_1.0-1_i386.buildinfo 2017-04-19 13:08:31 +0000
250@@ -0,0 +1,23 @@
251+-----BEGIN PGP SIGNED MESSAGE-----
252+Hash: SHA1
253+
254+Format: 1.0
255+Source: bar
256+Binary: bar
257+Architecture: i386
258+Version: 1.0-1
259+Checksums-Md5:
260+ 39a6dde58f0b84139e9877892f6ac56a 644 bar_1.0-1_i386.deb
261+Build-Origin: Ubuntu
262+Build-Architecture: i386
263+Build-Date: Wed, 29 Mar 2017 00:01:21 +0100
264+Installed-Build-Depends:
265+ dpkg (= 1.18.22),
266+ dpkg-dev (= 1.18.22)
267+
268+-----BEGIN PGP SIGNATURE-----
269+
270+iF0EARECAB0WIQQ0DKO7Jw4nFsnuC3aOfrcIbGSoxQUCWNwqmQAKCRCOfrcIbGSo
271+xc7HAJ9JMKX0NRHSvhaY11jKITVazztw6QCfdQAKsVEzM6tIRNUIwfJnOi7N2Pg=
272+=aQ0x
273+-----END PGP SIGNATURE-----
274
275=== added file 'lib/lp/archiveuploader/tests/data/suite/bar_1.0-1_binary_buildinfo/bar_1.0-1_i386.changes'
276--- lib/lp/archiveuploader/tests/data/suite/bar_1.0-1_binary_buildinfo/bar_1.0-1_i386.changes 1970-01-01 00:00:00 +0000
277+++ lib/lp/archiveuploader/tests/data/suite/bar_1.0-1_binary_buildinfo/bar_1.0-1_i386.changes 2017-04-19 13:08:31 +0000
278@@ -0,0 +1,29 @@
279+-----BEGIN PGP SIGNED MESSAGE-----
280+Hash: SHA1
281+
282+Format: 1.7
283+Date: Thu, 16 Feb 2006 15:34:09 +0000
284+Source: bar
285+Binary: bar
286+Architecture: i386
287+Version: 1.0-1
288+Distribution: breezy
289+Urgency: low
290+Maintainer: Launchpad team <launchpad@lists.canonical.com>
291+Changed-By: Daniel Silverstone <daniel.silverstone@canonical.com>
292+Description:
293+ bar - Stuff for testing
294+Changes:
295+ bar (1.0-1) breezy; urgency=low
296+ .
297+ * Initial version
298+Files:
299+ 39a6dde58f0b84139e9877892f6ac56a 644 devel optional bar_1.0-1_i386.deb
300+ fa48116db5bb103a310e7e5ffd6a14f4 541 devel optional bar_1.0-1_i386.buildinfo
301+
302+-----BEGIN PGP SIGNATURE-----
303+
304+iF0EARECAB0WIQQ0DKO7Jw4nFsnuC3aOfrcIbGSoxQUCWNwqmQAKCRCOfrcIbGSo
305+xfGYAJ9ha1G54CIb6L+RNtt2Y1BoCc8NfgCfbNqjebToNPL5Egxd0U7nfC+yzlA=
306+=WNoA
307+-----END PGP SIGNATURE-----
308
309=== added file 'lib/lp/archiveuploader/tests/data/suite/bar_1.0-1_binary_buildinfo/bar_1.0-1_i386.deb'
310Binary files lib/lp/archiveuploader/tests/data/suite/bar_1.0-1_binary_buildinfo/bar_1.0-1_i386.deb 1970-01-01 00:00:00 +0000 and lib/lp/archiveuploader/tests/data/suite/bar_1.0-1_binary_buildinfo/bar_1.0-1_i386.deb 2017-04-19 13:08:31 +0000 differ
311=== added directory 'lib/lp/archiveuploader/tests/data/suite/bar_1.0-1_buildinfo'
312=== added file 'lib/lp/archiveuploader/tests/data/suite/bar_1.0-1_buildinfo/bar_1.0-1.diff.gz'
313Binary files lib/lp/archiveuploader/tests/data/suite/bar_1.0-1_buildinfo/bar_1.0-1.diff.gz 1970-01-01 00:00:00 +0000 and lib/lp/archiveuploader/tests/data/suite/bar_1.0-1_buildinfo/bar_1.0-1.diff.gz 2017-04-19 13:08:31 +0000 differ
314=== added file 'lib/lp/archiveuploader/tests/data/suite/bar_1.0-1_buildinfo/bar_1.0-1.dsc'
315--- lib/lp/archiveuploader/tests/data/suite/bar_1.0-1_buildinfo/bar_1.0-1.dsc 1970-01-01 00:00:00 +0000
316+++ lib/lp/archiveuploader/tests/data/suite/bar_1.0-1_buildinfo/bar_1.0-1.dsc 2017-04-19 13:08:31 +0000
317@@ -0,0 +1,21 @@
318+-----BEGIN PGP SIGNED MESSAGE-----
319+Hash: SHA1
320+
321+Format: 1.0
322+Source: bar
323+Version: 1.0-1
324+Binary: bar
325+Maintainer: Launchpad team <launchpad@lists.canonical.com>
326+Architecture: any
327+Standards-Version: 3.6.2
328+Files:
329+ fc1464e5985b962a042d5354452f361d 164 bar_1.0.orig.tar.gz
330+ 1e35b810764f140af9616de8274e6e73 537 bar_1.0-1.diff.gz
331+
332+-----BEGIN PGP SIGNATURE-----
333+Version: GnuPG v1.4.3 (GNU/Linux)
334+
335+iD8DBQFFt7Cojn63CGxkqMURAo6FAJ9ZUagUNtYpmZrqFwL6LXDKOUSOPwCdFqPa
336+BdrMeT+0Hg+yMS69uO+qJRI=
337+=mjFU
338+-----END PGP SIGNATURE-----
339
340=== added file 'lib/lp/archiveuploader/tests/data/suite/bar_1.0-1_buildinfo/bar_1.0-1_source.buildinfo'
341--- lib/lp/archiveuploader/tests/data/suite/bar_1.0-1_buildinfo/bar_1.0-1_source.buildinfo 1970-01-01 00:00:00 +0000
342+++ lib/lp/archiveuploader/tests/data/suite/bar_1.0-1_buildinfo/bar_1.0-1_source.buildinfo 2017-04-19 13:08:31 +0000
343@@ -0,0 +1,23 @@
344+-----BEGIN PGP SIGNED MESSAGE-----
345+Hash: SHA1
346+
347+Format: 1.0
348+Source: bar
349+Binary: bar
350+Architecture: source
351+Version: 1.0-1
352+Checksums-Md5:
353+ 5d533778b698edc1a122098a98c8490e 512 bar_1.0-1.dsc
354+Build-Origin: Ubuntu
355+Build-Architecture: amd64
356+Build-Date: Tue, 28 Mar 2017 23:51:59 +0100
357+Installed-Build-Depends:
358+ dpkg (= 1.18.22),
359+ dpkg-dev (= 1.18.22)
360+
361+-----BEGIN PGP SIGNATURE-----
362+
363+iF0EARECAB0WIQQ0DKO7Jw4nFsnuC3aOfrcIbGSoxQUCWNwqgQAKCRCOfrcIbGSo
364+xee9AJwK4AIvXCWE409SBvALeq9/6PoR5QCfREHF/gdmZmvsI3hDunrSi3EzA0o=
365+=Zgxq
366+-----END PGP SIGNATURE-----
367
368=== added file 'lib/lp/archiveuploader/tests/data/suite/bar_1.0-1_buildinfo/bar_1.0-1_source.changes'
369--- lib/lp/archiveuploader/tests/data/suite/bar_1.0-1_buildinfo/bar_1.0-1_source.changes 1970-01-01 00:00:00 +0000
370+++ lib/lp/archiveuploader/tests/data/suite/bar_1.0-1_buildinfo/bar_1.0-1_source.changes 2017-04-19 13:08:31 +0000
371@@ -0,0 +1,31 @@
372+-----BEGIN PGP SIGNED MESSAGE-----
373+Hash: SHA1
374+
375+Format: 1.7
376+Date: Thu, 16 Feb 2006 15:34:09 +0000
377+Source: bar
378+Binary: bar
379+Architecture: source
380+Version: 1.0-1
381+Distribution: breezy
382+Urgency: low
383+Maintainer: Launchpad team <launchpad@lists.canonical.com>
384+Changed-By: Daniel Silverstone <daniel.silverstone@canonical.com>
385+Description:
386+ bar - Stuff for testing
387+Changes:
388+ bar (1.0-1) breezy; urgency=low
389+ .
390+ * Initial version
391+Files:
392+ 5d533778b698edc1a122098a98c8490e 512 devel optional bar_1.0-1.dsc
393+ fc1464e5985b962a042d5354452f361d 164 devel optional bar_1.0.orig.tar.gz
394+ 1e35b810764f140af9616de8274e6e73 537 devel optional bar_1.0-1.diff.gz
395+ 0b66a844c11fa81df970ac8d4edd1ed7 539 devel optional bar_1.0-1_source.buildinfo
396+
397+-----BEGIN PGP SIGNATURE-----
398+
399+iF0EARECAB0WIQQ0DKO7Jw4nFsnuC3aOfrcIbGSoxQUCWNwqhQAKCRCOfrcIbGSo
400+xfGGAKCBdw76SkJc8Fx6CCE2dmqJEkuSXwCfX6YlZRv+8dmu5nb5JAUcBzDxQ60=
401+=m/R9
402+-----END PGP SIGNATURE-----
403
404=== added file 'lib/lp/archiveuploader/tests/data/suite/bar_1.0-1_buildinfo/bar_1.0.orig.tar.gz'
405Binary files lib/lp/archiveuploader/tests/data/suite/bar_1.0-1_buildinfo/bar_1.0.orig.tar.gz 1970-01-01 00:00:00 +0000 and lib/lp/archiveuploader/tests/data/suite/bar_1.0-1_buildinfo/bar_1.0.orig.tar.gz 2017-04-19 13:08:31 +0000 differ
406=== added file 'lib/lp/archiveuploader/tests/test_buildinfofile.py'
407--- lib/lp/archiveuploader/tests/test_buildinfofile.py 1970-01-01 00:00:00 +0000
408+++ lib/lp/archiveuploader/tests/test_buildinfofile.py 2017-04-19 13:08:31 +0000
409@@ -0,0 +1,92 @@
410+# Copyright 2017 Canonical Ltd. This software is licensed under the
411+# GNU Affero General Public License version 3 (see the file LICENSE).
412+
413+"""Build information file tests."""
414+
415+from __future__ import absolute_import, print_function, unicode_literals
416+
417+__metaclass__ = type
418+
419+from debian.deb822 import Changes
420+
421+from lp.archiveuploader.buildinfofile import BuildInfoFile
422+from lp.archiveuploader.nascentuploadfile import UploadError
423+from lp.archiveuploader.tests.test_nascentuploadfile import (
424+ PackageUploadFileTestCase,
425+ )
426+from lp.testing.layers import LaunchpadZopelessLayer
427+
428+
429+class TestBuildInfoFile(PackageUploadFileTestCase):
430+
431+ layer = LaunchpadZopelessLayer
432+
433+ def getBaseBuildInfo(self):
434+ # XXX cjwatson 2017-03-20: This will need to be fleshed out if we
435+ # ever start doing non-trivial buildinfo parsing.
436+ # A Changes object is close enough.
437+ buildinfo = Changes()
438+ buildinfo["Format"] = "1.0"
439+ return buildinfo
440+
441+ def makeBuildInfoFile(self, filename, buildinfo, component_and_section,
442+ priority_name, package, version, changes):
443+ path, md5, sha1, size = self.writeUploadFile(
444+ filename, buildinfo.dump())
445+ return BuildInfoFile(
446+ path, {"MD5": md5}, size, component_and_section, priority_name,
447+ package, version, changes, self.policy, self.logger)
448+
449+ def test_properties(self):
450+ buildinfo = self.getBaseBuildInfo()
451+ changes = self.getBaseChanges()
452+ for (arch, is_sourceful, is_binaryful, is_archindep) in (
453+ ("source", True, False, False),
454+ ("all", False, True, True),
455+ ("i386", False, True, False),
456+ ):
457+ buildinfofile = self.makeBuildInfoFile(
458+ "foo_0.1-1_%s.buildinfo" % arch, buildinfo,
459+ "main/net", "extra", "dulwich", "0.42",
460+ self.createChangesFile("foo_0.1-1_%s.changes" % arch, changes))
461+ self.assertEqual(arch, buildinfofile.architecture)
462+ self.assertEqual(is_sourceful, buildinfofile.is_sourceful)
463+ self.assertEqual(is_binaryful, buildinfofile.is_binaryful)
464+ self.assertEqual(is_archindep, buildinfofile.is_archindep)
465+
466+ def test_storeInDatabase(self):
467+ buildinfo = self.getBaseBuildInfo()
468+ changes = self.getBaseChanges()
469+ buildinfofile = self.makeBuildInfoFile(
470+ "foo_0.1-1_source.buildinfo", buildinfo,
471+ "main/net", "extra", "dulwich", "0.42",
472+ self.createChangesFile("foo_0.1-1_source.changes", changes))
473+ lfa = buildinfofile.storeInDatabase()
474+ self.layer.txn.commit()
475+ self.assertEqual(buildinfo.dump(), lfa.read())
476+
477+ def test_checkBuild(self):
478+ das = self.factory.makeDistroArchSeries(
479+ distroseries=self.policy.distroseries, architecturetag="i386")
480+ build = self.factory.makeBinaryPackageBuild(
481+ distroarchseries=das, archive=self.policy.archive)
482+ buildinfo = self.getBaseBuildInfo()
483+ changes = self.getBaseChanges()
484+ buildinfofile = self.makeBuildInfoFile(
485+ "foo_0.1-1_i386.buildinfo", buildinfo,
486+ "main/net", "extra", "dulwich", "0.42",
487+ self.createChangesFile("foo_0.1-1_i386.changes", changes))
488+ buildinfofile.checkBuild(build)
489+
490+ def test_checkBuild_inconsistent(self):
491+ das = self.factory.makeDistroArchSeries(
492+ distroseries=self.policy.distroseries, architecturetag="amd64")
493+ build = self.factory.makeBinaryPackageBuild(
494+ distroarchseries=das, archive=self.policy.archive)
495+ buildinfo = self.getBaseBuildInfo()
496+ changes = self.getBaseChanges()
497+ buildinfofile = self.makeBuildInfoFile(
498+ "foo_0.1-1_i386.buildinfo", buildinfo,
499+ "main/net", "extra", "dulwich", "0.42",
500+ self.createChangesFile("foo_0.1-1_i386.changes", changes))
501+ self.assertRaises(UploadError, buildinfofile.checkBuild, build)
502
503=== modified file 'lib/lp/archiveuploader/tests/test_changesfile.py'
504--- lib/lp/archiveuploader/tests/test_changesfile.py 2015-07-29 04:17:26 +0000
505+++ lib/lp/archiveuploader/tests/test_changesfile.py 2017-04-19 13:08:31 +0000
506@@ -1,4 +1,4 @@
507-# Copyright 2010-2011 Canonical Ltd. This software is licensed under the
508+# Copyright 2010-2017 Canonical Ltd. This software is licensed under the
509 # GNU Affero General Public License version 3 (see the file LICENSE).
510
511 """Test ChangesFile functionality."""
512@@ -15,6 +15,7 @@
513 )
514 from zope.component import getUtility
515
516+from lp.archiveuploader.buildinfofile import BuildInfoFile
517 from lp.archiveuploader.changesfile import (
518 CannotDetermineFileTypeError,
519 ChangesFile,
520@@ -71,6 +72,12 @@
521 ('foo', UdebBinaryUploadFile),
522 determine_file_class_and_name('foo_1.0_all.udeb'))
523
524+ def testBuildInfoFile(self):
525+ # A buildinfo file is a BuildInfoFile.
526+ self.assertEqual(
527+ ('foo', BuildInfoFile),
528+ determine_file_class_and_name('foo_1.0_all.buildinfo'))
529+
530 def testUnmatchingFile(self):
531 # Files with unknown extensions or none at all are not
532 # identified.
533
534=== modified file 'lib/lp/archiveuploader/tests/test_uploadpolicy.py'
535--- lib/lp/archiveuploader/tests/test_uploadpolicy.py 2016-05-09 18:06:07 +0000
536+++ lib/lp/archiveuploader/tests/test_uploadpolicy.py 2017-04-19 13:08:31 +0000
537@@ -1,6 +1,6 @@
538 #!/usr/bin/python
539 #
540-# Copyright 2010-2016 Canonical Ltd. This software is licensed under the
541+# Copyright 2010-2017 Canonical Ltd. This software is licensed under the
542 # GNU Affero General Public License version 3 (see the file LICENSE).
543
544 from zope.component import getUtility
545@@ -174,6 +174,8 @@
546 self.assertTrue(buildd_policy.unsigned_changes_ok)
547 self.assertFalse(insecure_policy.unsigned_dsc_ok)
548 self.assertTrue(buildd_policy.unsigned_dsc_ok)
549+ self.assertFalse(insecure_policy.unsigned_buildinfo_ok)
550+ self.assertTrue(buildd_policy.unsigned_buildinfo_ok)
551
552 def test_setOptions_distro_name(self):
553 # Policies pick up the distribution name from options.
554
555=== modified file 'lib/lp/archiveuploader/tests/test_uploadprocessor.py'
556--- lib/lp/archiveuploader/tests/test_uploadprocessor.py 2017-01-10 15:18:48 +0000
557+++ lib/lp/archiveuploader/tests/test_uploadprocessor.py 2017-04-19 13:08:31 +0000
558@@ -119,6 +119,7 @@
559 self.name = "broken"
560 self.unsigned_changes_ok = True
561 self.unsigned_dsc_ok = True
562+ self.unsigned_buildinfo_ok = True
563
564 def checkUpload(self, upload):
565 """Raise an exception upload processing is not expecting."""
566@@ -2017,6 +2018,47 @@
567 version=u"1.0-2", exact_match=True)
568 self.assertEqual(PackagePublishingPocket.PROPOSED, queue_item.pocket)
569
570+ def test_source_buildinfo(self):
571+ # A buildinfo file is attached to the SPR.
572+ uploadprocessor = self.setupBreezyAndGetUploadProcessor()
573+ upload_dir = self.queueUpload("bar_1.0-1_buildinfo")
574+ with open(os.path.join(upload_dir, "bar_1.0-1_source.buildinfo")) as f:
575+ buildinfo_contents = f.read()
576+ self.processUpload(uploadprocessor, upload_dir)
577+ source_pub = self.publishPackage("bar", "1.0-1")
578+ self.assertEqual(
579+ buildinfo_contents,
580+ source_pub.sourcepackagerelease.buildinfo.read())
581+
582+ def test_binary_buildinfo(self):
583+ # A buildinfo file is attached to the BPB.
584+ uploadprocessor = self.setupBreezyAndGetUploadProcessor()
585+ upload_dir = self.queueUpload("bar_1.0-1")
586+ self.processUpload(uploadprocessor, upload_dir)
587+ source_pub = self.publishPackage("bar", "1.0-1")
588+ [build] = source_pub.createMissingBuilds()
589+ self.switchToAdmin()
590+ [queue_item] = self.breezy.getPackageUploads(
591+ status=PackageUploadStatus.ACCEPTED,
592+ version=u"1.0-1", name=u"bar")
593+ queue_item.setDone()
594+ build.buildqueue_record.markAsBuilding(self.factory.makeBuilder())
595+ build.updateStatus(BuildStatus.UPLOADING)
596+ self.switchToUploader()
597+ shutil.rmtree(upload_dir)
598+ self.layer.txn.commit()
599+ behaviour = IBuildFarmJobBehaviour(build)
600+ leaf_name = behaviour.getUploadDirLeaf(build.build_cookie)
601+ upload_dir = self.queueUpload(
602+ "bar_1.0-1_binary_buildinfo", queue_entry=leaf_name)
603+ with open(os.path.join(upload_dir, "bar_1.0-1_i386.buildinfo")) as f:
604+ buildinfo_contents = f.read()
605+ self.options.context = "buildd"
606+ self.options.builds = True
607+ BuildUploadHandler(
608+ uploadprocessor, self.incoming_folder, leaf_name).process()
609+ self.assertEqual(buildinfo_contents, build.buildinfo.read())
610+
611
612 class TestUploadHandler(TestUploadProcessorBase):
613
614
615=== modified file 'lib/lp/archiveuploader/uploadpolicy.py'
616--- lib/lp/archiveuploader/uploadpolicy.py 2016-01-26 15:47:37 +0000
617+++ lib/lp/archiveuploader/uploadpolicy.py 2017-04-19 13:08:31 +0000
618@@ -1,4 +1,4 @@
619-# Copyright 2009-2013 Canonical Ltd. This software is licensed under the
620+# Copyright 2009-2017 Canonical Ltd. This software is licensed under the
621 # GNU Affero General Public License version 3 (see the file LICENSE).
622
623 """Policy management for the upload handler."""
624@@ -87,6 +87,7 @@
625 self.archive = None
626 self.unsigned_changes_ok = False
627 self.unsigned_dsc_ok = False
628+ self.unsigned_buildinfo_ok = False
629 self.create_people = True
630 # future_time_grace is in seconds
631 self.future_time_grace = 24 * HOURS
632@@ -290,6 +291,7 @@
633 # We permit unsigned uploads because we trust our build daemons
634 self.unsigned_changes_ok = True
635 self.unsigned_dsc_ok = True
636+ self.unsigned_buildinfo_ok = True
637
638 def setOptions(self, options):
639 """Store the options for later."""
640@@ -330,9 +332,10 @@
641
642 def __init__(self):
643 AbstractUploadPolicy.__init__(self)
644- # We don't require changes or dsc to be signed for syncs
645+ # We don't require changes/dsc/buildinfo to be signed for syncs
646 self.unsigned_changes_ok = True
647 self.unsigned_dsc_ok = True
648+ self.unsigned_buildinfo_ok = True
649
650 def policySpecificChecks(self, upload):
651 """Perform sync specific checks."""
652
653=== modified file 'lib/lp/archiveuploader/utils.py'
654--- lib/lp/archiveuploader/utils.py 2016-06-01 01:59:32 +0000
655+++ lib/lp/archiveuploader/utils.py 2017-04-19 13:08:31 +0000
656@@ -1,4 +1,4 @@
657-# Copyright 2009-2016 Canonical Ltd. This software is licensed under the
658+# Copyright 2009-2017 Canonical Ltd. This software is licensed under the
659 # GNU Affero General Public License version 3 (see the file LICENSE).
660
661 """Archive uploader utilities."""
662@@ -17,6 +17,7 @@
663 'prefix_multi_line_string',
664 're_taint_free',
665 're_isadeb',
666+ 're_isbuildinfo',
667 're_issource',
668 're_is_component_orig_tar_ext',
669 're_is_component_orig_tar_ext_sig',
670@@ -66,6 +67,7 @@
671 re_taint_free = re.compile(r"^[-+~/\.\w]+$")
672
673 re_isadeb = re.compile(r"(.+?)_(.+?)_(.+)\.(u?d?deb)$")
674+re_isbuildinfo = re.compile(r"(.+?)_(.+?)_(.+)\.buildinfo$")
675
676 source_file_exts = [
677 'orig(?:-.+)?\.tar\.(?:gz|bz2|xz)(?:\.asc)?', 'diff.gz',
678
679=== modified file 'lib/lp/registry/interfaces/distroseries.py'
680--- lib/lp/registry/interfaces/distroseries.py 2016-11-14 19:55:07 +0000
681+++ lib/lp/registry/interfaces/distroseries.py 2017-04-19 13:08:31 +0000
682@@ -1,4 +1,4 @@
683-# Copyright 2009-2016 Canonical Ltd. This software is licensed under the
684+# Copyright 2009-2017 Canonical Ltd. This software is licensed under the
685 # GNU Affero General Public License version 3 (see the file LICENSE).
686
687 """Interfaces including and related to IDistroSeries."""
688@@ -686,7 +686,7 @@
689 dsc_binaries, archive, copyright, build_conflicts,
690 build_conflicts_indep, dateuploaded=None,
691 source_package_recipe_build=None, user_defined_fields=None,
692- homepage=None):
693+ homepage=None, buildinfo=None):
694 """Create an uploads `SourcePackageRelease`.
695
696 Set this distroseries set to be the uploadeddistroseries.
697@@ -726,6 +726,7 @@
698 user defined fields.
699 :param homepage: optional string with (unchecked) upstream homepage
700 URL
701+ :param buildinfo: optional LFA with build information file
702 :return: the just creates `SourcePackageRelease`
703 """
704
705
706=== modified file 'lib/lp/registry/model/distroseries.py'
707--- lib/lp/registry/model/distroseries.py 2016-04-04 12:54:43 +0000
708+++ lib/lp/registry/model/distroseries.py 2017-04-19 13:08:31 +0000
709@@ -1,4 +1,4 @@
710-# Copyright 2009-2016 Canonical Ltd. This software is licensed under the
711+# Copyright 2009-2017 Canonical Ltd. This software is licensed under the
712 # GNU Affero General Public License version 3 (see the file LICENSE).
713
714 """Database classes for a distribution series."""
715@@ -1187,7 +1187,7 @@
716 dsc_binaries, archive, copyright, build_conflicts,
717 build_conflicts_indep, dateuploaded=DEFAULT,
718 source_package_recipe_build=None, user_defined_fields=None,
719- homepage=None):
720+ homepage=None, buildinfo=None):
721 """See `IDistroSeries`."""
722 return SourcePackageRelease(
723 upload_distroseries=self, sourcepackagename=sourcepackagename,
724@@ -1206,7 +1206,8 @@
725 build_conflicts=build_conflicts,
726 build_conflicts_indep=build_conflicts_indep,
727 source_package_recipe_build=source_package_recipe_build,
728- user_defined_fields=user_defined_fields, homepage=homepage)
729+ user_defined_fields=user_defined_fields, homepage=homepage,
730+ buildinfo=buildinfo)
731
732 def getComponentByName(self, name):
733 """See `IDistroSeries`."""
734
735=== modified file 'lib/lp/soyuz/enums.py'
736--- lib/lp/soyuz/enums.py 2016-05-26 14:53:06 +0000
737+++ lib/lp/soyuz/enums.py 2017-04-19 13:08:31 +0000
738@@ -1,4 +1,4 @@
739-# Copyright 2010-2016 Canonical Ltd. This software is licensed under the
740+# Copyright 2010-2017 Canonical Ltd. This software is licensed under the
741 # GNU Affero General Public License version 3 (see the file LICENSE).
742
743 """Enumerations used in the lp/soyuz modules."""
744@@ -197,6 +197,13 @@
745 similar operating systems for distributing debug symbols.
746 """)
747
748+ BUILDINFO = DBItem(5, """
749+ Build information
750+
751+ A file used by Debian-based systems to record information about the
752+ build environment.
753+ """)
754+
755
756 class BinaryPackageFormat(DBEnumeratedType):
757 """Binary Package Format
758
759=== modified file 'lib/lp/soyuz/interfaces/binarypackagebuild.py'
760--- lib/lp/soyuz/interfaces/binarypackagebuild.py 2016-01-08 13:15:02 +0000
761+++ lib/lp/soyuz/interfaces/binarypackagebuild.py 2017-04-19 13:08:31 +0000
762@@ -1,4 +1,4 @@
763-# Copyright 2009-2016 Canonical Ltd. This software is licensed under the
764+# Copyright 2009-2017 Canonical Ltd. This software is licensed under the
765 # GNU Affero General Public License version 3 (see the file LICENSE).
766
767 """BinaryPackageBuild interfaces."""
768@@ -145,6 +145,10 @@
769 description=_("The URL for the changes file for this build. "
770 "Will be None if the build was imported by Gina.")))
771
772+ buildinfo = Attribute(
773+ "The `LibraryFileAlias` object containing build information for "
774+ "this build, if any.")
775+
776 package_upload = Attribute(
777 "The `PackageUpload` record corresponding to the original upload "
778 "of the binaries resulted from this build. It's 'None' if it is "
779@@ -265,6 +269,12 @@
780 If the build is not in a cancellable state, this method is a no-op.
781 """
782
783+ def addBuildInfo(buildinfo):
784+ """Add a buildinfo file to this build.
785+
786+ :param buildinfo: An `ILibraryFileAlias`.
787+ """
788+
789
790 class IBinaryPackageBuildRestricted(Interface):
791 """Restricted `IBinaryPackageBuild` attributes.
792@@ -337,7 +347,8 @@
793 """Interface for BinaryPackageBuildSet"""
794
795 def new(source_package_release, archive, distro_arch_series, pocket,
796- arch_indep=False, status=BuildStatus.NEEDSBUILD, builder=None):
797+ arch_indep=False, status=BuildStatus.NEEDSBUILD, builder=None,
798+ buildinfo=None):
799 """Create a new `IBinaryPackageBuild`.
800
801 :param source_package_release: An `ISourcePackageRelease`.
802@@ -348,6 +359,7 @@
803 addition to architecture specific ones.
804 :param status: A `BuildStatus` item indicating the builds status.
805 :param builder: An optional `IBuilder`.
806+ :param buildinfo: An optional `ILibraryFileAlias`.
807 """
808
809 def getBySourceAndLocation(source_package_release, archive,
810
811=== modified file 'lib/lp/soyuz/interfaces/sourcepackagerelease.py'
812--- lib/lp/soyuz/interfaces/sourcepackagerelease.py 2016-07-29 07:37:33 +0000
813+++ lib/lp/soyuz/interfaces/sourcepackagerelease.py 2017-04-19 13:08:31 +0000
814@@ -1,4 +1,4 @@
815-# Copyright 2009-2014 Canonical Ltd. This software is licensed under the
816+# Copyright 2009-2017 Canonical Ltd. This software is licensed under the
817 # GNU Affero General Public License version 3 (see the file LICENSE).
818
819 """Source package release interfaces."""
820@@ -44,6 +44,9 @@
821 change_summary = Attribute(
822 "The message on the latest change in this release. This is usually "
823 "a snippet from the changelog")
824+ buildinfo = Attribute(
825+ "LibraryFileAlias containing build information for this source "
826+ "upload, if any.")
827 builddepends = TextLine(
828 title=_("DSC build depends"),
829 description=_("A comma-separated list of packages on which this "
830
831=== modified file 'lib/lp/soyuz/model/binarypackagebuild.py'
832--- lib/lp/soyuz/model/binarypackagebuild.py 2016-01-08 13:15:02 +0000
833+++ lib/lp/soyuz/model/binarypackagebuild.py 2017-04-19 13:08:31 +0000
834@@ -1,4 +1,4 @@
835-# Copyright 2009-2016 Canonical Ltd. This software is licensed under the
836+# Copyright 2009-2017 Canonical Ltd. This software is licensed under the
837 # GNU Affero General Public License version 3 (see the file LICENSE).
838
839 __metaclass__ = type
840@@ -81,11 +81,11 @@
841 LibraryFileAlias,
842 LibraryFileContent,
843 )
844+from lp.soyuz.adapters.buildarch import determine_architectures_to_build
845 from lp.soyuz.enums import (
846 ArchivePurpose,
847 PackagePublishingStatus,
848 )
849-from lp.soyuz.adapters.buildarch import determine_architectures_to_build
850 from lp.soyuz.interfaces.archive import (
851 InvalidExternalDependencies,
852 validate_external_dependencies,
853@@ -229,6 +229,9 @@
854 name='external_dependencies',
855 validator=storm_validate_external_dependencies)
856
857+ buildinfo_id = Int(name='buildinfo')
858+ buildinfo = Reference(buildinfo_id, 'LibraryFileAlias.id')
859+
860 def getLatestSourcePublication(self):
861 from lp.soyuz.model.publishing import SourcePackagePublishingHistory
862 store = Store.of(self)
863@@ -703,6 +706,13 @@
864 estimate = 5
865 return datetime.timedelta(minutes=estimate)
866
867+ def addBuildInfo(self, buildinfo):
868+ """See `IBinaryPackageBuild`."""
869+ if self.buildinfo is None:
870+ self.buildinfo = buildinfo
871+ else:
872+ assert self.buildinfo == buildinfo
873+
874 def verifySuccessfulUpload(self):
875 return bool(self.binarypackages)
876
877@@ -783,7 +793,8 @@
878 class BinaryPackageBuildSet(SpecificBuildFarmJobSourceMixin):
879
880 def new(self, source_package_release, archive, distro_arch_series, pocket,
881- arch_indep=None, status=BuildStatus.NEEDSBUILD, builder=None):
882+ arch_indep=None, status=BuildStatus.NEEDSBUILD, builder=None,
883+ buildinfo=None):
884 """See `IBinaryPackageBuildSet`."""
885 # Force the current timestamp instead of the default UTC_NOW for
886 # the transaction, avoid several row with same datecreated.
887@@ -806,7 +817,7 @@
888 distribution=distro_arch_series.distroseries.distribution,
889 distro_series=distro_arch_series.distroseries,
890 source_package_name=source_package_release.sourcepackagename,
891- date_created=date_created)
892+ buildinfo=buildinfo, date_created=date_created)
893
894 def getByID(self, id):
895 """See `IBinaryPackageBuildSet`."""
896
897=== modified file 'lib/lp/soyuz/model/sourcepackagerelease.py'
898--- lib/lp/soyuz/model/sourcepackagerelease.py 2016-12-22 16:32:38 +0000
899+++ lib/lp/soyuz/model/sourcepackagerelease.py 2017-04-19 13:08:31 +0000
900@@ -1,4 +1,4 @@
901-# Copyright 2009-2016 Canonical Ltd. This software is licensed under the
902+# Copyright 2009-2017 Canonical Ltd. This software is licensed under the
903 # GNU Affero General Public License version 3 (see the file LICENSE).
904
905 __metaclass__ = type
906@@ -100,6 +100,7 @@
907 version = StringCol(dbName='version', notNull=True)
908 changelog = ForeignKey(foreignKey='LibraryFileAlias', dbName='changelog')
909 changelog_entry = StringCol(dbName='changelog_entry')
910+ buildinfo = ForeignKey(foreignKey='LibraryFileAlias', dbName='buildinfo')
911 builddepends = StringCol(dbName='builddepends')
912 builddependsindep = StringCol(dbName='builddependsindep')
913 build_conflicts = StringCol(dbName='build_conflicts')