]> git.decadent.org.uk Git - dak.git/blobdiff - jennifer
rhona debugging code
[dak.git] / jennifer
index bb1fff7df646f014c5f7dac539fb72a23068d19f..c19e1ec545a0b9a5d739b8a9a30d738f8ad2f45c 100755 (executable)
--- a/jennifer
+++ b/jennifer
@@ -1,8 +1,8 @@
 #!/usr/bin/env python
 
 # Checks Debian packages from Incoming
-# Copyright (C) 2000, 2001, 2002, 2003, 2004  James Troup <james@nocrew.org>
-# $Id: jennifer,v 1.51 2004-06-17 15:01:18 troup Exp $
+# Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005  James Troup <james@nocrew.org>
+# $Id: jennifer,v 1.58 2005-11-24 15:40:16 ajt Exp $
 
 # 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
@@ -45,7 +45,7 @@ re_strip_revision = re.compile(r"-([^-]+)$");
 ################################################################################
 
 # Globals
-jennifer_version = "$Revision: 1.51 $";
+jennifer_version = "$Revision: 1.58 $";
 
 Cnf = None;
 Options = None;
@@ -180,11 +180,6 @@ def clean_holding():
 def check_changes():
     filename = pkg.changes_file;
 
-    # Defaults in case we bail out
-    changes["maintainer2047"] = Cnf["Dinstall::MyEmailAddress"];
-    changes["changedby2047"] = Cnf["Dinstall::MyEmailAddress"];
-    changes["architecture"] = {};
-
     # Parse the .changes field into a dictionary
     try:
         changes.update(utils.parse_changes(filename));
@@ -206,7 +201,7 @@ def check_changes():
 
     # Check for mandatory fields
     for i in ("source", "binary", "architecture", "version", "distribution",
-              "maintainer", "files", "changes"):
+              "maintainer", "files", "changes", "description"):
         if not changes.has_key(i):
             reject("%s: Missing mandatory field `%s'." % (filename, i));
             return 0    # Avoid <undef> errors during later tests
@@ -235,6 +230,9 @@ def check_changes():
          changes["changedbyname"], changes["changedbyemail"]) = \
          utils.fix_maintainer (changes.get("changed-by", ""));
     except utils.ParseMaintError, msg:
+        (changes["changedby822"], changes["changedby2047"],
+         changes["changedbyname"], changes["changedbyemail"]) = \
+        ("", "", "", "")
         reject("%s: Changed-By field ('%s') failed to parse: %s" \
                % (filename, changes["changed-by"], msg));
 
@@ -279,6 +277,9 @@ def check_distributions():
                 changes["distribution"][dest] = 1;
                 if type != "silent-map":
                     reject("Mapping %s to %s." % (source, dest),"");
+            if changes.has_key("distribution-version"):
+                if changes["distribution-version"].has_key(source):
+                    changes["distribution-version"][source]=dest
         elif type == "map-unreleased":
             (source, dest) = args[1:3];
             if changes["distribution"].has_key(source):
@@ -293,6 +294,17 @@ def check_distributions():
             if changes["distribution"].has_key(suite):
                 del changes["distribution"][suite];
                 reject("Ignoring %s as a target suite." % (suite), "Warning: ");
+        elif type == "reject":
+            suite = args[1];
+            if changes["distribution"].has_key(suite):
+                reject("Uploads to %s are not accepted." % (suite));
+        elif type == "propup-version":
+            # give these as "uploaded-to(non-mapped) suites-to-add-when-upload-obsoletes"
+            #
+            # changes["distribution-version"] looks like: {'testing': 'testing-proposed-updates'}
+            if changes["distribution"].has_key(args[1]):
+                changes.setdefault("distribution-version", {})
+                for suite in args[2:]: changes["distribution-version"][suite]=suite
 
     # Ensure there is (still) a target distribution
     if changes["distribution"].keys() == []:
