]> git.decadent.org.uk Git - dak.git/blobdiff - katie.py
* vars: external-overrides variable added* cron.daily: Update testing/unstable Task...
[dak.git] / katie.py
index 996ae0283fef263e5bacc98c27031e7032adc7a0..55e49e08f00e446c1a28c58f8e90cff65c61a538 100644 (file)
--- a/katie.py
+++ b/katie.py
@@ -1,8 +1,8 @@
 #!/usr/bin/env python
 
 # Utility functions for katie
-# Copyright (C) 2001, 2002  James Troup <james@nocrew.org>
-# $Id: katie.py,v 1.27 2002-10-16 02:47:32 troup Exp $
+# Copyright (C) 2001, 2002, 2003  James Troup <james@nocrew.org>
+# $Id: katie.py,v 1.40 2003-09-17 23:36:17 troup 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
@@ -293,25 +293,42 @@ class Katie:
 
         bugs.sort();
         if not self.nmu.is_an_nmu(self.pkg):
-            summary += "Closing bugs: ";
-            for bug in bugs:
-                summary += "%s " % (bug);
-                if action:
-                    Subst["__BUG_NUMBER__"] = bug;
-                    if changes["distribution"].has_key("stable"):
-                        Subst["__STABLE_WARNING__"] = """
+            if changes["distribution"].has_key("experimental"):
+               # tag bugs as fixed-in-experimental for uploads to experimental
+               summary += "Setting bugs to severity fixed: ";
+               control_message = "";
+               for bug in bugs:
+                   summary += "%s " % (bug);
+                   control_message += "tag %s + fixed-in-experimental\n" % (bug);
+               if action and control_message != "":
+                   Subst["__CONTROL_MESSAGE__"] = control_message;
+                   mail_message = utils.TemplateSubst(Subst,Cnf["Dir::Templates"]+"/jennifer.bug-experimental-fixed");
+                   utils.send_mail (mail_message);
+               if action:
+                   self.Logger.log(["setting bugs to fixed"]+bugs);
+
+
+           else:
+               summary += "Closing bugs: ";
+               for bug in bugs:
+                   summary += "%s " % (bug);
+                   if action:
+                       Subst["__BUG_NUMBER__"] = bug;
+                       if changes["distribution"].has_key("stable"):
+                           Subst["__STABLE_WARNING__"] = """
 Note that this package is not part of the released stable Debian
 distribution.  It may have dependencies on other unreleased software,
 or other instabilities.  Please take care if you wish to install it.
 The update will eventually make its way into the next released Debian
 distribution.""";
-                    else:
-                        Subst["__STABLE_WARNING__"] = "";
-                    mail_message = utils.TemplateSubst(Subst,Cnf["Dir::Templates"]+"/jennifer.bug-close");
-                    utils.send_mail (mail_message, "");
-            if action:
-                self.Logger.log(["closing bugs"]+bugs);
-        else:                     # NMU
+                       else:
+                           Subst["__STABLE_WARNING__"] = "";
+                           mail_message = utils.TemplateSubst(Subst,Cnf["Dir::Templates"]+"/jennifer.bug-close");
+                           utils.send_mail (mail_message);
+                if action:
+                    self.Logger.log(["closing bugs"]+bugs);
+
+       else:                     # NMU
             summary += "Setting bugs to severity fixed: ";
             control_message = "";
             for bug in bugs:
@@ -320,7 +337,7 @@ distribution.""";
             if action and control_message != "":
                 Subst["__CONTROL_MESSAGE__"] = control_message;
                 mail_message = utils.TemplateSubst(Subst,Cnf["Dir::Templates"]+"/jennifer.bug-nmu-fixed");
-                utils.send_mail (mail_message, "");
+                utils.send_mail (mail_message);
             if action:
                 self.Logger.log(["setting bugs to fixed"]+bugs);
         summary += "\n";
@@ -353,7 +370,7 @@ distribution.""";
                 if Cnf.get("Dinstall::TrackingServer") and changes["architecture"].has_key("source"):
                     Subst["__ANNOUNCE_LIST_ADDRESS__"] = Subst["__ANNOUNCE_LIST_ADDRESS__"] + "\nBcc: %s@%s" % (changes["source"], Cnf["Dinstall::TrackingServer"]);
                 mail_message = utils.TemplateSubst(Subst,Cnf["Dir::Templates"]+"/jennifer.announce");
-                utils.send_mail (mail_message, "");
+                utils.send_mail (mail_message);
 
         if Cnf.FindB("Dinstall::CloseBugs"):
             summary = self.close_bugs(summary, action);
@@ -386,7 +403,7 @@ distribution.""";
             Subst["__SUITE__"] = "";
             Subst["__SUMMARY__"] = summary;
             mail_message = utils.TemplateSubst(Subst,Cnf["Dir::Templates"]+"/jennifer.accepted");
