]> git.decadent.org.uk Git - dak.git/commitdiff
Merge remote-tracking branch 'ansgar/pu/multiarchive-1' into merge
authorJoerg Jaspert <joerg@debian.org>
Mon, 30 Jul 2012 20:40:25 +0000 (22:40 +0200)
committerJoerg Jaspert <joerg@debian.org>
Mon, 30 Jul 2012 20:40:25 +0000 (22:40 +0200)
* ansgar/pu/multiarchive-1:
  Add export subcommand to export upload from policy queues.
  daklib/dbconn.py: add basename property to PoolFile
  daklib/dbconn.py: add path property to Suite
  daklib/archive.py: guess source component even with component mappings
  daklib/archive.py: implement component mappings
  Add BinaryTimestampCheck.
  daklib/config.py: add find_i method
  daklib/checks.py: add missing join
  daklib/checks.py: use apt_pkg.version_compare correctly
  daklib/checks.py: make version check slightly more verbose
  daklib/archive.py: handle unexpected names for byhand files
  daklib/archive.py: session → self.session
  daklib/checks.py: allow DMs to upload to all suites
  daklib/archive.py (install_binary): fix use of source_suites
  daklib/archive.py: use SQLAlchemy 0.6 syntax for join
  daklib/regexes.py: re_file_binary: arch names can have dashes
  daklib/regexes.py: re_field_source: fix matching version numbers
  daklib/contents.py: close db session after use
  error out if sql query fails
  use "set -o pipefail" for shell scripts

Signed-off-by: Joerg Jaspert <joerg@debian.org>
29 files changed:
config/backports/cron.daily
config/backports/cron.dinstall
config/backports/cron.hourly
config/backports/cron.monthly
config/backports/cron.reboot
config/backports/cron.unchecked
config/backports/cron.weekly
config/backports/dinstall.functions
config/debian-security/cron.buildd
config/debian-security/cron.daily
config/debian-security/cron.hourly
config/debian-security/cron.unchecked
config/debian-security/cron.weekly
config/debian/cron.daily
config/debian/cron.dinstall
config/debian/cron.hourly
config/debian/cron.monthly
config/debian/cron.reboot
config/debian/cron.unchecked
config/debian/cron.weekly
config/debian/dinstall.functions
dak/dak.py
dak/export.py [new file with mode: 0644]
daklib/archive.py
daklib/checks.py
daklib/config.py
daklib/contents.py
daklib/dbconn.py
daklib/regexes.py

index 23f2f66907b887a1be9a8836938534b85a2cec76..d57752437fce93e94932a7729b6fec5f6a3d6d48 100755 (executable)
@@ -2,6 +2,7 @@
 #
 
 set -e
+set -o pipefail
 set -u
 
 export SCRIPTVARS=/srv/backports-master.debian.org/dak/config/backports/vars
index 2c9429627572c8481e303896ca413d4e45258eee..4d3bef8fbb6553e72a1229b36c1f61985b8f66a0 100755 (executable)
@@ -27,6 +27,7 @@
 
 # exit on errors
 set -e
+set -o pipefail
 # make sure to only use defined variables
 set -u
 # ERR traps should be inherited from functions too. (And command
index f973606ec28e52f6fea081fe3b6d715e9963366b..627c8b0960b4b7cc1eeeab1bc602a98fa475a763 100755 (executable)
@@ -3,6 +3,7 @@
 # Executed hourly via cron, out of dak's crontab.
 
 set -e
+set -o pipefail
 set -u
 
 export SCRIPTVARS=/srv/backports-master.debian.org/dak/config/backports/vars
index 38a57fd12186b0330d5ae3944305eef1e7b11b30..34593a3edf7253512c41cb1b30dbe5ab092454b6 100755 (executable)
@@ -2,6 +2,7 @@
 #
 
 set -e
+set -o pipefail
 set -u
 
 export SCRIPTVARS=/srv/backports-master.debian.org/dak/config/backports/vars
index a5a132eaa7be7012558eabb44bb751542e0373f2..1e2e8d982da2a9de0c7a33e427ae89f37957a523 100755 (executable)
@@ -18,6 +18,7 @@
 
 # exit on errors
 set -e
+set -o pipefail
 # make sure to only use defined variables
 set -u
 # ERR traps should be inherited from functions too. (And command
