]> git.donarmstrong.com Git - dak.git/blobdiff - daklib/archive.py
Handle packages with overrides in multiple components
[dak.git] / daklib / archive.py
index 7dcc1af035c015c3d8c84a61bc518ac1980bb1d3..04c55c0ae8f45ba0e7b4f63870b24a627fe191e5 100644 (file)
@@ -26,13 +26,15 @@ import daklib.upload as upload
 import daklib.utils as utils
 from daklib.fstransactions import FilesystemTransaction
 from daklib.regexes import re_changelog_versions, re_bin_only_nmu
+import daklib.daksubprocess
 
 import apt_pkg
 from datetime import datetime
 import os
 import shutil
-import subprocess
 from sqlalchemy.orm.exc import NoResultFound
+from sqlalchemy.orm import object_session
+import sqlalchemy.exc
 import tempfile
 import traceback
 
@@ -85,7 +87,7 @@ class ArchiveTransaction(object):
         Will not give an error when the file is already present.
 
         @rtype:  L{daklib.dbconn.PoolFile}
-        @return: batabase object for the new file
+        @return: database object for the new file
         """
         session = self.session
 
@@ -335,6 +337,8 @@ class ArchiveTransaction(object):
         db_source.suites.append(suite)
 
         if not created:
+            for f in db_source.srcfiles:
+                self._copy_file(f.poolfile, archive, component, allow_tainted=allow_tainted)
             return db_source
 
         ### Now add remaining files and copy them to the archive.
@@ -386,13 +390,13 @@ class ArchiveTransaction(object):
         session = self.session
 
         if session.query(ArchiveFile).filter_by(archive=archive, component=component, file=db_file).first() is None:
-            query = session.query(ArchiveFile).filter_by(file=db_file, component=component)
+            query = session.query(ArchiveFile).filter_by(file=db_file)
             if not allow_tainted:
                 query = query.join(Archive).filter(Archive.tainted == False)
 
             source_af = query.first()
             if source_af is None:
-                raise ArchiveException('cp: Could not find {0} in component {1} in any archive.'.format(db_file.filename, component.component_name))
+                raise ArchiveException('cp: Could not find {0} in any archive.'.format(db_file.filename))
             target_af = ArchiveFile(archive, component, db_file)
             session.add(target_af)
             session.flush()
@@ -537,6 +541,34 @@ class ArchiveTransaction(object):
             self.rollback()
         return None
 
+def source_component_from_package_list(package_list, suite):
+    """Get component for a source package
+
+    This function will look at the Package-List field to determine the
+    component the source package belongs to. This is the first component
+    the source package provides binaries for (first with respect to the
+    ordering of components).
+
+    It the source package has no Package-List field, None is returned.
+
+    @type  package_list: L{daklib.packagelist.PackageList}
+    @param package_list: package list of the source to get the override for
+
+    @type  suite: L{daklib.dbconn.Suite}
+    @param suite: suite to consider for binaries produced
+
+    @rtype:  L{daklib.dbconn.Component} or C{None}
+    @return: component for the given source or C{None}
+    """
+    if package_list.fallback:
+        return None
+    session = object_session(suite)
+    packages = package_list.packages_for_suite(suite)
+    components = set(p.component for p in packages)
+    query = session.query(Component).order_by(Component.ordering) \
+            .filter(Component.component_name.in_(components))
+    return query.first()
+
 class ArchiveUpload(object):
     """handle an upload
 
@@ -633,8 +665,9 @@ class ArchiveUpload(object):
         cnf = Config()
         session = self.transaction.session
 
+        group = cnf.get('Dinstall::UnprivGroup') or None
         self.directory = utils.temp_dirname(parent=cnf.get('Dir::TempPath'),
-                                            mode=0o2750, group=cnf.unprivgroup)
+                                            mode=0o2750, group=group)
         with FilesystemTransaction() as fs:
             src = os.path.join(self.original_directory, self.original_changes.filename)
             dst = os.path.join(self.directory, self.original_changes.filename)
@@ -649,7 +682,13 @@ class ArchiveUpload(object):
                     continue
                 fs.copy(src, dst, mode=0o640)
 
