From: Joerg Jaspert Date: Sat, 18 Apr 2009 21:59:41 +0000 (+0200) Subject: Merge branch 'merge' X-Git-Url: https://git.decadent.org.uk/gitweb/?a=commitdiff_plain;h=1d800d8d6b8bcdd246ab9da3b33d9e13b38b13d7;hp=f2292c9a3f742a88bf161a82d14059984f55da5e;p=dak.git Merge branch 'merge' * merge: skip arches we should skip i think contents are actually working on separate thread now? possibly working multithread contents writer that has udebs disabled change reject email template to solicit a reply if new files are uploaded convert binaries.type to a enum some tweaking to the contents generation queries strip ./ from front of paths during contents.bootstrap --- diff --git a/config/debian/cron.dinstall b/config/debian/cron.dinstall index c730a116..eb9f37d5 100755 --- a/config/debian/cron.dinstall +++ b/config/debian/cron.dinstall @@ -269,6 +269,12 @@ function expire() { $scriptsdir/expire_dumps -d . -p -f "dump_*" } +function transitionsclean() { + log "Removing out of date transitions..." + cd $base + dak transitions -c -a +} + function reports() { # Send a report on NEW/BYHAND packages log "Nagging ftpteam about NEW/BYHAND packages" @@ -729,6 +735,14 @@ GO=( ) stage $GO +GO=( + FUNC="transitionsclean" + TIME="transitionsclean" + ARGS="" + ERR="" +) +stage $GO + GO=( FUNC="reports" TIME="reports" @@ -807,7 +821,7 @@ GO=( FUNC="aptftpcleanup" TIME="apt-ftparchive cleanup" ARGS="" - ERR="" + ERR="false" ) stage $GO diff --git a/config/debian/dak.conf b/config/debian/dak.conf index 24282855..756a37c3 100644 --- a/config/debian/dak.conf +++ b/config/debian/dak.conf @@ -47,6 +47,7 @@ Dinstall Transitions { + Notifications "team@release.debian.org"; TempPath "/srv/ftp.debian.org/tmp/"; }; @@ -173,6 +174,7 @@ Clean-Suites Process-New { AcceptedLockFile "/srv/ftp.debian.org/lock/unchecked.lock"; + LockDir "/srv/ftp.debian.org/lock/new/"; }; Check-Overrides diff --git a/dak/dakdb/update11.py b/dak/dakdb/update11.py new file mode 100755 index 00000000..53f1572a --- /dev/null +++ b/dak/dakdb/update11.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python +# coding=utf8 + +""" +Adding process-new comments to the DB + +@contact: Debian FTP Master +@copyright: 2009 Joerg Jaspert +@license: GNU General Public License version 2 or later +""" + +# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +################################################################################ + + +################################################################################ + +import psycopg2 +import time + +################################################################################ + +def do_update(self): + print "Adding process-new comments to the DB" + + try: + c = self.db.cursor() + c.execute("""CREATE TABLE new_comments ( + id SERIAL PRIMARY KEY NOT NULL, + package TEXT NOT NULL, + version TEXT NOT NULL, + comment TEXT NOT NULL, + author TEXT NOT NULL + )""") + + c.execute("GRANT SELECT ON new_comments TO ftptrainee;") + c.execute("GRANT INSERT ON new_comments TO ftptrainee;") + c.execute("GRANT UPDATE ON new_comments TO ftptrainee;") + c.execute("GRANT SELECT ON new_comments TO ftpteam;") + c.execute("GRANT INSERT ON new_comments TO ftpteam;") + c.execute("GRANT UPDATE ON new_comments TO ftpteam;") + c.execute("GRANT ALL ON new_comments TO ftpmaster;") + + c.execute("UPDATE config SET value = '11' WHERE name = 'db_revision'") + self.db.commit() + + except psycopg2.ProgrammingError, msg: + self.db.rollback() + raise DBUpdateError, "Unable to apply process-new comments update, rollback issued. Error message : %s" % (str(msg)) diff --git a/dak/dakdb/update12.py b/dak/dakdb/update12.py new file mode 100755 index 00000000..70a9e18e --- /dev/null +++ b/dak/dakdb/update12.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python +# coding=utf8 + +""" +Adding a date field to the process-new notes + +@contact: Debian FTP Master +@copyright: 2009 Joerg Jaspert +@license: GNU General Public License version 2 or later +""" + +# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +################################################################################ + + +################################################################################ + +import psycopg2 +import time + +################################################################################ + +def do_update(self): + print "Adding a date field to the process-new notes" + + try: + c = self.db.cursor() + c.execute("ALTER TABLE new_comments ADD COLUMN notedate TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now()") + + c.execute("UPDATE config SET value = '12' WHERE name = 'db_revision'") + self.db.commit() + + except psycopg2.ProgrammingError, msg: + self.db.rollback() + raise DBUpdateError, "Unable to apply process-new update 12, rollback issued. Error message : %s" % (str(msg)) diff --git a/dak/process_new.py b/dak/process_new.py index 9ecfcdc6..52e1f4e9 100755 --- a/dak/process_new.py +++ b/dak/process_new.py @@ -5,6 +5,7 @@ @contact: Debian FTP Master @copyright: 2001, 2002, 2003, 2004, 2005, 2006 James Troup +@copyright: 2009 Joerg Jaspert @license: GNU General Public License version 2 or later """ # This program is free software; you can redistribute it and/or modify @@ -40,6 +41,8 @@ ################################################################################ +from __future__ import with_statement + import copy import errno import os @@ -47,6 +50,8 @@ import readline import stat import sys import time +import contextlib +import pwd import apt_pkg, apt_inst import examine_package from daklib import database @@ -54,6 +59,7 @@ from daklib import logging from daklib import queue from daklib import utils from daklib.regexes import re_no_epoch, re_default_answer, re_isanum +from daklib.dak_exceptions import CantOpenError, AlreadyLockedError # Globals Cnf = None #: Configuration, apt_pkg.Configuration @@ -112,7 +118,7 @@ def recheck(): if reject_message.find("Rejected") != -1: answer = "XXX" - if Options["No-Action"] or Options["Automatic"]: + if Options["No-Action"] or Options["Automatic"] or Options["Trainee"]: answer = 'S' print "REJECT\n" + reject_message, @@ -219,7 +225,7 @@ def sort_changes(changes_files): mtime = os.stat(d["filename"])[stat.ST_MTIME] if mtime < oldest: oldest = mtime - have_note += (d.has_key("process-new note")) + have_note += (database.has_new_comment(d["source"], d["version"])) per_source[source]["oldest"] = oldest if not have_note: per_source[source]["note_state"] = 0; # none @@ -301,11 +307,10 @@ def print_new (new, indexed, file=sys.stdout): line = "%-20s %-20s %-20s" % (pkg, priority, section) line = line.strip()+'\n' file.write(line) - note = Upload.pkg.changes.get("process-new note") - if note: - print "*"*75 - print note - print "*"*75 + note = database.get_new_comments(Upload.pkg.changes.get("source")) + if len(note) > 0: + for line in note: + print line return broken, note ################################################################################ @@ -468,18 +473,15 @@ def edit_overrides (new): def edit_note(note): # Write the current data to a temporary file (fd, temp_filename) = utils.temp_filename() - temp_file = os.fdopen(fd, 'w') - temp_file.write(note) - temp_file.close() editor = os.environ.get("EDITOR","vi") answer = 'E' while answer == 'E': os.system("%s %s" % (editor, temp_filename)) temp_file = utils.open_file(temp_filename) - note = temp_file.read().rstrip() + newnote = temp_file.read().rstrip() temp_file.close() - print "Note:" - print utils.prefix_multi_line_string(note," ") + print "New Note:" + print utils.prefix_multi_line_string(newnote," ") prompt = "[D]one, Edit, Abandon, Quit ?" answer = "XXX" while prompt.find(answer) == -1: @@ -494,8 +496,7 @@ def edit_note(note): elif answer == 'Q': end() sys.exit(0) - Upload.pkg.changes["process-new note"] = note - Upload.dump_vars(Cnf["Dir::Queue::New"]) + database.add_new_comment(Upload.pkg.changes["source"], Upload.pkg.changes["version"], newnote, utils.whoami()) ################################################################################ @@ -572,16 +573,21 @@ def add_overrides (new): ################################################################################ -def prod_maintainer (): +def prod_maintainer (note): # Here we prepare an editor and get them ready to prod... (fd, temp_filename) = utils.temp_filename() + temp_file = os.fdopen(fd, 'w') + if len(note) > 0: + for line in note: + temp_file.write(line) + temp_file.close() editor = os.environ.get("EDITOR","vi") answer = 'E' while answer == 'E': os.system("%s %s" % (editor, temp_filename)) - f = os.fdopen(fd) - prod_message = "".join(f.readlines()) - f.close() + temp_fh = utils.open_file(temp_filename) + prod_message = "".join(temp_fh.readlines()) + temp_fh.close() print "Prod message:" print utils.prefix_multi_line_string(prod_message," ",include_blank_lines=1) prompt = "[P]rod, Edit, Abandon, Quit ?" @@ -592,12 +598,12 @@ def prod_maintainer (): if answer == "": answer = m.group(1) answer = answer[:1].upper() - os.unlink(temp_filename) - if answer == 'A': - return - elif answer == 'Q': - end() - sys.exit(0) + os.unlink(temp_filename) + if answer == 'A': + return + elif answer == 'Q': + end() + sys.exit(0) # Otherwise, do the proding... user_email_address = utils.whoami() + " <%s>" % ( Cnf["Dinstall::MyAdminAddress"]) @@ -680,25 +686,27 @@ def do_new(): answer = m.group(1) answer = answer[:1].upper() - if answer == 'A': + if answer == 'A' and not Options["Trainee"]: done = add_overrides (new) elif answer == 'C': check_pkg() - elif answer == 'E': + elif answer == 'E' and not Options["Trainee"]: new = edit_overrides (new) - elif answer == 'M': - aborted = Upload.do_reject(1, Options["Manual-Reject"]) + elif answer == 'M' and not Options["Trainee"]: + aborted = Upload.do_reject(manual=1, + reject_message=Options["Manual-Reject"], + note=database.get_new_comments(changes.get("source", ""))) if not aborted: os.unlink(Upload.pkg.changes_file[:-8]+".dak") done = 1 elif answer == 'N': - edit_note(changes.get("process-new note", "")) - elif answer == 'P': - prod_maintainer() - elif answer == 'R': + edit_note(database.get_new_comments(changes.get("source", ""))) + elif answer == 'P' and not Options["Trainee"]: + prod_maintainer(database.get_new_comments(changes.get("source", ""))) + elif answer == 'R' and not Options["Trainee"]: confirm = utils.our_raw_input("Really clear note (y/N)? ").lower() if confirm == "y": - del changes["process-new note"] + database.delete_new_comments(changes.get("source"), changes.get("version")) elif answer == 'S': done = 1 elif answer == 'Q': @@ -716,6 +724,7 @@ def usage (exit_code=0): -C, --comments-dir=DIR use DIR as comments-dir, for [o-]p-u-new -m, --manual-reject=MSG manual reject with `msg' -n, --no-action don't do anything + -t, --trainee FTP Trainee mode -V, --version display the version number and exit""" sys.exit(exit_code) @@ -730,9 +739,10 @@ def init(): ('h',"help","Process-New::Options::Help"), ('C',"comments-dir","Process-New::Options::Comments-Dir", "HasArg"), ('m',"manual-reject","Process-New::Options::Manual-Reject", "HasArg"), + ('t',"trainee","Process-New::Options::Trainee"), ('n',"no-action","Process-New::Options::No-Action")] - for i in ["automatic", "help", "manual-reject", "no-action", "version", "comments-dir"]: + for i in ["automatic", "help", "manual-reject", "no-action", "version", "comments-dir", "trainee"]: if not Cnf.has_key("Process-New::Options::%s" % (i)): Cnf["Process-New::Options::%s" % (i)] = "" @@ -748,7 +758,10 @@ def init(): Upload = queue.Upload(Cnf) if not Options["No-Action"]: - Logger = Upload.Logger = logging.Logger(Cnf, "process-new") + try: + Logger = Upload.Logger = logging.Logger(Cnf, "process-new") + except CantOpenError, e: + Options["Trainee"] = "Oh yes" projectB = Upload.projectB @@ -825,6 +838,29 @@ def get_accept_lock(): else: raise + +@contextlib.contextmanager +def lock_package(package): + """ + Lock C{package} so that noone else jumps in processing it. + + @type package: string + @param package: source package name to lock + """ + + path = os.path.join(Cnf["Process-New::LockDir"], package) + try: + fd = os.open(path, os.O_CREAT | os.O_EXCL | os.O_RDONLY) + except OSError, e: + if e.errno == errno.EEXIST or e.errno == errno.EACCES: + user = pwd.getpwuid(os.stat(path)[stat.ST_UID])[4].split(',')[0].replace('.', '') + raise AlreadyLockedError, user + + try: + yield fd + finally: + os.unlink(path) + def move_to_dir (dest, perms=0660, changesperms=0664): utils.move (Upload.pkg.changes_file, dest, perms=changesperms) file_keys = Upload.pkg.files.keys() @@ -949,19 +985,23 @@ def do_pkg(changes_file): Upload.update_subst() files = Upload.pkg.files - if not recheck(): - return - - (new, byhand) = check_status(files) - if new or byhand: - if new: - do_new() - if byhand: - do_byhand() - (new, byhand) = check_status(files) - - if not new and not byhand: - do_accept() + try: + with lock_package(Upload.pkg.changes["source"]): + if not recheck(): + return + + (new, byhand) = check_status(files) + if new or byhand: + if new: + do_new() + if byhand: + do_byhand() + (new, byhand) = check_status(files) + + if not new and not byhand: + do_accept() + except AlreadyLockedError, e: + print "Seems to be locked by %s already, skipping..." % (e) ################################################################################ @@ -976,7 +1016,7 @@ def end(): sys.stderr.write("Accepted %d package %s, %s.\n" % (accept_count, sets, utils.size_type(int(accept_bytes)))) Logger.log(["total",accept_count,accept_bytes]) - if not Options["No-Action"]: + if not Options["No-Action"] and not Options["Trainee"]: Logger.close() ################################################################################ diff --git a/dak/queue_report.py b/dak/queue_report.py index cc59a9f8..a4bcea0f 100755 --- a/dak/queue_report.py +++ b/dak/queue_report.py @@ -38,6 +38,7 @@ import copy, glob, os, stat, sys, time import apt_pkg import cgi from daklib import queue +from daklib import database from daklib import utils from daklib.dak_exceptions import * @@ -45,6 +46,7 @@ Cnf = None Upload = None direction = [] row_number = 0 +projectB = None ################################################################################ @@ -327,7 +329,7 @@ def process_changes_files(changes_files, type, log): else: if mtime < oldest: oldest = mtime - have_note += (d.has_key("process-new note")) + have_note += (database.has_new_comment(d["source"], d["version"])) per_source[source]["oldest"] = oldest if not have_note: per_source[source]["note_state"] = 0; # none @@ -531,6 +533,7 @@ def main(): usage() Upload = queue.Upload(Cnf) + projectB = Upload.projectB if Cnf.has_key("Queue-Report::Options::New"): header() diff --git a/dak/transitions.py b/dak/transitions.py index 90d2f620..00348f2d 100755 --- a/dak/transitions.py +++ b/dak/transitions.py @@ -66,14 +66,15 @@ def init(): Cnf = utils.get_conf() - Arguments = [('h',"help","Edit-Transitions::Options::Help"), + Arguments = [('a',"automatic","Edit-Transitions::Options::Automatic"), + ('h',"help","Edit-Transitions::Options::Help"), ('e',"edit","Edit-Transitions::Options::Edit"), ('i',"import","Edit-Transitions::Options::Import", "HasArg"), ('c',"check","Edit-Transitions::Options::Check"), ('s',"sudo","Edit-Transitions::Options::Sudo"), ('n',"no-action","Edit-Transitions::Options::No-Action")] - for i in ["help", "no-action", "edit", "import", "check", "sudo"]: + for i in ["automatic", "help", "no-action", "edit", "import", "check", "sudo"]: if not Cnf.has_key("Edit-Transitions::Options::%s" % (i)): Cnf["Edit-Transitions::Options::%s" % (i)] = "" @@ -107,6 +108,7 @@ Options: -i, --import check and import transitions from file -c, --check check the transitions file, remove outdated entries -S, --sudo use sudo to update transitions file + -a, --automatic don't prompt (only affects check). -n, --no-action don't do anything (only affects check)""" sys.exit(exit_code) @@ -389,11 +391,14 @@ def edit_transitions(): def check_transitions(transitions): """ Check if the defined transitions still apply and remove those that no longer do. - @note: Asks the user for confirmation first. + @note: Asks the user for confirmation first unless -a has been set. """ + global Cnf + to_dump = 0 to_remove = [] + info = {} # Now look through all defined transitions for trans in transitions: t = transitions[trans] @@ -403,7 +408,8 @@ def check_transitions(transitions): # Will be None if nothing is in testing. current = database.get_suite_version(source, "testing") - print_info(trans, source, expected, t["rm"], t["reason"], t["packages"]) + info[trans] = get_info(trans, source, expected, t["rm"], t["reason"], t["packages"]) + print info[trans] if current == None: # No package in testing @@ -432,6 +438,8 @@ def check_transitions(transitions): if Options["no-action"]: answer="n" + elif Options["automatic"]: + answer="y" else: answer = utils.our_raw_input(prompt).lower() @@ -443,9 +451,25 @@ def check_transitions(transitions): sys.exit(0) elif answer == 'y': print "Committing" + subst = {} + subst['__TRANSITION_MESSAGE__'] = "The following transitions were removed:\n" for remove in to_remove: + subst['__TRANSITION_MESSAGE__'] += info[remove] + '\n' del transitions[remove] + # If we have a mail address configured for transitions, + # send a notification + subst['__TRANSITION_EMAIL__'] = Cnf.get("Transitions::Notifications", "") + if subst['__TRANSITION_EMAIL__'] != "": + print "Sending notification to %s" % subst['__TRANSITION_EMAIL__'] + subst['__DAK_ADDRESS__'] = Cnf["Dinstall::MyEmailAddress"] + subst['__BCC__'] = 'X-DAK: dak transitions' + if Cnf.has_key("Dinstall::Bcc"): + subst["__BCC__"] += '\nBcc: %s' % Cnf["Dinstall::Bcc"] + message = utils.TemplateSubst(subst, + os.path.join(Cnf["Dir::Templates"], 'transition.removed')) + utils.send_mail(message) + edit_file = temp_transitions_file(transitions) write_transitions_from_file(edit_file) @@ -456,7 +480,7 @@ def check_transitions(transitions): ################################################################################ -def print_info(trans, source, expected, rm, reason, packages): +def get_info(trans, source, expected, rm, reason, packages): """ Print information about a single transition. @@ -479,21 +503,20 @@ def print_info(trans, source, expected, rm, reason, packages): @param packages: list of blocked packages """ - print """Looking at transition: %s + return """Looking at transition: %s Source: %s New Version: %s Responsible: %s Description: %s Blocked Packages (total: %d): %s """ % (trans, source, expected, rm, reason, len(packages), ", ".join(packages)) - return ################################################################################ def transition_info(transitions): """ Print information about all defined transitions. - Calls L{print_info} for every transition and then tells user if the transition is + Calls L{get_info} for every transition and then tells user if the transition is still ongoing or if the expected version already hit testing. @type transitions: dict @@ -507,7 +530,7 @@ def transition_info(transitions): # Will be None if nothing is in testing. current = database.get_suite_version(source, "testing") - print_info(trans, source, expected, t["rm"], t["reason"], t["packages"]) + print get_info(trans, source, expected, t["rm"], t["reason"], t["packages"]) if current == None: # No package in testing diff --git a/dak/update_db.py b/dak/update_db.py index 4d08721f..b559e544 100755 --- a/dak/update_db.py +++ b/dak/update_db.py @@ -45,7 +45,7 @@ from daklib.dak_exceptions import DBUpdateError Cnf = None projectB = None -required_database_schema = 10 +required_database_schema = 12 ################################################################################ diff --git a/daklib/dak_exceptions.py b/daklib/dak_exceptions.py index ccd63e50..21fce9be 100755 --- a/daklib/dak_exceptions.py +++ b/daklib/dak_exceptions.py @@ -60,7 +60,8 @@ dakerrors = { "NoSourceFieldError": """Exception raised - we cant find the source - wtf?""", "MissingContents": """Exception raised - we could not determine contents for this deb""", "DBUpdateError": """Exception raised - could not update the database""", - "ChangesUnicodeError": """Exception raised - changes file not properly utf-8 encoded""" + "ChangesUnicodeError": """Exception raised - changes file not properly utf-8 encoded""", + "AlreadyLockedError": """Exception raised - package already locked by someone else""" } #: All dak exceptions def construct_dak_exception(name, description): diff --git a/daklib/database.py b/daklib/database.py index 93d9ad53..a5255568 100755 --- a/daklib/database.py +++ b/daklib/database.py @@ -4,8 +4,9 @@ @group readonly: get_suite_id, get_section_id, get_priority_id, get_override_type_id, get_architecture_id, get_archive_id, get_component_id, get_location_id, get_source_id, get_suite_version, get_files_id, get_maintainer, get_suites, - get_suite_architectures + get_suite_architectures, get_new_comments, has_new_comment @group read/write: get_or_set*, set_files_id +@group writeonly: add_new_comment, delete_new_comments @contact: Debian FTP Master @copyright: 2000, 2001, 2002, 2003, 2004, 2006 James Troup @@ -838,6 +839,89 @@ def get_suites(pkgname, src=False): ################################################################################ +def get_new_comments(package): + """ + Returns all the possible comments attached to C{package} in NEW. All versions. + + @type package: string + @param package: name of the package + + @rtype: list + @return: list of strings containing comments for all versions from all authors for package + """ + + comments = [] + query = projectB.query(""" SELECT version, comment, author, notedate + FROM new_comments + WHERE package = '%s' + ORDER BY notedate + """ % (package)) + + for row in query.getresult(): + comments.append("\nAuthor: %s\nVersion: %s\nTimestamp: %s\n\n%s\n" % (row[2], row[0], row[3], row[1])) + comments.append("-"*72) + + return comments + +def has_new_comment(package, version): + """ + Returns true if the given combination of C{package}, C{version} has a comment. + + @type package: string + @param package: name of the package + + @type version: string + @param version: package version + + @rtype: boolean + @return: true/false + """ + + exists = projectB.query("""SELECT 1 FROM new_comments + WHERE package='%s' + AND version='%s' + LIMIT 1""" + % (package, version) ).getresult() + + if not exists: + return False + else: + return True + +def add_new_comment(package, version, comment, author): + """ + Add a new comment for C{package}, C{version} written by C{author} + + @type package: string + @param package: name of the package + + @type version: string + @param version: package version + + @type comment: string + @param comment: the comment + + @type author: string + @param author: the authorname + """ + + projectB.query(""" INSERT INTO new_comments (package, version, comment, author) + VALUES ('%s', '%s', '%s', '%s') + """ % (package, version, comment, author) ) + + return + +def delete_new_comments(package, version): + """ + Delete a comment for C{package}, C{version}, if one exists + """ + + projectB.query(""" DELETE FROM new_comments + WHERE package = '%s' AND version = '%s' + """ % (package, version)) + return + +################################################################################ def copy_temporary_contents(package, version, arch, deb, reject): """ copy the previously stored contents from the temp table to the permanant one diff --git a/daklib/queue.py b/daklib/queue.py index 599b077f..35754c8d 100755 --- a/daklib/queue.py +++ b/daklib/queue.py @@ -803,7 +803,7 @@ distribution.""" ########################################################################### - def do_reject (self, manual = 0, reject_message = ""): + def do_reject (self, manual = 0, reject_message = "", note = ""): """ Reject an upload. If called without a reject message or C{manual} is true, spawn an editor so the user can write one. @@ -821,6 +821,11 @@ distribution.""" # editor so the user can add one in... if manual and not reject_message: (fd, temp_filename) = utils.temp_filename() + temp_file = os.fdopen(fd, 'w') + if len(note) > 0: + for line in note: + temp_file.write(line) + temp_file.close() editor = os.environ.get("EDITOR","vi") answer = 'E' while answer == 'E': diff --git a/templates/transition.removed b/templates/transition.removed new file mode 100644 index 00000000..32b0aca1 --- /dev/null +++ b/templates/transition.removed @@ -0,0 +1,15 @@ +From: __DAK_ADDRESS__ +To: __TRANSITION_EMAIL__ +__BCC__ +X-Debian: DAK +Precedence: bulk +MIME-Version: 1.0 +Content-Type: text/plain; charset="utf-8" +Content-Transfer-Encoding: 8bit +Subject: Transitions Completed + +The following transitions are complete and have been removed +from the transitions list: + +__TRANSITION_MESSAGE__ +