-            utils.send_mail(mail_message, "")
+            utils.send_mail(mail_message)
             self.announce(short_summary, 1)
 
         # Special support to enable clean auto-building of accepted packages
@@ -460,25 +477,26 @@ distribution.""";
                     # Ignore this; it's a common mistake and not worth whining about
                     if section.lower() == "non-us/main" and override_section.lower() == "non-us":
                         continue;
-                    summary += "%s: section is overridden from %s to %s.\n" % (file, section, override_section);
+                    summary += "%s: package says section is %s, override says %s.\n" % (file, section, override_section);
                 priority = files[file]["priority"];
                 override_priority = files[file]["override priority"];
                 if priority != override_priority and priority != "-":
-                    summary += "%s: priority is overridden from %s to %s.\n" % (file, priority, override_priority);
+                    summary += "%s: package says priority is %s, override says %s.\n" % (file, priority, override_priority);
 
         if summary == "":
             return;
 
         Subst["__SUMMARY__"] = summary;
         mail_message = utils.TemplateSubst(Subst,self.Cnf["Dir::Templates"]+"/jennifer.override-disparity");
-        utils.send_mail (mail_message, "");
+        utils.send_mail(mail_message);
 
     ###########################################################################
 
-    def force_move (self, files):
-        """Forcefully move files from the current directory to the reject
-           directory.  If any file already exists it will be moved to the
-           morgue to make way for the new file."""
+    def force_reject (self, files):
+        """Forcefully move files from the current directory to the
+           reject directory.  If any file already exists in the reject
+           directory it will be moved to the morgue to make way for
+           the new file."""
 
         Cnf = self.Cnf
 
@@ -527,10 +545,10 @@ distribution.""";
             while answer == 'E':
                 os.system("%s %s" % (editor, temp_filename))
                 file = utils.open_file(temp_filename);
-                reject_message = " ".join(file.readlines());
+                reject_message = "".join(file.readlines());
                 file.close();
                 print "Reject message:";
-                print utils.prefix_multi_line_string(reject_message,"  ");
+                print utils.prefix_multi_line_string(reject_message,"  ",include_blank_lines=1);
                 prompt = "[R]eject, Edit, Abandon, Quit ?"
                 answer = "XXX";
                 while prompt.find(answer) == -1:
@@ -556,7 +574,7 @@ distribution.""";
 
         # Move all the files into the reject directory
         reject_files = pkg.files.keys() + [pkg.changes_file];
-        self.force_move(reject_files);
+        self.force_reject(reject_files);
 
         # If we fail here someone is probably trying to exploit the race
         # so let's just raise an exception ...
@@ -586,7 +604,7 @@ distribution.""";
 
         # Send the rejection mail if appropriate
         if not Cnf["Dinstall::Options::No-Mail"]:
-            utils.send_mail (reject_mail_message, "");
+            utils.send_mail(reject_mail_message);
 
         self.Logger.log(["rejected", pkg.changes_file]);
         return 0;