-            source = self.changes.source
+            source = None
+            try:
+                source = self.changes.source
+            except Exception:
+                # Do not raise an exception here if the .dsc is invalid.
+                pass
+
             if source is not None:
                 for f in source.files.itervalues():
                     src = os.path.join(self.original_directory, f.filename)
@@ -658,7 +697,7 @@ class ArchiveUpload(object):
                         try:
                             db_file = self.transaction.get_file(f, source.dsc['Source'], check_hashes=False)
                             db_archive_file = session.query(ArchiveFile).filter_by(file=db_file).first()
-                            fs.copy(db_archive_file.path, dst, symlink=True)
+                            fs.copy(db_archive_file.path, dst, mode=0o640)
                         except KeyError:
                             # Ignore if get_file could not find it. Upload will
                             # probably be rejected later.
@@ -685,7 +724,7 @@ class ArchiveUpload(object):
         sourcedir = os.path.join(self.directory, 'source')
         if not os.path.exists(sourcedir):
             devnull = open('/dev/null', 'w')
-            subprocess.check_call(["dpkg-source", "--no-copy", "--no-check", "-x", dsc_path, sourcedir], shell=False, stdout=devnull)
+            daklib.daksubprocess.check_call(["dpkg-source", "--no-copy", "--no-check", "-x", dsc_path, sourcedir], shell=False, stdout=devnull)
         if not os.path.isdir(sourcedir):
             raise Exception("{0} is not a directory after extracting source package".format(sourcedir))
         return sourcedir
@@ -708,7 +747,7 @@ class ArchiveUpload(object):
             elif rtype == "reject":
                 rejected = fields[1]
                 if suite_name == rejected:
-                    self.reject_reasons.append('Uploads to {0} are not accepted.'.format(suite))
+                    raise checks.Reject('Uploads to {0} are not accepted.'.format(rejected))
             ## XXX: propup-version and map-unreleased not yet implemented
         return suite_name
 
@@ -729,6 +768,23 @@ class ArchiveUpload(object):
         suites = session.query(Suite).filter(Suite.suite_name.in_(suite_names))
         return suites
 
+    def _check_new_binary_overrides(self, suite):
+        new = False
+
+        binaries = self.changes.binaries
+        source = self.changes.source
+        if source is not None and not source.package_list.fallback:
+            packages = source.package_list.packages_for_suite(suite)
+            binaries = [ entry for entry in packages ]
+
+        for b in binaries:
+            override = self._binary_override(suite, b)
+            if override is None:
+                self.warnings.append('binary:{0} is NEW.'.format(b.name))
+                new = True
+
+        return new
+
     def _check_new(self, suite):
         """Check if upload is NEW
 
@@ -740,17 +796,16 @@ class ArchiveUpload(object):
         @return: C{True} if the upload is NEW, C{False} otherwise
         """
         session = self.session
+        new = False
 
         # Check for missing overrides
-        for b in self.changes.binaries:
-            override = self._binary_override(suite, b)
-            if override is None:
-                return True
-
+        if self._check_new_binary_overrides(suite):
+            new = True
         if self.changes.source is not None:
             override = self._source_override(suite, self.changes.source)
             if override is None:
-                return True
+                self.warnings.append('source:{0} is NEW.'.format(self.changes.source.dsc['Source']))
+                new = True
 
         # Check if we reference a file only in a tainted archive
         files = self.changes.files.values()
@@ -764,7 +819,10 @@ class ArchiveUpload(object):
             in_untainted_archive = (query_untainted.first() is not None)
 
             if in_archive and not in_untainted_archive:
-                return True
+                self.warnings.append('{0} is only available in NEW.'.format(f.filename))
+                new = True
+
+        return new
 
     def _final_suites(self):
         session = self.session
@@ -788,7 +846,7 @@ class ArchiveUpload(object):
         @type  suite: L{daklib.dbconn.Suite}
         @param suite: suite to get override for
 
-        @type  binary: L{daklib.upload.Binary}
+        @type  binary: L{daklib.upload.Binary} or L{daklib.packagelist.PackageListEntry}
         @param binary: binary to get override for
 
         @rtype:  L{daklib.dbconn.Override} or C{None}
@@ -797,8 +855,12 @@ class ArchiveUpload(object):
         if suite.overridesuite is not None:
             suite = self.session.query(Suite).filter_by(suite_name=suite.overridesuite).one()
 
