2 # vim:set et ts=4 sw=4:
4 """ Handles NEW and BYHAND packages
6 @contact: Debian FTP Master <ftpmaster@debian.org>
7 @copyright: 2001, 2002, 2003, 2004, 2005, 2006 James Troup <james@nocrew.org>
8 @copyright: 2009 Joerg Jaspert <joerg@debian.org>
9 @copyright: 2009 Frank Lichtenheld <djpig@debian.org>
10 @license: GNU General Public License version 2 or later
12 # This program is free software; you can redistribute it and/or modify
13 # it under the terms of the GNU General Public License as published by
14 # the Free Software Foundation; either version 2 of the License, or
15 # (at your option) any later version.
17 # This program is distributed in the hope that it will be useful,
18 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # GNU General Public License for more details.
22 # You should have received a copy of the GNU General Public License
23 # along with this program; if not, write to the Free Software
24 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 ################################################################################
28 # 23:12|<aj> I will not hush!
30 # 23:12|<aj> Where there is injustice in the world, I shall be there!
31 # 23:13|<aj> I shall not be silenced!
32 # 23:13|<aj> The world shall know!
33 # 23:13|<aj> The world *must* know!
34 # 23:13|<elmo> oh dear, he's gone back to powerpuff girls... ;-)
35 # 23:13|<aj> yay powerpuff girls!!
36 # 23:13|<aj> buttercup's my favourite, who's yours?
37 # 23:14|<aj> you're backing away from the keyboard right now aren't you?
38 # 23:14|<aj> *AREN'T YOU*?!
39 # 23:15|<aj> I will not be treated like this.
40 # 23:15|<aj> I shall have my revenge.
41 # 23:15|<aj> I SHALL!!!
43 ################################################################################
45 from __future__ import with_statement
56 import apt_pkg, apt_inst
57 import examine_package
59 from daklib.dbconn import *
60 from daklib.queue import *
61 from daklib import daklog
62 from daklib import utils
63 from daklib.regexes import re_no_epoch, re_default_answer, re_isanum
64 from daklib.dak_exceptions import CantOpenError, AlreadyLockedError, CantGetLockError
65 from daklib.summarystats import SummaryStats
66 from daklib.config import Config
75 ################################################################################
76 ################################################################################
77 ################################################################################
79 def recheck(upload, session):
80 # STU: I'm not sure, but I don't thin kthis is necessary any longer: upload.recheck(session)
81 if len(upload.rejects) > 0:
83 if Options["No-Action"] or Options["Automatic"] or Options["Trainee"]:
86 print "REJECT\n%s" % '\n'.join(upload.rejects)
87 prompt = "[R]eject, Skip, Quit ?"
89 while prompt.find(answer) == -1:
90 answer = utils.our_raw_input(prompt)
91 m = re_default_answer.match(prompt)
94 answer = answer[:1].upper()
97 upload.do_reject(manual=0, reject_message='\n'.join(upload.rejects))
98 os.unlink(upload.pkg.changes_file[:-8]+".dak")
108 ################################################################################
110 def indiv_sg_compare (a, b):
111 """Sort by source name, source, version, 'have source', and
112 finally by filename."""
113 # Sort by source version
114 q = apt_pkg.VersionCompare(a["version"], b["version"])
118 # Sort by 'have source'
119 a_has_source = a["architecture"].get("source")
120 b_has_source = b["architecture"].get("source")
121 if a_has_source and not b_has_source:
123 elif b_has_source and not a_has_source:
126 return cmp(a["filename"], b["filename"])
128 ############################################################
130 def sg_compare (a, b):
133 """Sort by have note, source already in database and time of oldest upload."""
135 a_note_state = a["note_state"]
136 b_note_state = b["note_state"]
137 if a_note_state < b_note_state:
139 elif a_note_state > b_note_state:
141 # Sort by source already in database (descending)
142 source_in_database = cmp(a["source_in_database"], b["source_in_database"])
143 if source_in_database:
144 return -source_in_database
146 # Sort by time of oldest upload
147 return cmp(a["oldest"], b["oldest"])
149 def sort_changes(changes_files, session):
150 """Sort into source groups, then sort each source group by version,
151 have source, filename. Finally, sort the source groups by have
152 note, time of oldest upload of each source upload."""
153 if len(changes_files) == 1:
158 # Read in all the .changes files
159 for filename in changes_files:
162 u.pkg.changes_file = filename
163 u.load_changes(filename)
165 cache[filename] = copy.copy(u.pkg.changes)
166 cache[filename]["filename"] = filename
168 sorted_list.append(filename)
170 # Divide the .changes into per-source groups
172 for filename in cache.keys():
173 source = cache[filename]["source"]
174 if not per_source.has_key(source):
175 per_source[source] = {}
176 per_source[source]["list"] = []
177 per_source[source]["list"].append(cache[filename])
178 # Determine oldest time and have note status for each source group
179 for source in per_source.keys():
180 q = session.query(DBSource).filter_by(source = source).all()
181 per_source[source]["source_in_database"] = len(q)>0
182 source_list = per_source[source]["list"]
183 first = source_list[0]
184 oldest = os.stat(first["filename"])[stat.ST_MTIME]
186 for d in per_source[source]["list"]:
187 mtime = os.stat(d["filename"])[stat.ST_MTIME]
190 have_note += has_new_comment(d["source"], d["version"], session)
191 per_source[source]["oldest"] = oldest
193 per_source[source]["note_state"] = 0; # none
194 elif have_note < len(source_list):
195 per_source[source]["note_state"] = 1; # some
197 per_source[source]["note_state"] = 2; # all
198 per_source[source]["list"].sort(indiv_sg_compare)
199 per_source_items = per_source.items()
200 per_source_items.sort(sg_compare)
201 for i in per_source_items:
202 for j in i[1]["list"]:
203 sorted_list.append(j["filename"])
206 ################################################################################
208 class Section_Completer:
209 def __init__ (self, session):
212 for s, in session.query(Section.section):
213 self.sections.append(s)
215 def complete(self, text, state):
219 for word in self.sections:
221 self.matches.append(word)
223 return self.matches[state]
227 ############################################################
229 class Priority_Completer:
230 def __init__ (self, session):
233 for p, in session.query(Priority.priority):
234 self.priorities.append(p)
236 def complete(self, text, state):
240 for word in self.priorities:
242 self.matches.append(word)
244 return self.matches[state]
248 ################################################################################
250 def print_new (new, upload, indexed, file=sys.stdout):
254 for pkg in new.keys():
256 section = new[pkg]["section"]
257 priority = new[pkg]["priority"]
258 if new[pkg]["section id"] == -1:
261 if new[pkg]["priority id"] == -1:
265 line = "(%s): %-20s %-20s %-20s" % (index, pkg, priority, section)
267 line = "%-20s %-20s %-20s" % (pkg, priority, section)
268 line = line.strip()+'\n'
270 notes = get_new_comments(upload.pkg.changes.get("source"))
272 print "\nAuthor: %s\nVersion: %s\nTimestamp: %s\n\n%s" \
273 % (note.author, note.version, note.notedate, note.comment)
275 return broken, len(notes) > 0
277 ################################################################################
279 def index_range (index):
283 return "1-%s" % (index)
285 ################################################################################
286 ################################################################################
288 def edit_new (new, upload):
289 # Write the current data to a temporary file
290 (fd, temp_filename) = utils.temp_filename()
291 temp_file = os.fdopen(fd, 'w')
292 print_new (new, upload, indexed=0, file=temp_file)
294 # Spawn an editor on that file
295 editor = os.environ.get("EDITOR","vi")
296 result = os.system("%s %s" % (editor, temp_filename))
298 utils.fubar ("%s invocation failed for %s." % (editor, temp_filename), result)
299 # Read the edited data back in
300 temp_file = utils.open_file(temp_filename)
301 lines = temp_file.readlines()
303 os.unlink(temp_filename)
310 # Pad the list if necessary
311 s[len(s):3] = [None] * (3-len(s))
312 (pkg, priority, section) = s[:3]
313 if not new.has_key(pkg):
314 utils.warn("Ignoring unknown package '%s'" % (pkg))
316 # Strip off any invalid markers, print_new will readd them.
317 if section.endswith("[!]"):
318 section = section[:-3]
319 if priority.endswith("[!]"):
320 priority = priority[:-3]
321 for f in new[pkg]["files"]:
322 upload.pkg.files[f]["section"] = section
323 upload.pkg.files[f]["priority"] = priority
324 new[pkg]["section"] = section
325 new[pkg]["priority"] = priority
327 ################################################################################
329 def edit_index (new, upload, index):
330 priority = new[index]["priority"]
331 section = new[index]["section"]
332 ftype = new[index]["type"]
335 print "\t".join([index, priority, section])
339 prompt = "[B]oth, Priority, Section, Done ? "
341 prompt = "[S]ection, Done ? "
342 edit_priority = edit_section = 0
344 while prompt.find(answer) == -1:
345 answer = utils.our_raw_input(prompt)
346 m = re_default_answer.match(prompt)
349 answer = answer[:1].upper()
356 edit_priority = edit_section = 1
362 readline.set_completer(Priorities.complete)
364 while not got_priority:
365 new_priority = utils.our_raw_input("New priority: ").strip()
366 if new_priority not in Priorities.priorities:
367 print "E: '%s' is not a valid priority, try again." % (new_priority)
370 priority = new_priority
374 readline.set_completer(Sections.complete)
376 while not got_section:
377 new_section = utils.our_raw_input("New section: ").strip()
378 if new_section not in Sections.sections:
379 print "E: '%s' is not a valid section, try again." % (new_section)
382 section = new_section
384 # Reset the readline completer
385 readline.set_completer(None)
387 for f in new[index]["files"]:
388 upload.pkg.files[f]["section"] = section
389 upload.pkg.files[f]["priority"] = priority
390 new[index]["priority"] = priority
391 new[index]["section"] = section
394 ################################################################################
396 def edit_overrides (new, upload, session):
400 print_new (new, upload, indexed=1)
407 prompt = "(%s) edit override <n>, Editor, Done ? " % (index_range(index))
410 while not got_answer:
411 answer = utils.our_raw_input(prompt)
412 if not answer.isdigit():
413 answer = answer[:1].upper()
414 if answer == "E" or answer == "D":
416 elif re_isanum.match (answer):
418 if (answer < 1) or (answer > index):
419 print "%s is not a valid index (%s). Please retry." % (answer, index_range(index))
424 edit_new(new, upload)
428 edit_index (new, upload, new_index[answer])
432 ################################################################################
434 def edit_note(note, upload, session):
435 # Write the current data to a temporary file
436 (fd, temp_filename) = utils.temp_filename()
437 editor = os.environ.get("EDITOR","vi")
440 os.system("%s %s" % (editor, temp_filename))
441 temp_file = utils.open_file(temp_filename)
442 newnote = temp_file.read().rstrip()
445 print utils.prefix_multi_line_string(newnote," ")
446 prompt = "[D]one, Edit, Abandon, Quit ?"
448 while prompt.find(answer) == -1:
449 answer = utils.our_raw_input(prompt)
450 m = re_default_answer.search(prompt)
453 answer = answer[:1].upper()
454 os.unlink(temp_filename)
461 comment = NewComment()
462 comment.package = upload.pkg.changes["source"]
463 comment.version = upload.pkg.changes["version"]
464 comment.comment = newnote
465 comment.author = utils.whoami()
466 comment.trainee = bool(Options["Trainee"])
470 ################################################################################
472 def check_pkg (upload):
474 less_fd = os.popen("less -R -", 'w', 0)
475 stdout_fd = sys.stdout
478 changes = utils.parse_changes (upload.pkg.changes_file)
479 examine_package.display_changes(changes['distribution'], upload.pkg.changes_file)
480 files = upload.pkg.files
481 for f in files.keys():
482 if files[f].has_key("new"):
483 ftype = files[f]["type"]
485 examine_package.check_deb(changes['distribution'], f)
487 examine_package.check_dsc(changes['distribution'], f)
489 examine_package.output_package_relations()
490 sys.stdout = stdout_fd
492 if e.errno == errno.EPIPE:
493 utils.warn("[examine_package] Caught EPIPE; skipping.")
497 except KeyboardInterrupt:
498 utils.warn("[examine_package] Caught C-c; skipping.")
501 ################################################################################
503 ## FIXME: horribly Debian specific
505 def do_bxa_notification(upload):
506 files = upload.pkg.files
508 for f in files.keys():
509 if files[f]["type"] == "deb":
510 control = apt_pkg.ParseSection(apt_inst.debExtractControl(utils.open_file(f)))
512 summary += "Package: %s\n" % (control.Find("Package"))
513 summary += "Description: %s\n" % (control.Find("Description"))
514 upload.Subst["__BINARY_DESCRIPTIONS__"] = summary
515 bxa_mail = utils.TemplateSubst(upload.Subst,Config()["Dir::Templates"]+"/process-new.bxa_notification")
516 utils.send_mail(bxa_mail)
518 ################################################################################
520 def add_overrides (new, upload, session):
521 changes = upload.pkg.changes
522 files = upload.pkg.files
523 srcpkg = changes.get("source")
525 for suite in changes["suite"].keys():
526 suite_id = get_suite(suite).suite_id
527 for pkg in new.keys():
528 component_id = get_component(new[pkg]["component"]).component_id
529 type_id = get_override_type(new[pkg]["type"]).overridetype_id
530 priority_id = new[pkg]["priority id"]
531 section_id = new[pkg]["section id"]
532 Logger.log(["%s overrides" % (srcpkg), suite, new[pkg]["component"], new[pkg]["type"], new[pkg]["priority"], new[pkg]["section"]])
533 session.execute("INSERT INTO override (suite, component, type, package, priority, section, maintainer) VALUES (:sid, :cid, :tid, :pkg, :pid, :sectid, '')",
534 { 'sid': suite_id, 'cid': component_id, 'tid':type_id, 'pkg': pkg, 'pid': priority_id, 'sectid': section_id})
535 for f in new[pkg]["files"]:
536 if files[f].has_key("new"):
542 if Config().FindB("Dinstall::BXANotify"):
543 do_bxa_notification(upload)
545 ################################################################################
547 def prod_maintainer (note, upload):
549 # Here we prepare an editor and get them ready to prod...
550 (fd, temp_filename) = utils.temp_filename()
551 temp_file = os.fdopen(fd, 'w')
554 temp_file.write(line)
556 editor = os.environ.get("EDITOR","vi")
559 os.system("%s %s" % (editor, temp_filename))
560 temp_fh = utils.open_file(temp_filename)
561 prod_message = "".join(temp_fh.readlines())
563 print "Prod message:"
564 print utils.prefix_multi_line_string(prod_message," ",include_blank_lines=1)
565 prompt = "[P]rod, Edit, Abandon, Quit ?"
567 while prompt.find(answer) == -1:
568 answer = utils.our_raw_input(prompt)
569 m = re_default_answer.search(prompt)
572 answer = answer[:1].upper()
573 os.unlink(temp_filename)
579 # Otherwise, do the proding...
580 user_email_address = utils.whoami() + " <%s>" % (
581 cnf["Dinstall::MyAdminAddress"])
585 Subst["__FROM_ADDRESS__"] = user_email_address
586 Subst["__PROD_MESSAGE__"] = prod_message
587 Subst["__CC__"] = "Cc: " + cnf["Dinstall::MyEmailAddress"]
589 prod_mail_message = utils.TemplateSubst(
590 Subst,cnf["Dir::Templates"]+"/process-new.prod")
592 # Send the prod mail if appropriate
593 if not cnf["Dinstall::Options::No-Mail"]:
594 utils.send_mail(prod_mail_message)
596 print "Sent proding message"
598 ################################################################################
600 def do_new(upload, session):
602 files = upload.pkg.files
603 upload.check_files(not Options["No-Action"])
604 changes = upload.pkg.changes
607 # Make a copy of distribution we can happily trample on
608 changes["suite"] = copy.copy(changes["distribution"])
610 # Fix up the list of target suites
611 for suite in changes["suite"].keys():
612 override = cnf.Find("Suite::%s::OverrideSuite" % (suite))
614 (olderr, newerr) = (get_suite(suite, session) == None,
615 get_suite(override, session) == None)
617 (oinv, newinv) = ("", "")
618 if olderr: oinv = "invalid "
619 if newerr: ninv = "invalid "
620 print "warning: overriding %ssuite %s to %ssuite %s" % (
621 oinv, suite, ninv, override)
622 del changes["suite"][suite]
623 changes["suite"][override] = 1
625 for suite in changes["suite"].keys():
626 if get_suite(suite, session) is None:
627 utils.fubar("%s has invalid suite '%s' (possibly overriden). say wha?" % (changes, suite))
629 # The main NEW processing loop
632 # Find out what's new
633 new = determine_new(changes, files)
639 if Options["No-Action"] or Options["Automatic"]:
642 (broken, note) = print_new(new, upload, indexed=0)
645 if not broken and not note:
646 prompt = "Add overrides, "
648 print "W: [!] marked entries must be fixed before package can be processed."
650 print "W: note must be removed before package can be processed."
651 prompt += "RemOve all notes, Remove note, "
653 prompt += "Edit overrides, Check, Manual reject, Note edit, Prod, [S]kip, Quit ?"
655 while prompt.find(answer) == -1:
656 answer = utils.our_raw_input(prompt)
657 m = re_default_answer.search(prompt)
660 answer = answer[:1].upper()
662 if answer in ( 'A', 'E', 'M', 'O', 'R' ) and Options["Trainee"]:
663 utils.warn("Trainees can't do that")
666 if answer == 'A' and not Options["Trainee"]:
669 done = add_overrides (new, upload, session)
670 Logger.log(["NEW ACCEPT: %s" % (upload.pkg.changes_file)])
671 except CantGetLockError:
672 print "Hello? Operator! Give me the number for 911!"
673 print "Dinstall in the locked area, cant process packages, come back later"
676 elif answer == 'E' and not Options["Trainee"]:
677 new = edit_overrides (new, upload, session)
678 elif answer == 'M' and not Options["Trainee"]:
679 upload.pkg.remove_known_changes()
680 aborted = upload.do_reject(manual=1,
681 reject_message=Options["Manual-Reject"],
682 note=get_new_comments(changes.get("source", ""), session=session))
684 Logger.log(["NEW REJECT: %s" % (upload.pkg.changes_file)])
685 os.unlink(upload.pkg.changes_file[:-8]+".dak")
688 edit_note(get_new_comments(changes.get("source", ""), session=session),
690 elif answer == 'P' and not Options["Trainee"]:
691 prod_maintainer(get_new_comments(changes.get("source", ""), session=session),
693 Logger.log(["NEW PROD: %s" % (upload.pkg.changes_file)])
694 elif answer == 'R' and not Options["Trainee"]:
695 confirm = utils.our_raw_input("Really clear note (y/N)? ").lower()
697 for c in get_new_comments(changes.get("source", ""), changes.get("version", ""), session=session):
700 elif answer == 'O' and not Options["Trainee"]:
701 confirm = utils.our_raw_input("Really clear all notes (y/N)? ").lower()
703 for c in get_new_comments(changes.get("source", ""), session=session):
713 ################################################################################
714 ################################################################################
715 ################################################################################
717 def usage (exit_code=0):
718 print """Usage: dak process-new [OPTION]... [CHANGES]...
719 -a, --automatic automatic run
720 -h, --help show this help and exit.
721 -m, --manual-reject=MSG manual reject with `msg'
722 -n, --no-action don't do anything
723 -t, --trainee FTP Trainee mode
724 -V, --version display the version number and exit"""
727 ################################################################################
729 def do_byhand(upload, session):
732 files = upload.pkg.files
736 for f in files.keys():
737 if files[f]["type"] == "byhand":
738 if os.path.exists(f):
739 print "W: %s still present; please process byhand components and try again." % (f)
745 if Options["No-Action"]:
748 if Options["Automatic"] and not Options["No-Action"]:
750 prompt = "[A]ccept, Manual reject, Skip, Quit ?"
752 prompt = "Manual reject, [S]kip, Quit ?"
754 while prompt.find(answer) == -1:
755 answer = utils.our_raw_input(prompt)
756 m = re_default_answer.search(prompt)
759 answer = answer[:1].upper()
767 Logger.log(["BYHAND ACCEPT: %s" % (upload.pkg.changes_file)])
768 except CantGetLockError:
769 print "Hello? Operator! Give me the number for 911!"
770 print "Dinstall in the locked area, cant process packages, come back later"
772 Logger.log(["BYHAND REJECT: %s" % (upload.pkg.changes_file)])
773 upload.do_reject(manual=1, reject_message=Options["Manual-Reject"])
774 os.unlink(upload.pkg.changes_file[:-8]+".dak")
782 ################################################################################
784 def check_daily_lock():
786 Raises CantGetLockError if the dinstall daily.lock exists.
791 os.open(cnf["Process-New::DinstallLockFile"],
792 os.O_RDONLY | os.O_CREAT | os.O_EXCL)
794 if e.errno == errno.EEXIST or e.errno == errno.EACCES:
795 raise CantGetLockError
797 os.unlink(cnf["Process-New::DinstallLockFile"])
800 @contextlib.contextmanager
801 def lock_package(package):
803 Lock C{package} so that noone else jumps in processing it.
805 @type package: string
806 @param package: source package name to lock
809 path = os.path.join(Config()["Process-New::LockDir"], package)
811 fd = os.open(path, os.O_CREAT | os.O_EXCL | os.O_RDONLY)
813 if e.errno == errno.EEXIST or e.errno == errno.EACCES:
814 user = pwd.getpwuid(os.stat(path)[stat.ST_UID])[4].split(',')[0].replace('.', '')
815 raise AlreadyLockedError, user
822 def move_file_to_queue(to_q, f, session):
823 """mark a file as being in the unchecked queue"""
824 # update the queue_file entry for the existing queue
825 qf = session.query(QueueFile).filter_by(queueid=to_q.queueid,
829 # update the changes_pending_files row
832 def changes_to_unchecked(changes, session):
833 """move a changes file to unchecked"""
834 unchecked = get_policy_queue('unchecked', session );
835 changes.in_queue = unchecked
837 for f in changes.pkg.files:
838 move_file_to_queue(unchecked, f)
840 # actually move files
841 changes.move_to_queue(unchecked)
844 if Options["No-Action"]:
846 (summary, short_summary) = upload.build_summaries()
847 # upload.accept(summary, short_summary, targetqueue)
848 # os.unlink(upload.pkg.changes_file[:-8]+".dak")
849 changes_to_unchecked(upload)
851 def do_accept(upload):
854 if not Options["No-Action"]:
855 (summary, short_summary) = upload.build_summaries()
857 if cnf.FindB("Dinstall::SecurityQueueHandling"):
858 upload.dump_vars(cnf["Dir::Queue::Embargoed"])
859 upload.move_to_queue(get_policy_queue('embargoed'))
860 upload.queue_build("embargoed", cnf["Dir::Queue::Embargoed"])
861 # Check for override disparities
862 upload.Subst["__SUMMARY__"] = summary
864 # Just a normal upload, accept it...
867 def do_pkg(changes_file, session):
868 new_queue = get_policy_queue('new', session );
870 u.pkg.changes_file = changes_file
871 u.load_changes(changes_file)
872 u.pkg.directory = new_queue.path
874 origchanges = os.path.abspath(u.pkg.changes_file)
877 bcc = "X-DAK: dak process-new"
878 if cnf.has_key("Dinstall::Bcc"):
879 u.Subst["__BCC__"] = bcc + "\nBcc: %s" % (cnf["Dinstall::Bcc"])
881 u.Subst["__BCC__"] = bcc
886 with lock_package(u.pkg.changes["source"]):
887 if not recheck(u, session):
892 # (new, byhand) = check_status(files)
897 # do_byhand(u, session)
898 # (new, byhand) = check_status(files)
900 # if not new and not byhand:
904 # except CantGetLockError:
905 # print "Hello? Operator! Give me the number for 911!"
906 # print "Dinstall in the locked area, cant process packages, come back later"
907 except AlreadyLockedError, e:
908 print "Seems to be locked by %s already, skipping..." % (e)
910 ################################################################################
913 accept_count = SummaryStats().accept_count
914 accept_bytes = SummaryStats().accept_bytes
920 sys.stderr.write("Accepted %d package %s, %s.\n" % (accept_count, sets, utils.size_type(int(accept_bytes))))
921 Logger.log(["total",accept_count,accept_bytes])
923 if not Options["No-Action"] and not Options["Trainee"]:
926 ################################################################################
929 global Options, Logger, Sections, Priorities
932 session = DBConn().session()
934 Arguments = [('a',"automatic","Process-New::Options::Automatic"),
935 ('h',"help","Process-New::Options::Help"),
936 ('m',"manual-reject","Process-New::Options::Manual-Reject", "HasArg"),
937 ('t',"trainee","Process-New::Options::Trainee"),
938 ('n',"no-action","Process-New::Options::No-Action")]
940 for i in ["automatic", "help", "manual-reject", "no-action", "version", "trainee"]:
941 if not cnf.has_key("Process-New::Options::%s" % (i)):
942 cnf["Process-New::Options::%s" % (i)] = ""
944 changes_files = apt_pkg.ParseCommandLine(cnf.Cnf,Arguments,sys.argv)
945 if len(changes_files) == 0:
946 new_queue = get_policy_queue('new', session );
947 changes_files = utils.get_changes_files(new_queue.path)
949 Options = cnf.SubTree("Process-New::Options")
954 if not Options["No-Action"]:
956 Logger = daklog.Logger(cnf, "process-new")
957 except CantOpenError, e:
958 Options["Trainee"] = "True"
960 Sections = Section_Completer(session)
961 Priorities = Priority_Completer(session)
962 readline.parse_and_bind("tab: complete")
964 if len(changes_files) > 1:
965 sys.stderr.write("Sorting changes...\n")
966 changes_files = sort_changes(changes_files, session)
968 # Kill me now? **FIXME**
969 cnf["Dinstall::Options::No-Mail"] = ""
971 for changes_file in changes_files:
972 changes_file = utils.validate_changes_file_arg(changes_file, 0)
975 print "\n" + changes_file
977 do_pkg (changes_file, session)
981 ################################################################################
983 if __name__ == '__main__':