@@ -600,28 +618,49 @@ distribution.""";
     # (2) Bin-only NMU of an MU            => 1.0-3.0.1
     # (3) Bin-only NMU of a sourceful-NMU  => 1.0-3.1.1
 
-    def source_exists (self, package, source_version):
-        q = self.projectB.query("SELECT s.version FROM source s WHERE s.source = '%s'" % (package));
-
-        # Reduce the query results to a list of version numbers
-        ql = map(lambda x: x[0], q.getresult());
-
-        # Try (1)
-        if ql.count(source_version):
-            return 1;
-
-        # Try (2)
-        orig_source_version = re_bin_only_nmu_of_mu.sub('', source_version);
-        if ql.count(orig_source_version):
-            return 1;
-
-        # Try (3)
-        orig_source_version = re_bin_only_nmu_of_nmu.sub('', source_version);
-        if ql.count(orig_source_version):
-            return 1;
-
-        # No source found...
-        return 0;
+    def source_exists (self, package, source_version, suites = ["any"]):
+       okay = 1
+       for suite in suites:
+           if suite == "any":
+               que = "SELECT s.version FROM source s WHERE s.source = '%s'" % \
+                   (package)
+           else:
+               # source must exist in suite X, or in some other suite that's
+               # mapped to X, recursively... silent-maps are counted too,
+               # unreleased-maps aren't.
+               maps = self.Cnf.ValueList("SuiteMappings")[:]
+               maps.reverse()
+               maps = [ m.split() for m in maps ]
+               maps = [ (x[1], x[2]) for x in maps 
+                               if x[0] == "map" or x[0] == "silent-map" ]
+               s = [suite]
+               for x in maps:
+                       if x[1] in s and x[0] not in s:
+                               s.append(x[0])
+               
+               que = "SELECT s.version FROM source s JOIN src_associations sa ON (s.id = sa.source) JOIN suite su ON (sa.suite = su.id) WHERE s.source = '%s' AND (%s)" % (package, string.join(["su.suite_name = '%s'" % a for a in s], " OR "));
+            q = self.projectB.query(que)
+
+            # Reduce the query results to a list of version numbers
+            ql = map(lambda x: x[0], q.getresult());
+
+            # Try (1)
+            if ql.count(source_version):
+                continue
+
+            # Try (2)
+            orig_source_version = re_bin_only_nmu_of_mu.sub('', source_version)
+            if ql.count(orig_source_version):
+                continue
+
+            # Try (3)
+            orig_source_version = re_bin_only_nmu_of_nmu.sub('', source_version)
+            if ql.count(orig_source_version):
+                continue
+
+            # No source found...
+            okay = 0
+       return okay
 
     ################################################################################
 
@@ -725,7 +764,7 @@ SELECT b.id FROM binaries b, architecture a
                                    files[file]["version"],
                                    files[file]["architecture"]))
         if q.getresult():
-            self.reject("can not overwrite existing copy of '%s' already in the archive." % (file));
+            self.reject("%s: can not overwrite existing copy already in the archive." % (file));
 
         return self.reject_message;
 
@@ -745,6 +784,14 @@ SELECT s.version, su.suite_name FROM source s, src_associations sa, suite su
 
     ################################################################################
 
+    # **WARNING**
+    # NB: this function can remove entries from the 'files' index [if
+    # the .orig.tar.gz is a duplicate of the one in the archive]; if
+    # you're iterating over 'files' and call this function as part of
+    # the loop, be sure to add a check to the top of the loop to
+    # ensure you haven't just tried to derefernece the deleted entry.
+    # **WARNING**
+
     def check_dsc_against_db(self, file):
         self.reject_message = "";
         files = self.pkg.files;
@@ -762,9 +809,16 @@ SELECT s.version, su.suite_name FROM source s, src_associations sa, suite su
                 actual_size = int(files[dsc_file]["size"]);
                 found = "%s in incoming" % (dsc_file)
                 # Check the file does not already exist in the archive
-                q = self.projectB.query("SELECT f.size, f.md5sum FROM files f, location l WHERE (f.filename ~ '/%s$' OR f.filename = '%s') AND l.id = f.location" % (utils.regex_safe(dsc_file), dsc_file));
+                q = self.projectB.query("SELECT size, md5sum, filename FROM files WHERE filename LIKE '%%%s%%'" % (dsc_file));
+
+                ql = q.getresult();
+                # Strip out anything that isn't '%s' or '/%s$'
+                for i in ql:
+                    if i[2] != dsc_file and i[2][-(len(dsc_file)+1):] != '/'+dsc_file:
+                        self.Logger.log(["check_dsc_against_db",i[2],dsc_file]);
+                        ql.remove(i);
 
-                # "It has not broken them.  It has fixed a
+                # "[katie] has not broken them.  [katie] has fixed a
                 # brokenness.  Your crappy hack exploited a bug in
                 # the old dinstall.
                 #
@@ -773,7 +827,6 @@ SELECT s.version, su.suite_name FROM source s, src_associations sa, suite su
                 # the same name and version.)"
                 #                        -- ajk@ on d-devel@l.d.o
 
-                ql = q.getresult();
                 if ql:
                     # Ignore exact matches for .orig.tar.gz
                     match = 0;
@@ -790,8 +843,13 @@ SELECT s.version, su.suite_name FROM source s, src_associations sa, suite su
                         self.reject("can not overwrite existing copy of '%s' already in the archive." % (dsc_file));
             elif dsc_file.endswith(".orig.tar.gz"):
                 # Check in the pool
-                q = self.projectB.query("SELECT l.path, f.filename, l.type, f.id, l.id FROM files f, location l WHERE (f.filename ~ '/%s$' OR f.filename = '%s') AND l.id = f.location" % (utils.regex_safe(dsc_file), dsc_file));
+                q = self.projectB.query("SELECT l.path, f.filename, l.type, f.id, l.id FROM files f, location l WHERE f.filename LIKE '%%%s%%' AND l.id = f.location" % (dsc_file));
                 ql = q.getresult();
+                # Strip out anything that isn't '%s' or '/%s$'
+                for i in ql:
+                    if i[1] != dsc_file and i[1][-(len(dsc_file)+1):] != '/'+dsc_file:
+                        self.Logger.log(["check_dsc_against_db",i[1],dsc_file]);
+                        ql.remove(i);
 
                 if ql:
                     # Unfortunately, we make get more than one