mirror of
https://git.launchpad.net/~ubuntu-release/britney/+git/britney2-ubuntu
synced 2025-04-03 22:31:12 +00:00
Initial import, excuses generation almost working.
This commit is contained in:
commit
b1603db2e4
BIN
.britney.conf.swp
Normal file
BIN
.britney.conf.swp
Normal file
Binary file not shown.
9
INSTALL
Normal file
9
INSTALL
Normal file
@ -0,0 +1,9 @@
|
||||
INSTALL for britney v2.0
|
||||
========================
|
||||
|
||||
Requirements:
|
||||
-------------
|
||||
|
||||
* Python 2.4 (>= 2.4.2) http://www.python.org
|
||||
* Python APT/DPKG bindings aptitude install python2.4-apt
|
||||
|
340
LICENSE
Normal file
340
LICENSE
Normal file
@ -0,0 +1,340 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||||
51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Library General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Library General
|
||||
Public License instead of this License.
|
5
README
Normal file
5
README
Normal file
@ -0,0 +1,5 @@
|
||||
README for britney v2.0
|
||||
=======================
|
||||
|
||||
Please write here some useful stuff.
|
||||
|
5
TODO
Normal file
5
TODO
Normal file
@ -0,0 +1,5 @@
|
||||
TODO list for britney
|
||||
=====================
|
||||
|
||||
- check if it is need to consider fake source packages
|
||||
|
44
britney.conf
Normal file
44
britney.conf
Normal file
@ -0,0 +1,44 @@
|
||||
# Configuration file for britney
|
||||
|
||||
# Paths for control files
|
||||
TESTING = ../data/testing
|
||||
TPU = ../data/testing-proposed-updates
|
||||
UNSTABLE = ../data/unstable
|
||||
|
||||
# Output
|
||||
EXCUSES_OUTPUT = update.EXCUSES_py
|
||||
|
||||
# List of release architectures
|
||||
# ARCHITECTURES = i386 sparc alpha powerpc arm m68k hppa ia64 mips mipsel s390 amd64
|
||||
ARCHITECTURES = i386 amd64
|
||||
|
||||
# if you're not in this list, arch: all packages are allowed to break on you
|
||||
NOBREAKALL_ARCHES = i386
|
||||
|
||||
# if you're in this list, your packages may not stay in sync with the source
|
||||
FUCKED_ARCHES = m68k sparc s390 amd64
|
||||
|
||||
# if you're in this list, your uninstallability count may increase
|
||||
BREAK_ARCHES = m68k sparc s390 amd64
|
||||
|
||||
# priorities and delays
|
||||
MINDAYS_LOW = 10
|
||||
MINDAYS_MEDIUM = 5
|
||||
MINDAYS_HIGH = 2
|
||||
MINDAYS_CRITICAL = 0
|
||||
MINDAYS_EMERGENCY = 0
|
||||
DEFAULT_URGENCY = low
|
||||
|
||||
# approvers
|
||||
APPROVERS = ajt security-team ftpmaster cjwatson vorlon
|
||||
|
||||
# hint permissions
|
||||
HINTS_AJT = ALL
|
||||
HINTS_RMURRAY = ALL
|
||||
HINTS_CJWATSON = ALL
|
||||
HINTS_VORLON = ALL
|
||||
HINTS_ABA = ALL
|
||||
HINTS_JOEYH = STANDARD force
|
||||
HINTS_DJPIG = STANDARD
|
||||
HINTS_FREEZE = block block-all
|
||||
HINTS_FTPTEAM = block
|
730
britney.py
Normal file
730
britney.py
Normal file
@ -0,0 +1,730 @@
|
||||
#!/usr/bin/env python2.4
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (C) 2001-2004 Anthony Towns <ajt@debian.org>
|
||||
# Andreas Barth <aba@debian.org>
|
||||
# Fabio Tranchitella <kobold@debian.org>
|
||||
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import string
|
||||
import time
|
||||
import optparse
|
||||
|
||||
import apt_pkg
|
||||
|
||||
from excuse import Excuse
|
||||
|
||||
VERSION = '2.0.alpha1'
|
||||
|
||||
|
||||
class Britney:
|
||||
"""Debian testing updater script"""
|
||||
|
||||
BINARY_FIELDS = ('Version', 'Pre-Depends', 'Depends', 'Conflicts', 'Provides', 'Source', 'Architecture', 'Version')
|
||||
SOURCE_FIELDS = ('Version', 'Maintainer', 'Section')
|
||||
|
||||
HINTS_STANDARD = ("easy", "hint", "remove", "block", "unblock", "urgent", "approve")
|
||||
HINTS_ALL = ("force", "force-hint", "block-all") + HINTS_STANDARD
|
||||
|
||||
def __init__(self):
|
||||
"""Class constructor method: initialize and populate the data lists"""
|
||||
self.__parse_arguments()
|
||||
apt_pkg.init()
|
||||
self.date_now = int(((time.time() / (60*60)) - 15) / 24)
|
||||
self.sources = {'testing': self.read_sources(self.options.testing),
|
||||
'unstable': self.read_sources(self.options.unstable),
|
||||
'tpu': self.read_sources(self.options.tpu),}
|
||||
self.binaries = {'testing': {}, 'unstable': {}, 'tpu': {}}
|
||||
for arch in self.options.architectures.split():
|
||||
self.binaries['testing'][arch] = self.read_binaries(self.options.testing, "testing", arch)
|
||||
self.binaries['unstable'][arch] = self.read_binaries(self.options.unstable, "unstable", arch)
|
||||
self.binaries['tpu'][arch] = self.read_binaries(self.options.tpu, "tpu", arch)
|
||||
self.bugs = {'unstable': self.read_bugs(self.options.unstable),
|
||||
'testing': self.read_bugs(self.options.testing),}
|
||||
self.normalize_bugs()
|
||||
self.dates = self.read_dates(self.options.testing)
|
||||
self.urgencies = self.read_urgencies(self.options.testing)
|
||||
self.approvals = self.read_approvals(self.options.tpu)
|
||||
self.hints = self.read_hints(self.options.unstable)
|
||||
self.excuses = []
|
||||
|
||||
def __parse_arguments(self):
|
||||
"""Parse command line arguments"""
|
||||
self.parser = optparse.OptionParser(version="%prog " + VERSION)
|
||||
self.parser.add_option("-v", "", action="count", dest="verbose", help="enable verbose output")
|
||||
self.parser.add_option("-c", "--config", action="store", dest="config",
|
||||
default="/etc/britney.conf", help="path for the configuration file")
|
||||
(self.options, self.args) = self.parser.parse_args()
|
||||
if not os.path.isfile(self.options.config):
|
||||
self.__log("Unable to read the configuration file (%s), exiting!" % self.options.config, type="E")
|
||||
sys.exit(1)
|
||||
self.MINDAYS = {}
|
||||
self.HINTS = {}
|
||||
for k, v in [map(string.strip,r.split('=', 1)) for r in file(self.options.config) if '=' in r and not r.strip().startswith('#')]:
|
||||
if k.startswith("MINDAYS_"):
|
||||
self.MINDAYS[k.split("_")[1].lower()] = int(v)
|
||||
elif k.startswith("HINTS_"):
|
||||
self.HINTS[k.split("_")[1].lower()] = \
|
||||
reduce(lambda x,y: x+y, [hasattr(self, "HINTS_" + i) and getattr(self, "HINTS_" + i) or (i,) for i in v.split()])
|
||||
else:
|
||||
setattr(self.options, k.lower(), v)
|
||||
|
||||
def __log(self, msg, type="I"):
|
||||
"""Print info messages according to verbosity level"""
|
||||
if self.options.verbose or type in ("E", "W"):
|
||||
print "%s: [%s] - %s" % (type, time.asctime(), msg)
|
||||
|
||||
# Data reading/writing
|
||||
|
||||
def read_sources(self, basedir):
|
||||
"""Read the list of source packages from the specified directory"""
|
||||
sources = {}
|
||||
package = None
|
||||
filename = os.path.join(basedir, "Sources")
|
||||
self.__log("Loading source packages from %s" % filename)
|
||||
for l in open(filename):
|
||||
if l.startswith(' ') or ':' not in l: continue
|
||||
fields = map(string.strip, l.split(":",1))
|
||||
if fields[0] == 'Package':
|
||||
package = fields[1]
|
||||
sources[package] = dict([(k.lower(), None) for k in self.SOURCE_FIELDS] + [('binaries', [])])
|
||||
elif fields[0] in self.SOURCE_FIELDS:
|
||||
sources[package][fields[0].lower()] = fields[1]
|
||||
return sources
|
||||
|
||||
def read_bugs(self, basedir):
|
||||
"""Read the RC bugs count from the specified directory"""
|
||||
bugs = {}
|
||||
filename = os.path.join(basedir, "Bugs")
|
||||
self.__log("Loading RC bugs count from %s" % filename)
|
||||
for line in open(filename):
|
||||
l = line.strip().split()
|
||||
if len(l) != 2: continue
|
||||
try:
|
||||
bugs[l[0]] = int(l[1])
|
||||
except ValueError:
|
||||
self.__log("Bugs, unable to parse \"%s\"" % line, type="E")
|
||||
return bugs
|
||||
|
||||
def maxver(self, pkg, dist):
|
||||
maxver = None
|
||||
if self.sources[dist].has_key(pkg):
|
||||
maxver = self.sources[dist][pkg]['version']
|
||||
for arch in self.options.architectures.split():
|
||||
if not self.binaries[dist][arch][0].has_key(pkg): continue
|
||||
pkgv = self.binaries[dist][arch][0][pkg]['version']
|
||||
if maxver == None or apt_pkg.VersionCompare(pkgv, maxver) > 0:
|
||||
maxver = pkgv
|
||||
return maxver
|
||||
|
||||
def normalize_bugs(self):
|
||||
"""Normalize the RC bugs count for testing and unstable"""
|
||||
for pkg in set(self.bugs['testing'].keys() + self.bugs['unstable'].keys()):
|
||||
if not self.bugs['testing'].has_key(pkg):
|
||||
self.bugs['testing'][pkg] = 0
|
||||
elif not self.bugs['unstable'].has_key(pkg):
|
||||
self.bugs['unstable'][pkg] = 0
|
||||
|
||||
maxvert = self.maxver(pkg, 'testing')
|
||||
if maxvert == None or \
|
||||
self.bugs['testing'][pkg] == self.bugs['unstable'][pkg]:
|
||||
continue
|
||||
|
||||
maxveru = self.maxver(pkg, 'unstable')
|
||||
if maxveru == None:
|
||||
continue
|
||||
elif apt_pkg.VersionCompare(maxvert, maxveru) >= 0:
|
||||
self.bugs['testing'][pkg] = self.bugs['unstable'][pkg]
|
||||
|
||||
def read_dates(self, basedir):
|
||||
"""Read the upload data for the packages from the specified directory"""
|
||||
dates = {}
|
||||
filename = os.path.join(basedir, "Dates")
|
||||
self.__log("Loading upload data from %s" % filename)
|
||||
for line in open(filename):
|
||||
l = line.strip().split()
|
||||
if len(l) != 3: continue
|
||||
try:
|
||||
dates[l[0]] = (l[1], int(l[2]))
|
||||
except ValueError:
|
||||
self.__log("Dates, unable to parse \"%s\"" % line, type="E")
|
||||
return dates
|
||||
|
||||
def read_urgencies(self, basedir):
|
||||
"""Read the upload urgency of the packages from the specified directory"""
|
||||
urgencies = {}
|
||||
filename = os.path.join(basedir, "Urgency")
|
||||
self.__log("Loading upload urgencies from %s" % filename)
|
||||
for line in open(filename):
|
||||
l = line.strip().split()
|
||||
if len(l) != 3: continue
|
||||
|
||||
urgency_old = urgencies.get(l[0], self.options.default_urgency)
|
||||
mindays_old = self.MINDAYS.get(urgency_old, self.MINDAYS[self.options.default_urgency])
|
||||
mindays_new = self.MINDAYS.get(l[2], self.MINDAYS[self.options.default_urgency])
|
||||
if mindays_old <= mindays_new:
|
||||
continue
|
||||
tsrcv = self.sources['testing'].get(l[0], None)
|
||||
if tsrcv and apt_pkg.VersionCompare(tsrcv['version'], l[1]) >= 0:
|
||||
continue
|
||||
usrcv = self.sources['unstable'].get(l[0], None)
|
||||
if not usrcv or apt_pkg.VersionCompare(usrcv['version'], l[1]) < 0:
|
||||
continue
|
||||
urgencies[l[0]] = l[2]
|
||||
|
||||
return urgencies
|
||||
|
||||
def read_approvals(self, basedir):
|
||||
"""Read the approvals data from the specified directory"""
|
||||
approvals = {}
|
||||
for approver in self.options.approvers.split():
|
||||
filename = os.path.join(basedir, "Approved", approver)
|
||||
self.__log("Loading approvals list from %s" % filename)
|
||||
for line in open(filename):
|
||||
l = line.strip().split()
|
||||
if len(l) != 2: continue
|
||||
approvals["%s_%s" % (l[0], l[1])] = approver
|
||||
return approvals
|
||||
|
||||
def read_hints(self, basedir):
|
||||
"""Read the approvals data from the specified directory"""
|
||||
hints = dict([(k,[]) for k in self.HINTS_ALL])
|
||||
|
||||
for who in self.HINTS.keys():
|
||||
filename = os.path.join(basedir, "Hints", who)
|
||||
self.__log("Loading hints list from %s" % filename)
|
||||
for line in open(filename):
|
||||
line = line.strip()
|
||||
if line == "": continue
|
||||
l = line.split()
|
||||
if l[0] == 'finished':
|
||||
break
|
||||
elif l[0] not in self.HINTS[who]:
|
||||
continue
|
||||
elif l[0] in ["easy", "hint", "force-hint"]:
|
||||
hints[l[0]].append((who, [k.split("/") for k in l if "/" in k]))
|
||||
elif l[0] in ["block-all"]:
|
||||
hints[l[0]].extend([(y, who) for y in l[1:]])
|
||||
elif l[0] in ["block"]:
|
||||
hints[l[0]].extend([(y, who) for y in l[1:]])
|
||||
elif l[0] in ["remove", "approve", "unblock", "force", "urgent"]:
|
||||
hints[l[0]].extend([(k.split("/")[0], (k.split("/")[1],who) ) for k in l if "/" in k])
|
||||
|
||||
for x in ["block", "block-all", "unblock", "force", "urgent", "remove"]:
|
||||
z = {}
|
||||
for a, b in hints[x]:
|
||||
if z.has_key(a):
|
||||
self.__log("Overriding %s[%s] = %s with %s" % (x, a, z[a], b), type="W")
|
||||
z[a] = b
|
||||
hints[x] = z
|
||||
|
||||
return hints
|
||||
|
||||
def read_binaries(self, basedir, distribution, arch):
|
||||
"""Read the list of binary packages from the specified directory"""
|
||||
packages = {}
|
||||
package = None
|
||||
filename = os.path.join(basedir, "Packages_%s" % arch)
|
||||
self.__log("Loading binary packages from %s" % filename)
|
||||
for l in open(filename):
|
||||
if l.startswith(' ') or ':' not in l: continue
|
||||
fields = map(string.strip, l.split(":",1))
|
||||
if fields[0] == 'Package':
|
||||
package = fields[1]
|
||||
packages[package] = dict([(k.lower(), None) for k in self.BINARY_FIELDS] + [('rdepends', [])])
|
||||
packages[package]['source'] = package
|
||||
packages[package]['source-ver'] = None
|
||||
elif fields[0] == 'Source':
|
||||
packages[package][fields[0].lower()] = fields[1].split(" ")[0]
|
||||
if "(" in fields[1]:
|
||||
packages[package]['source-ver'] = fields[1].split("(")[1].split(")")[0]
|
||||
elif fields[0] in self.BINARY_FIELDS:
|
||||
packages[package][fields[0].lower()] = fields[1]
|
||||
|
||||
provides = {}
|
||||
for pkgname in packages:
|
||||
if not packages[pkgname]['source-ver']:
|
||||
packages[pkgname]['source-ver'] = packages[pkgname]['version']
|
||||
if packages[pkgname]['source'] in self.sources[distribution]:
|
||||
self.sources[distribution][packages[pkgname]['source']]['binaries'].append(pkgname + "/" + arch)
|
||||
if not packages[pkgname]['provides']:
|
||||
continue
|
||||
parts = map(string.strip, packages[pkgname]["provides"].split(","))
|
||||
del packages[pkgname]["provides"]
|
||||
for p in parts:
|
||||
if p in provides:
|
||||
provides[p].append(pkgname)
|
||||
else:
|
||||
provides[p] = [pkgname]
|
||||
|
||||
for pkgname in packages:
|
||||
dependencies = []
|
||||
if packages[pkgname]['depends']:
|
||||
packages[pkgname]['depends-txt'] = packages[pkgname]['depends']
|
||||
packages[pkgname]['depends'] = apt_pkg.ParseDepends(packages[pkgname]['depends'])
|
||||
dependencies.extend(packages[pkgname]['depends'])
|
||||
if packages[pkgname]['pre-depends']:
|
||||
packages[pkgname]['pre-depends-txt'] = packages[pkgname]['pre-depends']
|
||||
packages[pkgname]['pre-depends'] = apt_pkg.ParseDepends(packages[pkgname]['pre-depends'])
|
||||
dependencies.extend(packages[pkgname]['pre-depends'])
|
||||
for p in dependencies:
|
||||
for a in p:
|
||||
if a[0] not in packages: continue
|
||||
packages[a[0]]['rdepends'].append((pkgname, a[1], a[2]))
|
||||
|
||||
return (packages, provides)
|
||||
|
||||
# Package analisys
|
||||
|
||||
def should_remove_source(self, pkg):
|
||||
"""Check if a source package should be removed from testing"""
|
||||
if self.sources['unstable'].has_key(pkg):
|
||||
return False
|
||||
src = self.sources['testing'][pkg]
|
||||
excuse = Excuse("-" + pkg)
|
||||
excuse.set_vers(src['version'], None)
|
||||
src['maintainer'] and excuse.set_maint(src['maintainer'].strip())
|
||||
src['section'] and excuse.set_section(src['section'].strip())
|
||||
excuse.addhtml("Valid candidate")
|
||||
self.excuses.append(excuse)
|
||||
return True
|
||||
|
||||
def same_source(self, sv1, sv2):
|
||||
if sv1 == sv2:
|
||||
return 1
|
||||
|
||||
m = re.match(r'^(.*)\+b\d+$', sv1)
|
||||
if m: sv1 = m.group(1)
|
||||
m = re.match(r'^(.*)\+b\d+$', sv2)
|
||||
if m: sv2 = m.group(1)
|
||||
|
||||
if sv1 == sv2:
|
||||
return 1
|
||||
|
||||
if re.search("-", sv1) or re.search("-", sv2):
|
||||
m = re.match(r'^(.*-[^.]+)\.0\.\d+$', sv1)
|
||||
if m: sv1 = m.group(1)
|
||||
m = re.match(r'^(.*-[^.]+\.[^.]+)\.\d+$', sv1)
|
||||
if m: sv1 = m.group(1)
|
||||
|
||||
m = re.match(r'^(.*-[^.]+)\.0\.\d+$', sv2)
|
||||
if m: sv2 = m.group(1)
|
||||
m = re.match(r'^(.*-[^.]+\.[^.]+)\.\d+$', sv2)
|
||||
if m: sv2 = m.group(1)
|
||||
|
||||
return (sv1 == sv2)
|
||||
else:
|
||||
m = re.match(r'^([^-]+)\.0\.\d+$', sv1)
|
||||
if m and sv2 == m.group(1): return 1
|
||||
|
||||
m = re.match(r'^([^-]+)\.0\.\d+$', sv2)
|
||||
if m and sv1 == m.group(1): return 1
|
||||
|
||||
return 0
|
||||
|
||||
def get_dependency_solvers(self, block, arch, distribution):
|
||||
packages = []
|
||||
missing = []
|
||||
|
||||
for name, version, op in block:
|
||||
real_package = False
|
||||
if name in self.binaries[distribution][arch][0]:
|
||||
real_package = True
|
||||
package = self.binaries[distribution][arch][0][name]
|
||||
if op == '' and version == '' or apt_pkg.CheckDep(package['version'], op, version):
|
||||
packages.append(name)
|
||||
return (True, packages)
|
||||
|
||||
# TODO: this would be enough according to policy, but not according to britney v.1
|
||||
#if op == '' and version == '' and name in self.binaries['unstable'][arch][1]:
|
||||
# # packages.extend(self.binaries['unstable'][arch][1][name])
|
||||
# return (True, packages)
|
||||
|
||||
if name in self.binaries['unstable'][arch][1]:
|
||||
for prov in self.binaries['unstable'][arch][1][name]:
|
||||
package = self.binaries['unstable'][arch][0][prov]
|
||||
if op == '' and version == '' or apt_pkg.CheckDep(package['version'], op, version):
|
||||
packages.append(name)
|
||||
break
|
||||
if len(packages) > 0:
|
||||
return (True, packages)
|
||||
|
||||
if real_package:
|
||||
missing.append(name)
|
||||
|
||||
return (False, missing)
|
||||
|
||||
def excuse_unsat_deps(self, pkg, src, arch, suite, excuse, ignore_break=0):
|
||||
binary_u = self.binaries[suite][arch][0][pkg]
|
||||
for type in ('Pre-Depends', 'Depends'):
|
||||
type_key = type.lower()
|
||||
if not binary_u[type_key]:
|
||||
continue
|
||||
|
||||
packages = []
|
||||
for block, block_txt in map(None, binary_u[type_key], binary_u[type_key + '-txt'].split(',')):
|
||||
solved, packages = self.get_dependency_solvers(block, arch, 'testing')
|
||||
if solved: continue
|
||||
|
||||
solved, packages = self.get_dependency_solvers(block, arch, suite)
|
||||
packages = [self.binaries[suite][arch][0][p]['source'] for p in packages]
|
||||
if src in packages: continue
|
||||
|
||||
if len(packages) == 0:
|
||||
excuse.addhtml("%s/%s unsatisfiable %s: %s" % (pkg, arch, type, block_txt.strip()))
|
||||
|
||||
for p in packages:
|
||||
if ignore_break or arch not in self.options.break_arches.split():
|
||||
excuse.add_dep(p)
|
||||
else:
|
||||
excuse.add_break_dep(p, arch)
|
||||
|
||||
def should_upgrade_srcarch(self, src, arch, suite):
|
||||
# binnmu this arch?
|
||||
source_t = self.sources['testing'][src]
|
||||
source_u = self.sources[suite][src]
|
||||
|
||||
ref = "%s/%s%s" % (src, arch, suite != 'unstable' and "_" + suite or "")
|
||||
|
||||
excuse = Excuse(ref)
|
||||
excuse.set_vers(source_t['version'], source_t['version'])
|
||||
source_u['maintainer'] and excuse.set_maint(source_u['maintainer'].strip())
|
||||
source_u['section'] and excuse.set_section(source_u['section'].strip())
|
||||
|
||||
anywrongver = False
|
||||
anyworthdoing = False
|
||||
|
||||
if self.hints["remove"].has_key(src) and \
|
||||
self.same_source(source_t['version'], self.hints["remove"][src][0]):
|
||||
excuse.addhtml("Removal request by %s" % (self.hints["remove"][src][1]))
|
||||
excuse.addhtml("Trying to remove package, not update it")
|
||||
excuse.addhtml("Not considered")
|
||||
self.excuses.append(excuse)
|
||||
return False
|
||||
|
||||
for pkg in source_u['binaries']:
|
||||
if not pkg.endswith("/" + arch): continue
|
||||
pkg_name = pkg.split("/")[0]
|
||||
|
||||
binary_t = pkg in source_t['binaries'] and self.binaries['testing'][arch][0][pkg_name] or None
|
||||
binary_u = self.binaries[suite][arch][0][pkg_name]
|
||||
pkgsv = self.binaries[suite][arch][0][pkg_name]['source-ver']
|
||||
|
||||
if binary_u['architecture'] == 'all':
|
||||
excuse.addhtml("Ignoring %s %s (from %s) as it is arch: all" % (pkg, binary_u['version'], pkgsv))
|
||||
continue
|
||||
|
||||
if not self.same_source(source_t['version'], pkgsv):
|
||||
anywrongver = True
|
||||
excuse.addhtml("From wrong source: %s %s (%s not %s)" % (pkg, binary_u['version'], pkgsv, source_t['version']))
|
||||
break
|
||||
|
||||
self.excuse_unsat_deps(pkg_name, src, arch, suite, excuse)
|
||||
|
||||
if not binary_t:
|
||||
excuse.addhtml("New binary: %s (%s)" % (pkg, binary_u['version']))
|
||||
anyworthdoing = True
|
||||
continue
|
||||
|
||||
vcompare = apt_pkg.VersionCompare(binary_t['version'], binary_u['version'])
|
||||
if vcompare > 0:
|
||||
anywrongver = True
|
||||
excuse.addhtml("Not downgrading: %s (%s to %s)" % (pkg, binary_t['version'], binary_u['version']))
|
||||
break
|
||||
elif vcompare < 0:
|
||||
excuse.addhtml("Updated binary: %s (%s to %s)" % (pkg, binary_t['version'], binary_u['version']))
|
||||
anyworthdoing = True
|
||||
|
||||
if not anywrongver and (anyworthdoing or src in self.sources[suite]):
|
||||
srcv = self.sources[suite][src]['version']
|
||||
ssrc = self.same_source(source_t['version'], srcv)
|
||||
for pkg in set(k.split("/")[0] for k in self.sources['testing'][src]['binaries']):
|
||||
if self.binaries['testing'][arch][0][pkg]['architecture'] == 'all':
|
||||
excuse.addhtml("Ignoring removal of %s as it is arch: all" % (pkg))
|
||||
continue
|
||||
if not self.binaries[suite][arch][0].has_key(pkg):
|
||||
tpkgv = self.binaries['testing'][arch][0][pkg]['version']
|
||||
excuse.addhtml("Removed binary: %s %s" % (pkg, tpkgv))
|
||||
if ssrc: anyworthdoing = True
|
||||
|
||||
if not anywrongver and anyworthdoing:
|
||||
excuse.addhtml("Valid candidate")
|
||||
self.excuses.append(excuse)
|
||||
elif anyworthdoing:
|
||||
excuse.addhtml("Not considered")
|
||||
self.excuses.append(excuse)
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def should_upgrade_src(self, src, suite):
|
||||
source_u = self.sources[suite][src]
|
||||
if src in self.sources['testing']:
|
||||
source_t = self.sources['testing'][src]
|
||||
if apt_pkg.VersionCompare(source_t['version'], source_u['version']) == 0:
|
||||
# Candidate for binnmus only
|
||||
return False
|
||||
else:
|
||||
source_t = None
|
||||
|
||||
ref = "%s%s" % (src, suite != 'unstable' and "_" + suite or "")
|
||||
|
||||
update_candidate = True
|
||||
|
||||
excuse = Excuse(ref)
|
||||
excuse.set_vers(source_t and source_t['version'] or None, source_u['version'])
|
||||
source_u['maintainer'] and excuse.set_maint(source_u['maintainer'].strip())
|
||||
source_u['section'] and excuse.set_section(source_u['section'].strip())
|
||||
|
||||
if source_t and apt_pkg.VersionCompare(source_u['version'], source_t['version']) < 0:
|
||||
# Version in unstable is older!
|
||||
excuse.addhtml("ALERT: %s is newer in testing (%s %s)" % (src, source_t['version'], source_u['version']))
|
||||
self.excuses.append(excuse)
|
||||
return False
|
||||
|
||||
urgency = self.urgencies.get(src, self.options.default_urgency)
|
||||
if not source_t and urgency != self.options.default_urgency:
|
||||
excuse.addhtml("Ignoring %s urgency setting for NEW package" % (urgency))
|
||||
urgency = self.options.default_urgency
|
||||
|
||||
if self.hints["remove"].has_key(src):
|
||||
if source_t and self.same_source(source_t['version'], self.hints['remove'][src][0]) or \
|
||||
self.same_source(source_u['version'], self.hints['remove'][src][0]):
|
||||
excuse.addhtml("Removal request by %s" % (self.hints["remove"][src][1]))
|
||||
excuse.addhtml("Trying to remove package, not update it")
|
||||
update_candidate = False
|
||||
|
||||
blocked = None
|
||||
if self.hints["block"].has_key(src):
|
||||
blocked = self.hints["block"][src]
|
||||
elif self.hints["block-all"].has_key("source"):
|
||||
blocked = self.hints["block-all"]["source"]
|
||||
|
||||
if blocked:
|
||||
unblock = self.hints["unblock"].get(src,(None,None))
|
||||
if unblock[0] != None and self.same_source(unblock[0], source_u['version']):
|
||||
excuse.addhtml("Ignoring request to block package by %s, due to unblock request by %s" % (blocked, unblock[1]))
|
||||
else:
|
||||
if unblock[0] != None:
|
||||
excuse.addhtml("Unblock request by %s ignored due to version mismatch: %s" % (unblock[1], unblock[0]))
|
||||
excuse.addhtml("Not touching package, as requested by %s (contact debian-release if update is needed)" % (blocked))
|
||||
update_candidate = False
|
||||
|
||||
if suite == 'unstable':
|
||||
if not self.dates.has_key(src):
|
||||
self.dates[src] = (source_u['version'], self.date_now)
|
||||
elif not self.same_source(self.dates[src][0], source_u['version']):
|
||||
self.dates[src] = (source_u['version'], self.date_now)
|
||||
|
||||
days_old = self.date_now - self.dates[src][1]
|
||||
min_days = self.MINDAYS[urgency]
|
||||
excuse.setdaysold(days_old, min_days)
|
||||
if days_old < min_days:
|
||||
if self.hints["urgent"].has_key(src) and self.same_source(source_u['version'], self.hints["urgent"][src][0]):
|
||||
excuse.addhtml("Too young, but urgency pushed by %s" % (self.hints["urgent"][src][1]))
|
||||
else:
|
||||
update_candidate = False
|
||||
|
||||
pkgs = {src: ["source"]}
|
||||
for arch in self.options.architectures.split():
|
||||
oodbins = {}
|
||||
for pkg in set(k.split("/")[0] for k in self.sources[suite][src]['binaries']):
|
||||
if not pkgs.has_key(pkg): pkgs[pkg] = []
|
||||
pkgs[pkg].append(arch)
|
||||
|
||||
binary_u = self.binaries[suite][arch][0][pkg]
|
||||
pkgsv = binary_u['source-ver']
|
||||
if not self.same_source(source_u['version'], pkgsv):
|
||||
if not oodbins.has_key(pkgsv):
|
||||
oodbins[pkgsv] = []
|
||||
oodbins[pkgsv].append(pkg)
|
||||
continue
|
||||
|
||||
if binary_u['architecture'] != 'all' or arch in self.options.nobreakall_arches:
|
||||
self.excuse_unsat_deps(pkg, src, arch, suite, excuse)
|
||||
|
||||
if oodbins:
|
||||
oodtxt = ""
|
||||
for v in oodbins.keys():
|
||||
if oodtxt: oodtxt = oodtxt + "; "
|
||||
oodtxt = oodtxt + "%s (from <a href=\"http://buildd.debian.org/build.php?arch=%s&pkg=%s&ver=%s\" target=\"_blank\">%s</a>)" % (", ".join(sorted(oodbins[v])), arch, src, v, v)
|
||||
text = "out of date on <a href=\"http://buildd.debian.org/build.php?arch=%s&pkg=%s&ver=%s\" target=\"_blank\">%s</a>: %s" % (arch, src, source_u['version'], arch, oodtxt)
|
||||
|
||||
if arch in self.options.fucked_arches:
|
||||
text = text + " (but %s isn't keeping up, so nevermind)" % (arch)
|
||||
else:
|
||||
update_candidate = False
|
||||
|
||||
if self.date_now != self.dates[src][1]:
|
||||
excuse.addhtml(text)
|
||||
|
||||
if len(self.sources[suite][src]['binaries']) == 0:
|
||||
excuse.addhtml("%s has no binaries on any arch" % src)
|
||||
update_candidate = False
|
||||
|
||||
if suite == 'unstable':
|
||||
for pkg in pkgs.keys():
|
||||
if not self.bugs['testing'].has_key(pkg):
|
||||
self.bugs['testing'][pkg] = 0
|
||||
if not self.bugs['unstable'].has_key(pkg):
|
||||
self.bugs['unstable'][pkg] = 0
|
||||
|
||||
if self.bugs['unstable'][pkg] > self.bugs['testing'][pkg]:
|
||||
excuse.addhtml("%s (%s) is <a href=\"http://bugs.debian.org/cgi-bin/pkgreport.cgi?which=pkg&data=%s&sev-inc=critical&sev-inc=grave&sev-inc=serious\" target=\"_blank\">buggy</a>! (%d > %d)" % (pkg, ", ".join(pkgs[pkg]), pkg, self.bugs['unstable'][pkg], self.bugs['testing'][pkg]))
|
||||
update_candidate = False
|
||||
elif self.bugs['unstable'][pkg] > 0:
|
||||
excuse.addhtml("%s (%s) is (less) <a href=\"http://bugs.debian.org/cgi-bin/pkgreport.cgi?which=pkg&data=%s&sev-inc=critical&sev-inc=grave&sev-inc=serious\" target=\"_blank\">buggy</a>! (%d <= %d)" % (pkg, ", ".join(pkgs[pkg]), pkg, self.bugs['unstable'][pkg], self.bugs['testing'][pkg]))
|
||||
|
||||
if not update_candidate and self.hints["force"].has_key(src) and self.same_source(source_u['version'], self.hints["force"][src][0]) :
|
||||
excuse.dontinvalidate = 1
|
||||
excuse.addhtml("Should ignore, but forced by %s" % (self.hints["force"][src][1]))
|
||||
update_candidate = True
|
||||
|
||||
if suite == "tpu":
|
||||
if self.approvals.has_key("%s_%s" % (src, source_u['version'])):
|
||||
excuse.addhtml("Approved by %s" % approvals["%s_%s" % (src, source_u['version'])])
|
||||
else:
|
||||
excuse.addhtml("NEEDS APPROVAL BY RM")
|
||||
update_candidate = False
|
||||
|
||||
if update_candidate:
|
||||
excuse.addhtml("Valid candidate")
|
||||
else:
|
||||
excuse.addhtml("Not considered")
|
||||
|
||||
self.excuses.append(excuse)
|
||||
return update_candidate
|
||||
|
||||
def reversed_exc_deps(self):
|
||||
res = {}
|
||||
for exc in self.excuses:
|
||||
for d in exc.deps:
|
||||
if not res.has_key(d): res[d] = []
|
||||
res[d].append(exc.name)
|
||||
return res
|
||||
|
||||
def invalidate_excuses(self, valid, invalid):
|
||||
i = 0
|
||||
exclookup = {}
|
||||
for e in self.excuses:
|
||||
exclookup[e.name] = e
|
||||
revdeps = self.reversed_exc_deps()
|
||||
while i < len(invalid):
|
||||
if not revdeps.has_key(invalid[i]):
|
||||
i += 1
|
||||
continue
|
||||
if (invalid[i] + "_tpu") in valid:
|
||||
i += 1
|
||||
continue
|
||||
for x in revdeps[invalid[i]]:
|
||||
if x in valid and exclookup[x].dontinvalidate:
|
||||
continue
|
||||
|
||||
exclookup[x].invalidate_dep(invalid[i])
|
||||
if x in valid:
|
||||
p = valid.index(x)
|
||||
invalid.append(valid.pop(p))
|
||||
exclookup[x].addhtml("Invalidated by dependency")
|
||||
exclookup[x].addhtml("Not considered")
|
||||
i = i + 1
|
||||
|
||||
def main(self):
|
||||
"""Main method, entry point for the analisys"""
|
||||
|
||||
upgrade_me = []
|
||||
|
||||
# Packages to be removed
|
||||
for pkg in self.sources['testing']:
|
||||
if self.should_remove_source(pkg):
|
||||
upgrade_me.append("-" + pkg)
|
||||
|
||||
# Packages to be upgraded from unstable
|
||||
for pkg in self.sources['unstable']:
|
||||
if self.sources['testing'].has_key(pkg):
|
||||
for arch in self.options.architectures.split():
|
||||
if self.should_upgrade_srcarch(pkg, arch, 'unstable'):
|
||||
upgrade_me.append("%s/%s" % (pkg, arch))
|
||||
|
||||
if self.should_upgrade_src(pkg, 'unstable'):
|
||||
upgrade_me.append(pkg)
|
||||
|
||||
# Packages to be upgraded from testing-proposed-updates
|
||||
for pkg in self.sources['tpu']:
|
||||
if self.sources['testing'].has_key(pkg):
|
||||
for arch in self.options.architectures.split():
|
||||
if self.should_upgrade_srcarch(pkg, arch, 'tpu'):
|
||||
upgrade_me.append("%s/%s_tpu" % (pkg, arch))
|
||||
|
||||
if self.should_upgrade_src(pkg, 'tpu'):
|
||||
upgrade_me.append("%s_tpu" % pkg)
|
||||
|
||||
# Process 'remove' hints
|
||||
for src in self.hints["remove"].keys():
|
||||
if src in upgrade_me: continue
|
||||
if ("-"+src) in upgrade_me: continue
|
||||
if not self.sources['testing'].has_key(src): continue
|
||||
|
||||
tsrcv = self.sources['testing'][src]['version']
|
||||
if not self.same_source(tsrcv, self.hints["remove"][src][0]): continue
|
||||
|
||||
upgrade_me.append("-%s" % (src))
|
||||
excuse = Excuse("-%s" % (src))
|
||||
excuse.set_vers(tsrcv, None)
|
||||
excuse.addhtml("Removal request by %s" % (self.hints["remove"][src][1]))
|
||||
excuse.addhtml("Package is broken, will try to remove")
|
||||
self.excuses.append(excuse)
|
||||
|
||||
# Sort excuses by daysold and name
|
||||
self.excuses.sort(lambda x, y: cmp(x.daysold, y.daysold) or cmp(x.name, y.name))
|
||||
|
||||
# Extract unconsidered packages
|
||||
unconsidered = [e.name for e in self.excuses if e.name not in upgrade_me]
|
||||
|
||||
# Invalidate impossible excuses
|
||||
for e in self.excuses:
|
||||
for d in e.deps:
|
||||
if d not in upgrade_me and d not in unconsidered:
|
||||
e.addhtml("Unpossible dep: %s -> %s" % (e.name, d))
|
||||
self.invalidate_excuses(upgrade_me, unconsidered)
|
||||
|
||||
# Write excuses
|
||||
f = open(self.options.excuses_output, 'w')
|
||||
f.write("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\">\n")
|
||||
f.write("<html><head><title>excuses...</title>")
|
||||
f.write("<meta http-equiv=\"Content-Type\" content=\"text/html;charset=utf-8\"></head><body>\n")
|
||||
f.write("<p>Generated: " + time.strftime("%Y.%m.%d %H:%M:%S %z", time.gmtime(time.time())) + "</p>\n")
|
||||
f.write("<ul>\n")
|
||||
for e in self.excuses:
|
||||
f.write("<li>%s" % e.html())
|
||||
f.write("</ul></body></html>\n")
|
||||
f.close()
|
||||
del self.excuses
|
||||
|
||||
# Some examples ...
|
||||
# print self.sources['testing']['zsh-beta']['version']
|
||||
# print self.sources['unstable']['zsh-beta']['version']
|
||||
# print self.urgencies['zsh-beta']
|
||||
# Which packages depend on passwd?
|
||||
# for i in self.binaries['testing']['i386'][0]['passwd']['rdepends']:
|
||||
# print i
|
||||
# Which packages provide mysql-server?
|
||||
# for i in self.binaries['testing']['i386'][1]['mysql-server']:
|
||||
# print i
|
||||
# Which binary packages are build from php4 testing source package?
|
||||
# print self.sources['testing']['php4']['binaries']
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
Britney().main()
|
103
excuse.py
Normal file
103
excuse.py
Normal file
@ -0,0 +1,103 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (C) 2001-2004 Anthony Towns <ajt@debian.org>
|
||||
# Andreas Barth <aba@debian.org>
|
||||
# Fabio Tranchitella <kobold@debian.org>
|
||||
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
import re
|
||||
import string
|
||||
|
||||
|
||||
class Excuse:
|
||||
reemail = re.compile(r"<.*?>")
|
||||
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
self.ver = ("-", "-")
|
||||
self.maint = None
|
||||
self.pri = None
|
||||
self.date = None
|
||||
self.urgency = None
|
||||
self.daysold = None
|
||||
self.mindays = None
|
||||
self.section = None
|
||||
self.dontinvalidate = 0
|
||||
|
||||
self.invalid_deps = []
|
||||
self.deps = []
|
||||
self.break_deps = []
|
||||
self.bugs = []
|
||||
self.htmlline = []
|
||||
|
||||
def set_vers(self, tver, uver):
|
||||
if tver: self.ver = (tver, self.ver[1])
|
||||
if uver: self.ver = (self.ver[0], uver)
|
||||
|
||||
def set_maint(self, maint):
|
||||
self.maint = self.reemail.sub("", maint)
|
||||
|
||||
def set_section(self, section):
|
||||
self.section = section
|
||||
|
||||
def set_priority(self, pri):
|
||||
self.pri = pri
|
||||
|
||||
def set_date(self, date):
|
||||
self.date = date
|
||||
|
||||
def set_urgency(self, date):
|
||||
self.urgency = date
|
||||
|
||||
def add_dep(self, name):
|
||||
if name not in self.deps: self.deps.append(name)
|
||||
|
||||
def add_break_dep(self, name, arch):
|
||||
if (name, arch) not in self.break_deps:
|
||||
self.break_deps.append( (name, arch) )
|
||||
|
||||
def invalidate_dep(self, name):
|
||||
if name not in self.invalid_deps: self.invalid_deps.append(name)
|
||||
|
||||
def setdaysold(self, daysold, mindays):
|
||||
self.daysold = daysold
|
||||
self.mindays = mindays
|
||||
|
||||
def addhtml(self, note):
|
||||
self.htmlline.append(note)
|
||||
|
||||
def html(self):
|
||||
res = "<a id=\"%s\" name=\"%s\">%s</a> (%s to %s)\n<ul>\n" % \
|
||||
(self.name, self.name, self.name, self.ver[0], self.ver[1])
|
||||
if self.maint:
|
||||
res = res + "<li>Maintainer: %s\n" % (self.maint)
|
||||
if self.section and string.find(self.section, "/") > -1:
|
||||
res = res + "<li>Section: %s\n" % (self.section)
|
||||
if self.daysold != None:
|
||||
if self.daysold < self.mindays:
|
||||
res = res + ("<li>Too young, only %d of %d days old\n" %
|
||||
(self.daysold, self.mindays))
|
||||
else:
|
||||
res = res + ("<li>%d days old (needed %d days)\n" %
|
||||
(self.daysold, self.mindays))
|
||||
for x in self.htmlline:
|
||||
res = res + "<li>" + x + "\n"
|
||||
for x in self.deps:
|
||||
if x in self.invalid_deps:
|
||||
res = res + "<li>Depends: %s <a href=\"#%s\">%s</a> (not considered)\n" % (self.name, x, x)
|
||||
else:
|
||||
res = res + "<li>Depends: %s <a href=\"#%s\">%s</a>\n" % (self.name, x, x)
|
||||
for (n,a) in self.break_deps:
|
||||
if n not in self.deps:
|
||||
res += "<li>Ignoring %s depends: <a href=\"#%s\">%s</a>\n" % (a, n, n)
|
||||
res = res + "</ul>\n"
|
||||
return res
|
14948
update.EXCUSES_py
Normal file
14948
update.EXCUSES_py
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user