-        query = self.session.query(Override).filter_by(suite=suite, package=binary.control['Package']) \
-                .join(Component).filter(Component.component_name == binary.component) \
+        mapped_component = get_mapped_component(binary.component)
+        if mapped_component is None:
+            return None
+
+        query = self.session.query(Override).filter_by(suite=suite, package=binary.name) \
+                .join(Component).filter(Component.component_name == mapped_component.component_name) \
                 .join(OverrideType).filter(OverrideType.overridetype == binary.type)
 
         try:
@@ -821,10 +883,13 @@ class ArchiveUpload(object):
         if suite.overridesuite is not None:
             suite = self.session.query(Suite).filter_by(suite_name=suite.overridesuite).one()
 
-        # XXX: component for source?
         query = self.session.query(Override).filter_by(suite=suite, package=source.dsc['Source']) \
                 .join(OverrideType).filter(OverrideType.overridetype == 'dsc')
 
+        component = source_component_from_package_list(source.package_list, suite)
+        if component is not None:
+            query = query.filter(Override.component == component)
+
         try:
             return query.one()
         except NoResultFound:
@@ -868,9 +933,9 @@ class ArchiveUpload(object):
         try:
             # Validate signatures and hashes before we do any real work:
             for chk in (
-                    checks.SignatureCheck,
+                    checks.SignatureAndHashesCheck,
+                    checks.SignatureTimestampCheck,
                     checks.ChangesCheck,
-                    checks.HashesCheck,
                     checks.ExternalHashesCheck,
                     checks.SourceCheck,
                     checks.BinaryCheck,
@@ -992,8 +1057,11 @@ class ArchiveUpload(object):
         db_changes.changelog_id = changelog_id
         db_changes.closes = self.changes.closed_bugs
 
-        self.transaction.session.add(db_changes)
-        self.transaction.session.flush()
+        try:
+            self.transaction.session.add(db_changes)
+            self.transaction.session.flush()
+        except sqlalchemy.exc.IntegrityError:
+            raise ArchiveException('{0} is already known.'.format(self.changes.filename))
 
         return db_changes
 
@@ -1039,14 +1107,26 @@ class ArchiveUpload(object):
 
         remaining = []
         for f in byhand:
-            parts = f.filename.split('_', 2)
-            if len(parts) != 3:
-                print "W: unexpected byhand filename {0}. No automatic processing.".format(f.filename)
-                remaining.append(f)
-                continue
+            if '_' in f.filename:
+                parts = f.filename.split('_', 2)
+                if len(parts) != 3:
+                    print "W: unexpected byhand filename {0}. No automatic processing.".format(f.filename)
+                    remaining.append(f)
+                    continue
+
+                package, version, archext = parts
+                arch, ext = archext.split('.', 1)
+            else:
+                parts = f.filename.split('.')
+                if len(parts) < 2:
+                    print "W: unexpected byhand filename {0}. No automatic processing.".format(f.filename)
+                    remaining.append(f)
+                    continue
 
-            package, version, archext = parts
-            arch, ext = archext.split('.', 1)
+                package = parts[0]
+                version = '0'
+                arch = 'all'
+                ext = parts[-1]
 
             try:
                 rule = automatic_byhand_packages.subtree(package)
@@ -1054,12 +1134,14 @@ class ArchiveUpload(object):
                 remaining.append(f)
                 continue
 
-            if rule['Source'] != self.changes.source_name or rule['Section'] != f.section or rule['Extension'] != ext:
+            if rule['Source'] != self.changes.source_name \
+                    or rule['Section'] != f.section \
+                    or ('Extension' in rule and rule['Extension'] != ext):
                 remaining.append(f)
                 continue
 
             script = rule['Script']
-            retcode = subprocess.call([script, os.path.join(self.directory, f.filename), control['Version'], arch, os.path.join(self.directory, self.changes.filename)], shell=False)
+            retcode = daklib.daksubprocess.call([script, os.path.join(self.directory, f.filename), control['Version'], arch, os.path.join(self.directory, self.changes.filename)], shell=False)
             if retcode != 0:
                 print "W: error processing {0}.".format(f.filename)
                 remaining.append(f)