@@ -305,6 +317,48 @@ def check_distributions():
 
 ################################################################################
 
+def check_deb_ar(filename, control):
+    """Sanity check the ar of a .deb, i.e. that there is:
+
+ o debian-binary
+ o control.tar.gz
+ o data.tar.gz or data.tar.bz2
+
+in that order, and nothing else.  If the third member is a
+data.tar.bz2, an additional check is performed for the required
+Pre-Depends on dpkg (>= 1.10.24)."""
+    cmd = "ar t %s" % (filename)
+    (result, output) = commands.getstatusoutput(cmd)
+    if result != 0:
+        reject("%s: 'ar t' invocation failed." % (filename))
+        reject(utils.prefix_multi_line_string(output, " [ar output:] "), "")
+    chunks = output.split('\n')
+    if len(chunks) != 3:
+        reject("%s: found %d chunks, expected 3." % (filename, len(chunks)))
+    if chunks[0] != "debian-binary":
+        reject("%s: first chunk is '%s', expected 'debian-binary'." % (filename, chunks[0]))
+    if chunks[1] != "control.tar.gz":
+        reject("%s: second chunk is '%s', expected 'control.tar.gz'." % (filename, chunks[1]))
+    if chunks[2] == "data.tar.bz2":
+        # Packages using bzip2 compression must have a Pre-Depends on dpkg >= 1.10.24.
+        found_needed_predep = 0
+        for parsed_dep in apt_pkg.ParseDepends(control.Find("Pre-Depends", "")):
+            for atom in parsed_dep:
+                (dep, version, constraint) = atom
+                if dep != "dpkg" or (constraint != ">=" and constraint != ">>") or \
+                       len(parsed_dep) > 1: # or'ed deps don't count
+                    continue
+                if (constraint == ">=" and apt_pkg.VersionCompare(version, "1.10.24") < 0) or \
+                       (constraint == ">>" and apt_pkg.VersionCompare(version, "1.10.23") < 0):
+                    continue
+                found_needed_predep = 1
+        if not found_needed_predep:
+            reject("%s: uses bzip2 compression, but doesn't Pre-Depend on dpkg (>= 1.10.24)" % (filename))
+    elif chunks[2] != "data.tar.gz":
+        reject("%s: third chunk is '%s', expected 'data.tar.gz' or 'data.tar.bz2'." % (filename, chunks[2]))
+
+################################################################################
+
 def check_files():
     global reprocess
 
@@ -362,7 +416,7 @@ def check_files():
             files[file]["type"] = "unreadable";
             continue;
         # If it's byhand skip remaining checks
-        if files[file]["section"] == "byhand":
+        if files[file]["section"] == "byhand" or files[file]["section"] == "raw-installer":
             files[file]["byhand"] = 1;
             files[file]["type"] = "byhand";
         # Checks for a binary package...
@@ -484,6 +538,8 @@ def check_files():
             # Check the version and for file overwrites
             reject(Katie.check_binary_against_db(file),"");
 
+            check_deb_ar(file, control)
+
         # Checks for a source package...
         else:
             m = utils.re_issource.match(file);
@@ -533,16 +589,13 @@ def check_files():
                 if files[file]["component"] == source:
                     files[file]["original component"] = source;
                     files[file]["component"] = dest;
+
             # Ensure the component is valid for the target suite
             if Cnf.has_key("Suite:%s::Components" % (suite)) and \
                files[file]["component"] not in Cnf.ValueList("Suite::%s::Components" % (suite)):
                 reject("unknown component `%s' for suite `%s'." % (files[file]["component"], suite));
                 continue;
 
-            # See if the package is NEW
-            if not Katie.in_override_p(files[file]["package"], files[file]["component"], suite, files[file].get("dbtype",""), file):
-                files[file]["new"] = 1;
-
             # Validate the component
             component = files[file]["component"];
             component_id = db_access.get_component_id(component);
