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
54 import apt_pkg, apt_inst
55 import examine_package
56 from daklib import database
57 from daklib import logging
58 from daklib import queue
59 from daklib import utils
60 from daklib.regexes import re_no_epoch, re_default_answer, re_isanum
61 from daklib.dak_exceptions import CantOpenError, AlreadyLockedError
64 Cnf = None #: Configuration, apt_pkg.Configuration
67 projectB = None #: database connection, pgobject
75 ################################################################################
76 ################################################################################
77 ################################################################################
79 def reject (str, prefix="Rejected: "):
82 reject_message += prefix + str + "\n"
86 files = Upload.pkg.files
89 for f in files.keys():
90 # The .orig.tar.gz can disappear out from under us is it's a
91 # duplicate of one in the archive.
92 if not files.has_key(f):
94 # Check that the source still exists
95 if files[f]["type"] == "deb":
96 source_version = files[f]["source version"]
97 source_package = files[f]["source package"]
98 if not Upload.pkg.changes["architecture"].has_key("source") \
99 and not Upload.source_exists(source_package, source_version, Upload.pkg.changes["distribution"].keys()):
100 source_epochless_version = re_no_epoch.sub('', source_version)
101 dsc_filename = "%s_%s.dsc" % (source_package, source_epochless_version)
103 for q in ["Accepted", "Embargoed", "Unembargoed"]:
104 if Cnf.has_key("Dir::Queue::%s" % (q)):
105 if os.path.exists(Cnf["Dir::Queue::%s" % (q)] + '/' + dsc_filename):
108 reject("no source found for %s %s (%s)." % (source_package, source_version, f))
110 # Version and file overwrite checks
111 if files[f]["type"] == "deb":
112 reject(Upload.check_binary_against_db(f), "")
113 elif files[f]["type"] == "dsc":
114 reject(Upload.check_source_against_db(f), "")
115 (reject_msg, is_in_incoming) = Upload.check_dsc_against_db(f)
116 reject(reject_msg, "")
118 if reject_message.find("Rejected") != -1:
120 if Options["No-Action"] or Options["Automatic"] or Options["Trainee"]:
123 print "REJECT\n" + reject_message,
124 prompt = "[R]eject, Skip, Quit ?"
126 while prompt.find(answer) == -1:
127 answer = utils.our_raw_input(prompt)
128 m = re_default_answer.match(prompt)
131 answer = answer[:1].upper()
134 Upload.do_reject(0, reject_message)
135 os.unlink(Upload.pkg.changes_file[:-8]+".dak")
145 ################################################################################
147 def indiv_sg_compare (a, b):
148 """Sort by source name, source, version, 'have source', and
149 finally by filename."""
150 # Sort by source version
151 q = apt_pkg.VersionCompare(a["version"], b["version"])
155 # Sort by 'have source'
156 a_has_source = a["architecture"].get("source")
157 b_has_source = b["architecture"].get("source")
158 if a_has_source and not b_has_source:
160 elif b_has_source and not a_has_source:
163 return cmp(a["filename"], b["filename"])
165 ############################################################
167 def sg_compare (a, b):
170 """Sort by have note, source already in database and time of oldest upload."""
172 a_note_state = a["note_state"]
173 b_note_state = b["note_state"]
174 if a_note_state < b_note_state:
176 elif a_note_state > b_note_state:
178 # Sort by source already in database (descending)
179 source_in_database = cmp(a["source_in_database"], b["source_in_database"])
180 if source_in_database:
181 return -source_in_database
183 # Sort by time of oldest upload
184 return cmp(a["oldest"], b["oldest"])
186 def sort_changes(changes_files):
187 """Sort into source groups, then sort each source group by version,
188 have source, filename. Finally, sort the source groups by have
189 note, time of oldest upload of each source upload."""
190 if len(changes_files) == 1:
195 # Read in all the .changes files
196 for filename in changes_files:
198 Upload.pkg.changes_file = filename
201 cache[filename] = copy.copy(Upload.pkg.changes)
202 cache[filename]["filename"] = filename
204 sorted_list.append(filename)
206 # Divide the .changes into per-source groups
208 for filename in cache.keys():
209 source = cache[filename]["source"]
210 if not per_source.has_key(source):
211 per_source[source] = {}
212 per_source[source]["list"] = []
213 per_source[source]["list"].append(cache[filename])
214 # Determine oldest time and have note status for each source group
215 for source in per_source.keys():
216 q = projectB.query("SELECT 1 FROM source WHERE source = '%s'" % source)
218 per_source[source]["source_in_database"] = len(ql)>0
219 source_list = per_source[source]["list"]
220 first = source_list[0]
221 oldest = os.stat(first["filename"])[stat.ST_MTIME]
223 for d in per_source[source]["list"]:
224 mtime = os.stat(d["filename"])[stat.ST_MTIME]
227 have_note += (database.has_new_comment(d["source"], d["version"]))
228 per_source[source]["oldest"] = oldest
230 per_source[source]["note_state"] = 0; # none
231 elif have_note < len(source_list):
232 per_source[source]["note_state"] = 1; # some
234 per_source[source]["note_state"] = 2; # all
235 per_source[source]["list"].sort(indiv_sg_compare)
236 per_source_items = per_source.items()
237 per_source_items.sort(sg_compare)
238 for i in per_source_items:
239 for j in i[1]["list"]:
240 sorted_list.append(j["filename"])
243 ################################################################################
245 class Section_Completer:
249 q = projectB.query("SELECT section FROM section")
250 for i in q.getresult():
251 self.sections.append(i[0])
253 def complete(self, text, state):
257 for word in self.sections:
259 self.matches.append(word)
261 return self.matches[state]
265 ############################################################
267 class Priority_Completer:
271 q = projectB.query("SELECT priority FROM priority")
272 for i in q.getresult():
273 self.priorities.append(i[0])
275 def complete(self, text, state):
279 for word in self.priorities:
281 self.matches.append(word)
283 return self.matches[state]
287 ################################################################################
289 def print_new (new, indexed, file=sys.stdout):
290 queue.check_valid(new)
293 for pkg in new.keys():
295 section = new[pkg]["section"]
296 priority = new[pkg]["priority"]
297 if new[pkg]["section id"] == -1:
300 if new[pkg]["priority id"] == -1:
304 line = "(%s): %-20s %-20s %-20s" % (index, pkg, priority, section)
306 line = "%-20s %-20s %-20s" % (pkg, priority, section)
307 line = line.strip()+'\n'
309 note = database.get_new_comments(Upload.pkg.changes.get("source"))
315 ################################################################################
317 def index_range (index):
321 return "1-%s" % (index)
323 ################################################################################
324 ################################################################################
327 # Write the current data to a temporary file
328 (fd, temp_filename) = utils.temp_filename()
329 temp_file = os.fdopen(fd, 'w')
330 print_new (new, 0, temp_file)
332 # Spawn an editor on that file
333 editor = os.environ.get("EDITOR","vi")
334 result = os.system("%s %s" % (editor, temp_filename))
336 utils.fubar ("%s invocation failed for %s." % (editor, temp_filename), result)
337 # Read the edited data back in
338 temp_file = utils.open_file(temp_filename)
339 lines = temp_file.readlines()
341 os.unlink(temp_filename)
348 # Pad the list if necessary
349 s[len(s):3] = [None] * (3-len(s))
350 (pkg, priority, section) = s[:3]
351 if not new.has_key(pkg):
352 utils.warn("Ignoring unknown package '%s'" % (pkg))
354 # Strip off any invalid markers, print_new will readd them.
355 if section.endswith("[!]"):
356 section = section[:-3]
357 if priority.endswith("[!]"):
358 priority = priority[:-3]
359 for f in new[pkg]["files"]:
360 Upload.pkg.files[f]["section"] = section
361 Upload.pkg.files[f]["priority"] = priority
362 new[pkg]["section"] = section
363 new[pkg]["priority"] = priority
365 ################################################################################
367 def edit_index (new, index):
368 priority = new[index]["priority"]
369 section = new[index]["section"]
370 ftype = new[index]["type"]
373 print "\t".join([index, priority, section])
377 prompt = "[B]oth, Priority, Section, Done ? "
379 prompt = "[S]ection, Done ? "
380 edit_priority = edit_section = 0
382 while prompt.find(answer) == -1:
383 answer = utils.our_raw_input(prompt)
384 m = re_default_answer.match(prompt)
387 answer = answer[:1].upper()
394 edit_priority = edit_section = 1
400 readline.set_completer(Priorities.complete)
402 while not got_priority:
403 new_priority = utils.our_raw_input("New priority: ").strip()
404 if new_priority not in Priorities.priorities:
405 print "E: '%s' is not a valid priority, try again." % (new_priority)
408 priority = new_priority
412 readline.set_completer(Sections.complete)
414 while not got_section:
415 new_section = utils.our_raw_input("New section: ").strip()
416 if new_section not in Sections.sections:
417 print "E: '%s' is not a valid section, try again." % (new_section)
420 section = new_section
422 # Reset the readline completer
423 readline.set_completer(None)
425 for f in new[index]["files"]:
426 Upload.pkg.files[f]["section"] = section
427 Upload.pkg.files[f]["priority"] = priority
428 new[index]["priority"] = priority
429 new[index]["section"] = section
432 ################################################################################
434 def edit_overrides (new):
445 prompt = "(%s) edit override <n>, Editor, Done ? " % (index_range(index))
448 while not got_answer:
449 answer = utils.our_raw_input(prompt)
450 if not answer.isdigit():
451 answer = answer[:1].upper()
452 if answer == "E" or answer == "D":
454 elif re_isanum.match (answer):
456 if (answer < 1) or (answer > index):
457 print "%s is not a valid index (%s). Please retry." % (answer, index_range(index))
466 edit_index (new, new_index[answer])
470 ################################################################################
473 # Write the current data to a temporary file
474 (fd, temp_filename) = utils.temp_filename()
475 editor = os.environ.get("EDITOR","vi")
478 os.system("%s %s" % (editor, temp_filename))
479 temp_file = utils.open_file(temp_filename)
480 newnote = temp_file.read().rstrip()
483 print utils.prefix_multi_line_string(newnote," ")
484 prompt = "[D]one, Edit, Abandon, Quit ?"
486 while prompt.find(answer) == -1:
487 answer = utils.our_raw_input(prompt)
488 m = re_default_answer.search(prompt)
491 answer = answer[:1].upper()
492 os.unlink(temp_filename)
498 database.add_new_comment(Upload.pkg.changes["source"], Upload.pkg.changes["version"], newnote, utils.whoami())
500 ################################################################################
504 less_fd = os.popen("less -R -", 'w', 0)
505 stdout_fd = sys.stdout
508 changes = utils.parse_changes (Upload.pkg.changes_file)
509 examine_package.display_changes(changes['distribution'], Upload.pkg.changes_file)
510 files = Upload.pkg.files
511 for f in files.keys():
512 if files[f].has_key("new"):
513 ftype = files[f]["type"]
515 examine_package.check_deb(changes['distribution'], f)
517 examine_package.check_dsc(changes['distribution'], f)
519 examine_package.output_package_relations()
520 sys.stdout = stdout_fd
522 if e.errno == errno.EPIPE:
523 utils.warn("[examine_package] Caught EPIPE; skipping.")
527 except KeyboardInterrupt:
528 utils.warn("[examine_package] Caught C-c; skipping.")
531 ################################################################################
533 ## FIXME: horribly Debian specific
535 def do_bxa_notification():
536 files = Upload.pkg.files
538 for f in files.keys():
539 if files[f]["type"] == "deb":
540 control = apt_pkg.ParseSection(apt_inst.debExtractControl(utils.open_file(f)))
542 summary += "Package: %s\n" % (control.Find("Package"))
543 summary += "Description: %s\n" % (control.Find("Description"))
544 Upload.Subst["__BINARY_DESCRIPTIONS__"] = summary
545 bxa_mail = utils.TemplateSubst(Upload.Subst,Cnf["Dir::Templates"]+"/process-new.bxa_notification")
546 utils.send_mail(bxa_mail)
548 ################################################################################
550 def add_overrides (new):
551 changes = Upload.pkg.changes
552 files = Upload.pkg.files
554 projectB.query("BEGIN WORK")
555 for suite in changes["suite"].keys():
556 suite_id = database.get_suite_id(suite)
557 for pkg in new.keys():
558 component_id = database.get_component_id(new[pkg]["component"])
559 type_id = database.get_override_type_id(new[pkg]["type"])
560 priority_id = new[pkg]["priority id"]
561 section_id = new[pkg]["section id"]
562 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))
563 for f in new[pkg]["files"]:
564 if files[f].has_key("new"):
568 projectB.query("COMMIT WORK")
570 if Cnf.FindB("Dinstall::BXANotify"):
571 do_bxa_notification()
573 ################################################################################
575 def prod_maintainer (note):
576 # Here we prepare an editor and get them ready to prod...
577 (fd, temp_filename) = utils.temp_filename()
578 temp_file = os.fdopen(fd, 'w')
581 temp_file.write(line)
583 editor = os.environ.get("EDITOR","vi")
586 os.system("%s %s" % (editor, temp_filename))
587 temp_fh = utils.open_file(temp_filename)
588 prod_message = "".join(temp_fh.readlines())
590 print "Prod message:"
591 print utils.prefix_multi_line_string(prod_message," ",include_blank_lines=1)
592 prompt = "[P]rod, Edit, Abandon, Quit ?"
594 while prompt.find(answer) == -1:
595 answer = utils.our_raw_input(prompt)
596 m = re_default_answer.search(prompt)
599 answer = answer[:1].upper()
600 os.unlink(temp_filename)
606 # Otherwise, do the proding...
607 user_email_address = utils.whoami() + " <%s>" % (
608 Cnf["Dinstall::MyAdminAddress"])
612 Subst["__FROM_ADDRESS__"] = user_email_address
613 Subst["__PROD_MESSAGE__"] = prod_message
614 Subst["__CC__"] = "Cc: " + Cnf["Dinstall::MyEmailAddress"]
616 prod_mail_message = utils.TemplateSubst(
617 Subst,Cnf["Dir::Templates"]+"/process-new.prod")
619 # Send the prod mail if appropriate
620 if not Cnf["Dinstall::Options::No-Mail"]:
621 utils.send_mail(prod_mail_message)
623 print "Sent proding message"
625 ################################################################################
629 files = Upload.pkg.files
630 changes = Upload.pkg.changes
632 # Make a copy of distribution we can happily trample on
633 changes["suite"] = copy.copy(changes["distribution"])
635 # Fix up the list of target suites
636 for suite in changes["suite"].keys():
637 override = Cnf.Find("Suite::%s::OverrideSuite" % (suite))
639 (olderr, newerr) = (database.get_suite_id(suite) == -1,
640 database.get_suite_id(override) == -1)
642 (oinv, newinv) = ("", "")
643 if olderr: oinv = "invalid "
644 if newerr: ninv = "invalid "
645 print "warning: overriding %ssuite %s to %ssuite %s" % (
646 oinv, suite, ninv, override)
647 del changes["suite"][suite]
648 changes["suite"][override] = 1
650 for suite in changes["suite"].keys():
651 suite_id = database.get_suite_id(suite)
653 utils.fubar("%s has invalid suite '%s' (possibly overriden). say wha?" % (changes, suite))
655 # The main NEW processing loop
658 # Find out what's new
659 new = queue.determine_new(changes, files, projectB)
665 if Options["No-Action"] or Options["Automatic"]:
668 (broken, note) = print_new(new, 0)
671 if not broken and not note:
672 prompt = "Add overrides, "
674 print "W: [!] marked entries must be fixed before package can be processed."
676 print "W: note must be removed before package can be processed."
677 prompt += "Remove note, "
679 prompt += "Edit overrides, Check, Manual reject, Note edit, Prod, [S]kip, Quit ?"
681 while prompt.find(answer) == -1:
682 answer = utils.our_raw_input(prompt)
683 m = re_default_answer.search(prompt)
686 answer = answer[:1].upper()
688 if answer == 'A' and not Options["Trainee"]:
689 done = add_overrides (new)
692 elif answer == 'E' and not Options["Trainee"]:
693 new = edit_overrides (new)
694 elif answer == 'M' and not Options["Trainee"]:
695 aborted = Upload.do_reject(manual=1,
696 reject_message=Options["Manual-Reject"],
697 note=database.get_new_comments(changes.get("source", "")))
699 os.unlink(Upload.pkg.changes_file[:-8]+".dak")
702 edit_note(database.get_new_comments(changes.get("source", "")))
703 elif answer == 'P' and not Options["Trainee"]:
704 prod_maintainer(database.get_new_comments(changes.get("source", "")))
706 confirm = utils.our_raw_input("Really clear note (y/N)? ").lower()
708 database.delete_new_comments(changes.get("source"), changes.get("version"))
715 ################################################################################
716 ################################################################################
717 ################################################################################
719 def usage (exit_code=0):
720 print """Usage: dak process-new [OPTION]... [CHANGES]...
721 -a, --automatic automatic run
722 -h, --help show this help and exit.
723 -C, --comments-dir=DIR use DIR as comments-dir, for [o-]p-u-new
724 -m, --manual-reject=MSG manual reject with `msg'
725 -n, --no-action don't do anything
726 -t, --trainee FTP Trainee mode
727 -V, --version display the version number and exit"""
730 ################################################################################
733 global Cnf, Options, Logger, Upload, projectB, Sections, Priorities
735 Cnf = utils.get_conf()
737 Arguments = [('a',"automatic","Process-New::Options::Automatic"),
738 ('h',"help","Process-New::Options::Help"),
739 ('C',"comments-dir","Process-New::Options::Comments-Dir", "HasArg"),
740 ('m',"manual-reject","Process-New::Options::Manual-Reject", "HasArg"),
741 ('t',"trainee","Process-New::Options::Trainee"),
742 ('n',"no-action","Process-New::Options::No-Action")]
744 for i in ["automatic", "help", "manual-reject", "no-action", "version", "comments-dir", "trainee"]:
745 if not Cnf.has_key("Process-New::Options::%s" % (i)):
746 Cnf["Process-New::Options::%s" % (i)] = ""
748 changes_files = apt_pkg.ParseCommandLine(Cnf,Arguments,sys.argv)
749 if len(changes_files) == 0 and not Cnf.get("Process-New::Options::Comments-Dir",""):
750 changes_files = utils.get_changes_files(Cnf["Dir::Queue::New"])
752 Options = Cnf.SubTree("Process-New::Options")
757 Upload = queue.Upload(Cnf)
759 if not Options["No-Action"]:
761 Logger = Upload.Logger = logging.Logger(Cnf, "process-new")
762 except CantOpenError, e:
763 Options["Trainee"] = "Oh yes"
765 projectB = Upload.projectB
767 Sections = Section_Completer()
768 Priorities = Priority_Completer()
769 readline.parse_and_bind("tab: complete")
773 ################################################################################
778 files = Upload.pkg.files
782 for f in files.keys():
783 if files[f]["type"] == "byhand":
784 if os.path.exists(f):
785 print "W: %s still present; please process byhand components and try again." % (f)
791 if Options["No-Action"]:
794 if Options["Automatic"] and not Options["No-Action"]:
796 prompt = "[A]ccept, Manual reject, Skip, Quit ?"
798 prompt = "Manual reject, [S]kip, Quit ?"
800 while prompt.find(answer) == -1:
801 answer = utils.our_raw_input(prompt)
802 m = re_default_answer.search(prompt)
805 answer = answer[:1].upper()
812 Upload.do_reject(1, Options["Manual-Reject"])
813 os.unlink(Upload.pkg.changes_file[:-8]+".dak")
821 ################################################################################
823 def get_accept_lock():
827 os.open(Cnf["Process-New::AcceptedLockFile"], os.O_RDONLY | os.O_CREAT | os.O_EXCL)
830 if e.errno == errno.EACCES or e.errno == errno.EEXIST:
833 utils.fubar("Couldn't obtain lock; assuming 'dak process-unchecked' is already running.")
835 print("Unable to get accepted lock (try %d of 10)" % retry)
841 @contextlib.contextmanager
842 def lock_package(package):
844 Lock C{package} so that noone else jumps in processing it.
846 @type package: string
847 @param package: source package name to lock
850 path = os.path.join(Cnf["Process-New::LockDir"], package)
852 fd = os.open(path, os.O_CREAT | os.O_EXCL | os.O_RDONLY)
854 if e.errno == errno.EEXIST or e.errno == errno.EACCES:
855 raise AlreadyLockedError, e.errno
862 def move_to_dir (dest, perms=0660, changesperms=0664):
863 utils.move (Upload.pkg.changes_file, dest, perms=changesperms)
864 file_keys = Upload.pkg.files.keys()
866 utils.move (f, dest, perms=perms)
868 def is_source_in_queue_dir(qdir):
869 entries = [ x for x in os.listdir(qdir) if x.startswith(Upload.pkg.changes["source"])
870 and x.endswith(".changes") ]
871 for entry in entries:
873 u = queue.Upload(Cnf)
874 u.pkg.changes_file = os.path.join(qdir, entry)
876 if not u.pkg.changes["architecture"].has_key("source"):
877 # another binary upload, ignore
879 if Upload.pkg.changes["version"] != u.pkg.changes["version"]:
880 # another version, ignore
886 def move_to_holding(suite, queue_dir):
887 print "Moving to %s holding area." % (suite.upper(),)
888 if Options["No-Action"]:
890 Logger.log(["Moving to %s" % (suite,), Upload.pkg.changes_file])
891 Upload.dump_vars(queue_dir)
892 move_to_dir(queue_dir, perms=0664)
893 os.unlink(Upload.pkg.changes_file[:-8]+".dak")
896 if Options["No-Action"]:
898 (summary, short_summary) = Upload.build_summaries()
899 Upload.accept(summary, short_summary)
900 os.unlink(Upload.pkg.changes_file[:-8]+".dak")
902 def do_accept_stableupdate(suite, q):
903 queue_dir = Cnf["Dir::Queue::%s" % (q,)]
904 if not Upload.pkg.changes["architecture"].has_key("source"):
905 # It is not a sourceful upload. So its source may be either in p-u
906 # holding, in new, in accepted or already installed.
907 if is_source_in_queue_dir(queue_dir):
908 # It's in p-u holding, so move it there.
909 print "Binary-only upload, source in %s." % (q,)
910 move_to_holding(suite, queue_dir)
911 elif Upload.source_exists(Upload.pkg.changes["source"],
912 Upload.pkg.changes["version"]):
913 # dak tells us that there is source available. At time of
914 # writing this means that it is installed, so put it into
916 print "Binary-only upload, source installed."
918 elif is_source_in_queue_dir(Cnf["Dir::Queue::Accepted"]):
919 # The source is in accepted, the binary cleared NEW: accept it.
920 print "Binary-only upload, source in accepted."
922 elif is_source_in_queue_dir(Cnf["Dir::Queue::New"]):
923 # It's in NEW. We expect the source to land in p-u holding
925 print "Binary-only upload, source in new."
926 move_to_holding(suite, queue_dir)
928 # No case applicable. Bail out. Return will cause the upload
931 print "Stable update failed. Source not found."
934 # We are handling a sourceful upload. Move to accepted if currently
935 # in p-u holding and to p-u holding otherwise.
936 if is_source_in_queue_dir(queue_dir):
937 print "Sourceful upload in %s, accepting." % (q,)
940 move_to_holding(suite, queue_dir)
944 if not Options["No-Action"]:
946 (summary, short_summary) = Upload.build_summaries()
948 if Cnf.FindB("Dinstall::SecurityQueueHandling"):
949 Upload.dump_vars(Cnf["Dir::Queue::Embargoed"])
950 move_to_dir(Cnf["Dir::Queue::Embargoed"])
951 Upload.queue_build("embargoed", Cnf["Dir::Queue::Embargoed"])
952 # Check for override disparities
953 Upload.Subst["__SUMMARY__"] = summary
955 # Stable updates need to be copied to proposed-updates holding
956 # area instead of accepted. Sourceful uploads need to go
957 # to it directly, binaries only if the source has not yet been
959 for suite, q in [("proposed-updates", "ProposedUpdates"),
960 ("oldstable-proposed-updates", "OldProposedUpdates")]:
961 if not Upload.pkg.changes["distribution"].has_key(suite):
963 return do_accept_stableupdate(suite, q)
964 # Just a normal upload, accept it...
967 if not Options["No-Action"]:
968 os.unlink(Cnf["Process-New::AcceptedLockFile"])
970 def check_status(files):
972 for f in files.keys():
973 if files[f]["type"] == "byhand":
975 elif files[f].has_key("new"):
979 def do_pkg(changes_file):
980 Upload.pkg.changes_file = changes_file
983 Upload.update_subst()
984 files = Upload.pkg.files
987 with lock_package(Upload.pkg.changes["source"]):
991 (new, byhand) = check_status(files)
997 (new, byhand) = check_status(files)
999 if not new and not byhand:
1001 except AlreadyLockedError, e:
1002 print "Seems to be locked already, skipping..."
1004 ################################################################################
1007 accept_count = Upload.accept_count
1008 accept_bytes = Upload.accept_bytes
1012 if accept_count > 1:
1014 sys.stderr.write("Accepted %d package %s, %s.\n" % (accept_count, sets, utils.size_type(int(accept_bytes))))
1015 Logger.log(["total",accept_count,accept_bytes])
1017 if not Options["No-Action"] and not Options["Trainee"]:
1020 ################################################################################
1022 def do_comments(dir, opref, npref, line, fn):
1023 for comm in [ x for x in os.listdir(dir) if x.startswith(opref) ]:
1024 lines = open("%s/%s" % (dir, comm)).readlines()
1025 if len(lines) == 0 or lines[0] != line + "\n": continue
1026 changes_files = [ x for x in os.listdir(".") if x.startswith(comm[7:]+"_")
1027 and x.endswith(".changes") ]
1028 changes_files = sort_changes(changes_files)
1029 for f in changes_files:
1030 f = utils.validate_changes_file_arg(f, 0)
1033 fn(f, "".join(lines[1:]))
1035 if opref != npref and not Options["No-Action"]:
1036 newcomm = npref + comm[len(opref):]
1037 os.rename("%s/%s" % (dir, comm), "%s/%s" % (dir, newcomm))
1039 ################################################################################
1041 def comment_accept(changes_file, comments):
1042 Upload.pkg.changes_file = changes_file
1044 Upload.update_vars()
1045 Upload.update_subst()
1046 files = Upload.pkg.files
1049 return # dak wants to REJECT, crap
1051 (new, byhand) = check_status(files)
1052 if not new and not byhand:
1055 ################################################################################
1057 def comment_reject(changes_file, comments):
1058 Upload.pkg.changes_file = changes_file
1060 Upload.update_vars()
1061 Upload.update_subst()
1064 pass # dak has its own reasons to reject as well, which is fine
1067 print "REJECT\n" + reject_message,
1068 if not Options["No-Action"]:
1069 Upload.do_reject(0, reject_message)
1070 os.unlink(Upload.pkg.changes_file[:-8]+".dak")
1072 ################################################################################
1075 changes_files = init()
1076 if len(changes_files) > 50:
1077 sys.stderr.write("Sorting changes...\n")
1078 changes_files = sort_changes(changes_files)
1080 # Kill me now? **FIXME**
1081 Cnf["Dinstall::Options::No-Mail"] = ""
1082 bcc = "X-DAK: dak process-new\nX-Katie: lisa $Revision: 1.31 $"
1083 if Cnf.has_key("Dinstall::Bcc"):
1084 Upload.Subst["__BCC__"] = bcc + "\nBcc: %s" % (Cnf["Dinstall::Bcc"])
1086 Upload.Subst["__BCC__"] = bcc
1088 commentsdir = Cnf.get("Process-New::Options::Comments-Dir","")
1090 if changes_files != []:
1091 sys.stderr.write("Can't specify any changes files if working with comments-dir")
1093 do_comments(commentsdir, "ACCEPT.", "ACCEPTED.", "OK", comment_accept)
1094 do_comments(commentsdir, "REJECT.", "REJECTED.", "NOTOK", comment_reject)
1096 for changes_file in changes_files:
1097 changes_file = utils.validate_changes_file_arg(changes_file, 0)
1098 if not changes_file:
1100 print "\n" + changes_file
1101 do_pkg (changes_file)
1105 ################################################################################
1107 if __name__ == '__main__':