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 @license: GNU General Public License version 2 or later
11 # This program is free software; you can redistribute it and/or modify
12 # it under the terms of the GNU General Public License as published by
13 # the Free Software Foundation; either version 2 of the License, or
14 # (at your option) any later version.
16 # This program is distributed in the hope that it will be useful,
17 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 # GNU General Public License for more details.
21 # You should have received a copy of the GNU General Public License
22 # along with this program; if not, write to the Free Software
23 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 ################################################################################
27 # 23:12|<aj> I will not hush!
29 # 23:12|<aj> Where there is injustice in the world, I shall be there!
30 # 23:13|<aj> I shall not be silenced!
31 # 23:13|<aj> The world shall know!
32 # 23:13|<aj> The world *must* know!
33 # 23:13|<elmo> oh dear, he's gone back to powerpuff girls... ;-)
34 # 23:13|<aj> yay powerpuff girls!!
35 # 23:13|<aj> buttercup's my favourite, who's yours?
36 # 23:14|<aj> you're backing away from the keyboard right now aren't you?
37 # 23:14|<aj> *AREN'T YOU*?!
38 # 23:15|<aj> I will not be treated like this.
39 # 23:15|<aj> I shall have my revenge.
40 # 23:15|<aj> I SHALL!!!
42 ################################################################################
44 from __future__ import with_statement
55 import apt_pkg, apt_inst
56 import examine_package
58 from daklib import database
59 from daklib import daklog
60 from daklib import queue
61 from daklib import utils
62 from daklib.regexes import re_no_epoch, re_default_answer, re_isanum
63 from daklib.dak_exceptions import CantOpenError, AlreadyLockedError, CantGetLockError
64 from daklib.summarystats import SummaryStats
67 Cnf = None #: Configuration, apt_pkg.Configuration
70 projectB = None #: database connection, pgobject
78 ################################################################################
79 ################################################################################
80 ################################################################################
82 def reject (str, prefix="Rejected: "):
85 reject_message += prefix + str + "\n"
89 files = Upload.pkg.files
92 for f in files.keys():
93 # The .orig.tar.gz can disappear out from under us is it's a
94 # duplicate of one in the archive.
95 if not files.has_key(f):
97 # Check that the source still exists
98 if files[f]["type"] == "deb":
99 source_version = files[f]["source version"]
100 source_package = files[f]["source package"]
101 if not Upload.pkg.changes["architecture"].has_key("source") \
102 and not Upload.source_exists(source_package, source_version, Upload.pkg.changes["distribution"].keys()):
103 source_epochless_version = re_no_epoch.sub('', source_version)
104 dsc_filename = "%s_%s.dsc" % (source_package, source_epochless_version)
106 for q in ["Accepted", "Embargoed", "Unembargoed", "Newstage"]:
107 if Cnf.has_key("Dir::Queue::%s" % (q)):
108 if os.path.exists(Cnf["Dir::Queue::%s" % (q)] + '/' + dsc_filename):
111 reject("no source found for %s %s (%s)." % (source_package, source_version, f))
113 # Version and file overwrite checks
114 if files[f]["type"] == "deb":
115 reject(Upload.check_binary_against_db(f), "")
116 elif files[f]["type"] == "dsc":
117 reject(Upload.check_source_against_db(f), "")
118 (reject_msg, is_in_incoming) = Upload.check_dsc_against_db(f)
119 reject(reject_msg, "")
121 if reject_message.find("Rejected") != -1:
123 if Options["No-Action"] or Options["Automatic"] or Options["Trainee"]:
126 print "REJECT\n" + reject_message,
127 prompt = "[R]eject, Skip, Quit ?"
129 while prompt.find(answer) == -1:
130 answer = utils.our_raw_input(prompt)
131 m = re_default_answer.match(prompt)
134 answer = answer[:1].upper()
137 Upload.do_reject(0, reject_message)
138 os.unlink(Upload.pkg.changes_file[:-8]+".dak")
148 ################################################################################
150 def indiv_sg_compare (a, b):
151 """Sort by source name, source, version, 'have source', and
152 finally by filename."""
153 # Sort by source version
154 q = apt_pkg.VersionCompare(a["version"], b["version"])
158 # Sort by 'have source'
159 a_has_source = a["architecture"].get("source")
160 b_has_source = b["architecture"].get("source")
161 if a_has_source and not b_has_source:
163 elif b_has_source and not a_has_source:
166 return cmp(a["filename"], b["filename"])
168 ############################################################
170 def sg_compare (a, b):
173 """Sort by have note, source already in database and time of oldest upload."""
175 a_note_state = a["note_state"]
176 b_note_state = b["note_state"]
177 if a_note_state < b_note_state:
179 elif a_note_state > b_note_state:
181 # Sort by source already in database (descending)
182 source_in_database = cmp(a["source_in_database"], b["source_in_database"])
183 if source_in_database:
184 return -source_in_database
186 # Sort by time of oldest upload
187 return cmp(a["oldest"], b["oldest"])
189 def sort_changes(changes_files):
190 """Sort into source groups, then sort each source group by version,
191 have source, filename. Finally, sort the source groups by have
192 note, time of oldest upload of each source upload."""
193 if len(changes_files) == 1:
198 # Read in all the .changes files
199 for filename in changes_files:
201 Upload.pkg.changes_file = filename
204 cache[filename] = copy.copy(Upload.pkg.changes)
205 cache[filename]["filename"] = filename
207 sorted_list.append(filename)
209 # Divide the .changes into per-source groups
211 for filename in cache.keys():
212 source = cache[filename]["source"]
213 if not per_source.has_key(source):
214 per_source[source] = {}
215 per_source[source]["list"] = []
216 per_source[source]["list"].append(cache[filename])
217 # Determine oldest time and have note status for each source group
218 for source in per_source.keys():
219 q = projectB.query("SELECT 1 FROM source WHERE source = '%s'" % source)
221 per_source[source]["source_in_database"] = len(ql)>0
222 source_list = per_source[source]["list"]
223 first = source_list[0]
224 oldest = os.stat(first["filename"])[stat.ST_MTIME]
226 for d in per_source[source]["list"]:
227 mtime = os.stat(d["filename"])[stat.ST_MTIME]
230 have_note += (database.has_new_comment(d["source"], d["version"], True))
231 per_source[source]["oldest"] = oldest
233 per_source[source]["note_state"] = 0; # none
234 elif have_note < len(source_list):
235 per_source[source]["note_state"] = 1; # some
237 per_source[source]["note_state"] = 2; # all
238 per_source[source]["list"].sort(indiv_sg_compare)
239 per_source_items = per_source.items()
240 per_source_items.sort(sg_compare)
241 for i in per_source_items:
242 for j in i[1]["list"]:
243 sorted_list.append(j["filename"])
246 ################################################################################
248 class Section_Completer:
252 q = projectB.query("SELECT section FROM section")
253 for i in q.getresult():
254 self.sections.append(i[0])
256 def complete(self, text, state):
260 for word in self.sections:
262 self.matches.append(word)
264 return self.matches[state]
268 ############################################################
270 class Priority_Completer:
274 q = projectB.query("SELECT priority FROM priority")
275 for i in q.getresult():
276 self.priorities.append(i[0])
278 def complete(self, text, state):
282 for word in self.priorities:
284 self.matches.append(word)
286 return self.matches[state]
290 ################################################################################
292 def print_new (new, indexed, file=sys.stdout):
293 queue.check_valid(new)
296 for pkg in new.keys():
298 section = new[pkg]["section"]
299 priority = new[pkg]["priority"]
300 if new[pkg]["section id"] == -1:
303 if new[pkg]["priority id"] == -1:
307 line = "(%s): %-20s %-20s %-20s" % (index, pkg, priority, section)
309 line = "%-20s %-20s %-20s" % (pkg, priority, section)
310 line = line.strip()+'\n'
312 note = database.get_new_comments(Upload.pkg.changes.get("source"))
318 ################################################################################
320 def index_range (index):
324 return "1-%s" % (index)
326 ################################################################################
327 ################################################################################
330 # Write the current data to a temporary file
331 (fd, temp_filename) = utils.temp_filename()
332 temp_file = os.fdopen(fd, 'w')
333 print_new (new, 0, temp_file)
335 # Spawn an editor on that file
336 editor = os.environ.get("EDITOR","vi")
337 result = os.system("%s %s" % (editor, temp_filename))
339 utils.fubar ("%s invocation failed for %s." % (editor, temp_filename), result)
340 # Read the edited data back in
341 temp_file = utils.open_file(temp_filename)
342 lines = temp_file.readlines()
344 os.unlink(temp_filename)
351 # Pad the list if necessary
352 s[len(s):3] = [None] * (3-len(s))
353 (pkg, priority, section) = s[:3]
354 if not new.has_key(pkg):
355 utils.warn("Ignoring unknown package '%s'" % (pkg))
357 # Strip off any invalid markers, print_new will readd them.
358 if section.endswith("[!]"):
359 section = section[:-3]
360 if priority.endswith("[!]"):
361 priority = priority[:-3]
362 for f in new[pkg]["files"]:
363 Upload.pkg.files[f]["section"] = section
364 Upload.pkg.files[f]["priority"] = priority
365 new[pkg]["section"] = section
366 new[pkg]["priority"] = priority
368 ################################################################################
370 def edit_index (new, index):
371 priority = new[index]["priority"]
372 section = new[index]["section"]
373 ftype = new[index]["type"]
376 print "\t".join([index, priority, section])
380 prompt = "[B]oth, Priority, Section, Done ? "
382 prompt = "[S]ection, Done ? "
383 edit_priority = edit_section = 0
385 while prompt.find(answer) == -1:
386 answer = utils.our_raw_input(prompt)
387 m = re_default_answer.match(prompt)
390 answer = answer[:1].upper()
397 edit_priority = edit_section = 1
403 readline.set_completer(Priorities.complete)
405 while not got_priority:
406 new_priority = utils.our_raw_input("New priority: ").strip()
407 if new_priority not in Priorities.priorities:
408 print "E: '%s' is not a valid priority, try again." % (new_priority)
411 priority = new_priority
415 readline.set_completer(Sections.complete)
417 while not got_section:
418 new_section = utils.our_raw_input("New section: ").strip()
419 if new_section not in Sections.sections:
420 print "E: '%s' is not a valid section, try again." % (new_section)
423 section = new_section
425 # Reset the readline completer
426 readline.set_completer(None)
428 for f in new[index]["files"]:
429 Upload.pkg.files[f]["section"] = section
430 Upload.pkg.files[f]["priority"] = priority
431 new[index]["priority"] = priority
432 new[index]["section"] = section
435 ################################################################################
437 def edit_overrides (new):
448 prompt = "(%s) edit override <n>, Editor, Done ? " % (index_range(index))
451 while not got_answer:
452 answer = utils.our_raw_input(prompt)
453 if not answer.isdigit():
454 answer = answer[:1].upper()
455 if answer == "E" or answer == "D":
457 elif re_isanum.match (answer):
459 if (answer < 1) or (answer > index):
460 print "%s is not a valid index (%s). Please retry." % (answer, index_range(index))
469 edit_index (new, new_index[answer])
473 ################################################################################
476 # Write the current data to a temporary file
477 (fd, temp_filename) = utils.temp_filename()
478 editor = os.environ.get("EDITOR","vi")
481 os.system("%s %s" % (editor, temp_filename))
482 temp_file = utils.open_file(temp_filename)
483 newnote = temp_file.read().rstrip()
486 print utils.prefix_multi_line_string(newnote," ")
487 prompt = "[D]one, Edit, Abandon, Quit ?"
489 while prompt.find(answer) == -1:
490 answer = utils.our_raw_input(prompt)
491 m = re_default_answer.search(prompt)
494 answer = answer[:1].upper()
495 os.unlink(temp_filename)
502 database.add_new_comment(Upload.pkg.changes["source"], Upload.pkg.changes["version"], newnote, utils.whoami(), bool(Options["Trainee"]))
504 ################################################################################
508 less_fd = os.popen("less -R -", 'w', 0)
509 stdout_fd = sys.stdout
512 changes = utils.parse_changes (Upload.pkg.changes_file)
513 examine_package.display_changes(changes['distribution'], Upload.pkg.changes_file)
514 files = Upload.pkg.files
515 for f in files.keys():
516 if files[f].has_key("new"):
517 ftype = files[f]["type"]
519 examine_package.check_deb(changes['distribution'], f)
521 examine_package.check_dsc(changes['distribution'], f)
523 examine_package.output_package_relations()
524 sys.stdout = stdout_fd
526 if e.errno == errno.EPIPE:
527 utils.warn("[examine_package] Caught EPIPE; skipping.")
531 except KeyboardInterrupt:
532 utils.warn("[examine_package] Caught C-c; skipping.")
535 ################################################################################
537 ## FIXME: horribly Debian specific
539 def do_bxa_notification():
540 files = Upload.pkg.files
542 for f in files.keys():
543 if files[f]["type"] == "deb":
544 control = apt_pkg.ParseSection(apt_inst.debExtractControl(utils.open_file(f)))
546 summary += "Package: %s\n" % (control.Find("Package"))
547 summary += "Description: %s\n" % (control.Find("Description"))
548 Upload.Subst["__BINARY_DESCRIPTIONS__"] = summary
549 bxa_mail = utils.TemplateSubst(Upload.Subst,Cnf["Dir::Templates"]+"/process-new.bxa_notification")
550 utils.send_mail(bxa_mail)
552 ################################################################################
554 def add_overrides (new):
555 changes = Upload.pkg.changes
556 files = Upload.pkg.files
557 srcpkg = changes.get("source")
559 projectB.query("BEGIN WORK")
560 for suite in changes["suite"].keys():
561 suite_id = database.get_suite_id(suite)
562 for pkg in new.keys():
563 component_id = database.get_component_id(new[pkg]["component"])
564 type_id = database.get_override_type_id(new[pkg]["type"])
565 priority_id = new[pkg]["priority id"]
566 section_id = new[pkg]["section id"]
567 Logger.log(["%s overrides" % (srcpkg), suite, new[pkg]["component"], new[pkg]["type"], new[pkg]["priority"], new[pkg]["section"]])
568 projectB.query("INSERT INTO override (suite, component, type, package, priority, section, maintainer) VALUES (%s, %s, %s, '%s', %s, %s, '')" % (suite_id, component_id, type_id, pkg, priority_id, section_id))
569 for f in new[pkg]["files"]:
570 if files[f].has_key("new"):
574 projectB.query("COMMIT WORK")
576 if Cnf.FindB("Dinstall::BXANotify"):
577 do_bxa_notification()
579 ################################################################################
581 def prod_maintainer (note):
582 # Here we prepare an editor and get them ready to prod...
583 (fd, temp_filename) = utils.temp_filename()
584 temp_file = os.fdopen(fd, 'w')
587 temp_file.write(line)
589 editor = os.environ.get("EDITOR","vi")
592 os.system("%s %s" % (editor, temp_filename))
593 temp_fh = utils.open_file(temp_filename)
594 prod_message = "".join(temp_fh.readlines())
596 print "Prod message:"
597 print utils.prefix_multi_line_string(prod_message," ",include_blank_lines=1)
598 prompt = "[P]rod, Edit, Abandon, Quit ?"
600 while prompt.find(answer) == -1:
601 answer = utils.our_raw_input(prompt)
602 m = re_default_answer.search(prompt)
605 answer = answer[:1].upper()
606 os.unlink(temp_filename)
612 # Otherwise, do the proding...
613 user_email_address = utils.whoami() + " <%s>" % (
614 Cnf["Dinstall::MyAdminAddress"])
618 Subst["__FROM_ADDRESS__"] = user_email_address
619 Subst["__PROD_MESSAGE__"] = prod_message
620 Subst["__CC__"] = "Cc: " + Cnf["Dinstall::MyEmailAddress"]
622 prod_mail_message = utils.TemplateSubst(
623 Subst,Cnf["Dir::Templates"]+"/process-new.prod")
625 # Send the prod mail if appropriate
626 if not Cnf["Dinstall::Options::No-Mail"]:
627 utils.send_mail(prod_mail_message)
629 print "Sent proding message"
631 ################################################################################
635 files = Upload.pkg.files
636 changes = Upload.pkg.changes
638 # Make a copy of distribution we can happily trample on
639 changes["suite"] = copy.copy(changes["distribution"])
641 # Fix up the list of target suites
642 for suite in changes["suite"].keys():
643 override = Cnf.Find("Suite::%s::OverrideSuite" % (suite))
645 (olderr, newerr) = (database.get_suite_id(suite) == -1,
646 database.get_suite_id(override) == -1)
648 (oinv, newinv) = ("", "")
649 if olderr: oinv = "invalid "
650 if newerr: ninv = "invalid "
651 print "warning: overriding %ssuite %s to %ssuite %s" % (
652 oinv, suite, ninv, override)
653 del changes["suite"][suite]
654 changes["suite"][override] = 1
656 for suite in changes["suite"].keys():
657 suite_id = database.get_suite_id(suite)
659 utils.fubar("%s has invalid suite '%s' (possibly overriden). say wha?" % (changes, suite))
661 # The main NEW processing loop
664 # Find out what's new
665 new = queue.determine_new(changes, files, projectB)
671 if Options["No-Action"] or Options["Automatic"]:
674 (broken, note) = print_new(new, 0)
677 if not broken and not note:
678 prompt = "Add overrides, "
680 print "W: [!] marked entries must be fixed before package can be processed."
682 print "W: note must be removed before package can be processed."
683 prompt += "RemOve all notes, Remove note, "
685 prompt += "Edit overrides, Check, Manual reject, Note edit, Prod, [S]kip, Quit ?"
687 while prompt.find(answer) == -1:
688 answer = utils.our_raw_input(prompt)
689 m = re_default_answer.search(prompt)
692 answer = answer[:1].upper()
694 if answer == 'A' and not Options["Trainee"]:
697 done = add_overrides (new)
698 Logger.log([utils.getusername(), "NEW ACCEPT: %s" % (Upload.pkg.changes_file)])
699 except CantGetLockError:
700 print "Hello? Operator! Give me the number for 911!"
701 print "Dinstall in the locked area, cant process packages, come back later"
704 elif answer == 'E' and not Options["Trainee"]:
705 new = edit_overrides (new)
706 elif answer == 'M' and not Options["Trainee"]:
707 aborted = Upload.do_reject(manual=1,
708 reject_message=Options["Manual-Reject"],
709 note=database.get_new_comments(changes.get("source", "")))
711 Logger.log([utils.getusername(), "NEW REJECT: %s" % (Upload.pkg.changes_file)])
712 os.unlink(Upload.pkg.changes_file[:-8]+".dak")
715 edit_note(database.get_new_comments(changes.get("source", "")))
716 elif answer == 'P' and not Options["Trainee"]:
717 prod_maintainer(database.get_new_comments(changes.get("source", "")))
718 Logger.log([utils.getusername(), "NEW PROD: %s" % (Upload.pkg.changes_file)])
719 elif answer == 'R' and not Options["Trainee"]:
720 confirm = utils.our_raw_input("Really clear note (y/N)? ").lower()
722 database.delete_new_comments(changes.get("source"), changes.get("version"))
723 elif answer == 'O' and not Options["Trainee"]:
724 confirm = utils.our_raw_input("Really clear all notes (y/N)? ").lower()
726 database.delete_all_new_comments(changes.get("source"))
733 ################################################################################
734 ################################################################################
735 ################################################################################
737 def usage (exit_code=0):
738 print """Usage: dak process-new [OPTION]... [CHANGES]...
739 -a, --automatic automatic run
740 -h, --help show this help and exit.
741 -C, --comments-dir=DIR use DIR as comments-dir, for [o-]p-u-new
742 -m, --manual-reject=MSG manual reject with `msg'
743 -n, --no-action don't do anything
744 -t, --trainee FTP Trainee mode
745 -V, --version display the version number and exit"""
748 ################################################################################
751 global Cnf, Options, Logger, Upload, projectB, Sections, Priorities
753 Cnf = utils.get_conf()
755 Arguments = [('a',"automatic","Process-New::Options::Automatic"),
756 ('h',"help","Process-New::Options::Help"),
757 ('C',"comments-dir","Process-New::Options::Comments-Dir", "HasArg"),
758 ('m',"manual-reject","Process-New::Options::Manual-Reject", "HasArg"),
759 ('t',"trainee","Process-New::Options::Trainee"),
760 ('n',"no-action","Process-New::Options::No-Action")]
762 for i in ["automatic", "help", "manual-reject", "no-action", "version", "comments-dir", "trainee"]:
763 if not Cnf.has_key("Process-New::Options::%s" % (i)):
764 Cnf["Process-New::Options::%s" % (i)] = ""
766 changes_files = apt_pkg.ParseCommandLine(Cnf,Arguments,sys.argv)
767 if len(changes_files) == 0 and not Cnf.get("Process-New::Options::Comments-Dir",""):
768 changes_files = utils.get_changes_files(Cnf["Dir::Queue::New"])
770 Options = Cnf.SubTree("Process-New::Options")
775 Upload = queue.Upload(Cnf)
777 if not Options["No-Action"]:
779 Logger = Upload.Logger = daklog.Logger(Cnf, "process-new")
780 except CantOpenError, e:
781 Options["Trainee"] = "True"
783 projectB = Upload.projectB
785 Sections = Section_Completer()
786 Priorities = Priority_Completer()
787 readline.parse_and_bind("tab: complete")
791 ################################################################################
796 files = Upload.pkg.files
800 for f in files.keys():
801 if files[f]["type"] == "byhand":
802 if os.path.exists(f):
803 print "W: %s still present; please process byhand components and try again." % (f)
809 if Options["No-Action"]:
812 if Options["Automatic"] and not Options["No-Action"]:
814 prompt = "[A]ccept, Manual reject, Skip, Quit ?"
816 prompt = "Manual reject, [S]kip, Quit ?"
818 while prompt.find(answer) == -1:
819 answer = utils.our_raw_input(prompt)
820 m = re_default_answer.search(prompt)
823 answer = answer[:1].upper()
831 Logger.log([utils.getusername(), "BYHAND ACCEPT: %s" % (Upload.pkg.changes_file)])
832 except CantGetLockError:
833 print "Hello? Operator! Give me the number for 911!"
834 print "Dinstall in the locked area, cant process packages, come back later"
836 Logger.log([utils.getusername(), "BYHAND REJECT: %s" % (Upload.pkg.changes_file)])
837 Upload.do_reject(1, Options["Manual-Reject"])
838 os.unlink(Upload.pkg.changes_file[:-8]+".dak")
846 ################################################################################
848 def check_daily_lock():
850 Raises CantGetLockError if the dinstall daily.lock exists.
854 os.open(Cnf["Process-New::DinstallLockFile"], os.O_RDONLY | os.O_CREAT | os.O_EXCL)
856 if e.errno == errno.EEXIST or e.errno == errno.EACCES:
857 raise CantGetLockError
859 os.unlink(Cnf["Process-New::DinstallLockFile"])
862 @contextlib.contextmanager
863 def lock_package(package):
865 Lock C{package} so that noone else jumps in processing it.
867 @type package: string
868 @param package: source package name to lock
871 path = os.path.join(Cnf["Process-New::LockDir"], package)
873 fd = os.open(path, os.O_CREAT | os.O_EXCL | os.O_RDONLY)
875 if e.errno == errno.EEXIST or e.errno == errno.EACCES:
876 user = pwd.getpwuid(os.stat(path)[stat.ST_UID])[4].split(',')[0].replace('.', '')
877 raise AlreadyLockedError, user
884 def move_to_dir (dest, perms=0660, changesperms=0664):
885 utils.move (Upload.pkg.changes_file, dest, perms=changesperms)
886 file_keys = Upload.pkg.files.keys()
888 utils.move (f, dest, perms=perms)
890 def is_source_in_queue_dir(qdir):
891 entries = [ x for x in os.listdir(qdir) if x.startswith(Upload.pkg.changes["source"])
892 and x.endswith(".changes") ]
893 for entry in entries:
895 u = queue.Upload(Cnf)
896 u.pkg.changes_file = os.path.join(qdir, entry)
898 if not u.pkg.changes["architecture"].has_key("source"):
899 # another binary upload, ignore
901 if Upload.pkg.changes["version"] != u.pkg.changes["version"]:
902 # another version, ignore
908 def move_to_holding(suite, queue_dir):
909 print "Moving to %s holding area." % (suite.upper(),)
910 if Options["No-Action"]:
912 Logger.log(["Moving to %s" % (suite,), Upload.pkg.changes_file])
913 Upload.dump_vars(queue_dir)
914 move_to_dir(queue_dir, perms=0664)
915 os.unlink(Upload.pkg.changes_file[:-8]+".dak")
918 if Options["No-Action"]:
920 (summary, short_summary) = Upload.build_summaries()
921 Upload.accept(summary, short_summary, targetdir=Cnf["Dir::Queue::Newstage"])
922 os.unlink(Upload.pkg.changes_file[:-8]+".dak")
924 def do_accept_stableupdate(suite, q):
925 queue_dir = Cnf["Dir::Queue::%s" % (q,)]
926 if not Upload.pkg.changes["architecture"].has_key("source"):
927 # It is not a sourceful upload. So its source may be either in p-u
928 # holding, in new, in accepted or already installed.
929 if is_source_in_queue_dir(queue_dir):
930 # It's in p-u holding, so move it there.
931 print "Binary-only upload, source in %s." % (q,)
932 move_to_holding(suite, queue_dir)
933 elif Upload.source_exists(Upload.pkg.changes["source"],
934 Upload.pkg.changes["version"]):
935 # dak tells us that there is source available. At time of
936 # writing this means that it is installed, so put it into
938 print "Binary-only upload, source installed."
939 Logger.log([utils.getusername(), "PUNEW ACCEPT: %s" % (Upload.pkg.changes_file)])
941 elif is_source_in_queue_dir(Cnf["Dir::Queue::Accepted"]):
942 # The source is in accepted, the binary cleared NEW: accept it.
943 print "Binary-only upload, source in accepted."
944 Logger.log([utils.getusername(), "PUNEW ACCEPT: %s" % (Upload.pkg.changes_file)])
946 elif is_source_in_queue_dir(Cnf["Dir::Queue::New"]):
947 # It's in NEW. We expect the source to land in p-u holding
949 print "Binary-only upload, source in new."
950 move_to_holding(suite, queue_dir)
951 elif is_source_in_queue_dir(Cnf["Dir::Queue::Newstage"]):
952 # It's in newstage. Accept into the holding area
953 print "Binary-only upload, source in newstage."
954 Logger.log([utils.getusername(), "PUNEW ACCEPT: %s" % (Upload.pkg.changes_file)])
957 # No case applicable. Bail out. Return will cause the upload
960 print "Stable update failed. Source not found."
963 # We are handling a sourceful upload. Move to accepted if currently
964 # in p-u holding and to p-u holding otherwise.
965 if is_source_in_queue_dir(queue_dir):
966 print "Sourceful upload in %s, accepting." % (q,)
969 move_to_holding(suite, queue_dir)
973 if not Options["No-Action"]:
974 (summary, short_summary) = Upload.build_summaries()
975 if Cnf.FindB("Dinstall::SecurityQueueHandling"):
976 Upload.dump_vars(Cnf["Dir::Queue::Embargoed"])
977 move_to_dir(Cnf["Dir::Queue::Embargoed"])
978 Upload.queue_build("embargoed", Cnf["Dir::Queue::Embargoed"])
979 # Check for override disparities
980 Upload.Subst["__SUMMARY__"] = summary
982 # Stable updates need to be copied to proposed-updates holding
983 # area instead of accepted. Sourceful uploads need to go
984 # to it directly, binaries only if the source has not yet been
986 for suite, q in [("proposed-updates", "ProposedUpdates"),
987 ("oldstable-proposed-updates", "OldProposedUpdates")]:
988 if not Upload.pkg.changes["distribution"].has_key(suite):
990 return do_accept_stableupdate(suite, q)
991 # Just a normal upload, accept it...
994 def check_status(files):
996 for f in files.keys():
997 if files[f]["type"] == "byhand":
999 elif files[f].has_key("new"):
1001 return (new, byhand)
1003 def do_pkg(changes_file):
1004 Upload.pkg.changes_file = changes_file
1006 Upload.update_vars()
1007 Upload.update_subst()
1008 files = Upload.pkg.files
1011 with lock_package(Upload.pkg.changes["source"]):
1015 (new, byhand) = check_status(files)
1021 (new, byhand) = check_status(files)
1023 if not new and not byhand:
1027 except CantGetLockError:
1028 print "Hello? Operator! Give me the number for 911!"
1029 print "Dinstall in the locked area, cant process packages, come back later"
1030 except AlreadyLockedError, e:
1031 print "Seems to be locked by %s already, skipping..." % (e)
1033 ################################################################################
1036 accept_count = SummaryStats().accept_count
1037 accept_bytes = SummaryStats().accept_bytes
1041 if accept_count > 1:
1043 sys.stderr.write("Accepted %d package %s, %s.\n" % (accept_count, sets, utils.size_type(int(accept_bytes))))
1044 Logger.log([utils.getusername(), "total",accept_count,accept_bytes])
1046 if not Options["No-Action"] and not Options["Trainee"]:
1049 ################################################################################
1051 def do_comments(dir, opref, npref, line, fn):
1052 for comm in [ x for x in os.listdir(dir) if x.startswith(opref) ]:
1053 lines = open("%s/%s" % (dir, comm)).readlines()
1054 if len(lines) == 0 or lines[0] != line + "\n": continue
1055 changes_files = [ x for x in os.listdir(".") if x.startswith(comm[7:]+"_")
1056 and x.endswith(".changes") ]
1057 changes_files = sort_changes(changes_files)
1058 for f in changes_files:
1059 f = utils.validate_changes_file_arg(f, 0)
1062 fn(f, "".join(lines[1:]))
1064 if opref != npref and not Options["No-Action"]:
1065 newcomm = npref + comm[len(opref):]
1066 os.rename("%s/%s" % (dir, comm), "%s/%s" % (dir, newcomm))
1068 ################################################################################
1070 def comment_accept(changes_file, comments):
1071 Upload.pkg.changes_file = changes_file
1073 Upload.update_vars()
1074 Upload.update_subst()
1075 files = Upload.pkg.files
1078 return # dak wants to REJECT, crap
1080 (new, byhand) = check_status(files)
1081 if not new and not byhand:
1084 ################################################################################
1086 def comment_reject(changes_file, comments):
1087 Upload.pkg.changes_file = changes_file
1089 Upload.update_vars()
1090 Upload.update_subst()
1093 pass # dak has its own reasons to reject as well, which is fine
1096 print "REJECT\n" + reject_message,
1097 if not Options["No-Action"]:
1098 Upload.do_reject(0, reject_message)
1099 os.unlink(Upload.pkg.changes_file[:-8]+".dak")
1101 ################################################################################
1104 changes_files = init()
1105 if len(changes_files) > 50:
1106 sys.stderr.write("Sorting changes...\n")
1107 changes_files = sort_changes(changes_files)
1109 # Kill me now? **FIXME**
1110 Cnf["Dinstall::Options::No-Mail"] = ""
1111 bcc = "X-DAK: dak process-new\nX-Katie: lisa $Revision: 1.31 $"
1112 if Cnf.has_key("Dinstall::Bcc"):
1113 Upload.Subst["__BCC__"] = bcc + "\nBcc: %s" % (Cnf["Dinstall::Bcc"])
1115 Upload.Subst["__BCC__"] = bcc
1117 commentsdir = Cnf.get("Process-New::Options::Comments-Dir","")
1119 if changes_files != []:
1120 sys.stderr.write("Can't specify any changes files if working with comments-dir")
1122 do_comments(commentsdir, "ACCEPT.", "ACCEPTED.", "OK", comment_accept)
1123 do_comments(commentsdir, "REJECT.", "REJECTED.", "NOTOK", comment_reject)
1125 for changes_file in changes_files:
1126 changes_file = utils.validate_changes_file_arg(changes_file, 0)
1127 if not changes_file:
1129 print "\n" + changes_file
1131 do_pkg (changes_file)
1135 ################################################################################
1137 if __name__ == '__main__':