@@ -550,6 +603,10 @@ def check_files():
                 reject("file '%s' has unknown component '%s'." % (file, component));
                 continue;
 
+            # See if the package is NEW
+            if not Katie.in_override_p(files[file]["package"], files[file]["component"], suite, files[file].get("dbtype",""), file):
+                files[file]["new"] = 1;
+
             # Validate the priority
             if files[file]["priority"].find('/') != -1:
                 reject("file '%s' has invalid priority '%s' [contains '/']." % (file, files[file]["priority"]));
@@ -613,11 +670,12 @@ def check_dsc():
 
     # If there isn't one, we have nothing to do. (We have reject()ed the upload already)
     if not dsc_filename:
+        reject("source uploads must contain a dsc file");
         return 0;
 
     # Parse the .dsc file
     try:
-        dsc.update(utils.parse_changes(dsc_filename, dsc_whitespace_rules=1));
+        dsc.update(utils.parse_changes(dsc_filename, signing_rules=1));
     except utils.cant_open_exc:
         # if not -n copy_to_holding() will have done this for us...
         if Options["No-Action"]:
@@ -658,7 +716,7 @@ def check_dsc():
         utils.fix_maintainer (dsc["maintainer"]);
     except utils.ParseMaintError, msg:
         reject("%s: Maintainer field ('%s') failed to parse: %s" \
-               % (dsc_filename, changes["changed-by"], msg));
+               % (dsc_filename, dsc["maintainer"], msg));
 
     # Validate the build-depends field(s)
     for field_name in [ "build-depends", "build-depends-indep" ]:
@@ -913,7 +971,14 @@ def check_timestamps():
                 deb_file = utils.open_file(filename);
                 apt_inst.debExtract(deb_file,tar.callback,"control.tar.gz");
                 deb_file.seek(0);
-                apt_inst.debExtract(deb_file,tar.callback,"data.tar.gz");
+                try:
+                    apt_inst.debExtract(deb_file,tar.callback,"data.tar.gz")
+                except SystemError, e:
+                    # If we can't find a data.tar.gz, look for data.tar.bz2 instead.
+                    if not re.match(r"Cannot f[ui]nd chunk data.tar.gz$", str(e)):
+                        raise
+                    deb_file.seek(0)
+                    apt_inst.debExtract(deb_file,tar.callback,"data.tar.bz2")
                 deb_file.close();
                 #
                 future_files = tar.future_files.keys();
@@ -1109,6 +1174,9 @@ def process_it (changes_file):
     # Reset some globals
     reprocess = 1;
     Katie.init_vars();
+    # Some defaults in case we can't fully process the .changes file
+    changes["maintainer2047"] = Cnf["Dinstall::MyEmailAddress"];
+    changes["changedby2047"] = Cnf["Dinstall::MyEmailAddress"];
     reject_message = "";
 
     # Absolutize the filename to avoid the requirement of being in the
@@ -1129,7 +1197,10 @@ def process_it (changes_file):
             # rather than the original...
             pkg.changes_file = os.path.basename(pkg.changes_file);
         changes["fingerprint"] = utils.check_signature(pkg.changes_file, reject);
-        valid_changes_p = check_changes();
+        if changes["fingerprint"]:
+            valid_changes_p = check_changes();
+        else:
+            valid_changes_p = 0;
         if valid_changes_p:
             while reprocess:
                 check_distributions();
@@ -1174,7 +1245,7 @@ def main():
 
     # Check that we aren't going to clash with the daily cron job
 
-    if not Options["No-Action"] and os.path.exists("%s/Archive_Maintenance_In_Progress" % (Cnf["Dir::Root"])) and not Options["No-Lock"]:
+    if not Options["No-Action"] and os.path.exists("%s/daily.lock" % (Cnf["Dir::Lock"])) and not Options["No-Lock"]:
         utils.fubar("Archive maintenance in progress.  Try again later.");
 
     # Obtain lock if not in no-action mode and initialize the log