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
57 from daklib import database
58 from daklib import logging
59 from daklib import queue
60 from daklib import utils
61 from daklib.regexes import re_no_epoch, re_default_answer, re_isanum
62 from daklib.dak_exceptions import CantOpenError, AlreadyLockedError, CantGetLockError
65 Cnf = None #: Configuration, apt_pkg.Configuration
68 projectB = None #: database connection, pgobject
76 ################################################################################
77 ################################################################################
78 ################################################################################
80 def reject (str, prefix="Rejected: "):
83 reject_message += prefix + str + "\n"
87 files = Upload.pkg.files
90 for f in files.keys():
91 # The .orig.tar.gz can disappear out from under us is it's a
92 # duplicate of one in the archive.
93 if not files.has_key(f):
95 # Check that the source still exists
96 if files[f]["type"] == "deb":
97 source_version = files[f]["source version"]
98 source_package = files[f]["source package"]
99 if not Upload.pkg.changes["architecture"].has_key("source") \
100 and not Upload.source_exists(source_package, source_version, Upload.pkg.changes["distribution"].keys()):
101 source_epochless_version = re_no_epoch.sub('', source_version)
102 dsc_filename = "%s_%s.dsc" % (source_package, source_epochless_version)
104 for q in ["Accepted", "Embargoed", "Unembargoed", "Newstage"]:
105 if Cnf.has_key("Dir::Queue::%s" % (q)):
106 if os.path.exists(Cnf["Dir::Queue::%s" % (q)] + '/' + dsc_filename):
109 reject("no source found for %s %s (%s)." % (source_package, source_version, f))
111 # Version and file overwrite checks
112 if files[f]["type"] == "deb":
113 reject(Upload.check_binary_against_db(f), "")
114 elif files[f]["type"] == "dsc":
115 reject(Upload.check_source_against_db(f), "")
116 (reject_msg, is_in_incoming) = Upload.check_dsc_against_db(f)
117 reject(reject_msg, "")
119 if reject_message.find("Rejected") != -1:
121 if Options["No-Action"] or Options["Automatic"] or Options["Trainee"]:
124 print "REJECT\n" + reject_message,
125 prompt = "[R]eject, Skip, Quit ?"
127 while prompt.find(answer) == -1:
128 answer = utils.our_raw_input(prompt)
129 m = re_default_answer.match(prompt)
132 answer = answer[:1].upper()
135 Upload.do_reject(0, reject_message)
136 os.unlink(Upload.pkg.changes_file[:-8]+".dak")
146 ################################################################################
148 def indiv_sg_compare (a, b):
149 """Sort by source name, source, version, 'have source', and
150 finally by filename."""
151 # Sort by source version
152 q = apt_pkg.VersionCompare(a["version"], b["version"])
156 # Sort by 'have source'
157 a_has_source = a["architecture"].get("source")
158 b_has_source = b["architecture"].get("source")
159 if a_has_source and not b_has_source:
161 elif b_has_source and not a_has_source:
164 return cmp(a["filename"], b["filename"])
166 ############################################################
168 def sg_compare (a, b):
171 """Sort by have note, source already in database and time of oldest upload."""
173 a_note_state = a["note_state"]
174 b_note_state = b["note_state"]
175 if a_note_state < b_note_state:
177 elif a_note_state > b_note_state:
179 # Sort by source already in database (descending)
180 source_in_database = cmp(a["source_in_database"], b["source_in_database"])
181 if source_in_database:
182 return -source_in_database
184 # Sort by time of oldest upload
185 return cmp(a["oldest"], b["oldest"])
187 def sort_changes(changes_files):
188 """Sort into source groups, then sort each source group by version,
189 have source, filename. Finally, sort the source groups by have
190 note, time of oldest upload of each source upload."""
191 if len(changes_files) == 1:
196 # Read in all the .changes files
197 for filename in changes_files:
199 Upload.pkg.changes_file = filename
202 cache[filename] = copy.copy(Upload.pkg.changes)
203 cache[filename]["filename"] = filename
205 sorted_list.append(filename)
207 # Divide the .changes into per-source groups
209 for filename in cache.keys():
210 source = cache[filename]["source"]
211 if not per_source.has_key(source):
212 per_source[source] = {}
213 per_source[source]["list"] = []
214 per_source[source]["list"].append(cache[filename])
215 # Determine oldest time and have note status for each source group
216 for source in per_source.keys():
217 q = projectB.query("SELECT 1 FROM source WHERE source = '%s'" % source)
219 per_source[source]["source_in_database"] = len(ql)>0
220 source_list = per_source[source]["list"]
221 first = source_list[0]
222 oldest = os.stat(first["filename"])[stat.ST_MTIME]
224 for d in per_source[source]["list"]:
225 mtime = os.stat(d["filename"])[stat.ST_MTIME]
228 have_note += (database.has_new_comment(d["source"], d["version"], True))
229 per_source[source]["oldest"] = oldest
231 per_source[source]["note_state"] = 0; # none
232 elif have_note < len(source_list):
233 per_source[source]["note_state"] = 1; # some
235 per_source[source]["note_state"] = 2; # all
236 per_source[source]["list"].sort(indiv_sg_compare)
237 per_source_items = per_source.items()
238 per_source_items.sort(sg_compare)
239 for i in per_source_items:
240 for j in i[1]["list"]:
241 sorted_list.append(j["filename"])
244 ################################################################################
246 class Section_Completer:
250 q = projectB.query("SELECT section FROM section")
251 for i in q.getresult():
252 self.sections.append(i[0])
254 def complete(self, text, state):
258 for word in self.sections:
260 self.matches.append(word)
262 return self.matches[state]
266 ############################################################
268 class Priority_Completer:
272 q = projectB.query("SELECT priority FROM priority")
273 for i in q.getresult():
274 self.priorities.append(i[0])
276 def complete(self, text, state):
280 for word in self.priorities:
282 self.matches.append(word)
284 return self.matches[state]
288 ################################################################################
290 def print_new (new, indexed, file=sys.stdout):
291 queue.check_valid(new)
294 for pkg in new.keys():
296 section = new[pkg]["section"]
297 priority = new[pkg]["priority"]
298 if new[pkg]["section id"] == -1:
301 if new[pkg]["priority id"] == -1:
305 line = "(%s): %-20s %-20s %-20s" % (index, pkg, priority, section)
307 line = "%-20s %-20s %-20s" % (pkg, priority, section)
308 line = line.strip()+'\n'
310 note = database.get_new_comments(Upload.pkg.changes.get("source"))
316 ################################################################################
318 def index_range (index):
322 return "1-%s" % (index)
324 ################################################################################
325 ################################################################################
328 # Write the current data to a temporary file
329 (fd, temp_filename) = utils.temp_filename()
330 temp_file = os.fdopen(fd, 'w')
331 print_new (new, 0, temp_file)
333 # Spawn an editor on that file
334 editor = os.environ.get("EDITOR","vi")
335 result = os.system("%s %s" % (editor, temp_filename))
337 utils.fubar ("%s invocation failed for %s." % (editor, temp_filename), result)
338 # Read the edited data back in
339 temp_file = utils.open_file(temp_filename)
340 lines = temp_file.readlines()
342 os.unlink(temp_filename)
349 # Pad the list if necessary
350 s[len(s):3] = [None] * (3-len(s))
351 (pkg, priority, section) = s[:3]
352 if not new.has_key(pkg):
353 utils.warn("Ignoring unknown package '%s'" % (pkg))
355 # Strip off any invalid markers, print_new will readd them.
356 if section.endswith("[!]"):
357 section = section[:-3]
358 if priority.endswith("[!]"):
359 priority = priority[:-3]
360 for f in new[pkg]["files"]:
361 Upload.pkg.files[f]["section"] = section
362 Upload.pkg.files[f]["priority"] = priority
363 new[pkg]["section"] = section
364 new[pkg]["priority"] = priority
366 ################################################################################
368 def edit_index (new, index):
369 priority = new[index]["priority"]
370 section = new[index]["section"]
371 ftype = new[index]["type"]
374 print "\t".join([index, priority, section])
378 prompt = "[B]oth, Priority, Section, Done ? "
380 prompt = "[S]ection, Done ? "
381 edit_priority = edit_section = 0
383 while prompt.find(answer) == -1:
384 answer = utils.our_raw_input(prompt)
385 m = re_default_answer.match(prompt)
388 answer = answer[:1].upper()
395 edit_priority = edit_section = 1
401 readline.set_completer(Priorities.complete)
403 while not got_priority:
404 new_priority = utils.our_raw_input("New priority: ").strip()
405 if new_priority not in Priorities.priorities:
406 print "E: '%s' is not a valid priority, try again." % (new_priority)
409 priority = new_priority
413 readline.set_completer(Sections.complete)
415 while not got_section:
416 new_section = utils.our_raw_input("New section: ").strip()
417 if new_section not in Sections.sections:
418 print "E: '%s' is not a valid section, try again." % (new_section)
421 section = new_section
423 # Reset the readline completer
424 readline.set_completer(None)
426 for f in new[index]["files"]:
427 Upload.pkg.files[f]["section"] = section
428 Upload.pkg.files[f]["priority"] = priority
429 new[index]["priority"] = priority
430 new[index]["section"] = section
433 ################################################################################
435 def edit_overrides (new):
446 prompt = "(%s) edit override <n>, Editor, Done ? " % (index_range(index))
449 while not got_answer:
450 answer = utils.our_raw_input(prompt)
451 if not answer.isdigit():
452 answer = answer[:1].upper()
453 if answer == "E" or answer == "D":
455 elif re_isanum.match (answer):
457 if (answer < 1) or (answer > index):
458 print "%s is not a valid index (%s). Please retry." % (answer, index_range(index))
467 edit_index (new, new_index[answer])
471 ################################################################################
474 # Write the current data to a temporary file
475 (fd, temp_filename) = utils.temp_filename()
476 editor = os.environ.get("EDITOR","vi")
479 os.system("%s %s" % (editor, temp_filename))
480 temp_file = utils.open_file(temp_filename)
481 newnote = temp_file.read().rstrip()
484 print utils.prefix_multi_line_string(newnote," ")
485 prompt = "[D]one, Edit, Abandon, Quit ?"
487 while prompt.find(answer) == -1:
488 answer = utils.our_raw_input(prompt)
489 m = re_default_answer.search(prompt)
492 answer = answer[:1].upper()
493 os.unlink(temp_filename)
500 database.add_new_comment(Upload.pkg.changes["source"], Upload.pkg.changes["version"], newnote, utils.whoami(), bool(Options["Trainee"]))
502 ################################################################################
506 less_fd = os.popen("less -R -", 'w', 0)
507 stdout_fd = sys.stdout
510 changes = utils.parse_changes (Upload.pkg.changes_file)
511 examine_package.display_changes(changes['distribution'], Upload.pkg.changes_file)
512 files = Upload.pkg.files
513 for f in files.keys():
514 if files[f].has_key("new"):
515 ftype = files[f]["type"]
517 examine_package.check_deb(changes['distribution'], f)
519 examine_package.check_dsc(changes['distribution'], f)
521 examine_package.output_package_relations()
522 sys.stdout = stdout_fd
524 if e.errno == errno.EPIPE:
525 utils.warn("[examine_package] Caught EPIPE; skipping.")
529 except KeyboardInterrupt:
530 utils.warn("[examine_package] Caught C-c; skipping.")
533 ################################################################################
535 ## FIXME: horribly Debian specific
537 def do_bxa_notification():
538 files = Upload.pkg.files
540 for f in files.keys():
541 if files[f]["type"] == "deb":
542 control = apt_pkg.ParseSection(apt_inst.debExtractControl(utils.open_file(f)))
544 summary += "Package: %s\n" % (control.Find("Package"))
545 summary += "Description: %s\n" % (control.Find("Description"))
546 Upload.Subst["__BINARY_DESCRIPTIONS__"] = summary
547 bxa_mail = utils.TemplateSubst(Upload.Subst,Cnf["Dir::Templates"]+"/process-new.bxa_notification")
548 utils.send_mail(bxa_mail)
550 ################################################################################
552 def add_overrides (new):
553 changes = Upload.pkg.changes
554 files = Upload.pkg.files
555 srcpkg = changes.get("source")
557 projectB.query("BEGIN WORK")
558 for suite in changes["suite"].keys():
559 suite_id = database.get_suite_id(suite)
560 for pkg in new.keys():
561 component_id = database.get_component_id(new[pkg]["component"])
562 type_id = database.get_override_type_id(new[pkg]["type"])
563 priority_id = new[pkg]["priority id"]
564 section_id = new[pkg]["section id"]
565 Logger.log(["%s overrides" % (srcpkg), suite, new[pkg]["component"], new[pkg]["type"], new[pkg]["priority"], new[pkg]["section"]])
566 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))
567 for f in new[pkg]["files"]:
568 if files[f].has_key("new"):
572 projectB.query("COMMIT WORK")
574 if Cnf.FindB("Dinstall::BXANotify"):
575 do_bxa_notification()
577 ################################################################################
579 def prod_maintainer (note):
580 # Here we prepare an editor and get them ready to prod...
581 (fd, temp_filename) = utils.temp_filename()
582 temp_file = os.fdopen(fd, 'w')
585 temp_file.write(line)
587 editor = os.environ.get("EDITOR","vi")
590 os.system("%s %s" % (editor, temp_filename))
591 temp_fh = utils.open_file(temp_filename)
592 prod_message = "".join(temp_fh.readlines())
594 print "Prod message:"
595 print utils.prefix_multi_line_string(prod_message," ",include_blank_lines=1)
596 prompt = "[P]rod, Edit, Abandon, Quit ?"
598 while prompt.find(answer) == -1:
599 answer = utils.our_raw_input(prompt)
600 m = re_default_answer.search(prompt)
603 answer = answer[:1].upper()
604 os.unlink(temp_filename)
610 # Otherwise, do the proding...
611 user_email_address = utils.whoami() + " <%s>" % (
612 Cnf["Dinstall::MyAdminAddress"])
616 Subst["__FROM_ADDRESS__"] = user_email_address
617 Subst["__PROD_MESSAGE__"] = prod_message
618 Subst["__CC__"] = "Cc: " + Cnf["Dinstall::MyEmailAddress"]
620 prod_mail_message = utils.TemplateSubst(
621 Subst,Cnf["Dir::Templates"]+"/process-new.prod")
623 # Send the prod mail if appropriate
624 if not Cnf["Dinstall::Options::No-Mail"]:
625 utils.send_mail(prod_mail_message)
627 print "Sent proding message"
629 ################################################################################
633 files = Upload.pkg.files
634 changes = Upload.pkg.changes
636 # Make a copy of distribution we can happily trample on
637 changes["suite"] = copy.copy(changes["distribution"])
639 # Fix up the list of target suites
640 for suite in changes["suite"].keys():
641 override = Cnf.Find("Suite::%s::OverrideSuite" % (suite))
643 (olderr, newerr) = (database.get_suite_id(suite) == -1,
644 database.get_suite_id(override) == -1)
646 (oinv, newinv) = ("", "")
647 if olderr: oinv = "invalid "
648 if newerr: ninv = "invalid "
649 print "warning: overriding %ssuite %s to %ssuite %s" % (
650 oinv, suite, ninv, override)
651 del changes["suite"][suite]
652 changes["suite"][override] = 1
654 for suite in changes["suite"].keys():
655 suite_id = database.get_suite_id(suite)
657 utils.fubar("%s has invalid suite '%s' (possibly overriden). say wha?" % (changes, suite))
659 # The main NEW processing loop
662 # Find out what's new
663 new = queue.determine_new(changes, files, projectB)
669 if Options["No-Action"] or Options["Automatic"]:
672 (broken, note) = print_new(new, 0)
675 if not broken and not note:
676 prompt = "Add overrides, "
678 print "W: [!] marked entries must be fixed before package can be processed."
680 print "W: note must be removed before package can be processed."
681 prompt += "RemOve all notes, Remove note, "
683 prompt += "Edit overrides, Check, Manual reject, Note edit, Prod, [S]kip, Quit ?"
685 while prompt.find(answer) == -1:
686 answer = utils.our_raw_input(prompt)
687 m = re_default_answer.search(prompt)
690 answer = answer[:1].upper()
692 if answer == 'A' and not Options["Trainee"]:
695 done = add_overrides (new)
696 Logger.log([utils.getusername(), "NEW ACCEPT: %s" % (Upload.pkg.changes_file)])
697 except CantGetLockError:
698 print "Hello? Operator! Give me the number for 911!"
699 print "Dinstall in the locked area, cant process packages, come back later"
702 elif answer == 'E' and not Options["Trainee"]:
703 new = edit_overrides (new)
704 elif answer == 'M' and not Options["Trainee"]:
705 aborted = Upload.do_reject(manual=1,
706 reject_message=Options["Manual-Reject"],
707 note=database.get_new_comments(changes.get("source", "")))
709 Logger.log([utils.getusername(), "NEW REJECT: %s" % (Upload.pkg.changes_file)])
710 os.unlink(Upload.pkg.changes_file[:-8]+".dak")
713 edit_note(database.get_new_comments(changes.get("source", "")))
714 elif answer == 'P' and not Options["Trainee"]:
715 prod_maintainer(database.get_new_comments(changes.get("source", "")))
716 Logger.log([utils.getusername(), "NEW PROD: %s" % (Upload.pkg.changes_file)])
717 elif answer == 'R' and not Options["Trainee"]:
718 confirm = utils.our_raw_input("Really clear note (y/N)? ").lower()
720 database.delete_new_comments(changes.get("source"), changes.get("version"))
721 elif answer == 'O' and not Options["Trainee"]:
722 confirm = utils.our_raw_input("Really clear all notes (y/N)? ").lower()
724 database.delete_all_new_comments(changes.get("source"))
731 ################################################################################
732 ################################################################################
733 ################################################################################
735 def usage (exit_code=0):
736 print """Usage: dak process-new [OPTION]... [CHANGES]...
737 -a, --automatic automatic run
738 -h, --help show this help and exit.
739 -C, --comments-dir=DIR use DIR as comments-dir, for [o-]p-u-new
740 -m, --manual-reject=MSG manual reject with `msg'
741 -n, --no-action don't do anything
742 -t, --trainee FTP Trainee mode
743 -V, --version display the version number and exit"""
746 ################################################################################
749 global Cnf, Options, Logger, Upload, projectB, Sections, Priorities
751 Cnf = utils.get_conf()
753 Arguments = [('a',"automatic","Process-New::Options::Automatic"),
754 ('h',"help","Process-New::Options::Help"),
755 ('C',"comments-dir","Process-New::Options::Comments-Dir", "HasArg"),
756 ('m',"manual-reject","Process-New::Options::Manual-Reject", "HasArg"),
757 ('t',"trainee","Process-New::Options::Trainee"),
758 ('n',"no-action","Process-New::Options::No-Action")]
760 for i in ["automatic", "help", "manual-reject", "no-action", "version", "comments-dir", "trainee"]:
761 if not Cnf.has_key("Process-New::Options::%s" % (i)):
762 Cnf["Process-New::Options::%s" % (i)] = ""
764 changes_files = apt_pkg.ParseCommandLine(Cnf,Arguments,sys.argv)
765 if len(changes_files) == 0 and not Cnf.get("Process-New::Options::Comments-Dir",""):
766 changes_files = utils.get_changes_files(Cnf["Dir::Queue::New"])
768 Options = Cnf.SubTree("Process-New::Options")
773 Upload = queue.Upload(Cnf)
775 if not Options["No-Action"]:
777 Logger = Upload.Logger = logging.Logger(Cnf, "process-new")
778 except CantOpenError, e:
779 Options["Trainee"] = "True"
781 projectB = Upload.projectB
783 Sections = Section_Completer()
784 Priorities = Priority_Completer()
785 readline.parse_and_bind("tab: complete")
789 ################################################################################
794 files = Upload.pkg.files
798 for f in files.keys():
799 if files[f]["type"] == "byhand":
800 if os.path.exists(f):
801 print "W: %s still present; please process byhand components and try again." % (f)
807 if Options["No-Action"]:
810 if Options["Automatic"] and not Options["No-Action"]:
812 prompt = "[A]ccept, Manual reject, Skip, Quit ?"
814 prompt = "Manual reject, [S]kip, Quit ?"
816 while prompt.find(answer) == -1:
817 answer = utils.our_raw_input(prompt)
818 m = re_default_answer.search(prompt)
821 answer = answer[:1].upper()
829 Logger.log([utils.getusername(), "BYHAND ACCEPT: %s" % (Upload.pkg.changes_file)])
830 except CantGetLockError:
831 print "Hello? Operator! Give me the number for 911!"
832 print "Dinstall in the locked area, cant process packages, come back later"
834 Logger.log([utils.getusername(), "BYHAND REJECT: %s" % (Upload.pkg.changes_file)])
835 Upload.do_reject(1, Options["Manual-Reject"])
836 os.unlink(Upload.pkg.changes_file[:-8]+".dak")
844 ################################################################################
846 def check_daily_lock():
848 Raises CantGetLockError if the dinstall daily.lock exists.
852 os.open(Cnf["Process-New::DinstallLockFile"], os.O_RDONLY | os.O_CREAT | os.O_EXCL)
854 if e.errno == errno.EEXIST or e.errno == errno.EACCES:
855 raise CantGetLockError
857 os.unlink(Cnf["Process-New::DinstallLockFile"])
860 @contextlib.contextmanager
861 def lock_package(package):
863 Lock C{package} so that noone else jumps in processing it.
865 @type package: string
866 @param package: source package name to lock
869 path = os.path.join(Cnf["Process-New::LockDir"], package)
871 fd = os.open(path, os.O_CREAT | os.O_EXCL | os.O_RDONLY)
873 if e.errno == errno.EEXIST or e.errno == errno.EACCES:
874 user = pwd.getpwuid(os.stat(path)[stat.ST_UID])[4].split(',')[0].replace('.', '')
875 raise AlreadyLockedError, user
882 def move_to_dir (dest, perms=0660, changesperms=0664):
883 utils.move (Upload.pkg.changes_file, dest, perms=changesperms)
884 file_keys = Upload.pkg.files.keys()
886 utils.move (f, dest, perms=perms)
888 def is_source_in_queue_dir(qdir):
889 entries = [ x for x in os.listdir(qdir) if x.startswith(Upload.pkg.changes["source"])
890 and x.endswith(".changes") ]
891 for entry in entries:
893 u = queue.Upload(Cnf)
894 u.pkg.changes_file = os.path.join(qdir, entry)
896 if not u.pkg.changes["architecture"].has_key("source"):
897 # another binary upload, ignore
899 if Upload.pkg.changes["version"] != u.pkg.changes["version"]:
900 # another version, ignore
906 def move_to_holding(suite, queue_dir):
907 print "Moving to %s holding area." % (suite.upper(),)
908 if Options["No-Action"]:
910 Logger.log(["Moving to %s" % (suite,), Upload.pkg.changes_file])
911 Upload.dump_vars(queue_dir)
912 move_to_dir(queue_dir, perms=0664)
913 os.unlink(Upload.pkg.changes_file[:-8]+".dak")
916 if Options["No-Action"]:
918 (summary, short_summary) = Upload.build_summaries()
919 Upload.accept(summary, short_summary, targetdir=Cnf["Dir::Queue::Newstage"])
920 os.unlink(Upload.pkg.changes_file[:-8]+".dak")
922 def do_accept_stableupdate(suite, q):
923 queue_dir = Cnf["Dir::Queue::%s" % (q,)]
924 if not Upload.pkg.changes["architecture"].has_key("source"):
925 # It is not a sourceful upload. So its source may be either in p-u
926 # holding, in new, in accepted or already installed.
927 if is_source_in_queue_dir(queue_dir):
928 # It's in p-u holding, so move it there.
929 print "Binary-only upload, source in %s." % (q,)
930 move_to_holding(suite, queue_dir)
931 elif Upload.source_exists(Upload.pkg.changes["source"],
932 Upload.pkg.changes["version"]):
933 # dak tells us that there is source available. At time of
934 # writing this means that it is installed, so put it into
936 print "Binary-only upload, source installed."
937 Logger.log([utils.getusername(), "PUNEW ACCEPT: %s" % (Upload.pkg.changes_file)])
939 elif is_source_in_queue_dir(Cnf["Dir::Queue::Accepted"]):
940 # The source is in accepted, the binary cleared NEW: accept it.
941 print "Binary-only upload, source in accepted."
942 Logger.log([utils.getusername(), "PUNEW ACCEPT: %s" % (Upload.pkg.changes_file)])
944 elif is_source_in_queue_dir(Cnf["Dir::Queue::New"]):
945 # It's in NEW. We expect the source to land in p-u holding
947 print "Binary-only upload, source in new."
948 move_to_holding(suite, queue_dir)
949 elif is_source_in_queue_dir(Cnf["Dir::Queue::Newstage"]):
950 # It's in newstage. Accept into the holding area
951 print "Binary-only upload, source in newstage."
952 Logger.log([utils.getusername(), "PUNEW ACCEPT: %s" % (Upload.pkg.changes_file)])
955 # No case applicable. Bail out. Return will cause the upload
958 print "Stable update failed. Source not found."
961 # We are handling a sourceful upload. Move to accepted if currently
962 # in p-u holding and to p-u holding otherwise.
963 if is_source_in_queue_dir(queue_dir):
964 print "Sourceful upload in %s, accepting." % (q,)
967 move_to_holding(suite, queue_dir)
971 if not Options["No-Action"]:
972 (summary, short_summary) = Upload.build_summaries()
973 if Cnf.FindB("Dinstall::SecurityQueueHandling"):
974 Upload.dump_vars(Cnf["Dir::Queue::Embargoed"])
975 move_to_dir(Cnf["Dir::Queue::Embargoed"])
976 Upload.queue_build("embargoed", Cnf["Dir::Queue::Embargoed"])
977 # Check for override disparities
978 Upload.Subst["__SUMMARY__"] = summary
980 # Stable updates need to be copied to proposed-updates holding
981 # area instead of accepted. Sourceful uploads need to go
982 # to it directly, binaries only if the source has not yet been
984 for suite, q in [("proposed-updates", "ProposedUpdates"),
985 ("oldstable-proposed-updates", "OldProposedUpdates")]:
986 if not Upload.pkg.changes["distribution"].has_key(suite):
988 return do_accept_stableupdate(suite, q)
989 # Just a normal upload, accept it...
992 def check_status(files):
994 for f in files.keys():
995 if files[f]["type"] == "byhand":
997 elif files[f].has_key("new"):
1001 def do_pkg(changes_file):
1002 Upload.pkg.changes_file = changes_file
1004 Upload.update_vars()
1005 Upload.update_subst()
1006 files = Upload.pkg.files
1009 with lock_package(Upload.pkg.changes["source"]):
1013 (new, byhand) = check_status(files)
1019 (new, byhand) = check_status(files)
1021 if not new and not byhand:
1025 except CantGetLockError:
1026 print "Hello? Operator! Give me the number for 911!"
1027 print "Dinstall in the locked area, cant process packages, come back later"
1028 except AlreadyLockedError, e:
1029 print "Seems to be locked by %s already, skipping..." % (e)
1031 ################################################################################
1034 accept_count = Upload.accept_count
1035 accept_bytes = Upload.accept_bytes
1039 if accept_count > 1:
1041 sys.stderr.write("Accepted %d package %s, %s.\n" % (accept_count, sets, utils.size_type(int(accept_bytes))))
1042 Logger.log([utils.getusername(), "total",accept_count,accept_bytes])
1044 if not Options["No-Action"] and not Options["Trainee"]:
1047 ################################################################################
1049 def do_comments(dir, opref, npref, line, fn):
1050 for comm in [ x for x in os.listdir(dir) if x.startswith(opref) ]:
1051 lines = open("%s/%s" % (dir, comm)).readlines()
1052 if len(lines) == 0 or lines[0] != line + "\n": continue
1053 changes_files = [ x for x in os.listdir(".") if x.startswith(comm[7:]+"_")
1054 and x.endswith(".changes") ]
1055 changes_files = sort_changes(changes_files)
1056 for f in changes_files:
1057 f = utils.validate_changes_file_arg(f, 0)
1060 fn(f, "".join(lines[1:]))
1062 if opref != npref and not Options["No-Action"]:
1063 newcomm = npref + comm[len(opref):]
1064 os.rename("%s/%s" % (dir, comm), "%s/%s" % (dir, newcomm))
1066 ################################################################################
1068 def comment_accept(changes_file, comments):
1069 Upload.pkg.changes_file = changes_file
1071 Upload.update_vars()
1072 Upload.update_subst()
1073 files = Upload.pkg.files
1076 return # dak wants to REJECT, crap
1078 (new, byhand) = check_status(files)
1079 if not new and not byhand:
1082 ################################################################################
1084 def comment_reject(changes_file, comments):
1085 Upload.pkg.changes_file = changes_file
1087 Upload.update_vars()
1088 Upload.update_subst()
1091 pass # dak has its own reasons to reject as well, which is fine
1094 print "REJECT\n" + reject_message,
1095 if not Options["No-Action"]:
1096 Upload.do_reject(0, reject_message)
1097 os.unlink(Upload.pkg.changes_file[:-8]+".dak")
1099 ################################################################################
1102 changes_files = init()
1103 if len(changes_files) > 50:
1104 sys.stderr.write("Sorting changes...\n")
1105 changes_files = sort_changes(changes_files)
1107 # Kill me now? **FIXME**
1108 Cnf["Dinstall::Options::No-Mail"] = ""
1109 bcc = "X-DAK: dak process-new\nX-Katie: lisa $Revision: 1.31 $"
1110 if Cnf.has_key("Dinstall::Bcc"):
1111 Upload.Subst["__BCC__"] = bcc + "\nBcc: %s" % (Cnf["Dinstall::Bcc"])
1113 Upload.Subst["__BCC__"] = bcc
1115 commentsdir = Cnf.get("Process-New::Options::Comments-Dir","")
1117 if changes_files != []:
1118 sys.stderr.write("Can't specify any changes files if working with comments-dir")
1120 do_comments(commentsdir, "ACCEPT.", "ACCEPTED.", "OK", comment_accept)
1121 do_comments(commentsdir, "REJECT.", "REJECTED.", "NOTOK", comment_reject)
1123 for changes_file in changes_files:
1124 changes_file = utils.validate_changes_file_arg(changes_file, 0)
1125 if not changes_file:
1127 print "\n" + changes_file
1129 do_pkg (changes_file)
1133 ################################################################################
1135 if __name__ == '__main__':