]> git.decadent.org.uk Git - dak.git/blob - dak/process_new.py
Merge remote branch 'drkranz/master' into merge
[dak.git] / dak / process_new.py
1 #!/usr/bin/env python
2 # vim:set et ts=4 sw=4:
3
4 """ Handles NEW and BYHAND packages
5
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
11 """
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.
16
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.
21
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
25
26 ################################################################################
27
28 # 23:12|<aj> I will not hush!
29 # 23:12|<elmo> :>
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!!!
42
43 ################################################################################
44
45 from __future__ import with_statement
46
47 import copy
48 import errno
49 import os
50 import readline
51 import stat
52 import sys
53 import time
54 import contextlib
55 import pwd
56 import apt_pkg, apt_inst
57 import examine_package
58
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, re_package
64 from daklib.dak_exceptions import CantOpenError, AlreadyLockedError, CantGetLockError
65 from daklib.summarystats import SummaryStats
66 from daklib.config import Config
67 from daklib.changesutils import *
68
69 # Globals
70 Options = None
71 Logger = None
72
73 Priorities = None
74 Sections = None
75
76 ################################################################################
77 ################################################################################
78 ################################################################################
79
80 def recheck(upload, session):
81 # STU: I'm not sure, but I don't thin kthis is necessary any longer:    upload.recheck(session)
82     if len(upload.rejects) > 0:
83         answer = "XXX"
84         if Options["No-Action"] or Options["Automatic"] or Options["Trainee"]:
85             answer = 'S'
86
87         print "REJECT\n%s" % '\n'.join(upload.rejects)
88         prompt = "[R]eject, Skip, Quit ?"
89
90         while prompt.find(answer) == -1:
91             answer = utils.our_raw_input(prompt)
92             m = re_default_answer.match(prompt)
93             if answer == "":
94                 answer = m.group(1)
95             answer = answer[:1].upper()
96
97         if answer == 'R':
98             upload.do_reject(manual=0, reject_message='\n'.join(upload.rejects))
99             upload.pkg.remove_known_changes(session=session)
100             session.commit()
101             return 0
102         elif answer == 'S':
103             return 0
104         elif answer == 'Q':
105             end()
106             sys.exit(0)
107
108     return 1
109
110 ################################################################################
111
112 class Section_Completer:
113     def __init__ (self, session):
114         self.sections = []
115         self.matches = []
116         for s, in session.query(Section.section):
117             self.sections.append(s)
118
119     def complete(self, text, state):
120         if state == 0:
121             self.matches = []
122             n = len(text)
123             for word in self.sections:
124                 if word[:n] == text:
125                     self.matches.append(word)
126         try:
127             return self.matches[state]
128         except IndexError:
129             return None
130
131 ############################################################
132
133 class Priority_Completer:
134     def __init__ (self, session):
135         self.priorities = []
136         self.matches = []
137         for p, in session.query(Priority.priority):
138             self.priorities.append(p)
139
140     def complete(self, text, state):
141         if state == 0:
142             self.matches = []
143             n = len(text)
144             for word in self.priorities:
145                 if word[:n] == text:
146                     self.matches.append(word)
147         try:
148             return self.matches[state]
149         except IndexError:
150             return None
151
152 ################################################################################
153
154 def print_new (new, upload, indexed, file=sys.stdout):
155     check_valid(new)
156     broken = False
157     index = 0
158     for pkg in new.keys():
159         index += 1
160         section = new[pkg]["section"]
161         priority = new[pkg]["priority"]
162         if new[pkg]["section id"] == -1:
163             section += "[!]"
164             broken = True
165         if new[pkg]["priority id"] == -1:
166             priority += "[!]"
167             broken = True
168         if indexed:
169             line = "(%s): %-20s %-20s %-20s" % (index, pkg, priority, section)
170         else:
171             line = "%-20s %-20s %-20s" % (pkg, priority, section)
172         line = line.strip()+'\n'
173         file.write(line)
174     notes = get_new_comments(upload.pkg.changes.get("source"))
175     for note in notes:
176         print "\nAuthor: %s\nVersion: %s\nTimestamp: %s\n\n%s" \
177               % (note.author, note.version, note.notedate, note.comment)
178         print "-" * 72
179     return broken, len(notes) > 0
180
181 ################################################################################
182
183 def index_range (index):
184     if index == 1:
185         return "1"
186     else:
187         return "1-%s" % (index)
188
189 ################################################################################
190 ################################################################################
191
192 def edit_new (new, upload):
193     # Write the current data to a temporary file
194     (fd, temp_filename) = utils.temp_filename()
195     temp_file = os.fdopen(fd, 'w')
196     print_new (new, upload, indexed=0, file=temp_file)
197     temp_file.close()
198     # Spawn an editor on that file
199     editor = os.environ.get("EDITOR","vi")
200     result = os.system("%s %s" % (editor, temp_filename))
201     if result != 0:
202         utils.fubar ("%s invocation failed for %s." % (editor, temp_filename), result)
203     # Read the edited data back in
204     temp_file = utils.open_file(temp_filename)
205     lines = temp_file.readlines()
206     temp_file.close()
207     os.unlink(temp_filename)
208     # Parse the new data
209     for line in lines:
210         line = line.strip()
211         if line == "":
212             continue
213         s = line.split()
214         # Pad the list if necessary
215         s[len(s):3] = [None] * (3-len(s))
216         (pkg, priority, section) = s[:3]
217         if not new.has_key(pkg):
218             utils.warn("Ignoring unknown package '%s'" % (pkg))
219         else:
220             # Strip off any invalid markers, print_new will readd them.
221             if section.endswith("[!]"):
222                 section = section[:-3]
223             if priority.endswith("[!]"):
224                 priority = priority[:-3]
225             for f in new[pkg]["files"]:
226                 upload.pkg.files[f]["section"] = section
227                 upload.pkg.files[f]["priority"] = priority
228             new[pkg]["section"] = section
229             new[pkg]["priority"] = priority
230
231 ################################################################################
232
233 def edit_index (new, upload, index):
234     priority = new[index]["priority"]
235     section = new[index]["section"]
236     ftype = new[index]["type"]
237     done = 0
238     while not done:
239         print "\t".join([index, priority, section])
240
241         answer = "XXX"
242         if ftype != "dsc":
243             prompt = "[B]oth, Priority, Section, Done ? "
244         else:
245             prompt = "[S]ection, Done ? "
246         edit_priority = edit_section = 0
247
248         while prompt.find(answer) == -1:
249             answer = utils.our_raw_input(prompt)
250             m = re_default_answer.match(prompt)
251             if answer == "":
252                 answer = m.group(1)
253             answer = answer[:1].upper()
254
255         if answer == 'P':
256             edit_priority = 1
257         elif answer == 'S':
258             edit_section = 1
259         elif answer == 'B':
260             edit_priority = edit_section = 1
261         elif answer == 'D':
262             done = 1
263
264         # Edit the priority
265         if edit_priority:
266             readline.set_completer(Priorities.complete)
267             got_priority = 0
268             while not got_priority:
269                 new_priority = utils.our_raw_input("New priority: ").strip()
270                 if new_priority not in Priorities.priorities:
271                     print "E: '%s' is not a valid priority, try again." % (new_priority)
272                 else:
273                     got_priority = 1
274                     priority = new_priority
275
276         # Edit the section
277         if edit_section:
278             readline.set_completer(Sections.complete)
279             got_section = 0
280             while not got_section:
281                 new_section = utils.our_raw_input("New section: ").strip()
282                 if new_section not in Sections.sections:
283                     print "E: '%s' is not a valid section, try again." % (new_section)
284                 else:
285                     got_section = 1
286                     section = new_section
287
288         # Reset the readline completer
289         readline.set_completer(None)
290
291     for f in new[index]["files"]:
292         upload.pkg.files[f]["section"] = section
293         upload.pkg.files[f]["priority"] = priority
294     new[index]["priority"] = priority
295     new[index]["section"] = section
296     return new
297
298 ################################################################################
299
300 def edit_overrides (new, upload, session):
301     print
302     done = 0
303     while not done:
304         print_new (new, upload, indexed=1)
305         new_index = {}
306         index = 0
307         for i in new.keys():
308             index += 1
309             new_index[index] = i
310
311         prompt = "(%s) edit override <n>, Editor, Done ? " % (index_range(index))
312
313         got_answer = 0
314         while not got_answer:
315             answer = utils.our_raw_input(prompt)
316             if not answer.isdigit():
317                 answer = answer[:1].upper()
318             if answer == "E" or answer == "D":
319                 got_answer = 1
320             elif re_isanum.match (answer):
321                 answer = int(answer)
322                 if (answer < 1) or (answer > index):
323                     print "%s is not a valid index (%s).  Please retry." % (answer, index_range(index))
324                 else:
325                     got_answer = 1
326
327         if answer == 'E':
328             edit_new(new, upload)
329         elif answer == 'D':
330             done = 1
331         else:
332             edit_index (new, upload, new_index[answer])
333
334     return new
335
336 ################################################################################
337
338 def edit_note(note, upload, session):
339     # Write the current data to a temporary file
340     (fd, temp_filename) = utils.temp_filename()
341     editor = os.environ.get("EDITOR","vi")
342     answer = 'E'
343     while answer == 'E':
344         os.system("%s %s" % (editor, temp_filename))
345         temp_file = utils.open_file(temp_filename)
346         newnote = temp_file.read().rstrip()
347         temp_file.close()
348         print "New Note:"
349         print utils.prefix_multi_line_string(newnote,"  ")
350         prompt = "[D]one, Edit, Abandon, Quit ?"
351         answer = "XXX"
352         while prompt.find(answer) == -1:
353             answer = utils.our_raw_input(prompt)
354             m = re_default_answer.search(prompt)
355             if answer == "":
356                 answer = m.group(1)
357             answer = answer[:1].upper()
358     os.unlink(temp_filename)
359     if answer == 'A':
360         return
361     elif answer == 'Q':
362         end()
363         sys.exit(0)
364
365     comment = NewComment()
366     comment.package = upload.pkg.changes["source"]
367     comment.version = upload.pkg.changes["version"]
368     comment.comment = newnote
369     comment.author  = utils.whoami()
370     comment.trainee = bool(Options["Trainee"])
371     session.add(comment)
372     session.commit()
373
374 ################################################################################
375
376 def check_pkg (upload):
377     try:
378         less_fd = os.popen("less -R -", 'w', 0)
379         stdout_fd = sys.stdout
380         try:
381             sys.stdout = less_fd
382             changes = utils.parse_changes (upload.pkg.changes_file)
383             print examine_package.display_changes(changes['distribution'], upload.pkg.changes_file)
384             files = upload.pkg.files
385             for f in files.keys():
386                 if files[f].has_key("new"):
387                     ftype = files[f]["type"]
388                     if ftype == "deb":
389                         print examine_package.check_deb(changes['distribution'], f)
390                     elif ftype == "dsc":
391                         print examine_package.check_dsc(changes['distribution'], f)
392         finally:
393             print examine_package.output_package_relations()
394             sys.stdout = stdout_fd
395     except IOError, e:
396         if e.errno == errno.EPIPE:
397             utils.warn("[examine_package] Caught EPIPE; skipping.")
398             pass
399         else:
400             raise
401     except KeyboardInterrupt:
402         utils.warn("[examine_package] Caught C-c; skipping.")
403         pass
404
405 ################################################################################
406
407 ## FIXME: horribly Debian specific
408
409 def do_bxa_notification(upload):
410     files = upload.pkg.files
411     summary = ""
412     for f in files.keys():
413         if files[f]["type"] == "deb":
414             control = apt_pkg.ParseSection(apt_inst.debExtractControl(utils.open_file(f)))
415             summary += "\n"
416             summary += "Package: %s\n" % (control.Find("Package"))
417             summary += "Description: %s\n" % (control.Find("Description"))
418     upload.Subst["__BINARY_DESCRIPTIONS__"] = summary
419     bxa_mail = utils.TemplateSubst(upload.Subst,Config()["Dir::Templates"]+"/process-new.bxa_notification")
420     utils.send_mail(bxa_mail)
421
422 ################################################################################
423
424 def add_overrides (new, upload, session):
425     changes = upload.pkg.changes
426     files = upload.pkg.files
427     srcpkg = changes.get("source")
428
429     for suite in changes["suite"].keys():
430         suite_id = get_suite(suite).suite_id
431         for pkg in new.keys():
432             component_id = get_component(new[pkg]["component"]).component_id
433             type_id = get_override_type(new[pkg]["type"]).overridetype_id
434             priority_id = new[pkg]["priority id"]
435             section_id = new[pkg]["section id"]
436             Logger.log(["%s overrides" % (srcpkg), suite, new[pkg]["component"], new[pkg]["type"], new[pkg]["priority"], new[pkg]["section"]])
437             session.execute("INSERT INTO override (suite, component, type, package, priority, section, maintainer) VALUES (:sid, :cid, :tid, :pkg, :pid, :sectid, '')",
438                             { 'sid': suite_id, 'cid': component_id, 'tid':type_id, 'pkg': pkg, 'pid': priority_id, 'sectid': section_id})
439             for f in new[pkg]["files"]:
440                 if files[f].has_key("new"):
441                     del files[f]["new"]
442             del new[pkg]
443
444     session.commit()
445
446     if Config().FindB("Dinstall::BXANotify"):
447         do_bxa_notification(upload)
448
449 ################################################################################
450
451 def prod_maintainer (notes, upload):
452     cnf = Config()
453     # Here we prepare an editor and get them ready to prod...
454     (fd, temp_filename) = utils.temp_filename()
455     temp_file = os.fdopen(fd, 'w')
456     for note in notes:
457         temp_file.write(note.comment)
458     temp_file.close()
459     editor = os.environ.get("EDITOR","vi")
460     answer = 'E'
461     while answer == 'E':
462         os.system("%s %s" % (editor, temp_filename))
463         temp_fh = utils.open_file(temp_filename)
464         prod_message = "".join(temp_fh.readlines())
465         temp_fh.close()
466         print "Prod message:"
467         print utils.prefix_multi_line_string(prod_message,"  ",include_blank_lines=1)
468         prompt = "[P]rod, Edit, Abandon, Quit ?"
469         answer = "XXX"
470         while prompt.find(answer) == -1:
471             answer = utils.our_raw_input(prompt)
472             m = re_default_answer.search(prompt)
473             if answer == "":
474                 answer = m.group(1)
475             answer = answer[:1].upper()
476     os.unlink(temp_filename)
477     if answer == 'A':
478         return
479     elif answer == 'Q':
480         end()
481         sys.exit(0)
482     # Otherwise, do the proding...
483     user_email_address = utils.whoami() + " <%s>" % (
484         cnf["Dinstall::MyAdminAddress"])
485
486     Subst = upload.Subst
487
488     Subst["__FROM_ADDRESS__"] = user_email_address
489     Subst["__PROD_MESSAGE__"] = prod_message
490     Subst["__CC__"] = "Cc: " + cnf["Dinstall::MyEmailAddress"]
491
492     prod_mail_message = utils.TemplateSubst(
493         Subst,cnf["Dir::Templates"]+"/process-new.prod")
494
495     # Send the prod mail
496     utils.send_mail(prod_mail_message)
497
498     print "Sent proding message"
499
500 ################################################################################
501
502 def do_new(upload, session):
503     print "NEW\n"
504     files = upload.pkg.files
505     upload.check_files(not Options["No-Action"])
506     changes = upload.pkg.changes
507     cnf = Config()
508
509     # Check for a valid distribution
510     upload.check_distributions()
511
512     # Make a copy of distribution we can happily trample on
513     changes["suite"] = copy.copy(changes["distribution"])
514
515     # The main NEW processing loop
516     done = 0
517     while not done:
518         # Find out what's new
519         new = determine_new(changes, files)
520
521         if not new:
522             break
523
524         answer = "XXX"
525         if Options["No-Action"] or Options["Automatic"]:
526             answer = 'S'
527
528         (broken, note) = print_new(new, upload, indexed=0)
529         prompt = ""
530
531         if not broken and not note:
532             prompt = "Add overrides, "
533         if broken:
534             print "W: [!] marked entries must be fixed before package can be processed."
535         if note:
536             print "W: note must be removed before package can be processed."
537             prompt += "RemOve all notes, Remove note, "
538
539         prompt += "Edit overrides, Check, Manual reject, Note edit, Prod, [S]kip, Quit ?"
540
541         while prompt.find(answer) == -1:
542             answer = utils.our_raw_input(prompt)
543             m = re_default_answer.search(prompt)
544             if answer == "":
545                 answer = m.group(1)
546             answer = answer[:1].upper()
547
548         if answer in ( 'A', 'E', 'M', 'O', 'R' ) and Options["Trainee"]:
549             utils.warn("Trainees can't do that")
550             continue
551
552         if answer == 'A' and not Options["Trainee"]:
553             try:
554                 check_daily_lock()
555                 done = add_overrides (new, upload, session)
556                 new_accept(upload, Options["No-Action"], session)
557                 Logger.log(["NEW ACCEPT: %s" % (upload.pkg.changes_file)])
558             except CantGetLockError:
559                 print "Hello? Operator! Give me the number for 911!"
560                 print "Dinstall in the locked area, cant process packages, come back later"
561         elif answer == 'C':
562             check_pkg(upload)
563         elif answer == 'E' and not Options["Trainee"]:
564             new = edit_overrides (new, upload, session)
565         elif answer == 'M' and not Options["Trainee"]:
566             aborted = upload.do_reject(manual=1,
567                                        reject_message=Options["Manual-Reject"],
568                                        notes=get_new_comments(changes.get("source", ""), session=session))
569             if not aborted:
570                 upload.pkg.remove_known_changes(session=session)
571                 session.commit()
572                 Logger.log(["NEW REJECT: %s" % (upload.pkg.changes_file)])
573                 done = 1
574         elif answer == 'N':
575             edit_note(get_new_comments(changes.get("source", ""), session=session),
576                       upload, session)
577         elif answer == 'P' and not Options["Trainee"]:
578             prod_maintainer(get_new_comments(changes.get("source", ""), session=session),
579                             upload)
580             Logger.log(["NEW PROD: %s" % (upload.pkg.changes_file)])
581         elif answer == 'R' and not Options["Trainee"]:
582             confirm = utils.our_raw_input("Really clear note (y/N)? ").lower()
583             if confirm == "y":
584                 for c in get_new_comments(changes.get("source", ""), changes.get("version", ""), session=session):
585                     session.delete(c)
586                 session.commit()
587         elif answer == 'O' and not Options["Trainee"]:
588             confirm = utils.our_raw_input("Really clear all notes (y/N)? ").lower()
589             if confirm == "y":
590                 for c in get_new_comments(changes.get("source", ""), session=session):
591                     session.delete(c)
592                 session.commit()
593
594         elif answer == 'S':
595             done = 1
596         elif answer == 'Q':
597             end()
598             sys.exit(0)
599
600 ################################################################################
601 ################################################################################
602 ################################################################################
603
604 def usage (exit_code=0):
605     print """Usage: dak process-new [OPTION]... [CHANGES]...
606   -a, --automatic           automatic run
607   -h, --help                show this help and exit.
608   -m, --manual-reject=MSG   manual reject with `msg'
609   -n, --no-action           don't do anything
610   -t, --trainee             FTP Trainee mode
611   -V, --version             display the version number and exit"""
612     sys.exit(exit_code)
613
614 ################################################################################
615
616 def do_byhand(upload, session):
617     done = 0
618     while not done:
619         files = upload.pkg.files
620         will_install = 1
621         byhand = []
622
623         for f in files.keys():
624             if files[f]["type"] == "byhand":
625                 if os.path.exists(f):
626                     print "W: %s still present; please process byhand components and try again." % (f)
627                     will_install = 0
628                 else:
629                     byhand.append(f)
630
631         answer = "XXXX"
632         if Options["No-Action"]:
633             answer = "S"
634         if will_install:
635             if Options["Automatic"] and not Options["No-Action"]:
636                 answer = 'A'
637             prompt = "[A]ccept, Manual reject, Skip, Quit ?"
638         else:
639             prompt = "Manual reject, [S]kip, Quit ?"
640
641         while prompt.find(answer) == -1:
642             answer = utils.our_raw_input(prompt)
643             m = re_default_answer.search(prompt)
644             if answer == "":
645                 answer = m.group(1)
646             answer = answer[:1].upper()
647
648         if answer == 'A':
649             try:
650                 check_daily_lock()
651                 done = 1
652                 for f in byhand:
653                     del files[f]
654                 Logger.log(["BYHAND ACCEPT: %s" % (upload.pkg.changes_file)])
655             except CantGetLockError:
656                 print "Hello? Operator! Give me the number for 911!"
657                 print "Dinstall in the locked area, cant process packages, come back later"
658         elif answer == 'M':
659             Logger.log(["BYHAND REJECT: %s" % (upload.pkg.changes_file)])
660             upload.do_reject(manual=1, reject_message=Options["Manual-Reject"])
661             upload.pkg.remove_known_changes(session=session)
662             session.commit()
663             done = 1
664         elif answer == 'S':
665             done = 1
666         elif answer == 'Q':
667             end()
668             sys.exit(0)
669
670 ################################################################################
671
672 def check_daily_lock():
673     """
674     Raises CantGetLockError if the dinstall daily.lock exists.
675     """
676
677     cnf = Config()
678     try:
679         os.open(cnf["Process-New::DinstallLockFile"],
680                 os.O_RDONLY | os.O_CREAT | os.O_EXCL)
681     except OSError, e:
682         if e.errno == errno.EEXIST or e.errno == errno.EACCES:
683             raise CantGetLockError
684
685     os.unlink(cnf["Process-New::DinstallLockFile"])
686
687
688 @contextlib.contextmanager
689 def lock_package(package):
690     """
691     Lock C{package} so that noone else jumps in processing it.
692
693     @type package: string
694     @param package: source package name to lock
695     """
696
697     path = os.path.join(Config()["Process-New::LockDir"], package)
698     try:
699         fd = os.open(path, os.O_CREAT | os.O_EXCL | os.O_RDONLY)
700     except OSError, e:
701         if e.errno == errno.EEXIST or e.errno == errno.EACCES:
702             user = pwd.getpwuid(os.stat(path)[stat.ST_UID])[4].split(',')[0].replace('.', '')
703             raise AlreadyLockedError, user
704
705     try:
706         yield fd
707     finally:
708         os.unlink(path)
709
710 class clean_holding(object):
711     def __init__(self,pkg):
712         self.pkg = pkg
713
714     def __enter__(self):
715         pass
716
717     def __exit__(self, type, value, traceback):
718         h = Holding()
719
720         for f in self.pkg.files.keys():
721             if os.path.exists(os.path.join(h.holding_dir, f)):
722                 os.unlink(os.path.join(h.holding_dir, f))
723
724
725 def do_pkg(changes_file, session):
726     new_queue = get_policy_queue('new', session );
727     u = Upload()
728     u.pkg.changes_file = changes_file
729     (u.pkg.changes["fingerprint"], rejects) = utils.check_signature(changes_file)
730     u.load_changes(changes_file)
731     u.pkg.directory = new_queue.path
732     u.update_subst()
733     u.logger = Logger
734     origchanges = os.path.abspath(u.pkg.changes_file)
735
736     cnf = Config()
737     bcc = "X-DAK: dak process-new"
738     if cnf.has_key("Dinstall::Bcc"):
739         u.Subst["__BCC__"] = bcc + "\nBcc: %s" % (cnf["Dinstall::Bcc"])
740     else:
741         u.Subst["__BCC__"] = bcc
742
743     files = u.pkg.files
744     for deb_filename, f in files.items():
745         if deb_filename.endswith(".udeb") or deb_filename.endswith(".deb"):
746             u.binary_file_checks(deb_filename, session)
747             u.check_binary_against_db(deb_filename, session)
748         else:
749             u.source_file_checks(deb_filename, session)
750             u.check_source_against_db(deb_filename, session)
751
752         u.pkg.changes["suite"] = copy.copy(u.pkg.changes["distribution"])
753
754     try:
755         with lock_package(u.pkg.changes["source"]):
756             with clean_holding(u.pkg):
757                 if not recheck(u, session):
758                     return
759
760                 # FIXME: This does need byhand checks added!
761                 new = determine_new(u.pkg.changes, files)
762                 if new:
763                     do_new(u, session)
764                 else:
765                     try:
766                         check_daily_lock()
767                         new_accept(u, Options["No-Action"], session)
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"
771
772     except AlreadyLockedError, e:
773         print "Seems to be locked by %s already, skipping..." % (e)
774
775 ################################################################################
776
777 def end():
778     accept_count = SummaryStats().accept_count
779     accept_bytes = SummaryStats().accept_bytes
780
781     if accept_count:
782         sets = "set"
783         if accept_count > 1:
784             sets = "sets"
785         sys.stderr.write("Accepted %d package %s, %s.\n" % (accept_count, sets, utils.size_type(int(accept_bytes))))
786         Logger.log(["total",accept_count,accept_bytes])
787
788     if not Options["No-Action"] and not Options["Trainee"]:
789         Logger.close()
790
791 ################################################################################
792
793 def main():
794     global Options, Logger, Sections, Priorities
795
796     cnf = Config()
797     session = DBConn().session()
798
799     Arguments = [('a',"automatic","Process-New::Options::Automatic"),
800                  ('h',"help","Process-New::Options::Help"),
801                  ('m',"manual-reject","Process-New::Options::Manual-Reject", "HasArg"),
802                  ('t',"trainee","Process-New::Options::Trainee"),
803                  ('n',"no-action","Process-New::Options::No-Action")]
804
805     for i in ["automatic", "help", "manual-reject", "no-action", "version", "trainee"]:
806         if not cnf.has_key("Process-New::Options::%s" % (i)):
807             cnf["Process-New::Options::%s" % (i)] = ""
808
809     changes_files = apt_pkg.ParseCommandLine(cnf.Cnf,Arguments,sys.argv)
810     if len(changes_files) == 0:
811         new_queue = get_policy_queue('new', session );
812         changes_files = utils.get_changes_files(new_queue.path)
813
814     Options = cnf.SubTree("Process-New::Options")
815
816     if Options["Help"]:
817         usage()
818
819     if not Options["No-Action"]:
820         try:
821             Logger = daklog.Logger(cnf, "process-new")
822         except CantOpenError, e:
823             Options["Trainee"] = "True"
824
825     Sections = Section_Completer(session)
826     Priorities = Priority_Completer(session)
827     readline.parse_and_bind("tab: complete")
828
829     if len(changes_files) > 1:
830         sys.stderr.write("Sorting changes...\n")
831     changes_files = sort_changes(changes_files, session)
832
833     for changes_file in changes_files:
834         changes_file = utils.validate_changes_file_arg(changes_file, 0)
835         if not changes_file:
836             continue
837         print "\n" + changes_file
838
839         do_pkg (changes_file, session)
840
841     end()
842
843 ################################################################################
844
845 if __name__ == '__main__':
846     main()