index 423ce77dd7f3d995589310d0068c3f42954667b2..13be8d592a3ac0ce18f57e90c1762a98181a8fb4 100755 (executable)
@@ -17,6 +17,7 @@
 
 # exit on errors
 set -e
+set -o pipefail
 # make sure to only use defined variables
 set -u
 # ERR traps should be inherited from functions too. (And command
index 1929e10faa617e64fd26e26adce60fb3dbb35710..ee57d39ffc762c5acae1c8bedc5edc5bc028b770 100755 (executable)
@@ -2,6 +2,7 @@
 #
 
 set -e
+set -o pipefail
 set -u
 # ERR traps should be inherited from functions too. (And command
 # substitutions and subshells and whatnot, but for us the functions is
index 8709e6e1132d80eb848895393d1d0606062f61cf..dc3457b55ef47edaccb6613bcd736b436ee1440b 100644 (file)
@@ -263,7 +263,8 @@ function mkfilesindices() {
     ARCHLIST=$(tempfile)
 
     log "Querying postgres"
-    echo 'SELECT l.path, f.filename, a.arch_string FROM location l JOIN files f ON (f.location = l.id) LEFT OUTER JOIN (binaries b JOIN architecture a ON (b.architecture = a.id)) ON (f.id = b.file)' | psql -At | sed 's/|//;s,^/srv/ftp-master.debian.org/ftp,.,' | sort >$ARCHLIST
+    local query='SELECT l.path, f.filename, a.arch_string FROM location l JOIN files f ON (f.location = l.id) LEFT OUTER JOIN (binaries b JOIN architecture a ON (b.architecture = a.id)) ON (f.id = b.file)'
+    psql -At -c "$query" | sed 's/|//;s,^/srv/ftp-master.debian.org/ftp,.,' | sort >$ARCHLIST
 
     includedirs () {
         perl -ne 'print; while (m,/[^/]+$,) { $_=$`; print $_ . "\n" unless $d{$_}++; }'
@@ -296,12 +297,15 @@ function mkfilesindices() {
     log "Generating suite lists"
 
     suite_list () {
-        printf 'SELECT DISTINCT l.path, f.filename FROM (SELECT sa.source AS source FROM src_associations sa WHERE sa.suite = %d UNION SELECT b.source AS source FROM bin_associations ba JOIN binaries b ON (ba.bin = b.id) WHERE ba.suite = %d) s JOIN dsc_files df ON (s.source = df.source) JOIN files f ON (df.file = f.id) JOIN location l ON (f.location = l.id)\n' $1 $1 | psql -F' ' -A -t
+        local query
+       query="$(printf 'SELECT DISTINCT l.path, f.filename FROM (SELECT sa.source AS source FROM src_associations sa WHERE sa.suite = %d UNION SELECT b.source AS source FROM bin_associations ba JOIN binaries b ON (ba.bin = b.id) WHERE ba.suite = %d) s JOIN dsc_files df ON (s.source = df.source) JOIN files f ON (df.file = f.id) JOIN location l ON (f.location = l.id)' $1 $1)"
+       psql -F' ' -A -t -c "$query"
 
-        printf 'SELECT l.path, f.filename FROM bin_associations ba JOIN binaries b ON (ba.bin = b.id) JOIN files f ON (b.file = f.id) JOIN location l ON (f.location = l.id) WHERE ba.suite = %d\n' $1 | psql -F' ' -A -t
+        query="$(printf 'SELECT l.path, f.filename FROM bin_associations ba JOIN binaries b ON (ba.bin = b.id) JOIN files f ON (b.file = f.id) JOIN location l ON (f.location = l.id) WHERE ba.suite = %d' $1)"
+       psql -F' ' -A -t -c "$query"
     }
 
-    printf 'SELECT id, suite_name FROM suite\n' | psql -F' ' -At |
+    psql -F' ' -At -c 'SELECT id, suite_name FROM suite' |
     while read id suite; do
         [ -e $base/ftp/dists/$suite ] || continue
         (
index 970a232263f3e9a8d9307db7ff1c6dccda37bd33..016b1af57cc248d963ea81aae687deadf8881b07 100755 (executable)
@@ -3,6 +3,7 @@
 # Executed after cron.unchecked
 
 set -e
+set -o pipefail
 set -u
 
 export SCRIPTVARS=/srv/security-master.debian.org/dak/config/debian-security/vars
index 3c34d769ec2bea6d1abccf73910ef2aff5aae791..bfa6dd38f97a9ef8c810e12d8a95acdb993086ab 100755 (executable)
@@ -3,6 +3,7 @@
 # Executed daily via cron, out of dak's crontab.
 
 set -e
+set -o pipefail
 export SCRIPTVARS=/srv/security-master.debian.org/dak/config/debian-security/vars
 . $SCRIPTVARS
 
index ddbf09fcf4f3ccebe575d7aaf8ececcfd630cf84..6a6980579e1204adc6ec566a2d3d1342438d57ca 100755 (executable)
@@ -3,6 +3,7 @@
 # Executed hourly via cron, out of dak's crontab.
 
 set -e
+set -o pipefail
 set -u
 
 export SCRIPTVARS=/srv/security-master.debian.org/dak/config/debian-security/vars
index d8ae45342bd82971a3c95d4e0198b3801d06b2a6..7baf55ac10cd8b6f45b2afa38de905f52c1b881d 100755 (executable)
@@ -1,6 +1,7 @@
 #! /bin/bash
 
 set -e
+set -o pipefail
 set -u
 
 export SCRIPTVARS=/srv/security-master.debian.org/dak/config/debian-security/vars
index 15c9d16fe2fa6db2c2265aae7b97570381d13f1d..8843ac1c043be612cda3a2dcf5b7f750cc46a606 100755 (executable)
@@ -3,6 +3,7 @@
 # Executed weekly via cron, out of dak's crontab.
 
 set -e
+set -o pipefail
 export SCRIPTVARS=/srv/security-master.debian.org/dak/config/debian-security/vars
 . $SCRIPTVARS
 
index 172b5bba57c44f842e202067ad7b16afc9583ab3..182e103b65e4beb4a261f1997028c08f1f219ab5 100755 (executable)
@@ -3,6 +3,7 @@
 # Run daily via cron, out of dak's crontab.
 
 set -e
+set -o pipefail
 set -u
 export SCRIPTVARS=/srv/ftp-master.debian.org/dak/config/debian/vars
 . $SCRIPTVARS
index d2443f79cea4768d95112d8e9d449e0423c7a68c..0fcf599c0cdeb8416399a0dbcb99e34d8ee30a50 100755 (executable)
@@ -27,6 +27,7 @@
 
 # exit on errors
 set -e
+set -o pipefail
 # make sure to only use defined variables
 set -u
 # ERR traps should be inherited from functions too. (And command
index 39a4d0d8214f298e16f3748ae1b57fff502dd82c..2b80ffe16b964e6d3b2116e95b2bbb91d4a46e96 100755 (executable)
@@ -3,6 +3,7 @@
 # Executed hourly via cron, out of dak's crontab.
 
 set -e
+set -o pipefail
 set -u
 
 export SCRIPTVARS=/srv/ftp-master.debian.org/dak/config/debian/vars
index 980cb6d6905d63651932c50e6d516d80f804e876..3ca4a81a1e62a3f4a30713e0f2714a3254fd0f2e 100755 (executable)
@@ -3,6 +3,7 @@
 # Run at the beginning of the month via cron, out of dak's crontab.
 
 set -e
+set -o pipefail
 set -u
 export SCRIPTVARS=/srv/ftp-master.debian.org/dak/config/debian/vars
 . $SCRIPTVARS
index b0129733422ee1ff4e8e4ec40e13f356a4cd1654..26d507e47c0263bcd28930deb2650bf90c653923 100755 (executable)
@@ -18,6 +18,7 @@
 
 # exit on errors
 set -e
+set -o pipefail
 # make sure to only use defined variables
 set -u
 # ERR traps should be inherited from functions too. (And command
index a534ba06cf2cd159045f1c009c818fe8581fad47..8dc3b1f037a10766ccf2ce97a1d3e059c7a1a1ea 100755 (executable)
@@ -18,6 +18,7 @@
 
 # exit on errors
 set -e
+set -o pipefail
 # make sure to only use defined variables
 set -u
 # ERR traps should be inherited from functions too. (And command
index b75539ed055a46d646a9466fdd90f0fc9cffd12b..52e95eb561b3ce9cb22fd4264e34b1e4c3109211 100755 (executable)
@@ -3,6 +3,7 @@
 # Run once a week via cron, out of dak's crontab.
 
 set -e
+set -o pipefail
 set -u
 # ERR traps should be inherited from functions too. (And command
 # substitutions and subshells and whatnot, but for us the functions is
index b7679dcfa50edd7b59abf82813ce9a0499f4410a..a7bf95ca8b8bd9d2a72deaef04ed55e5f13a1f7b 100644 (file)
@@ -246,7 +246,8 @@ function mkfilesindices() {
     ARCHLIST=$(tempfile)
 
     log "Querying postgres"
-    echo 'SELECT l.path, f.filename, a.arch_string FROM location l JOIN files f ON (f.location = l.id) LEFT OUTER JOIN (binaries b JOIN architecture a ON (b.architecture = a.id)) ON (f.id = b.file)' | psql -At | sed 's/|//;s,^/srv/ftp-master.debian.org/ftp,.,' | sort >$ARCHLIST
+    local query='SELECT l.path, f.filename, a.arch_string FROM location l JOIN files f ON (f.location = l.id) LEFT OUTER JOIN (binaries b JOIN architecture a ON (b.architecture = a.id)) ON (f.id = b.file)'
+    psql -At -c "$query" | sed 's/|//;s,^/srv/ftp-master.debian.org/ftp,.,' | sort >$ARCHLIST
 
     includedirs () {
         perl -ne 'print; while (m,/[^/]+$,) { $_=$`; print $_ . "\n" unless $d{$_}++; }'
@@ -279,12 +280,15 @@ function mkfilesindices() {
     log "Generating suite lists"
 
     suite_list () {
-        printf 'SELECT DISTINCT l.path, f.filename FROM (SELECT sa.source AS source FROM src_associations sa WHERE sa.suite = %d UNION SELECT b.source AS source FROM bin_associations ba JOIN binaries b ON (ba.bin = b.id) WHERE ba.suite = %d) s JOIN dsc_files df ON (s.source = df.source) JOIN files f ON (df.file = f.id) JOIN location l ON (f.location = l.id)\n' $1 $1 | psql -F' ' -A -t
+       local query
+       query="$(printf 'SELECT DISTINCT l.path, f.filename FROM (SELECT sa.source AS source FROM src_associations sa WHERE sa.suite = %d UNION SELECT b.source AS source FROM bin_associations ba JOIN binaries b ON (ba.bin = b.id) WHERE ba.suite = %d) s JOIN dsc_files df ON (s.source = df.source) JOIN files f ON (df.file = f.id) JOIN location l ON (f.location = l.id)' $1 $1)"
+       psql -F' ' -A -t -c "$query"
 
-        printf 'SELECT l.path, f.filename FROM bin_associations ba JOIN binaries b ON (ba.bin = b.id) JOIN files f ON (b.file = f.id) JOIN location l ON (f.location = l.id) WHERE ba.suite = %d\n' $1 | psql -F' ' -A -t
+       query="$(printf 'SELECT l.path, f.filename FROM bin_associations ba JOIN binaries b ON (ba.bin = b.id) JOIN files f ON (b.file = f.id) JOIN location l ON (f.location = l.id) WHERE ba.suite = %d' $1)"
+       psql -F' ' -A -t -c "$query"
     }
 
-    printf 'SELECT id, suite_name FROM suite\n' | psql -F' ' -At |
+    psql -F' ' -At -c "SELECT id, suite_name FROM suite" |
     while read id suite; do
         [ -e $base/ftp/dists/$suite ] || continue
         (
index b6e77ad493689773f96b929c90a1dc679bc58b8c..20e8886489798a003b2ceef5776f2178339fe6b8 100755 (executable)
@@ -76,6 +76,8 @@ def init():
 
         ("dominate",
          "Remove obsolete source and binary associations from suites"),
+        ("export",
+         "Export uploads from policy queues"),
         ("make-pkg-file-mapping",
          "Generate package <-> file mapping"),
         ("generate-filelist",
diff --git a/dak/export.py b/dak/export.py
new file mode 100644 (file)
index 0000000..8545167
--- /dev/null
@@ -0,0 +1,75 @@
+#! /usr/bin/env python
+#
+# Copyright (C) 2012, Ansgar Burchardt <ansgar@debian.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+import apt_pkg
+import sys
+
+from daklib.config import Config
+from daklib.dbconn import *
+from daklib.policy import UploadCopy
+
+def usage():
+    print """Usage: dak export -q <queue> [options] -a|--all|<source...>
+
+Export uploads from policy queues, that is the changes files for the given
+source package and all other files associated with that.
+
+ -a --all          export all uploads
+ -c --copy         copy files instead of symlinking them
+ -d <directory>    target directory to export packages to
+                   default: current directory
+ -q <queue>        queue to grab uploads from
+ <source>          source package name to export
+"""
+
+def main(argv=None):
+    if argv is None:
+        argv = sys.argv
+
+    arguments = [('h', 'help', 'Export::Options::Help'),
+                 ('a', 'all', 'Export::Options::All'),
+                 ('c', 'copy', 'Export::Options::Copy'),
+                 ('d', 'directory', 'Export::Options::Directory', 'HasArg'),
+                 ('q', 'queue', 'Export::Options::Queue', 'HasArg')]
+
+    cnf = Config()
+    source_names = apt_pkg.parse_commandline(cnf.Cnf, arguments, argv)
+    options = cnf.subtree('Export::Options')
+
+    if 'Help' in options or 'Queue' not in options:
+        usage()
+        sys.exit(0)
+
+    session = DBConn().session()
+
+    queue = session.query(PolicyQueue).filter_by(queue_name=options['Queue']).first()
+    if queue is None:
+        print "Unknown queue '{0}'".format(options['Queue'])
+        sys.exit(1)
+    uploads = session.query(PolicyQueueUpload).filter_by(policy_queue=queue)
+    if 'All' not in options:
+        uploads = uploads.filter(DBChange.source.in_(source_names))
+    directory = options.get('Directory', '.')
+    symlink = 'Copy' not in options
+
+    for u in uploads:
+        print "Processing {0}...".format(u.changes.changesname)
+        UploadCopy(u).export(directory, symlink=symlink)
+
+if __name__ == '__main__':
+    main()
index b97d2f034c8d63fd1b3e4ee49e42a0e8ca73ed6d..539a8d20c55bde975a66c098baad0f657e369acb 100644 (file)
@@ -136,7 +136,8 @@ class ArchiveTransaction(object):
         source = source_query.filter(DBSource.suites.contains(suite)).first()
         if source is None:
             if source_suites != True:
-                source_query = source_query.filter(DBSource.suites.any(source_suites))
+                source_query = source_query.join(DBSource.suites) \
+                    .filter(Suite.suite_id == source_suites.c.id)
             source = source_query.first()
             if source is None:
                 raise ArchiveException('{0}: trying to install to {1}, but could not find source'.format(binary.hashed_file.filename, suite.suite_name))
@@ -628,6 +629,29 @@ class ArchiveUpload(object):
         suites = session.query(Suite).filter(Suite.suite_name.in_(suite_names))
         return suites
 
+    def _mapped_component(self, component_name):
+        """get component after mappings
+
+        Evaluate component mappings from ComponentMappings in dak.conf for the
+        given component name.
+
+        NOTE: ansgar wants to get rid of this. It's currently only used for
+        the security archive
+
+        Args:
+           component_name (str): component name
+
+        Returns:
+           `daklib.dbconn.Component` object
+        """
+        cnf = Config()
+        for m in cnf.value_list("ComponentMappings"):
+            (src, dst) = m.split()
+            if component_name == src:
+                component_name = dst
+        component = self.session.query(Component).filter_by(component_name=component_name).one()
+        return component
+
     def _check_new(self, suite):
         """Check if upload is NEW
 
@@ -692,7 +716,7 @@ class ArchiveUpload(object):
            daklib.dbconn.Override or None
         """
         if suite.overridesuite is not None:
-            suite = session.query(Suite).filter_by(suite_name=suite.overridesuite).one()
+            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) \
@@ -714,7 +738,7 @@ class ArchiveUpload(object):
            daklib.dbconn.Override or None
         """
         if suite.overridesuite is not None:
-            suite = session.query(Suite).filter_by(suite_name=suite.overridesuite).one()
+            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']) \
@@ -725,6 +749,30 @@ class ArchiveUpload(object):
         except NoResultFound:
             return None
 
+    def _binary_component(self, suite, binary, only_overrides=True):
+        """get component for a binary
+
+        By default this will only look at overrides to get the right component;
+        if `only_overrides` is False this method will also look at the Section field.
+
+        Args:
+           suite (daklib.dbconn.Suite)
+           binary (daklib.upload.Binary)
+
+        Kwargs:
+           only_overrides (bool): only use overrides to get the right component.
+               defaults to True.
+
+        Returns:
+           `daklib.dbconn.Component` object or None
+        """
+        override = self._binary_override(suite, binary)
+        if override is not None:
+            return override.component
+        if only_overrides:
+            return None
+        return self._mapped_component(binary.component)
+
     def check(self, force=False):
         """run checks against the upload
 
@@ -744,6 +792,7 @@ class ArchiveUpload(object):
                     checks.HashesCheck,
                     checks.SourceCheck,
                     checks.BinaryCheck,
+                    checks.BinaryTimestampCheck,
                     checks.ACLCheck,
                     checks.SingleDistributionCheck,
                     checks.NoSourceOnlyCheck,
@@ -802,7 +851,7 @@ class ArchiveUpload(object):
         changed_by = get_or_set_maintainer(control.get('Changed-By', control['Maintainer']), self.session)
 
         if source_suites is None:
-            source_suites = self.session.query(Suite).join(VersionCheck, VersionCheck.reference_id == Suite.suite_id).filter(VersionCheck.suite == suite).subquery()
+            source_suites = self.session.query(Suite).join((VersionCheck, VersionCheck.reference_id == Suite.suite_id)).filter(VersionCheck.suite == suite).subquery()
 
         source = self.changes.source
         if source is not None:
@@ -898,7 +947,13 @@ class ArchiveUpload(object):
 
         remaining = []
         for f in byhand:
-            package, version, archext = f.filename.split('_', 2)
+            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)
 
             rule = automatic_byhand_packages.get(package)
@@ -995,7 +1050,7 @@ class ArchiveUpload(object):
                 redirected_suite = suite.policy_queue.suite
 
             source_component_func = lambda source: self._source_override(overridesuite, source).component
-            binary_component_func = lambda binary: self._binary_override(overridesuite, binary).component
+            binary_component_func = lambda binary: self._binary_component(overridesuite, binary)
 
             (db_source, db_binaries) = self._install_to_suite(redirected_suite, source_component_func, binary_component_func, extra_source_archives=[suite.archive])
 
@@ -1037,12 +1092,7 @@ class ArchiveUpload(object):
         suite = suites[0]
 
         def binary_component_func(binary):
-            override = self._binary_override(suite, binary)
-            if override is not None:
-                return override.component
-            component_name = binary.component
-            component = self.session.query(Component).filter_by(component_name=component_name).one()
-            return component
+            return self._binary_component(suite, binary, only_overrides=False)
 
         # guess source component
         # XXX: should be moved into an extra method
@@ -1051,7 +1101,8 @@ class ArchiveUpload(object):
             component = binary_component_func(binary)
             binary_component_names.add(component.component_name)
         source_component_name = None
-        for guess in ('main', 'contrib', 'non-free'):
+        for c in self.session.query(Component).order_by(Component.component_id):
+            guess = c.component_name
             if guess in binary_component_names:
                 source_component_name = guess
                 break
index 9131bbadbe220e574b966dd099200329f1dd77c3..3895bb29fef044e90c3758c6fcd8c9119fd3fc53 100644 (file)
@@ -31,9 +31,11 @@ from .textutils import fix_maintainer, ParseMaintError
 import daklib.lintian as lintian
 import daklib.utils as utils
 
+import apt_inst
 import apt_pkg
 from apt_pkg import version_compare
 import os
+import time
 import yaml
 
 # TODO: replace by subprocess
@@ -241,6 +243,44 @@ class BinaryCheck(Check):
                 except:
                     raise Reject('{0}: APT could not parse {1} field'.format(fn, field))
 
+class BinaryTimestampCheck(Check):
+    """check timestamps of files in binary packages
+
+    Files in the near future cause ugly warnings and extreme time travel
+    can cause errors on extraction.
+    """
+    def check(self, upload):
+        cnf = Config()
+        future_cutoff = time.time() + cnf.find_i('Dinstall::FutureTimeTravelGrace', 24*3600)
+        past_cutoff = time.mktime(time.strptime(cnf.find('Dinstall::PastCutoffYear', '1984'), '%Y'))
+
+        class TarTime(object):
+            def __init__(self):
+                self.future_files = dict()
+                self.past_files = dict()
+            def callback(self, member, data):
+                if member.mtime > future_cutoff:
+                    future_files[member.name] = member.mtime
+                elif member.mtime < past_cutoff:
+                    past_files[member.name] = member.mtime
+
+        def format_reason(filename, direction, files):
+            reason = "{0}: has {1} file(s) with a timestamp too far in the {2}:\n".format(filename, len(files), direction)
+            for fn, ts in files.iteritems():
+                reason += "  {0} ({1})".format(fn, time.ctime(ts))
+            return reason
+
+        for binary in upload.changes.binaries:
+            filename = binary.hashed_file.filename
+            path = os.path.join(upload.directory, filename)
+            deb = apt_inst.DebFile(path)
+            tar = TarTime()
+            deb.control.go(tar.callback)
+            if tar.future_files:
+                raise Reject(format_reason(filename, 'future', tar.future_files))
+            if tar.past_files:
+                raise Reject(format_reason(filename, 'past', tar.past_files))
+
 class SourceCheck(Check):
     """Check source package for syntax errors."""
     def check_filename(self, control, filename, regex):
@@ -315,15 +355,12 @@ class ACLCheck(Check):
 
         if 'source' not in upload.changes.architectures:
             raise Reject('DM uploads must include source')
-        distributions = upload.changes.distributions
-        for dist in distributions:
-            if dist not in ('unstable', 'experimental', 'squeeze-backports'):
-                raise Reject("Uploading to {0} is not allowed for DMs.".format(dist))
         for f in upload.changes.files.itervalues():
             if f.section == 'byhand' or f.section[:4] == "raw-":
                 raise Reject("Uploading byhand packages is not allowed for DMs.")
 
         # Reject NEW packages
+        distributions = upload.changes.distributions
         assert len(distributions) == 1
         suite = session.query(Suite).filter_by(suite_name=distributions[0]).one()
         overridesuite = suite
@@ -503,6 +540,7 @@ class VersionCheck(Check):
     def _highest_binary_version(self, session, binary_name, suite, architecture):
         db_binary = session.query(DBBinary).filter_by(package=binary_name) \
             .filter(DBBinary.suites.contains(suite)) \
+            .join(DBBinary.architecture) \
             .filter(Architecture.arch_string.in_(['all', architecture])) \
             .order_by(DBBinary.version.desc()).first()
         if db_binary is None:
@@ -510,23 +548,23 @@ class VersionCheck(Check):
         else:
             return db_binary.version
 
-    def _version_checks(self, upload, suite, expected_result):
+    def _version_checks(self, upload, suite, op):
         session = upload.session
 
         if upload.changes.source is not None:
             source_name = upload.changes.source.dsc['Source']
             source_version = upload.changes.source.dsc['Version']
             v = self._highest_source_version(session, source_name, suite)
-            if v is not None and version_compare(source_version, v) != expected_result:
-                raise Reject('Version check failed (source={0}, version={1}, suite={2})'.format(source_name, source_version, suite.suite_name))
+            if v is not None and not op(version_compare(source_version, v)):
+                raise Reject('Version check failed (source={0}, version={1}, other-version={2}, suite={3})'.format(source_name, source_version, v, suite.suite_name))
 
         for binary in upload.changes.binaries:
             binary_name = binary.control['Package']
             binary_version = binary.control['Version']
             architecture = binary.control['Architecture']
             v = self._highest_binary_version(session, binary_name, suite, architecture)
-            if v is not None and version_compare(binary_version, v) != expected_result:
-                raise Reject('Version check failed (binary={0}, version={1}, suite={2})'.format(binary_name, binary_version, suite.suite_name))
+            if v is not None and not op(version_compare(binary_version, v)):
+                raise Reject('Version check failed (binary={0}, version={1}, other-version={2}, suite={3})'.format(binary_name, binary_version, v, suite.suite_name))
 
     def per_suite_check(self, upload, suite):
         session = upload.session
@@ -538,13 +576,13 @@ class VersionCheck(Check):
         must_be_newer_than.append(suite)
 
         for s in must_be_newer_than:
-            self._version_checks(upload, s, 1)
+            self._version_checks(upload, s, lambda result: result > 0)
 
         vc_older = session.query(dbconn.VersionCheck).filter_by(suite=suite, check='MustBeOlderThan')
         must_be_older_than = [ vc.reference for vc in vc_older ]
 
         for s in must_be_older_than:
-            self._version_checks(upload, s, -1)
+            self._version_checks(upload, s, lambda result: result < 0)
 
         return True
 
index 1e9299f21bbd9e5e004741215e89771d20312b00..51b7931270264d30a7b71130dfc4564a46f83cb6 100755 (executable)
@@ -83,6 +83,7 @@ class Config(object):
         self.value_list = self.Cnf.value_list
         self.find = self.Cnf.find
         self.find_b = self.Cnf.find_b
+        self.find_i = self.Cnf.find_i
 
     def has_key(self, name):
         return name in self.Cnf
index 81ce2226774c4766096d0e8ef1ff51f69816be30..e9517d637ec9588d89af9a362ecd14626f9a0d53 100755 (executable)
@@ -253,6 +253,7 @@ def binary_helper(suite_id, arch_id, overridetype_id, component_id):
         overridetype.overridetype, component.component_name]
     contents_writer = BinaryContentsWriter(suite, architecture, overridetype, component)
     contents_writer.write_file()
+    session.close()
     return log_message
 
 def source_helper(suite_id, component_id):
@@ -266,6 +267,7 @@ def source_helper(suite_id, component_id):
     log_message = [suite.suite_name, 'source', component.component_name]
     contents_writer = SourceContentsWriter(suite, component)
     contents_writer.write_file()
+    session.close()
     return log_message
 
 class ContentsWriter(object):
index c1c3d9ef848b646b95a2f261453c63fbf69c6860..72dbaad0f615c8ceda12b13b2163b095bdf2362f 100755 (executable)
@@ -1423,6 +1423,10 @@ class PoolFile(ORMObject):
     def fullpath(self):
         return os.path.join(self.location.path, self.filename)
 
+    @property
+    def basename(self):
+        return os.path.basename(self.filename)
+
     def is_valid(self, filesize = -1, md5sum = None):
         return self.filesize == long(filesize) and self.md5sum == md5sum
 
@@ -2999,6 +3003,10 @@ class Suite(ORMObject):
         else:
             return object_session(self).query(Suite).filter_by(suite_name=self.overridesuite).one()
 
+    @property
+    def path(self):
+        return os.path.join(self.archive.path, 'dists', self.suite_name)
+
 __all__.append('Suite')
 
 @session_wrapper
index 202aa57005045ac86d10073d02013d75505b1796..a8107113ecd54257ec7a72f922fe8828d8e6f7a5 100755 (executable)
@@ -145,7 +145,7 @@ _re_file_prefix = r'^(?P<package>[a-z0-9][a-z0-9.+-]+)_(?P<version>[A-Za-z0-9.:~
 
 # Match binary packages
 # Groups: package, version, architecture, type
-re_file_binary = re.compile(_re_file_prefix + r'_(?P<architecture>[a-z0-9]+)\.(?P<type>u?deb)$')
+re_file_binary = re.compile(_re_file_prefix + r'_(?P<architecture>[a-z0-9-]+)\.(?P<type>u?deb)$')
 
 # Match changes files
 # Groups: package, version, suffix
@@ -180,4 +180,4 @@ re_field_version_upstream = re.compile(r'^(?:[0-9]+:)?(?P<upstream>.*)-[^-]*$')
 
 # Match source field
 # Groups: package, version
-re_field_source = re.compile(r'^(?P<package>[a-z0-9][a-z0-9.+-]+)(:?\s*\((?P<version>[A-Za-z0-9.:~+-])\))?')
+re_field_source = re.compile(r'^(?P<package>[a-z0-9][a-z0-9.+-]+)(?:\s*\((?P<version>[A-Za-z0-9.:~+-]+)\))?$')