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 in ( 'A', 'E', 'M', 'O', 'R' ) and Options["Trainee"]:
695 utils.warn("Trainees can't do that")
698 if answer == 'A' and not Options["Trainee"]:
701 done = add_overrides (new)
702 Logger.log([utils.getusername(), "NEW ACCEPT: %s" % (Upload.pkg.changes_file)])
703 except CantGetLockError:
704 print "Hello? Operator! Give me the number for 911!"
705 print "Dinstall in the locked area, cant process packages, come back later"
708 elif answer == 'E' and not Options["Trainee"]:
709 new = edit_overrides (new)
710 elif answer == 'M' and not Options["Trainee"]:
711 aborted = Upload.do_reject(manual=1,
712 reject_message=Options["Manual-Reject"],
713 note=database.get_new_comments(changes.get("source", "")))
715 Logger.log([utils.getusername(), "NEW REJECT: %s" % (Upload.pkg.changes_file)])
716 os.unlink(Upload.pkg.changes_file[:-8]+".dak")
719 edit_note(database.get_new_comments(changes.get("source", "")))
720 elif answer == 'P' and not Options["Trainee"]:
721 prod_maintainer(database.get_new_comments(changes.get("source", "")))
722 Logger.log([utils.getusername(), "NEW PROD: %s" % (Upload.pkg.changes_file)])
723 elif answer == 'R' and not Options["Trainee"]:
724 confirm = utils.our_raw_input("Really clear note (y/N)? ").lower()
726 database.delete_new_comments(changes.get("source"), changes.get("version"))
727 elif answer == 'O' and not Options["Trainee"]:
728 confirm = utils.our_raw_input("Really clear all notes (y/N)? ").lower()
730 database.delete_all_new_comments(changes.get("source"))
737 ################################################################################
738 ################################################################################
739 ################################################################################
741 def usage (exit_code=0):
742 print """Usage: dak process-new [OPTION]... [CHANGES]...
743 -a, --automatic automatic run
744 -h, --help show this help and exit.
745 -C, --comments-dir=DIR use DIR as comments-dir, for [o-]p-u-new
746 -m, --manual-reject=MSG manual reject with `msg'
747 -n, --no-action don't do anything
748 -t, --trainee FTP Trainee mode
749 -V, --version display the version number and exit"""
752 ################################################################################
755 global Cnf, Options, Logger, Upload, projectB, Sections, Priorities
757 Cnf = utils.get_conf()
759 Arguments = [('a',"automatic","Process-New::Options::Automatic"),
760 ('h',"help","Process-New::Options::Help"),
761 ('C',"comments-dir","Process-New::Options::Comments-Dir", "HasArg"),
762 ('m',"manual-reject","Process-New::Options::Manual-Reject", "HasArg"),
763 ('t',"trainee","Process-New::Options::Trainee"),
764 ('n',"no-action","Process-New::Options::No-Action")]
766 for i in ["automatic", "help", "manual-reject", "no-action", "version", "comments-dir", "trainee"]:
767 if not Cnf.has_key("Process-New::Options::%s" % (i)):
768 Cnf["Process-New::Options::%s" % (i)] = ""
770 changes_files = apt_pkg.ParseCommandLine(Cnf,Arguments,sys.argv)
771 if len(changes_files) == 0 and not Cnf.get("Process-New::Options::Comments-Dir",""):
772 changes_files = utils.get_changes_files(Cnf["Dir::Queue::New"])
774 Options = Cnf.SubTree("Process-New::Options")
779 Upload = queue.Upload(Cnf)
781 if not Options["No-Action"]:
783 Logger = Upload.Logger = daklog.Logger(Cnf, "process-new")
784 except CantOpenError, e:
785 Options["Trainee"] = "True"
787 projectB = Upload.projectB
789 Sections = Section_Completer()
790 Priorities = Priority_Completer()
791 readline.parse_and_bind("tab: complete")
795 ################################################################################
800 files = Upload.pkg.files
804 for f in files.keys():
805 if files[f]["type"] == "byhand":
806 if os.path.exists(f):
807 print "W: %s still present; please process byhand components and try again." % (f)
813 if Options["No-Action"]:
816 if Options["Automatic"] and not Options["No-Action"]:
818 prompt = "[A]ccept, Manual reject, Skip, Quit ?"
820 prompt = "Manual reject, [S]kip, Quit ?"
822 while prompt.find(answer) == -1:
823 answer = utils.our_raw_input(prompt)
824 m = re_default_answer.search(prompt)
827 answer = answer[:1].upper()
835 Logger.log([utils.getusername(), "BYHAND ACCEPT: %s" % (Upload.pkg.changes_file)])
836 except CantGetLockError:
837 print "Hello? Operator! Give me the number for 911!"
838 print "Dinstall in the locked area, cant process packages, come back later"
840 Logger.log([utils.getusername(), "BYHAND REJECT: %s" % (Upload.pkg.changes_file)])
841 Upload.do_reject(1, Options["Manual-Reject"])
842 os.unlink(Upload.pkg.changes_file[:-8]+".dak")
850 ################################################################################
852 def check_daily_lock():
854 Raises CantGetLockError if the dinstall daily.lock exists.
858 os.open(Cnf["Process-New::DinstallLockFile"], os.O_RDONLY | os.O_CREAT | os.O_EXCL)
860 if e.errno == errno.EEXIST or e.errno == errno.EACCES:
861 raise CantGetLockError
863 os.unlink(Cnf["Process-New::DinstallLockFile"])
866 @contextlib.contextmanager
867 def lock_package(package):
869 Lock C{package} so that noone else jumps in processing it.
871 @type package: string
872 @param package: source package name to lock
875 path = os.path.join(Cnf["Process-New::LockDir"], package)
877 fd = os.open(path, os.O_CREAT | os.O_EXCL | os.O_RDONLY)
879 if e.errno == errno.EEXIST or e.errno == errno.EACCES:
880 user = pwd.getpwuid(os.stat(path)[stat.ST_UID])[4].split(',')[0].replace('.', '')
881 raise AlreadyLockedError, user
888 def move_to_dir (dest, perms=0660, changesperms=0664):
889 utils.move (Upload.pkg.changes_file, dest, perms=changesperms)
890 file_keys = Upload.pkg.files.keys()
892 utils.move (f, dest, perms=perms)
894 def is_source_in_queue_dir(qdir):
895 entries = [ x for x in os.listdir(qdir) if x.startswith(Upload.pkg.changes["source"])
896 and x.endswith(".changes") ]
897 for entry in entries:
899 u = queue.Upload(Cnf)
900 u.pkg.changes_file = os.path.join(qdir, entry)
902 if not u.pkg.changes["architecture"].has_key("source"):
903 # another binary upload, ignore
905 if Upload.pkg.changes["version"] != u.pkg.changes["version"]:
906 # another version, ignore
912 def move_to_holding(suite, queue_dir):
913 print "Moving to %s holding area." % (suite.upper(),)
914 if Options["No-Action"]:
916 Logger.log(["Moving to %s" % (suite,), Upload.pkg.changes_file])
917 Upload.dump_vars(queue_dir)
918 move_to_dir(queue_dir, perms=0664)
919 os.unlink(Upload.pkg.changes_file[:-8]+".dak")
922 if Options["No-Action"]:
924 (summary, short_summary) = Upload.build_summaries()
925 Upload.accept(summary, short_summary, targetdir=Cnf["Dir::Queue::Newstage"])
926 os.unlink(Upload.pkg.changes_file[:-8]+".dak")
928 def do_accept_stableupdate(suite, q):
929 queue_dir = Cnf["Dir::Queue::%s" % (q,)]
930 if not Upload.pkg.changes["architecture"].has_key("source"):
931 # It is not a sourceful upload. So its source may be either in p-u
932 # holding, in new, in accepted or already installed.
933 if is_source_in_queue_dir(queue_dir):
934 # It's in p-u holding, so move it there.
935 print "Binary-only upload, source in %s." % (q,)
936 move_to_holding(suite, queue_dir)
937 elif Upload.source_exists(Upload.pkg.changes["source"],
938 Upload.pkg.changes["version"]):
939 # dak tells us that there is source available. At time of
940 # writing this means that it is installed, so put it into
942 print "Binary-only upload, source installed."
943 Logger.log([utils.getusername(), "PUNEW ACCEPT: %s" % (Upload.pkg.changes_file)])
945 elif is_source_in_queue_dir(Cnf["Dir::Queue::Accepted"]):
946 # The source is in accepted, the binary cleared NEW: accept it.
947 print "Binary-only upload, source in accepted."
948 Logger.log([utils.getusername(), "PUNEW ACCEPT: %s" % (Upload.pkg.changes_file)])
950 elif is_source_in_queue_dir(Cnf["Dir::Queue::New"]):
951 # It's in NEW. We expect the source to land in p-u holding
953 print "Binary-only upload, source in new."
954 move_to_holding(suite, queue_dir)
955 elif is_source_in_queue_dir(Cnf["Dir::Queue::Newstage"]):
956 # It's in newstage. Accept into the holding area
957 print "Binary-only upload, source in newstage."
958 Logger.log([utils.getusername(), "PUNEW ACCEPT: %s" % (Upload.pkg.changes_file)])
961 # No case applicable. Bail out. Return will cause the upload
964 print "Stable update failed. Source not found."
967 # We are handling a sourceful upload. Move to accepted if currently
968 # in p-u holding and to p-u holding otherwise.
969 if is_source_in_queue_dir(queue_dir):
970 print "Sourceful upload in %s, accepting." % (q,)
973 move_to_holding(suite, queue_dir)
977 if not Options["No-Action"]:
978 (summary, short_summary) = Upload.build_summaries()
979 if Cnf.FindB("Dinstall::SecurityQueueHandling"):
980 Upload.dump_vars(Cnf["Dir::Queue::Embargoed"])
981 move_to_dir(Cnf["Dir::Queue::Embargoed"])
982 Upload.queue_build("embargoed", Cnf["Dir::Queue::Embargoed"])
983 # Check for override disparities
984 Upload.Subst["__SUMMARY__"] = summary
986 # Stable updates need to be copied to proposed-updates holding
987 # area instead of accepted. Sourceful uploads need to go
988 # to it directly, binaries only if the source has not yet been
990 for suite, q in [("proposed-updates", "ProposedUpdates"),
991 ("oldstable-proposed-updates", "OldProposedUpdates")]:
992 if not Upload.pkg.changes["distribution"].has_key(suite):
994 return do_accept_stableupdate(suite, q)
995 # Just a normal upload, accept it...
998 def check_status(files):
1000 for f in files.keys():
1001 if files[f]["type"] == "byhand":
1003 elif files[f].has_key("new"):
1005 return (new, byhand)
1007 def do_pkg(changes_file):
1008 Upload.pkg.changes_file = changes_file
1010 Upload.update_vars()
1011 Upload.update_subst()
1012 files = Upload.pkg.files
1015 with lock_package(Upload.pkg.changes["source"]):
1019 (new, byhand) = check_status(files)
1025 (new, byhand) = check_status(files)
1027 if not new and not byhand:
1031 except CantGetLockError:
1032 print "Hello? Operator! Give me the number for 911!"
1033 print "Dinstall in the locked area, cant process packages, come back later"
1034 except AlreadyLockedError, e:
1035 print "Seems to be locked by %s already, skipping..." % (e)
1037 ################################################################################
1040 accept_count = SummaryStats().accept_count
1041 accept_bytes = SummaryStats().accept_bytes
1045 if accept_count > 1:
1047 sys.stderr.write("Accepted %d package %s, %s.\n" % (accept_count, sets, utils.size_type(int(accept_bytes))))
1048 Logger.log([utils.getusername(), "total",accept_count,accept_bytes])
1050 if not Options["No-Action"] and not Options["Trainee"]:
1053 ################################################################################
1055 def do_comments(dir, opref, npref, line, fn):
1056 for comm in [ x for x in os.listdir(dir) if x.startswith(opref) ]:
1057 lines = open("%s/%s" % (dir, comm)).readlines()
1058 if len(lines) == 0 or lines[0] != line + "\n": continue
1059 changes_files = [ x for x in os.listdir(".") if x.startswith(comm[7:]+"_")
1060 and x.endswith(".changes") ]
1061 changes_files = sort_changes(changes_files)
1062 for f in changes_files:
1063 f = utils.validate_changes_file_arg(f, 0)
1066 fn(f, "".join(lines[1:]))
1068 if opref != npref and not Options["No-Action"]:
1069 newcomm = npref + comm[len(opref):]
1070 os.rename("%s/%s" % (dir, comm), "%s/%s" % (dir, newcomm))
1072 ################################################################################
1074 def comment_accept(changes_file, comments):
1075 Upload.pkg.changes_file = changes_file
1077 Upload.update_vars()
1078 Upload.update_subst()
1079 files = Upload.pkg.files
1082 return # dak wants to REJECT, crap
1084 (new, byhand) = check_status(files)
1085 if not new and not byhand:
1088 ################################################################################
1090 def comment_reject(changes_file, comments):
1091 Upload.pkg.changes_file = changes_file
1093 Upload.update_vars()
1094 Upload.update_subst()
1097 pass # dak has its own reasons to reject as well, which is fine
1100 print "REJECT\n" + reject_message,
1101 if not Options["No-Action"]:
1102 Upload.do_reject(0, reject_message)
1103 os.unlink(Upload.pkg.changes_file[:-8]+".dak")
1105 ################################################################################
1108 changes_files = init()
1109 if len(changes_files) > 50:
1110 sys.stderr.write("Sorting changes...\n")
1111 changes_files = sort_changes(changes_files)
1113 # Kill me now? **FIXME**
1114 Cnf["Dinstall::Options::No-Mail"] = ""
1115 bcc = "X-DAK: dak process-new\nX-Katie: lisa $Revision: 1.31 $"
1116 if Cnf.has_key("Dinstall::Bcc"):
1117 Upload.Subst["__BCC__"] = bcc + "\nBcc: %s" % (Cnf["Dinstall::Bcc"])
1119 Upload.Subst["__BCC__"] = bcc
1121 commentsdir = Cnf.get("Process-New::Options::Comments-Dir","")
1123 if changes_files != []:
1124 sys.stderr.write("Can't specify any changes files if working with comments-dir")
1126 do_comments(commentsdir, "ACCEPT.", "ACCEPTED.", "OK", comment_accept)
1127 do_comments(commentsdir, "REJECT.", "REJECTED.", "NOTOK", comment_reject)
1129 for changes_file in changes_files:
1130 changes_file = utils.validate_changes_file_arg(changes_file, 0)
1131 if not changes_file:
1133 print "\n" + changes_file
1135 do_pkg (changes_file)
1139 ################################################################################
1141 if __name__ == '__main__':