2015-04-25 17:20:49 +02:00
#!/usr/bin/python3 -u
2006-08-18 22:50:52 +00:00
# -*- coding: utf-8 -*-
2006-06-17 13:45:56 +00:00
2008-04-29 05:08:58 +00:00
# Copyright (C) 2001-2008 Anthony Towns <ajt@debian.org>
2006-06-17 13:45:56 +00:00
# Andreas Barth <aba@debian.org>
# Fabio Tranchitella <kobold@debian.org>
2013-09-07 18:21:00 +00:00
# Copyright (C) 2010-2013 Adam D. Barratt <adsb@debian.org>
2006-06-17 13:45:56 +00:00
# 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.
2006-06-24 17:49:43 +00:00
"""
2015-04-25 15:59:02 +02:00
= Introduction =
2006-06-24 17:49:43 +00:00
This is the Debian testing updater script , also known as " Britney " .
Packages are usually installed into the ` testing ' distribution after
they have undergone some degree of testing in unstable . The goal of
this software is to do this task in a smart way , allowing testing
2013-09-18 20:40:00 +00:00
to always be fully installable and close to being a release candidate .
2006-06-25 10:05:35 +00:00
2010-02-28 12:58:03 +00:00
Britney ' s source code is split between two different but related tasks:
2006-06-25 10:05:35 +00:00
the first one is the generation of the update excuses , while the
2019-01-05 13:43:10 +00:00
second tries to update testing with the valid candidates ; first
2006-06-25 10:05:35 +00:00
each package alone , then larger and even larger sets of packages
together . Each try is accepted if testing is not more uninstallable
after the update than before .
= Data Loading =
In order to analyze the entire Debian distribution , Britney needs to
load in memory the whole archive : this means more than 10.000 packages
2010-02-28 12:58:03 +00:00
for twelve architectures , as well as the dependency interconnections
between them . For this reason , the memory requirements for running this
2006-06-25 10:05:35 +00:00
software are quite high and at least 1 gigabyte of RAM should be available .
Britney loads the source packages from the ` Sources ' file and the binary
packages from the ` Packages_ $ { arch } ' files, where $ {arch} is substituted
with the supported architectures . While loading the data , the software
2010-02-28 12:58:03 +00:00
analyzes the dependencies and builds a directed weighted graph in memory
2006-06-25 10:05:35 +00:00
with all the interconnections between the packages ( see Britney . read_sources
and Britney . read_binaries ) .
Other than source and binary packages , Britney loads the following data :
2010-02-23 19:27:32 +00:00
* BugsV , which contains the list of release - critical bugs for a given
2016-03-26 08:16:39 +00:00
version of a source or binary package ( see RCBugPolicy . read_bugs ) .
2006-06-25 10:05:35 +00:00
2019-01-05 13:43:10 +00:00
* Dates , which contains the date of the upload of a given version
2006-06-25 10:05:35 +00:00
of a source package ( see Britney . read_dates ) .
* Urgencies , which contains the urgency of the upload of a given
2016-03-25 15:23:34 +00:00
version of a source package ( see AgePolicy . _read_urgencies ) .
2006-06-25 10:05:35 +00:00
* Hints , which contains lists of commands which modify the standard behaviour
of Britney ( see Britney . read_hints ) .
For a more detailed explanation about the format of these files , please read
the documentation of the related methods . The exact meaning of them will be
instead explained in the chapter " Excuses Generation " .
= Excuses =
An excuse is a detailed explanation of why a package can or cannot
2016-07-02 20:28:07 +00:00
be updated in the testing distribution from a newer package in
2006-06-25 10:05:35 +00:00
another distribution ( like for example unstable ) . The main purpose
2019-01-05 13:43:10 +00:00
of the excuses is to be written in an HTML file which will be
2006-06-25 10:05:35 +00:00
published over HTTP . The maintainers will be able to parse it manually
or automatically to find the explanation of why their packages have
been updated or not .
== Excuses generation ==
These are the steps ( with references to method names ) that Britney
does for the generation of the update excuses .
* If a source package is available in testing but it is not
present in unstable and no binary packages in unstable are
built from it , then it is marked for removal .
* Every source package in unstable and testing - proposed - updates ,
if already present in testing , is checked for binary - NMUs , new
or dropped binary packages in all the supported architectures
( see Britney . should_upgrade_srcarch ) . The steps to detect if an
upgrade is needed are :
1. If there is a ` remove ' hint for the source package, the package
is ignored : it will be removed and not updated .
2010-02-28 12:58:03 +00:00
2. For every binary package built from the new source , it checks
2013-09-18 20:40:00 +00:00
for unsatisfied dependencies , new binary packages and updated
binary packages ( binNMU ) , excluding the architecture - independent
ones , and packages not built from the same source .
2006-06-25 10:05:35 +00:00
2010-02-28 12:58:03 +00:00
3. For every binary package built from the old source , it checks
2006-06-25 10:05:35 +00:00
if it is still built from the new source ; if this is not true
and the package is not architecture - independent , the script
removes it from testing .
4. Finally , if there is something worth doing ( eg . a new or updated
binary package ) and nothing wrong it marks the source package
as " Valid candidate " , or " Not considered " if there is something
wrong which prevented the update .
* Every source package in unstable and testing - proposed - updates is
checked for upgrade ( see Britney . should_upgrade_src ) . The steps
to detect if an upgrade is needed are :
1. If the source package in testing is more recent the new one
is ignored .
2. If the source package doesn ' t exist (is fake), which means that
a binary package refers to it but it is not present in the
` Sources ' file, the new one is ignored.
3. If the package doesn ' t exist in testing, the urgency of the
upload is ignored and set to the default ( actually ` low ' ).
4. If there is a ` remove ' hint for the source package, the package
is ignored : it will be removed and not updated .
5. If there is a ` block ' hint for the source package without an
` unblock ` hint or a ` block - all source ` , the package is ignored .
2009-08-15 16:39:00 +01:00
6. If there is a ` block - udeb ' hint for the source package, it will
have the same effect as ` block ' , but may only be cancelled by
a subsequent ` unblock - udeb ' hint.
2006-06-25 10:05:35 +00:00
7. If the suite is unstable , the update can go ahead only if the
2010-02-28 12:58:03 +00:00
upload happened more than the minimum days specified by the
2006-06-25 10:05:35 +00:00
urgency of the upload ; if this is not true , the package is
ignored as ` too - young ' . Note that the urgency is sticky, meaning
that the highest urgency uploaded since the previous testing
transition is taken into account .
2010-09-20 22:03:43 +00:00
8. If the suite is unstable , all the architecture - dependent binary
packages and the architecture - independent ones for the ` nobreakall '
architectures have to be built from the source we are considering .
If this is not true , then these are called ` out - of - date '
architectures and the package is ignored .
2006-06-25 10:05:35 +00:00
2013-09-18 20:40:00 +00:00
9. The source package must have at least one binary package , otherwise
2006-06-25 10:05:35 +00:00
it is ignored .
2010-02-23 19:27:32 +00:00
10. If the suite is unstable , the new source package must have no
release critical bugs which do not also apply to the testing
2006-06-25 10:05:35 +00:00
one . If this is not true , the package is ignored as ` buggy ' .
11. If there is a ` force ' hint for the source package, then it is
updated even if it is marked as ignored from the previous steps .
2011-08-28 21:25:38 +00:00
12. If the suite is { testing - , } proposed - updates , the source package can
2010-09-20 22:03:43 +00:00
be updated only if there is an explicit approval for it . Unless
a ` force ' hint exists, the new package must also be available
on all of the architectures for which it has binary packages in
testing .
2006-06-25 10:05:35 +00:00
13. If the package will be ignored , mark it as " Valid candidate " ,
otherwise mark it as " Not considered " .
* The list of ` remove ' hints is processed: if the requested source
package is not already being updated or removed and the version
actually in testing is the same specified with the ` remove ' hint,
it is marked for removal .
* The excuses are sorted by the number of days from the last upload
( days - old ) and by name .
* A list of unconsidered excuses ( for which the package is not upgraded )
2011-08-02 17:36:03 +00:00
is built . Using this list , all of the excuses depending on them are
marked as invalid " impossible dependencies " .
2006-06-25 10:05:35 +00:00
* The excuses are written in an HTML file .
2006-06-24 17:49:43 +00:00
"""
2018-12-18 21:47:39 +00:00
import contextlib
2018-03-24 08:59:33 +00:00
import logging
2016-10-23 10:30:01 +00:00
import optparse
2006-06-17 13:45:56 +00:00
import os
import sys
import time
2016-11-13 16:32:11 +00:00
from collections import defaultdict
2015-11-22 11:29:06 +01:00
from functools import reduce
2018-09-30 16:31:21 +00:00
from itertools import chain
2011-12-27 11:36:50 +01:00
from operator import attrgetter
2015-04-25 17:22:43 +02:00
from urllib . parse import quote
2011-12-27 11:36:50 +01:00
2016-10-23 10:30:01 +00:00
import apt_pkg
2016-09-25 05:45:36 +00:00
2018-12-30 20:19:38 +00:00
from britney2 import SourcePackage , BinaryPackageId , BinaryPackage , DependencyType
2016-10-23 10:30:01 +00:00
from britney2 . excuse import Excuse
from britney2 . hints import HintParser
2018-12-15 21:35:04 +00:00
from britney2 . inputs . suiteloader import DebMirrorLikeSuiteContentLoader , MissingRequiredConfigurationError
2016-11-16 07:17:50 +00:00
from britney2 . installability . builder import build_installability_tester
2018-10-31 22:06:45 +00:00
from britney2 . installability . solver import InstallabilitySolver
2018-12-18 20:47:49 +00:00
from britney2 . migration import MigrationManager
2019-01-19 12:49:27 +00:00
from britney2 . migrationitem import MigrationItemFactory
2017-11-01 21:09:23 +00:00
from britney2 . policies import PolicyVerdict
2019-01-19 12:49:27 +00:00
from britney2 . policies . policy import ( AgePolicy , RCBugPolicy , PiupartsPolicy , BuildDependsPolicy , PolicyEngine ,
BlockPolicy , BuiltUsingPolicy )
2017-09-02 14:40:38 +02:00
from britney2 . policies . autopkgtest import AutopkgtestPolicy
2018-12-18 20:58:45 +00:00
from britney2 . utils import ( log_and_format_old_libraries , get_dependency_solvers ,
2016-10-23 10:30:01 +00:00
read_nuninst , write_nuninst , write_heidi ,
2018-12-23 18:08:23 +00:00
format_and_log_uninst , newly_uninst ,
2019-01-07 20:25:48 +00:00
write_excuses , write_heidi_delta ,
2016-10-23 10:30:01 +00:00
old_libraries , is_nuninst_asgood_generous ,
2019-01-03 19:46:42 +00:00
clone_nuninst , compute_item_name ,
2016-11-16 07:38:29 +00:00
invalidate_excuses , compile_nuninst ,
2018-12-15 21:04:14 +00:00
find_smooth_updateable_binaries , parse_provides ,
2018-12-17 18:11:52 +00:00
MigrationConstraintException ,
2016-10-23 10:30:01 +00:00
)
2006-06-17 13:45:56 +00:00
2011-08-28 21:28:47 +00:00
__author__ = ' Fabio Tranchitella and the Debian Release Team '
__version__ = ' 2.0 '
2006-06-17 13:45:56 +00:00
2016-09-24 09:42:39 +00:00
2012-01-06 19:39:57 +01:00
class Britney ( object ) :
2010-02-28 12:58:03 +00:00
""" Britney, the Debian testing updater script
2019-01-05 13:43:10 +00:00
2010-02-28 12:58:03 +00:00
This is the script that updates the testing distribution . It is executed
2019-01-05 13:43:10 +00:00
each day after the installation of the updated packages . It generates the
2006-06-24 17:49:43 +00:00
` Packages ' files for the testing distribution, but it does so in an
2010-02-28 12:58:03 +00:00
intelligent manner ; it tries to avoid any inconsistency and to use only
2006-06-24 17:49:43 +00:00
non - buggy packages .
2006-06-17 13:45:56 +00:00
2006-06-24 17:49:43 +00:00
For more documentation on this script , please read the Developers Reference .
"""
2006-06-17 13:45:56 +00:00
2017-02-03 16:47:24 +00:00
HINTS_HELPERS = ( " easy " , " hint " , " remove " , " block " , " block-udeb " , " unblock " , " unblock-udeb " , " approve " ,
2018-07-25 18:17:36 +00:00
" remark " , " ignore-piuparts " , " ignore-rc-bugs " , " force-skiptest " , " force-badtest " )
2008-01-19 17:24:06 +00:00
HINTS_STANDARD = ( " urgent " , " age-days " ) + HINTS_HELPERS
2016-07-02 20:28:07 +00:00
# ALL = {"force", "force-hint", "block-all"} | HINTS_STANDARD | registered policy hints (not covered above)
HINTS_ALL = ( ' ALL ' )
2006-06-17 13:45:56 +00:00
def __init__ ( self ) :
2006-06-24 17:49:43 +00:00
""" Class constructor
This method initializes and populates the data lists , which contain all
the information needed by the other methods of the class .
"""
2018-03-24 08:59:33 +00:00
# setup logging - provide the "short level name" (i.e. INFO -> I) that
# we used to use prior to using the logging module.
old_factory = logging . getLogRecordFactory ( )
short_level_mapping = {
2018-12-15 20:00:50 +00:00
' CRITICAL ' : ' F ' ,
2018-03-24 08:59:33 +00:00
' INFO ' : ' I ' ,
' WARNING ' : ' W ' ,
' ERROR ' : ' E ' ,
' DEBUG ' : ' N ' ,
}
2018-12-31 17:55:02 +00:00
def record_factory ( * args , * * kwargs ) : # pragma: no cover
2018-03-24 08:59:33 +00:00
record = old_factory ( * args , * * kwargs )
try :
record . shortlevelname = short_level_mapping [ record . levelname ]
except KeyError :
record . shortlevelname = record . levelname
return record
logging . setLogRecordFactory ( record_factory )
2018-03-31 06:05:41 +00:00
logging . basicConfig ( format = ' {shortlevelname} : [ {asctime} ] - {message} ' ,
style = ' { ' ,
datefmt = " % Y- % m- %d T % H: % M: % S % z " ,
stream = sys . stdout ,
)
2018-03-24 08:59:33 +00:00
self . logger = logging . getLogger ( )
2018-04-01 09:35:03 +00:00
# Logger for "upgrade_output"; the file handler will be attached later when
# we are ready to open the file.
self . output_logger = logging . getLogger ( ' britney2.output.upgrade_output ' )
self . output_logger . setLevel ( logging . INFO )
2018-12-15 21:35:04 +00:00
# initialize the apt_pkg back-end
apt_pkg . init ( )
2006-06-24 17:49:43 +00:00
# parse the command line arguments
2018-12-31 19:12:21 +00:00
self . _policy_engine = PolicyEngine ( )
2018-04-01 16:01:01 +00:00
self . suite_info = None # Initialized during __parse_arguments
2006-06-17 13:45:56 +00:00
self . __parse_arguments ( )
2006-06-24 17:49:43 +00:00
2014-01-11 08:31:36 +01:00
self . all_selected = [ ]
2016-03-25 09:18:35 +00:00
self . excuses = { }
2014-01-11 08:31:36 +01:00
2012-02-18 18:51:05 +01:00
if self . options . nuninst_cache :
2018-03-24 09:42:06 +00:00
self . logger . info ( " Not building the list of non-installable packages, as requested " )
2012-02-18 18:51:05 +01:00
if self . options . print_uninst :
2018-08-05 13:10:58 +00:00
nuninst = read_nuninst ( self . options . noninst_status ,
self . options . architectures )
2015-04-25 16:03:54 +02:00
print ( ' * summary ' )
print ( ' \n ' . join ( ' %4d %s ' % ( len ( nuninst [ x ] ) , x ) for x in self . options . architectures ) )
2012-02-18 18:51:05 +01:00
return
2006-08-18 22:50:52 +00:00
2016-05-07 08:56:41 +00:00
try :
constraints_file = os . path . join ( self . options . static_input_dir , ' constraints ' )
2016-05-07 12:36:49 +00:00
faux_packages = os . path . join ( self . options . static_input_dir , ' faux-packages ' )
2016-05-07 08:56:41 +00:00
except AttributeError :
2018-03-24 09:42:06 +00:00
self . logger . info ( " The static_input_dir option is not set " )
2016-05-07 08:56:41 +00:00
constraints_file = None
2016-05-07 12:36:49 +00:00
faux_packages = None
if faux_packages is not None and os . path . exists ( faux_packages ) :
2018-03-30 17:38:50 +00:00
self . logger . info ( " Loading faux packages from %s " , faux_packages )
2016-05-07 12:36:49 +00:00
self . _load_faux_packages ( faux_packages )
2016-05-26 17:48:19 +00:00
elif faux_packages is not None :
2018-03-30 17:38:50 +00:00
self . logger . info ( " No Faux packages as %s does not exist " , faux_packages )
2016-05-07 12:36:49 +00:00
2016-05-07 08:56:41 +00:00
if constraints_file is not None and os . path . exists ( constraints_file ) :
2018-03-30 17:38:50 +00:00
self . logger . info ( " Loading constraints from %s " , constraints_file )
2016-05-07 08:56:41 +00:00
self . constraints = self . _load_constraints ( constraints_file )
else :
if constraints_file is not None :
2018-03-30 17:38:50 +00:00
self . logger . info ( " No constraints as %s does not exist " , constraints_file )
2016-05-07 08:56:41 +00:00
self . constraints = {
' keep-installable ' : [ ] ,
}
2018-03-24 09:42:06 +00:00
self . logger . info ( " Compiling Installability tester " )
2018-10-31 07:28:23 +00:00
self . pkg_universe , self . _inst_tester = build_installability_tester ( self . suite_info , self . options . architectures )
2018-11-01 21:22:14 +00:00
target_suite = self . suite_info . target_suite
target_suite . inst_tester = self . _inst_tester
2006-06-24 17:49:43 +00:00
2018-12-23 18:08:23 +00:00
self . _migration_item_factory = MigrationItemFactory ( self . suite_info )
self . _hint_parser = HintParser ( self . _migration_item_factory )
2018-12-18 21:14:01 +00:00
self . _migration_manager = MigrationManager ( self . options , self . suite_info , self . all_binaries , self . pkg_universe ,
self . constraints )
2018-12-18 20:47:49 +00:00
2012-02-18 18:51:05 +01:00
if not self . options . nuninst_cache :
2018-03-24 09:42:06 +00:00
self . logger . info ( " Building the list of non-installable packages for the full archive " )
2018-12-15 22:32:04 +00:00
self . _inst_tester . compute_installability ( )
2018-11-01 21:22:38 +00:00
nuninst = compile_nuninst ( target_suite ,
2018-08-05 13:10:58 +00:00
self . options . architectures ,
self . options . nobreakall_arches )
2018-08-05 13:08:53 +00:00
self . nuninst_orig = nuninst
2012-02-18 18:51:05 +01:00
for arch in self . options . architectures :
2018-03-30 17:38:50 +00:00
self . logger . info ( " > Found %d non-installable packages " , len ( nuninst [ arch ] ) )
2012-02-18 18:51:05 +01:00
if self . options . print_uninst :
self . nuninst_arch_report ( nuninst , arch )
if self . options . print_uninst :
2015-04-25 16:03:54 +02:00
print ( ' * summary ' )
print ( ' \n ' . join ( map ( lambda x : ' %4d %s ' % ( len ( nuninst [ x ] ) , x ) , self . options . architectures ) ) )
2012-02-18 18:51:05 +01:00
return
else :
write_nuninst ( self . options . noninst_status , nuninst )
2015-04-05 12:21:49 +02:00
stats = self . _inst_tester . compute_stats ( )
2018-03-24 09:42:06 +00:00
self . logger . info ( " > Installability tester statistics (per architecture) " )
2015-04-05 12:21:49 +02:00
for arch in self . options . architectures :
arch_stat = stats [ arch ]
2018-03-30 17:38:50 +00:00
self . logger . info ( " > %s " , arch )
2015-04-05 12:21:49 +02:00
for stat in arch_stat . stat_summary ( ) :
2018-03-30 17:38:50 +00:00
self . logger . info ( " > - %s " , stat )
2018-08-05 13:08:53 +00:00
else :
self . logger . info ( " Loading uninstallability counters from cache " )
2018-08-05 13:10:58 +00:00
self . nuninst_orig = read_nuninst ( self . options . noninst_status ,
self . options . architectures )
2018-08-05 13:08:53 +00:00
# nuninst_orig may get updated during the upgrade process
self . nuninst_orig_save = clone_nuninst ( self . nuninst_orig , architectures = self . options . architectures )
2015-04-05 12:21:49 +02:00
2018-12-31 19:12:21 +00:00
self . _policy_engine . register_policy_hints ( self . _hint_parser )
2018-12-23 19:59:48 +00:00
try :
self . read_hints ( self . options . hintsdir )
except AttributeError :
self . read_hints ( os . path . join ( self . suite_info [ ' unstable ' ] . path , ' Hints ' ) )
2018-12-31 19:12:21 +00:00
self . _policy_engine . initialise ( self , self . hints )
2006-06-17 13:45:56 +00:00
def __parse_arguments ( self ) :
2006-06-24 17:49:43 +00:00
""" Parse the command line arguments
This method parses and initializes the command line arguments .
While doing so , it preprocesses some of the options to be converted
in a suitable form for the other methods of the class .
"""
# initialize the parser
2011-10-19 12:14:02 +02:00
parser = optparse . OptionParser ( version = " % prog " )
parser . add_option ( " -v " , " " , action = " count " , dest = " verbose " , help = " enable verbose output " )
parser . add_option ( " -c " , " --config " , action = " store " , dest = " config " , default = " /etc/britney.conf " ,
2006-07-28 13:21:44 +00:00
help = " path for the configuration file " )
2011-10-19 12:14:02 +02:00
parser . add_option ( " " , " --architectures " , action = " store " , dest = " architectures " , default = None ,
2006-07-28 13:21:44 +00:00
help = " override architectures from configuration file " )
2011-10-19 12:14:02 +02:00
parser . add_option ( " " , " --actions " , action = " store " , dest = " actions " , default = None ,
2006-07-28 13:21:44 +00:00
help = " override the list of actions to be performed " )
2011-10-19 12:14:02 +02:00
parser . add_option ( " " , " --hints " , action = " store " , dest = " hints " , default = None ,
2008-01-17 14:27:36 +00:00
help = " additional hints, separated by semicolons " )
2011-10-19 12:14:02 +02:00
parser . add_option ( " " , " --hint-tester " , action = " store_true " , dest = " hint_tester " , default = None ,
2008-01-15 16:09:51 +00:00
help = " provide a command line interface to test hints " )
2011-10-19 12:14:02 +02:00
parser . add_option ( " " , " --dry-run " , action = " store_true " , dest = " dry_run " , default = False ,
2006-08-06 09:29:32 +00:00
help = " disable all outputs to the testing directory " )
2011-10-19 12:14:02 +02:00
parser . add_option ( " " , " --nuninst-cache " , action = " store_true " , dest = " nuninst_cache " , default = False ,
2006-08-07 14:38:13 +00:00
help = " do not build the non-installability status, use the cache from file " )
2011-10-19 12:14:02 +02:00
parser . add_option ( " " , " --print-uninst " , action = " store_true " , dest = " print_uninst " , default = False ,
2008-05-02 13:57:37 +00:00
help = " just print a summary of uninstallable packages " )
2017-04-06 11:45:59 +00:00
parser . add_option ( " " , " --compute-migrations " , action = " store_true " , dest = " compute_migrations " , default = True ,
help = " Compute which packages can migrate (the default) " )
parser . add_option ( " " , " --no-compute-migrations " , action = " store_false " , dest = " compute_migrations " ,
help = " Do not compute which packages can migrate. " )
2017-09-08 10:43:41 +02:00
parser . add_option ( " " , " --series " , action = " store " , dest = " series " , default = ' testing ' ,
2017-09-02 20:38:35 +02:00
help = " set distribution series name " )
2011-10-19 12:14:02 +02:00
( self . options , self . args ) = parser . parse_args ( )
2018-03-24 08:59:33 +00:00
if self . options . verbose :
self . logger . setLevel ( logging . INFO )
else :
self . logger . setLevel ( logging . WARNING )
2018-03-31 07:04:35 +00:00
# TODO: Define a more obvious toggle for debug information
try : # pragma: no cover
if int ( os . environ . get ( ' BRITNEY_DEBUG ' , ' 0 ' ) ) :
self . logger . setLevel ( logging . DEBUG )
except ValueError : # pragma: no cover
pass
2008-04-29 05:08:58 +00:00
# integrity checks
2016-11-13 08:49:46 +00:00
if self . options . nuninst_cache and self . options . print_uninst : # pragma: no cover
2018-03-24 09:42:06 +00:00
self . logger . error ( " nuninst_cache and print_uninst are mutually exclusive! " )
2008-04-29 05:08:58 +00:00
sys . exit ( 1 )
2015-04-27 19:14:08 +00:00
# if the configuration file exists, then read it and set the additional options
2016-11-13 08:49:46 +00:00
elif not os . path . isfile ( self . options . config ) : # pragma: no cover
2018-03-30 17:38:50 +00:00
self . logger . error ( " Unable to read the configuration file ( %s ), exiting! " , self . options . config )
2006-06-17 13:45:56 +00:00
sys . exit ( 1 )
2006-06-24 17:49:43 +00:00
# minimum days for unstable-testing transition and the list of hints
# are handled as an ad-hoc case
2016-03-25 15:23:34 +00:00
MINDAYS = { }
2016-07-02 20:28:07 +00:00
2008-01-17 14:27:36 +00:00
self . HINTS = { ' command-line ' : self . HINTS_ALL }
2015-04-26 00:04:50 +02:00
with open ( self . options . config , encoding = ' utf-8 ' ) as config :
2015-04-25 16:30:04 +02:00
for line in config :
if ' = ' in line and not line . strip ( ) . startswith ( ' # ' ) :
k , v = line . split ( ' = ' , 1 )
k = k . strip ( )
v = v . strip ( )
if k . startswith ( " MINDAYS_ " ) :
2016-03-25 15:23:34 +00:00
MINDAYS [ k . split ( " _ " ) [ 1 ] . lower ( ) ] = int ( v )
2015-04-25 16:30:04 +02:00
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 ( ) ] )
elif not hasattr ( self . options , k . lower ( ) ) or \
not getattr ( self . options , k . lower ( ) ) :
setattr ( self . options , k . lower ( ) , v )
2016-10-24 19:36:44 +00:00
2018-12-31 17:55:02 +00:00
if hasattr ( self . options , ' components ' ) : # pragma: no cover
2018-12-16 19:55:02 +00:00
self . logger . error ( " The COMPONENTS configuration has been removed. " )
self . logger . error ( " Britney will read the value from the Release file automatically " )
sys . exit ( 1 )
2018-12-15 21:35:04 +00:00
suite_loader = DebMirrorLikeSuiteContentLoader ( self . options )
2018-04-01 16:01:01 +00:00
2016-10-23 12:28:12 +00:00
try :
2018-12-15 21:35:04 +00:00
self . suite_info = suite_loader . load_suites ( )
2018-12-31 17:55:02 +00:00
except MissingRequiredConfigurationError as e : # pragma: no cover
2018-12-15 21:35:04 +00:00
self . logger . error ( " Could not load the suite content due to missing configuration: %s " , str ( e ) )
sys . exit ( 1 )
self . all_binaries = suite_loader . all_binaries ( )
self . options . components = suite_loader . components
self . options . architectures = suite_loader . architectures
self . options . nobreakall_arches = suite_loader . nobreakall_arches
self . options . outofsync_arches = suite_loader . outofsync_arches
self . options . break_arches = suite_loader . break_arches
self . options . new_arches = suite_loader . new_arches
2015-04-27 02:13:27 +10:00
2014-01-11 08:31:36 +01:00
if not hasattr ( self . options , " heidi_delta_output " ) :
self . options . heidi_delta_output = self . options . heidi_output + " Delta "
2006-08-06 09:29:32 +00:00
self . options . smooth_updates = self . options . smooth_updates . split ( )
2006-06-17 13:45:56 +00:00
2015-09-06 15:21:30 +00:00
if not hasattr ( self . options , ' ignore_cruft ' ) or \
self . options . ignore_cruft == " 0 " :
self . options . ignore_cruft = False
2015-05-27 22:34:42 +02:00
2019-01-14 13:13:45 +00:00
if not hasattr ( self . options , ' check_consistency_level ' ) :
self . options . check_consistency_level = 2
else :
self . options . check_consistency_level = int ( self . options . check_consistency_level )
2018-04-02 07:39:06 +02:00
if not hasattr ( self . options , ' adt_retry_url_mech ' ) :
self . options . adt_retry_url_mech = ' '
2018-12-31 19:12:21 +00:00
self . _policy_engine . add_policy ( RCBugPolicy ( self . options , self . suite_info ) )
self . _policy_engine . add_policy ( PiupartsPolicy ( self . options , self . suite_info ) )
2017-09-02 14:40:38 +02:00
if getattr ( self . options , ' adt_enable ' ) == ' yes ' :
2018-12-31 19:12:21 +00:00
self . _policy_engine . add_policy ( AutopkgtestPolicy ( self . options , self . suite_info ) )
self . _policy_engine . add_policy ( AgePolicy ( self . options , self . suite_info , MINDAYS ) )
self . _policy_engine . add_policy ( BuildDependsPolicy ( self . options , self . suite_info ) )
2019-01-01 18:26:01 +00:00
self . _policy_engine . add_policy ( BlockPolicy ( self . options , self . suite_info ) )
2019-01-15 22:47:32 +00:00
self . _policy_engine . add_policy ( BuiltUsingPolicy ( self . options , self . suite_info ) )
2016-03-25 15:23:34 +00:00
2016-07-02 20:00:16 +00:00
@property
def hints ( self ) :
return self . _hint_parser . hints
2016-05-07 12:36:49 +00:00
def _load_faux_packages ( self , faux_packages_file ) :
""" Loads fake packages
In rare cases , it is useful to create a " fake " package that can be used to satisfy
dependencies . This is usually needed for packages that are not shipped directly
on this mirror but is a prerequisite for using this mirror ( e . g . some vendors provide
non - distributable " setup " packages and contrib / non - free packages depend on these ) .
: param faux_packages_file : Path to the file containing the fake package definitions
"""
tag_file = apt_pkg . TagFile ( faux_packages_file )
get_field = tag_file . section . get
step = tag_file . step
no = 0
2018-07-22 12:06:51 +00:00
pri_source_suite = self . suite_info . primary_source_suite
target_suite = self . suite_info . target_suite
2016-05-07 12:36:49 +00:00
while step ( ) :
no + = 1
pkg_name = get_field ( ' Package ' , None )
2016-11-13 08:49:46 +00:00
if pkg_name is None : # pragma: no cover
2016-05-21 13:38:52 +00:00
raise ValueError ( " Missing Package field in paragraph %d (file %s ) " % ( no , faux_packages_file ) )
2016-05-07 12:36:49 +00:00
pkg_name = sys . intern ( pkg_name )
version = sys . intern ( get_field ( ' Version ' , ' 1.0-1 ' ) )
provides_raw = get_field ( ' Provides ' )
archs_raw = get_field ( ' Architecture ' , None )
2016-05-21 13:39:09 +00:00
component = get_field ( ' Component ' , ' non-free ' )
2016-05-07 12:36:49 +00:00
if archs_raw :
archs = archs_raw . split ( )
else :
archs = self . options . architectures
faux_section = ' faux '
if component != ' main ' :
faux_section = " %s /faux " % component
2016-09-24 09:42:39 +00:00
src_data = SourcePackage ( version ,
2018-08-08 13:37:05 +00:00
sys . intern ( faux_section ) ,
2019-01-10 06:45:28 +00:00
set ( ) ,
2018-08-08 13:37:05 +00:00
None ,
True ,
None ,
None ,
[ ] ,
[ ] ,
)
2016-05-07 12:36:49 +00:00
2018-07-22 12:06:51 +00:00
target_suite . sources [ pkg_name ] = src_data
pri_source_suite . sources [ pkg_name ] = src_data
2016-05-07 12:36:49 +00:00
for arch in archs :
2016-05-16 06:42:26 +00:00
pkg_id = BinaryPackageId ( pkg_name , version , arch )
2016-05-07 12:36:49 +00:00
if provides_raw :
2018-12-15 21:04:14 +00:00
provides = parse_provides ( provides_raw , pkg_id = pkg_id , logger = self . logger )
2016-05-07 12:36:49 +00:00
else :
provides = [ ]
bin_data = BinaryPackage ( version ,
faux_section ,
pkg_name ,
version ,
arch ,
get_field ( ' Multi-Arch ' ) ,
None ,
None ,
provides ,
False ,
2016-05-16 06:24:36 +00:00
pkg_id ,
2018-12-17 12:34:59 +00:00
[ ] ,
2016-05-07 12:36:49 +00:00
)
2019-01-10 06:45:28 +00:00
src_data . binaries . add ( pkg_id )
2018-10-24 20:09:55 +00:00
target_suite . binaries [ arch ] [ pkg_name ] = bin_data
pri_source_suite . binaries [ arch ] [ pkg_name ] = bin_data
2016-05-07 12:36:49 +00:00
self . all_binaries [ pkg_id ] = bin_data
2016-05-07 08:56:41 +00:00
def _load_constraints ( self , constraints_file ) :
""" Loads configurable constraints
The constraints file can contain extra rules that Britney should attempt
to satisfy . Examples can be " keep package X in testing and ensure it is
installable " .
: param constraints_file : Path to the file containing the constraints
"""
tag_file = apt_pkg . TagFile ( constraints_file )
get_field = tag_file . section . get
step = tag_file . step
no = 0
faux_version = sys . intern ( ' 1 ' )
faux_section = sys . intern ( ' faux ' )
2016-05-07 10:44:32 +00:00
keep_installable = [ ]
constraints = {
' keep-installable ' : keep_installable
}
2018-07-22 12:06:51 +00:00
pri_source_suite = self . suite_info . primary_source_suite
target_suite = self . suite_info . target_suite
2016-05-07 08:56:41 +00:00
while step ( ) :
no + = 1
pkg_name = get_field ( ' Fake-Package-Name ' , None )
2016-11-13 08:49:46 +00:00
if pkg_name is None : # pragma: no cover
2016-05-07 08:56:41 +00:00
raise ValueError ( " Missing Fake-Package-Name field in paragraph %d (file %s ) " % ( no , constraints_file ) )
pkg_name = sys . intern ( pkg_name )
def mandatory_field ( x ) :
v = get_field ( x , None )
2016-11-13 08:49:46 +00:00
if v is None : # pragma: no cover
2016-05-07 08:56:41 +00:00
raise ValueError ( " Missing %s field for %s (file %s ) " % ( x , pkg_name , constraints_file ) )
return v
constraint = mandatory_field ( ' Constraint ' )
2016-11-13 08:49:46 +00:00
if constraint not in { ' present-and-installable ' } : # pragma: no cover
2016-05-07 08:56:41 +00:00
raise ValueError ( " Unsupported constraint %s for %s (file %s ) " % ( constraint , pkg_name , constraints_file ) )
2018-03-30 17:38:50 +00:00
self . logger . info ( " - constraint %s " , pkg_name )
2016-05-07 08:56:41 +00:00
2016-05-21 17:13:20 +00:00
pkg_list = [ x . strip ( ) for x in mandatory_field ( ' Package-List ' ) . split ( " \n " ) if x . strip ( ) != ' ' and not x . strip ( ) . startswith ( " # " ) ]
2016-09-24 09:42:39 +00:00
src_data = SourcePackage ( faux_version ,
2018-08-08 13:37:05 +00:00
faux_section ,
2019-01-10 06:45:28 +00:00
set ( ) ,
2018-08-08 13:37:05 +00:00
None ,
True ,
None ,
None ,
[ ] ,
[ ] ,
)
2018-07-22 12:06:51 +00:00
target_suite . sources [ pkg_name ] = src_data
pri_source_suite . sources [ pkg_name ] = src_data
2016-05-07 10:44:32 +00:00
keep_installable . append ( pkg_name )
2016-05-07 08:56:41 +00:00
for arch in self . options . architectures :
deps = [ ]
for pkg_spec in pkg_list :
2016-05-21 16:44:20 +00:00
s = pkg_spec . split ( None , 1 )
2016-05-07 08:56:41 +00:00
if len ( s ) == 1 :
deps . append ( s [ 0 ] )
else :
pkg , arch_res = s
2016-11-13 08:49:46 +00:00
if not ( arch_res . startswith ( ' [ ' ) and arch_res . endswith ( ' ] ' ) ) : # pragma: no cover
2016-05-07 08:56:41 +00:00
raise ValueError ( " Invalid arch-restriction on %s - should be [arch1 arch2] (for %s file %s ) "
% ( pkg , pkg_name , constraints_file ) )
arch_res = arch_res [ 1 : - 1 ] . split ( )
2016-11-13 08:49:46 +00:00
if not arch_res : # pragma: no cover
2016-05-07 08:56:41 +00:00
msg = " Empty arch-restriction for %s : Uses comma or negation (for %s file %s ) "
raise ValueError ( msg % ( pkg , pkg_name , constraints_file ) )
for a in arch_res :
if a == arch :
deps . append ( pkg )
2016-11-13 08:49:46 +00:00
elif ' , ' in a or ' ! ' in a : # pragma: no cover
2016-05-07 08:56:41 +00:00
msg = " Invalid arch-restriction for %s : Uses comma or negation (for %s file %s ) "
raise ValueError ( msg % ( pkg , pkg_name , constraints_file ) )
2016-05-16 06:42:26 +00:00
pkg_id = BinaryPackageId ( pkg_name , faux_version , arch )
2016-05-07 08:56:41 +00:00
bin_data = BinaryPackage ( faux_version ,
faux_section ,
pkg_name ,
faux_version ,
arch ,
' no ' ,
' , ' . join ( deps ) ,
None ,
[ ] ,
False ,
2016-05-16 06:24:36 +00:00
pkg_id ,
2018-12-17 12:34:59 +00:00
[ ] ,
2016-05-07 08:56:41 +00:00
)
2019-01-10 06:45:28 +00:00
src_data . binaries . add ( pkg_id )
2018-10-24 20:09:55 +00:00
target_suite . binaries [ arch ] [ pkg_name ] = bin_data
pri_source_suite . binaries [ arch ] [ pkg_name ] = bin_data
2016-05-07 08:56:41 +00:00
self . all_binaries [ pkg_id ] = bin_data
2016-05-07 10:44:32 +00:00
return constraints
2006-06-24 17:49:43 +00:00
# Data reading/writing methods
# ----------------------------
2006-06-17 13:45:56 +00:00
2016-05-26 16:49:09 +00:00
def read_hints ( self , hintsdir ) :
2006-06-24 17:49:43 +00:00
""" Read the hint commands from the specified directory
2019-01-05 13:43:10 +00:00
2016-05-26 16:49:09 +00:00
The hint commands are read from the files contained in the directory
specified by the ` hintsdir ' parameter.
2010-02-28 12:58:03 +00:00
The names of the files have to be the same as the authorized users
2006-06-24 17:49:43 +00:00
for the hints .
2019-01-05 13:43:10 +00:00
2006-06-24 17:49:43 +00:00
The file contains rows with the format :
< command > < package - name > [ / < version > ]
The method returns a dictionary where the key is the command , and
the value is the list of affected packages .
"""
2006-06-17 13:45:56 +00:00
for who in self . HINTS . keys ( ) :
2008-01-17 14:27:36 +00:00
if who == ' command-line ' :
lines = self . options . hints and self . options . hints . split ( ' ; ' ) or ( )
2015-02-01 10:59:57 +01:00
filename = ' <cmd-line> '
2016-07-02 20:00:16 +00:00
self . _hint_parser . parse_hints ( who , self . HINTS [ who ] , filename , lines )
2008-01-17 14:27:36 +00:00
else :
2016-05-26 16:49:09 +00:00
filename = os . path . join ( hintsdir , who )
2008-01-17 14:27:36 +00:00
if not os . path . isfile ( filename ) :
2018-03-30 17:38:50 +00:00
self . logger . error ( " Cannot read hints list from %s , no such file! " , filename )
2008-01-17 14:27:36 +00:00
continue
2018-03-30 17:38:50 +00:00
self . logger . info ( " Loading hints list from %s " , filename )
2015-04-26 00:04:50 +02:00
with open ( filename , encoding = ' utf-8 ' ) as f :
2016-07-02 20:00:16 +00:00
self . _hint_parser . parse_hints ( who , self . HINTS [ who ] , filename , f )
2016-03-25 07:49:31 +00:00
2016-07-02 20:00:16 +00:00
hints = self . _hint_parser . hints
2006-06-17 13:45:56 +00:00
2013-01-18 18:47:06 +00:00
for x in [ " block " , " block-all " , " block-udeb " , " unblock " , " unblock-udeb " , " force " , " urgent " , " remove " , " age-days " ] :
2019-01-01 10:28:17 +00:00
z = defaultdict ( dict )
2011-09-04 16:41:33 +00:00
for hint in hints [ x ] :
2011-09-04 19:46:38 +00:00
package = hint . package
2019-01-01 10:28:17 +00:00
architecture = hint . architecture
2011-11-14 19:52:42 +00:00
key = ( hint , hint . user )
2019-01-01 10:28:17 +00:00
if package in z and architecture in z [ package ] and z [ package ] [ architecture ] != key :
hint2 = z [ package ] [ architecture ] [ 0 ]
2010-02-28 13:11:22 +00:00
if x in [ ' unblock ' , ' unblock-udeb ' ] :
2011-12-18 21:19:22 +01:00
if apt_pkg . version_compare ( hint2 . version , hint . version ) < 0 :
2010-02-28 13:11:22 +00:00
# This hint is for a newer version, so discard the old one
2019-01-01 10:28:17 +00:00
self . logger . warning ( " Overriding %s [ %s ] = ( ' %s ' , ' %s ' , ' %s ' ) with ( ' %s ' , ' %s ' , ' %s ' ) " ,
x , package , hint2 . version , hint2 . architecture , hint2 . user , hint . version , hint . architecture , hint . user )
2011-11-14 19:52:42 +00:00
hint2 . set_active ( False )
2010-02-28 13:11:22 +00:00
else :
2011-08-28 21:45:14 +00:00
# This hint is for an older version, so ignore it in favour of the new one
2019-01-01 10:28:17 +00:00
self . logger . warning ( " Ignoring %s [ %s ] = ( ' %s ' , ' %s ' , ' %s ' ), ( ' %s ' , ' %s ' , ' %s ' ) is higher or equal " ,
x , package , hint . version , hint . architecture , hint . user , hint2 . version , hint2 . architecture , hint2 . user )
2011-09-04 16:41:33 +00:00
hint . set_active ( False )
2010-02-28 13:11:22 +00:00
else :
2018-03-30 17:38:50 +00:00
self . logger . warning ( " Overriding %s [ %s ] = ( ' %s ' , ' %s ' ) with ( ' %s ' , ' %s ' ) " ,
x , package , hint2 . user , hint2 , hint . user , hint )
2011-11-14 19:52:42 +00:00
hint2 . set_active ( False )
2011-11-10 10:30:13 +00:00
2019-01-01 10:28:17 +00:00
z [ package ] [ architecture ] = key
2006-06-17 13:45:56 +00:00
2010-02-28 14:41:59 +00:00
# Sanity check the hints hash
if len ( hints [ " block " ] ) == 0 and len ( hints [ " block-udeb " ] ) == 0 :
2018-03-24 09:42:06 +00:00
self . logger . warning ( " WARNING: No block hints at all, not even udeb ones! " )
2010-02-28 14:41:59 +00:00
2011-10-19 20:02:29 +00:00
# Utility methods for package analysis
2006-06-24 17:49:43 +00:00
# ------------------------------------
2006-06-17 13:45:56 +00:00
2018-07-22 15:45:46 +00:00
def excuse_unsat_deps ( self , pkg , src , arch , source_suite , excuse , get_dependency_solvers = get_dependency_solvers ) :
2006-06-24 17:49:43 +00:00
""" Find unsatisfied dependencies for a binary package
This method analyzes the dependencies of the binary package specified
by the parameter ` pkg ' , built from the source package `src ' , for the
architecture ` arch ' within the suite `suite ' . If the dependency can ' t
be satisfied in testing and / or unstable , it updates the excuse passed
as parameter .
"""
# retrieve the binary package from the specified suite and arch
2018-07-22 15:45:46 +00:00
target_suite = self . suite_info . target_suite
2018-10-24 20:09:55 +00:00
binaries_s_a = source_suite . binaries [ arch ]
provides_s_a = source_suite . provides_table [ arch ]
binaries_t_a = target_suite . binaries [ arch ]
provides_t_a = target_suite . provides_table [ arch ]
2016-09-24 08:42:52 +00:00
binary_u = binaries_s_a [ pkg ]
2006-06-24 17:49:43 +00:00
2018-10-30 12:20:06 +00:00
source_s = source_suite . sources [ binary_u . source ]
if ( binary_u . source_version != source_s . version ) :
# we don't want cruft to block packages, so if this is cruft, we
# can simply ignore it; if the cruft would migrate to testing, the
# installability check will catch missing deps
return True
2013-09-18 20:40:00 +00:00
# local copies for better performance
2011-12-18 21:19:22 +01:00
parse_depends = apt_pkg . parse_depends
2006-07-28 07:59:05 +00:00
2006-06-24 17:49:43 +00:00
# analyze the dependency fields (if present)
2016-04-06 20:49:40 +00:00
deps = binary_u . depends
if not deps :
2016-04-07 19:49:13 +00:00
return True
is_all_ok = True
2006-06-17 13:45:56 +00:00
2013-09-18 20:40:00 +00:00
# for every dependency block (formed as conjunction of disjunction)
2012-04-11 23:36:51 +02:00
for block , block_txt in zip ( parse_depends ( deps , False ) , deps . split ( ' , ' ) ) :
# if the block is satisfied in testing, then skip the block
2016-11-15 23:09:26 +00:00
packages = get_dependency_solvers ( block , binaries_t_a , provides_t_a )
2014-08-09 20:49:11 +02:00
if packages :
2012-04-11 23:36:51 +02:00
for p in packages :
2018-08-12 08:37:07 +00:00
if p . pkg_id . package_name not in binaries_s_a :
2015-09-08 21:45:53 +02:00
continue
2018-08-12 08:37:07 +00:00
excuse . add_sane_dep ( p . source )
2012-04-11 23:36:51 +02:00
continue
2006-06-17 13:45:56 +00:00
2015-09-08 21:45:53 +02:00
# check if the block can be satisfied in the source suite, and list the solving packages
2016-11-15 23:09:26 +00:00
packages = get_dependency_solvers ( block , binaries_s_a , provides_s_a )
2019-01-16 19:17:58 +00:00
packages = sorted ( p . source for p in packages )
2006-06-24 17:49:43 +00:00
2012-04-11 23:36:51 +02:00
# if the dependency can be satisfied by the same source package, skip the block:
# obviously both binary packages will enter testing together
if src in packages : continue
2006-06-17 13:45:56 +00:00
2012-04-11 23:36:51 +02:00
# if no package can satisfy the dependency, add this information to the excuse
2014-08-09 20:49:11 +02:00
if not packages :
2012-04-11 23:36:51 +02:00
excuse . addhtml ( " %s / %s unsatisfiable Depends: %s " % ( pkg , arch , block_txt . strip ( ) ) )
2018-03-06 17:32:13 +01:00
excuse . add_unsatisfiable_dep ( block_txt . strip ( ) , arch )
2015-02-01 11:00:24 +01:00
excuse . addreason ( " depends " )
2017-10-24 22:03:34 +02:00
excuse . add_unsatisfiable_on_arch ( arch )
2016-04-07 19:49:13 +00:00
if arch not in self . options . break_arches :
is_all_ok = False
2012-04-11 23:36:51 +02:00
continue
2006-06-17 13:45:56 +00:00
2012-04-11 23:36:51 +02:00
# for the solving packages, update the excuse to add the dependencies
2018-07-22 15:45:46 +00:00
if arch not in self . options . break_arches :
sources_t = target_suite . sources
sources_s = source_suite . sources
for p in packages :
2019-01-03 19:46:42 +00:00
item_name = compute_item_name ( sources_t , sources_s , p , arch )
excuse . add_dependency ( DependencyType . DEPENDS , item_name , arch )
2018-07-22 15:45:46 +00:00
else :
for p in packages :
2012-04-11 23:36:51 +02:00
excuse . add_break_dep ( p , arch )
2018-07-22 15:45:46 +00:00
2016-04-07 19:49:13 +00:00
return is_all_ok
2006-06-17 13:45:56 +00:00
2010-02-28 12:58:03 +00:00
# Package analysis methods
2006-06-24 17:49:43 +00:00
# ------------------------
def should_remove_source ( self , pkg ) :
""" Check if a source package should be removed from testing
2019-01-05 13:43:10 +00:00
2006-06-24 17:49:43 +00:00
This method checks if a source package should be removed from the
2018-07-22 14:44:24 +00:00
target suite ; this happens if the source package is not
present in the primary source suite anymore .
2006-06-24 17:49:43 +00:00
It returns True if the package can be removed , False otherwise .
2013-07-06 12:42:52 +00:00
In the former case , a new excuse is appended to the object
2006-06-24 17:49:43 +00:00
attribute excuses .
"""
2010-02-28 12:58:03 +00:00
# if the source package is available in unstable, then do nothing
2018-07-23 14:51:34 +00:00
source_suite = self . suite_info . primary_source_suite
if pkg in source_suite . sources :
2006-06-24 17:49:43 +00:00
return False
2015-04-27 19:14:08 +00:00
# otherwise, add a new excuse for its removal
2018-07-22 14:44:24 +00:00
src = self . suite_info . target_suite . sources [ pkg ]
2006-06-24 17:49:43 +00:00
excuse = Excuse ( " - " + pkg )
2018-07-23 14:51:34 +00:00
excuse . addhtml ( " Package not in %s , will try to remove " % source_suite . name )
2016-09-25 05:45:36 +00:00
excuse . set_vers ( src . version , None )
src . maintainer and excuse . set_maint ( src . maintainer )
src . section and excuse . set_section ( src . section )
2006-08-07 11:06:04 +00:00
# if the package is blocked, skip it
2011-09-04 20:26:36 +00:00
for hint in self . hints . search ( ' block ' , package = pkg , removal = True ) :
2015-08-11 22:24:39 +00:00
excuse . addhtml ( " Not touching package, as requested by %s "
2017-09-08 07:43:11 +00:00
" (contact debian-release if update is needed) " % hint . user )
2013-11-15 15:01:20 +00:00
excuse . addreason ( " block " )
2016-03-25 09:18:35 +00:00
self . excuses [ excuse . name ] = excuse
2006-08-07 11:06:04 +00:00
return False
2017-02-08 19:01:41 +00:00
excuse . policy_verdict = PolicyVerdict . PASS
2016-03-25 09:18:35 +00:00
self . excuses [ excuse . name ] = excuse
2006-06-24 17:49:43 +00:00
return True
2018-12-29 14:15:55 +00:00
def should_upgrade_srcarch ( self , src , arch , source_suite ) :
2013-07-06 11:21:53 +00:00
""" Check if a set of binary packages should be upgraded
2006-06-24 17:49:43 +00:00
2013-07-06 11:21:53 +00:00
This method checks if the binary packages produced by the source
package on the given architecture should be upgraded ; this can
happen also if the migration is a binary - NMU for the given arch .
2019-01-05 13:43:10 +00:00
2013-07-06 11:21:53 +00:00
It returns False if the given packages don ' t need to be upgraded,
2006-06-24 17:49:43 +00:00
True otherwise . In the former case , a new excuse is appended to
2013-07-06 12:42:52 +00:00
the object attribute excuses .
2006-06-24 17:49:43 +00:00
"""
# retrieve the source packages for testing and suite
2018-07-22 14:44:24 +00:00
target_suite = self . suite_info . target_suite
source_t = target_suite . sources [ src ]
source_u = source_suite . sources [ src ]
2016-10-24 19:42:05 +00:00
suffix = ' '
2018-07-22 14:44:24 +00:00
if source_suite . excuses_suffix :
suffix = " _ %s " % source_suite . excuses_suffix
2006-06-17 13:45:56 +00:00
2006-06-24 17:49:43 +00:00
# build the common part of the excuse, which will be filled by the code below
2016-10-24 19:42:05 +00:00
ref = " %s / %s %s " % ( src , arch , suffix )
2006-06-17 13:45:56 +00:00
excuse = Excuse ( ref )
2016-09-25 05:45:36 +00:00
excuse . set_vers ( source_t . version , source_t . version )
source_u . maintainer and excuse . set_maint ( source_u . maintainer )
source_u . section and excuse . set_section ( source_u . section )
2019-01-05 13:43:10 +00:00
2010-02-28 12:58:03 +00:00
# if there is a `remove' hint and the requested version is the same as the
2006-06-24 17:49:43 +00:00
# version in testing, then stop here and return False
2013-07-06 11:26:11 +00:00
# (as a side effect, a removal may generate such excuses for both the source
2013-07-06 11:21:53 +00:00
# package and its binary packages on each architecture)
2016-09-25 05:45:36 +00:00
for hint in self . hints . search ( ' remove ' , package = src , version = source_t . version ) :
2016-07-05 22:44:56 +00:00
excuse . add_hint ( hint )
2011-09-04 16:41:33 +00:00
excuse . addhtml ( " Removal request by %s " % ( hint . user ) )
2006-06-17 13:45:56 +00:00
excuse . addhtml ( " Trying to remove package, not update it " )
2016-03-25 09:18:35 +00:00
self . excuses [ excuse . name ] = excuse
2006-06-17 13:45:56 +00:00
return False
2006-06-24 17:49:43 +00:00
# the starting point is that there is nothing wrong and nothing worth doing
anywrongver = False
anyworthdoing = False
2018-10-24 20:09:55 +00:00
packages_t_a = target_suite . binaries [ arch ]
packages_s_a = source_suite . binaries [ arch ]
2016-03-23 15:21:27 +00:00
2006-06-24 17:49:43 +00:00
# for every binary package produced by this source in unstable for this architecture
2016-09-25 05:45:36 +00:00
for pkg_id in sorted ( x for x in source_u . binaries if x . architecture == arch ) :
2016-05-16 08:19:26 +00:00
pkg_name = pkg_id . package_name
2006-06-17 13:45:56 +00:00
2006-06-24 17:49:43 +00:00
# retrieve the testing (if present) and unstable corresponding binary packages
2017-09-02 11:10:56 +00:00
binary_t = packages_t_a [ pkg_name ] if pkg_name in packages_t_a else None
2016-03-23 15:21:27 +00:00
binary_u = packages_s_a [ pkg_name ]
2006-06-24 17:49:43 +00:00
# this is the source version for the new binary package
2016-04-06 20:49:40 +00:00
pkgsv = binary_u . source_version
2006-06-17 13:45:56 +00:00
2006-06-24 17:49:43 +00:00
# if the new binary package is architecture-independent, then skip it
2016-04-06 20:49:40 +00:00
if binary_u . architecture == ' all ' :
2016-09-25 05:45:36 +00:00
if pkg_id not in source_t . binaries :
2016-03-28 11:17:25 +00:00
# only add a note if the arch:all does not match the expected version
2016-04-06 20:49:40 +00:00
excuse . addhtml ( " Ignoring %s %s (from %s ) as it is arch: all " % ( pkg_name , binary_u . version , pkgsv ) )
2006-06-17 13:45:56 +00:00
continue
2006-06-24 17:49:43 +00:00
# if the new binary package is not from the same source as the testing one, then skip it
2013-07-06 11:21:53 +00:00
# this implies that this binary migration is part of a source migration
2016-09-25 05:45:36 +00:00
if source_u . version == pkgsv and source_t . version != pkgsv :
2006-06-17 13:45:56 +00:00
anywrongver = True
2016-09-25 05:45:36 +00:00
excuse . addhtml ( " From wrong source: %s %s ( %s not %s ) " % ( pkg_name , binary_u . version , pkgsv , source_t . version ) )
2015-10-20 18:39:28 +00:00
continue
2006-06-17 13:45:56 +00:00
2015-10-31 21:53:57 +01:00
# cruft in unstable
2016-09-25 05:45:36 +00:00
if source_u . version != pkgsv and source_t . version != pkgsv :
2015-10-31 21:53:57 +01:00
if self . options . ignore_cruft :
excuse . addhtml ( " Old cruft: %s %s (but ignoring cruft, so nevermind) " % ( pkg_name , pkgsv ) )
else :
anywrongver = True
excuse . addhtml ( " Old cruft: %s %s " % ( pkg_name , pkgsv ) )
continue
2013-06-08 15:40:07 +00:00
# if the source package has been updated in unstable and this is a binary migration, skip it
2013-07-06 11:21:53 +00:00
# (the binaries are now out-of-date)
2016-09-25 05:45:36 +00:00
if source_t . version == pkgsv and source_t . version != source_u . version :
2013-06-08 15:40:07 +00:00
anywrongver = True
2016-09-25 05:45:36 +00:00
excuse . addhtml ( " From wrong source: %s %s ( %s not %s ) " % ( pkg_name , binary_u . version , pkgsv , source_u . version ) )
2015-10-20 18:39:28 +00:00
continue
2013-06-08 15:40:07 +00:00
2006-06-24 17:49:43 +00:00
# find unsatisfied dependencies for the new binary package
2018-07-22 15:45:46 +00:00
self . excuse_unsat_deps ( pkg_name , src , arch , source_suite , excuse )
2006-06-17 13:45:56 +00:00
2006-06-24 17:49:43 +00:00
# if the binary is not present in testing, then it is a new binary;
# in this case, there is something worth doing
2006-06-17 13:45:56 +00:00
if not binary_t :
2016-04-06 20:49:40 +00:00
excuse . addhtml ( " New binary: %s ( %s ) " % ( pkg_name , binary_u . version ) )
2006-06-17 13:45:56 +00:00
anyworthdoing = True
continue
2006-06-24 17:49:43 +00:00
# at this point, the binary package is present in testing, so we can compare
# the versions of the packages ...
2016-04-06 20:49:40 +00:00
vcompare = apt_pkg . version_compare ( binary_t . version , binary_u . version )
2006-06-24 17:49:43 +00:00
# ... if updating would mean downgrading, then stop here: there is something wrong
2006-06-17 13:45:56 +00:00
if vcompare > 0 :
anywrongver = True
2016-04-06 20:49:40 +00:00
excuse . addhtml ( " Not downgrading: %s ( %s to %s ) " % ( pkg_name , binary_t . version , binary_u . version ) )
2006-06-17 13:45:56 +00:00
break
2006-06-24 17:49:43 +00:00
# ... if updating would mean upgrading, then there is something worth doing
2006-06-17 13:45:56 +00:00
elif vcompare < 0 :
2016-04-06 20:49:40 +00:00
excuse . addhtml ( " Updated binary: %s ( %s to %s ) " % ( pkg_name , binary_t . version , binary_u . version ) )
2006-06-17 13:45:56 +00:00
anyworthdoing = True
2018-12-11 14:52:44 +00:00
srcv = source_u . version
same_source = source_t . version == srcv
primary_source_suite = self . suite_info . primary_source_suite
is_primary_source = source_suite == primary_source_suite
2006-06-24 17:49:43 +00:00
# if there is nothing wrong and there is something worth doing or the source
2010-02-28 12:58:03 +00:00
# package is not fake, then check what packages should be removed
2016-09-25 05:45:36 +00:00
if not anywrongver and ( anyworthdoing or not source_u . is_fakesrc ) :
2018-12-11 10:49:24 +00:00
# we want to remove binaries that are no longer produced by the
# new source, but there are some special cases:
# - if this is binary-only (same_source) and not from the primary
# source, we don't do any removals:
# binNMUs in *pu on some architectures would otherwise result in
# the removal of binaries on other architectures
# - for the primary source, smooth binaries in the target suite
# are not considered for removal
if not same_source or is_primary_source :
2018-12-07 22:50:04 +00:00
smoothbins = set ( )
2018-12-11 10:49:24 +00:00
if is_primary_source :
2018-12-17 20:55:04 +00:00
binaries_t = target_suite . binaries
2018-12-29 04:11:53 +00:00
possible_smooth_updates = [ p for p in source_t . binaries if p . architecture == arch ]
2018-12-17 20:55:04 +00:00
smoothbins = find_smooth_updateable_binaries ( possible_smooth_updates ,
2018-12-29 04:11:53 +00:00
source_u ,
2018-12-17 20:55:04 +00:00
self . pkg_universe ,
target_suite ,
binaries_t ,
source_suite . binaries ,
frozenset ( ) ,
self . options . smooth_updates )
2013-07-09 17:33:49 +02:00
2018-12-07 22:50:04 +00:00
# for every binary package produced by this source in testing for this architecture
2016-09-25 05:45:36 +00:00
for pkg_id in sorted ( x for x in source_t . binaries if x . architecture == arch ) :
2016-05-16 08:19:26 +00:00
pkg = pkg_id . package_name
2013-02-02 22:47:56 +00:00
# if the package is architecture-independent, then ignore it
2016-03-23 15:21:27 +00:00
tpkg_data = packages_t_a [ pkg ]
2018-11-22 22:11:22 +00:00
if tpkg_data . architecture == ' all ' :
2016-09-25 05:45:36 +00:00
if pkg_id not in source_u . binaries :
2016-03-28 11:17:25 +00:00
# only add a note if the arch:all does not match the expected version
excuse . addhtml ( " Ignoring removal of %s as it is arch: all " % ( pkg ) )
2013-02-02 22:47:56 +00:00
continue
# if the package is not produced by the new source package, then remove it from testing
2016-03-23 15:21:27 +00:00
if pkg not in packages_s_a :
2016-04-06 20:49:40 +00:00
excuse . addhtml ( " Removed binary: %s %s " % ( pkg , tpkg_data . version ) )
2013-07-06 11:21:53 +00:00
# the removed binary is only interesting if this is a binary-only migration,
# as otherwise the updated source will already cause the binary packages
# to be updated
2018-12-11 10:49:24 +00:00
if same_source and pkg_id not in smoothbins :
2013-09-18 20:40:00 +00:00
# Special-case, if the binary is a candidate for a smooth update, we do not consider
2013-07-09 17:33:49 +02:00
# it "interesting" on its own. This case happens quite often with smooth updatable
# packages, where the old binary "survives" a full run because it still has
# reverse dependencies.
2018-09-30 12:05:17 +00:00
anyworthdoing = True
2006-06-17 13:45:56 +00:00
2018-12-31 20:23:50 +00:00
if not anyworthdoing :
# nothing worth doing, we don't add an excuse to the list, we just return false
return False
# there is something worth doing
# we assume that this package will be ok, if not invalidated below
excuse . policy_verdict = PolicyVerdict . PASS
# if there is something something wrong, reject this package
if anywrongver :
excuse . policy_verdict = PolicyVerdict . REJECTED_PERMANENTLY
2006-06-17 13:45:56 +00:00
2018-12-31 20:27:38 +00:00
self . _policy_engine . apply_srcarch_policies ( source_suite , src , arch , source_t , source_u , excuse )
2018-12-31 20:23:50 +00:00
self . excuses [ excuse . name ] = excuse
return excuse . is_valid
2006-06-17 13:45:56 +00:00
2018-12-29 14:15:55 +00:00
def should_upgrade_src ( self , src , source_suite ) :
2006-06-24 17:49:43 +00:00
""" Check if source package should be upgraded
2010-02-28 12:58:03 +00:00
This method checks if a source package should be upgraded . The analysis
2019-01-05 13:43:10 +00:00
is performed for the source package specified by the ` src ' parameter,
2018-12-29 14:15:55 +00:00
for the distribution ` source_suite ' .
2019-01-05 13:43:10 +00:00
2006-06-24 17:49:43 +00:00
It returns False if the given package doesn ' t need to be upgraded,
True otherwise . In the former case , a new excuse is appended to
2011-05-31 20:57:59 +00:00
the object attribute excuses .
2006-06-24 17:49:43 +00:00
"""
2018-12-29 14:15:55 +00:00
suite_name = source_suite . name
2018-07-23 15:13:22 +00:00
source_u = source_suite . sources [ src ]
2017-02-08 17:38:49 +00:00
if source_u . is_fakesrc :
# it is a fake package created to satisfy Britney implementation details; silently ignore it
return False
2018-07-22 14:44:24 +00:00
target_suite = self . suite_info . target_suite
2017-02-08 17:38:49 +00:00
# retrieve the source packages for testing (if available) and suite
2018-07-22 14:44:24 +00:00
if src in target_suite . sources :
source_t = target_suite . sources [ src ]
2006-06-24 17:49:43 +00:00
# if testing and unstable have the same version, then this is a candidate for binary-NMUs only
2016-09-25 05:45:36 +00:00
if apt_pkg . version_compare ( source_t . version , source_u . version ) == 0 :
2006-06-17 13:45:56 +00:00
return False
else :
source_t = None
2016-10-24 19:42:05 +00:00
suffix = ' '
2018-07-22 14:44:24 +00:00
if source_suite . excuses_suffix :
suffix = " _ %s " % source_suite . excuses_suffix
2016-10-24 19:42:05 +00:00
2006-06-24 17:49:43 +00:00
# build the common part of the excuse, which will be filled by the code below
2016-10-24 19:42:05 +00:00
ref = " %s %s " % ( src , suffix )
2006-06-17 13:45:56 +00:00
excuse = Excuse ( ref )
2016-09-25 05:45:36 +00:00
excuse . set_vers ( source_t and source_t . version or None , source_u . version )
source_u . maintainer and excuse . set_maint ( source_u . maintainer )
source_u . section and excuse . set_section ( source_u . section )
2006-06-24 17:49:43 +00:00
# if the version in unstable is older, then stop here with a warning in the excuse and return False
2016-09-25 05:45:36 +00:00
if source_t and apt_pkg . version_compare ( source_u . version , source_t . version ) < 0 :
2018-07-22 14:44:24 +00:00
excuse . addhtml ( " ALERT: %s is newer in the target suite ( %s %s ) " % ( src , source_t . version , source_u . version ) )
2016-03-25 09:18:35 +00:00
self . excuses [ excuse . name ] = excuse
2015-02-01 11:00:24 +01:00
excuse . addreason ( " newerintesting " )
2006-06-17 13:45:56 +00:00
return False
2016-11-01 11:59:11 +02:00
# the starting point is that we will update the candidate
2017-02-08 19:01:41 +00:00
excuse . policy_verdict = PolicyVerdict . PASS
2016-11-01 11:59:11 +02:00
2010-02-28 12:58:03 +00:00
# if there is a `remove' hint and the requested version is the same as the
2006-06-24 17:49:43 +00:00
# version in testing, then stop here and return False
2016-07-05 22:44:56 +00:00
for hint in self . hints . search ( ' remove ' , package = src ) :
2016-09-25 05:45:36 +00:00
if source_t and source_t . version == hint . version or \
source_u . version == hint . version :
2016-07-05 22:44:56 +00:00
excuse . add_hint ( hint )
excuse . addhtml ( " Removal request by %s " % ( hint . user ) )
2006-06-17 13:45:56 +00:00
excuse . addhtml ( " Trying to remove package, not update it " )
2017-02-08 19:01:41 +00:00
excuse . policy_verdict = PolicyVerdict . REJECTED_PERMANENTLY
2017-02-08 17:49:30 +00:00
break
2006-06-17 13:45:56 +00:00
2018-01-07 13:16:06 +00:00
all_binaries = self . all_binaries
2019-01-16 19:17:58 +00:00
for pkg_id in sorted ( source_u . binaries ) :
2018-07-22 15:45:46 +00:00
is_valid = self . excuse_unsat_deps ( pkg_id . package_name , src , pkg_id . architecture , source_suite , excuse )
2018-01-07 13:16:06 +00:00
if is_valid :
continue
binary_u = all_binaries [ pkg_id ]
# There is an issue with the package. If it is arch:any, then excuse_unsat_deps will have
# handled everything for us correctly. However, arch:all have some special-casing IRT
# nobreakall that we deal with ourselves here.
if binary_u . architecture == ' all ' and pkg_id . architecture in self . options . nobreakall_arches :
# We sometimes forgive uninstallable arch:all packages on nobreakall architectures
# (e.g. we sometimes force-hint in arch:all packages that are only installable on
# on a subset of all nobreak architectures).
# This forgivness is only done if the package is already in testing AND it is broken
# in testing on this architecture already. Anything else would be a regression
2018-11-01 21:22:38 +00:00
if target_suite . is_pkg_in_the_suite ( pkg_id ) and not target_suite . is_installable ( pkg_id ) :
2018-01-07 13:16:06 +00:00
# It is a regression.
excuse . policy_verdict = PolicyVerdict . REJECTED_PERMANENTLY
2016-10-20 09:00:14 +02:00
# at this point, we check the status of the builds on all the supported architectures
# to catch the out-of-date ones
2017-11-17 18:20:43 +01:00
archs_to_consider = list ( self . options . architectures )
archs_to_consider . append ( ' all ' )
for arch in archs_to_consider :
2016-10-20 09:00:14 +02:00
oodbins = { }
uptodatebins = False
# for every binary package produced by this source in the suite for this architecture
2017-11-17 18:20:43 +01:00
if arch == ' all ' :
consider_binaries = source_u . binaries
else :
2018-04-29 10:12:21 +00:00
# Will also include arch:all for the given architecture (they are filtered out
# below)
2017-11-17 18:20:43 +01:00
consider_binaries = sorted ( x for x in source_u . binaries if x . architecture == arch )
for pkg_id in consider_binaries :
2016-10-20 09:00:14 +02:00
pkg = pkg_id . package_name
# retrieve the binary package and its source version
binary_u = all_binaries [ pkg_id ]
pkgsv = binary_u . source_version
2017-11-17 18:20:43 +01:00
# arch:all packages are treated separately from arch:arch
if binary_u . architecture != arch :
continue
2016-10-20 09:00:14 +02:00
# if it wasn't built by the same source, it is out-of-date
# if there is at least one binary on this arch which is
# up-to-date, there is a build on this arch
if source_u . version != pkgsv :
if pkgsv not in oodbins :
2018-04-29 10:12:21 +00:00
oodbins [ pkgsv ] = set ( )
oodbins [ pkgsv ] . add ( pkg )
2016-10-20 09:00:14 +02:00
excuse . add_old_binary ( pkg , pkgsv )
continue
else :
2017-11-17 18:20:43 +01:00
uptodatebins = True
2016-10-20 09:00:14 +02:00
2016-11-01 11:59:11 +02:00
# if there are out-of-date packages, warn about them in the excuse and set excuse.is_valid
2016-10-20 09:00:14 +02:00
# to False to block the update; if the architecture where the package is out-of-date is
# in the `outofsync_arches' list, then do not block the update
if oodbins :
oodtxt = " "
2019-01-16 19:17:58 +00:00
for v in sorted ( oodbins ) :
2016-10-20 09:00:14 +02:00
if oodtxt : oodtxt = oodtxt + " ; "
oodtxt = oodtxt + " %s (from <a href= \" https://buildd.debian.org/status/logs.php? " \
" arch= %s &pkg= %s &ver= %s \" target= \" _blank \" > %s </a>) " % \
( " , " . join ( sorted ( oodbins [ v ] ) ) , quote ( arch ) , quote ( src ) , quote ( v ) , v )
if uptodatebins :
text = " old binaries left on <a href= \" https://buildd.debian.org/status/logs.php? " \
" arch= %s &pkg= %s &ver= %s \" target= \" _blank \" > %s </a>: %s " % \
( quote ( arch ) , quote ( src ) , quote ( source_u . version ) , arch , oodtxt )
else :
text = " missing build on <a href= \" https://buildd.debian.org/status/logs.php? " \
" arch= %s &pkg= %s &ver= %s \" target= \" _blank \" > %s </a>: %s " % \
( quote ( arch ) , quote ( src ) , quote ( source_u . version ) , arch , oodtxt )
if arch in self . options . outofsync_arches :
text = text + " (but %s isn ' t keeping up, so nevermind) " % ( arch )
if not uptodatebins :
excuse . missing_build_on_ood_arch ( arch )
else :
if uptodatebins :
if self . options . ignore_cruft :
text = text + " (but ignoring cruft, so nevermind) "
else :
2017-02-08 19:01:41 +00:00
excuse . policy_verdict = PolicyVerdict . REJECTED_PERMANENTLY
2016-10-20 09:00:14 +02:00
else :
2017-02-08 20:02:16 +00:00
excuse . policy_verdict = PolicyVerdict . REJECTED_CANNOT_DETERMINE_IF_PERMANENT
2016-10-20 09:00:14 +02:00
excuse . missing_build_on_arch ( arch )
excuse . addhtml ( text )
2016-11-01 11:59:11 +02:00
# if the source package has no binaries, set is_valid to False to block the update
2016-10-20 09:00:14 +02:00
if not source_u . binaries :
excuse . addhtml ( " %s has no binaries on any arch " % src )
excuse . addreason ( " no-binaries " )
2017-02-08 19:01:41 +00:00
excuse . policy_verdict = PolicyVerdict . REJECTED_PERMANENTLY
2016-10-20 09:00:14 +02:00
2018-12-31 19:12:21 +00:00
self . _policy_engine . apply_src_policies ( source_suite , src , source_t , source_u , excuse )
2016-03-25 15:23:34 +00:00
2018-07-22 14:44:24 +00:00
if source_suite . suite_class . is_additional_source and source_t :
2011-07-27 09:30:00 +00:00
# o-o-d(ish) checks for (t-)p-u
2016-03-23 15:21:27 +00:00
# This only makes sense if the package is actually in testing.
2010-09-20 22:03:43 +00:00
for arch in self . options . architectures :
2013-02-06 19:05:16 +00:00
# if the package in testing has no binaries on this
# architecture, it can't be out-of-date
2016-09-25 05:45:36 +00:00
if not any ( x for x in source_t . binaries
2016-05-16 08:19:26 +00:00
if x . architecture == arch and all_binaries [ x ] . architecture != ' all ' ) :
2013-02-06 19:05:16 +00:00
continue
2016-03-26 08:16:39 +00:00
2013-02-06 19:05:16 +00:00
# if the (t-)p-u package has produced any binaries on
# this architecture then we assume it's ok. this allows for
# uploads to (t-)p-u which intentionally drop binary
# packages
2018-10-24 20:09:55 +00:00
if any ( x for x in source_suite . binaries [ arch ] . values ( )
2018-07-23 15:13:22 +00:00
if x . source == src and x . source_version == source_u . version and
x . architecture != ' all ' ) :
2010-09-20 22:03:43 +00:00
continue
2018-07-22 14:44:24 +00:00
# TODO: Find a way to avoid hardcoding pu/stable relation.
if suite_name == ' pu ' :
2011-07-27 09:30:00 +00:00
base = ' stable '
2018-07-22 14:44:24 +00:00
else :
base = target_suite . name
text = " Not yet built on <a href= \" https://buildd.debian.org/status/logs.php?arch= %s &pkg= %s &ver= %s &suite= %s \" target= \" _blank \" > %s </a> (relative to target suite) " % ( quote ( arch ) , quote ( src ) , quote ( source_u . version ) , base , arch )
2010-09-20 22:03:43 +00:00
2013-09-09 12:37:52 +01:00
if arch in self . options . outofsync_arches :
2010-09-20 22:03:43 +00:00
text = text + " (but %s isn ' t keeping up, so never mind) " % ( arch )
2016-03-28 09:37:02 +00:00
excuse . missing_build_on_ood_arch ( arch )
2010-09-20 22:03:43 +00:00
else :
2017-02-08 20:02:16 +00:00
excuse . policy_verdict = PolicyVerdict . REJECTED_CANNOT_DETERMINE_IF_PERMANENT
2016-03-28 09:37:02 +00:00
excuse . missing_build_on_arch ( arch )
2010-09-20 22:03:43 +00:00
excuse . addhtml ( text )
2006-06-24 17:49:43 +00:00
# check if there is a `force' hint for this package, which allows it to go in even if it is not updateable
2016-09-25 05:45:36 +00:00
forces = self . hints . search ( ' force ' , package = src , version = source_u . version )
2011-09-04 16:41:33 +00:00
if forces :
2017-02-08 18:14:29 +00:00
# force() updates the final verdict for us
changed_state = excuse . force ( )
if changed_state :
2016-11-16 21:08:14 +00:00
excuse . addhtml ( " Should ignore, but forced by %s " % ( forces [ 0 ] . user ) )
2006-06-17 13:45:56 +00:00
2016-03-25 09:18:35 +00:00
self . excuses [ excuse . name ] = excuse
2016-11-01 11:59:11 +02:00
return excuse . is_valid
2019-01-05 13:43:10 +00:00
2016-03-23 09:55:07 +00:00
def write_excuses ( self ) :
2006-06-24 17:49:43 +00:00
""" Produce and write the update excuses
This method handles the update excuses generation : the packages are
2010-02-28 12:58:03 +00:00
looked at to determine whether they are valid candidates . For the details
2006-06-24 17:49:43 +00:00
of this procedure , please refer to the module docstring .
"""
2006-06-17 13:45:56 +00:00
2018-03-24 09:42:06 +00:00
self . logger . info ( " Update Excuses generation started " )
2006-06-25 13:51:53 +00:00
2006-07-26 19:48:54 +00:00
# list of local methods and variables (for better performance)
2018-07-22 14:54:56 +00:00
suite_info = self . suite_info
2018-07-23 14:51:34 +00:00
pri_source_suite = suite_info . primary_source_suite
2006-07-26 19:48:54 +00:00
architectures = self . options . architectures
should_remove_source = self . should_remove_source
should_upgrade_srcarch = self . should_upgrade_srcarch
should_upgrade_src = self . should_upgrade_src
2018-07-23 14:51:34 +00:00
sources_s = pri_source_suite . sources
2018-07-22 14:54:56 +00:00
sources_t = suite_info . target_suite . sources
2016-09-20 14:03:02 -07:00
2006-06-24 17:49:43 +00:00
# this list will contain the packages which are valid candidates;
# if a package is going to be removed, it will have a "-" prefix
2017-10-30 20:58:12 +00:00
upgrade_me = set ( )
upgrade_me_add = upgrade_me . add # Every . in a loop slows it down
2006-06-17 13:45:56 +00:00
2016-03-25 09:18:35 +00:00
excuses = self . excuses = { }
2013-03-16 16:31:04 +00:00
2006-06-24 17:49:43 +00:00
# for every source package in testing, check if it should be removed
2018-07-22 14:54:56 +00:00
for pkg in sources_t :
2006-07-26 19:48:54 +00:00
if should_remove_source ( pkg ) :
2017-10-30 20:58:12 +00:00
upgrade_me_add ( " - " + pkg )
2006-06-17 13:45:56 +00:00
2006-06-24 17:49:43 +00:00
# for every source package in unstable check if it should be upgraded
2018-07-22 14:54:56 +00:00
for pkg in sources_s :
if sources_s [ pkg ] . is_fakesrc :
continue
2006-06-24 17:49:43 +00:00
# if the source package is already present in testing,
# check if it should be upgraded for every binary package
2018-07-22 14:54:56 +00:00
if pkg in sources_t and not sources_t [ pkg ] . is_fakesrc :
2006-07-26 19:48:54 +00:00
for arch in architectures :
2018-12-29 14:15:55 +00:00
if should_upgrade_srcarch ( pkg , arch , pri_source_suite ) :
2017-10-30 20:58:12 +00:00
upgrade_me_add ( " %s / %s " % ( pkg , arch ) )
2006-06-17 13:45:56 +00:00
2006-06-24 17:49:43 +00:00
# check if the source package should be upgraded
2018-12-29 14:15:55 +00:00
if should_upgrade_src ( pkg , pri_source_suite ) :
2017-10-30 20:58:12 +00:00
upgrade_me_add ( pkg )
2006-06-17 13:45:56 +00:00
2018-03-30 18:57:52 +00:00
# for every source package in the additional source suites, check if it should be upgraded
for suite in self . suite_info . additional_source_suites :
2018-07-22 14:54:56 +00:00
for pkg in suite . sources :
2011-07-27 09:30:00 +00:00
# if the source package is already present in testing,
# check if it should be upgraded for every binary package
2018-07-22 14:54:56 +00:00
if pkg in sources_t :
2011-07-27 09:30:00 +00:00
for arch in architectures :
2018-12-29 14:15:55 +00:00
if should_upgrade_srcarch ( pkg , arch , suite ) :
2018-08-07 20:43:46 +00:00
upgrade_me_add ( " %s / %s _ %s " % ( pkg , arch , suite . excuses_suffix ) )
2006-06-17 13:45:56 +00:00
2011-07-27 09:30:00 +00:00
# check if the source package should be upgraded
2018-12-29 14:15:55 +00:00
if should_upgrade_src ( pkg , suite ) :
2018-08-07 20:43:46 +00:00
upgrade_me_add ( " %s _ %s " % ( pkg , suite . excuses_suffix ) )
2006-06-17 13:45:56 +00:00
2006-06-24 17:49:43 +00:00
# process the `remove' hints, if the given package is not yet in upgrade_me
2016-07-05 22:44:56 +00:00
for hint in self . hints [ ' remove ' ] :
src = hint . package
2006-06-17 13:45:56 +00:00
if src in upgrade_me : continue
if ( " - " + src ) in upgrade_me : continue
2018-07-22 14:54:56 +00:00
if src not in sources_t :
continue
2006-06-17 13:45:56 +00:00
2010-02-28 12:58:03 +00:00
# check if the version specified in the hint is the same as the considered package
2018-07-22 14:54:56 +00:00
tsrcv = sources_t [ src ] . version
2016-07-05 22:44:56 +00:00
if tsrcv != hint . version :
2016-03-23 09:55:07 +00:00
continue
2006-06-17 13:45:56 +00:00
2006-06-24 17:49:43 +00:00
# add the removal of the package to upgrade_me and build a new excuse
2006-06-17 13:45:56 +00:00
excuse = Excuse ( " - %s " % ( src ) )
excuse . set_vers ( tsrcv , None )
2016-07-05 22:44:56 +00:00
excuse . addhtml ( " Removal request by %s " % ( hint . user ) )
2018-11-19 15:46:34 +00:00
# if the removal of the package is blocked, skip it
blocked = False
for blockhint in self . hints . search ( ' block ' , package = src , removal = True ) :
excuse . addhtml ( " Not removing package, due to block hint by %s "
" (contact debian-release if update is needed) " % blockhint . user )
excuse . addreason ( " block " )
blocked = True
if blocked :
excuses [ excuse . name ] = excuse
continue
upgrade_me_add ( " - %s " % ( src ) )
2006-06-17 13:45:56 +00:00
excuse . addhtml ( " Package is broken, will try to remove " )
2016-07-05 22:44:56 +00:00
excuse . add_hint ( hint )
2017-02-08 19:01:41 +00:00
# Using "PASS" here as "Created by a hint" != "accepted due to hint". In a future
# where there might be policy checks on removals, it would make sense to distinguish
# those two states. Not sure that future will ever be.
excuse . policy_verdict = PolicyVerdict . PASS
2016-03-25 09:18:35 +00:00
excuses [ excuse . name ] = excuse
2006-06-17 13:45:56 +00:00
2006-06-24 17:49:43 +00:00
# extract the not considered packages, which are in the excuses but not in upgrade_me
2017-10-30 20:58:12 +00:00
unconsidered = { ename for ename in excuses if ename not in upgrade_me }
2006-06-17 13:45:56 +00:00
2006-06-24 17:49:43 +00:00
# invalidate impossible excuses
2016-03-25 09:18:35 +00:00
for e in excuses . values ( ) :
2011-08-02 18:46:17 +00:00
# parts[0] == package name
# parts[1] == optional architecture
parts = e . name . split ( ' / ' )
2019-01-19 11:38:21 +00:00
for d in sorted ( e . all_deps ) :
2018-12-30 20:19:38 +00:00
for deptype in e . all_deps [ d ] :
ok = False
# source -> source dependency; both packages must have
# valid excuses
if d in upgrade_me or d in unconsidered :
2011-08-02 18:46:17 +00:00
ok = True
2018-12-30 20:19:38 +00:00
# if the excuse is for a binNMU, also consider d/$arch as a
# valid excuse
elif len ( parts ) == 2 :
bd = ' %s / %s ' % ( d , parts [ 1 ] )
if bd in upgrade_me or bd in unconsidered :
ok = True
# if the excuse is for a source package, check each of the
# architectures on which the excuse lists a dependency on d,
# and consider the excuse valid if it is possible on each
# architecture
else :
arch_ok = True
for arch in e . all_deps [ d ] [ deptype ] :
bd = ' %s / %s ' % ( d , arch )
if bd not in upgrade_me and bd not in unconsidered :
arch_ok = False
break
if arch_ok :
ok = True
if not ok :
e . addhtml ( " Impossible %s : %s -> %s " % ( deptype , e . name , d ) )
e . addreason ( deptype . get_reason ( ) )
2016-11-16 07:23:58 +00:00
invalidate_excuses ( excuses , upgrade_me , unconsidered )
2006-06-17 13:45:56 +00:00
2006-07-26 22:12:09 +00:00
# sort the list of candidates
2018-12-23 18:08:23 +00:00
mi_factory = self . _migration_item_factory
self . upgrade_me = sorted ( mi_factory . parse_item ( x , versioned = False , auto_correct = False ) for x in upgrade_me )
2006-06-25 13:51:53 +00:00
2006-06-24 17:49:43 +00:00
# write excuses to the output file
2008-01-15 16:09:51 +00:00
if not self . options . dry_run :
2018-03-30 17:38:50 +00:00
self . logger . info ( " > Writing Excuses to %s " , self . options . excuses_output )
2016-03-25 09:18:35 +00:00
sorted_excuses = sorted ( excuses . values ( ) , key = lambda x : x . sortkey ( ) )
write_excuses ( sorted_excuses , self . options . excuses_output ,
2013-12-11 20:55:17 +01:00
output_format = " legacy-html " )
2013-08-26 20:12:46 +00:00
if hasattr ( self . options , ' excuses_yaml_output ' ) :
2018-03-30 17:38:50 +00:00
self . logger . info ( " > Writing YAML Excuses to %s " , self . options . excuses_yaml_output )
2016-03-25 09:18:35 +00:00
write_excuses ( sorted_excuses , self . options . excuses_yaml_output ,
output_format = " yaml " )
2013-08-26 20:12:46 +00:00
2018-03-24 09:42:06 +00:00
self . logger . info ( " Update Excuses generation completed " )
2006-06-25 13:51:53 +00:00
2006-07-20 19:00:47 +00:00
# Upgrade run
# -----------
2006-07-22 09:54:27 +00:00
def eval_nuninst ( self , nuninst , original = None ) :
2006-07-30 11:15:27 +00:00
""" Return a string which represents the uninstallability counters
This method returns a string which represents the uninstallability
counters reading the uninstallability statistics ` nuninst ` and , if
present , merging the results with the ` original ` one .
An example of the output string is :
1 + 2 : i - 0 : a - 0 : a - 0 : h - 0 : i - 1 : m - 0 : m - 0 : p - 0 : a - 0 : m - 0 : s - 2 : s - 0
where the first part is the number of broken packages in non - break
architectures + the total number of broken packages for all the
architectures .
"""
2006-07-20 19:00:47 +00:00
res = [ ]
total = 0
totalbreak = 0
for arch in self . options . architectures :
2006-07-26 19:26:38 +00:00
if arch in nuninst :
2006-07-25 20:32:23 +00:00
n = len ( nuninst [ arch ] )
2006-07-26 19:26:38 +00:00
elif original and arch in original :
2006-07-25 20:32:23 +00:00
n = len ( original [ arch ] )
else : continue
2015-05-27 22:34:42 +02:00
if arch in self . options . break_arches :
2006-07-25 20:32:23 +00:00
totalbreak = totalbreak + n
else :
total = total + n
res . append ( " %s - %d " % ( arch [ 0 ] , n ) )
2006-07-20 19:00:47 +00:00
return " %d + %d : %s " % ( total , totalbreak , " : " . join ( res ) )
2018-12-18 21:47:39 +00:00
def iter_packages ( self , packages , selected , nuninst = None , try_removals = True ) :
2006-07-30 11:15:27 +00:00
""" Iter on the list of actions and apply them one-by-one
2010-02-28 12:58:03 +00:00
This method applies the changes from ` packages ` to testing , checking the uninstallability
counters for every action performed . If the action does not improve them , it is reverted .
2006-07-30 11:15:27 +00:00
The method returns the new uninstallability counters and the remaining actions if the
2015-09-07 18:07:37 +02:00
final result is successful , otherwise ( None , [ ] ) .
2006-07-30 11:15:27 +00:00
"""
2015-09-07 18:07:37 +02:00
group_info = { }
2016-04-08 18:02:19 +00:00
rescheduled_packages = packages
maybe_rescheduled_packages = [ ]
2018-04-01 11:18:47 +00:00
output_logger = self . output_logger
2018-10-31 22:06:45 +00:00
solver = InstallabilitySolver ( self . pkg_universe , self . _inst_tester )
2018-12-18 20:47:49 +00:00
mm = self . _migration_manager
2019-01-19 16:47:40 +00:00
target_suite = self . suite_info . target_suite
2015-09-07 18:07:37 +02:00
2012-05-11 16:31:50 +02:00
for y in sorted ( ( y for y in packages ) , key = attrgetter ( ' uvname ' ) ) :
2018-12-17 18:11:52 +00:00
try :
2019-01-09 00:49:52 +00:00
_ , updates , rms , _ = mm . compute_groups ( y )
2018-12-17 18:11:52 +00:00
result = ( y , frozenset ( updates ) , frozenset ( rms ) )
group_info [ y ] = result
except MigrationConstraintException as e :
rescheduled_packages . remove ( y )
output_logger . info ( " not adding package to list: %s " , ( y . package ) )
output_logger . info ( " got exception: %s " % ( repr ( e ) ) )
2006-08-15 21:23:36 +00:00
if nuninst :
2012-05-11 16:31:50 +02:00
nuninst_orig = nuninst
2006-08-15 21:23:36 +00:00
else :
2012-05-11 16:31:50 +02:00
nuninst_orig = self . nuninst_orig
2015-09-07 18:07:37 +02:00
2012-05-11 16:31:50 +02:00
nuninst_last_accepted = nuninst_orig
2015-09-07 18:07:37 +02:00
2018-04-01 11:18:47 +00:00
output_logger . info ( " recur: [] %s %d /0 " , " , " . join ( x . uvname for x in selected ) , len ( packages ) )
2016-04-08 18:02:19 +00:00
while rescheduled_packages :
groups = { group_info [ x ] for x in rescheduled_packages }
2018-10-31 22:06:45 +00:00
worklist = solver . solve_groups ( groups )
2016-04-08 18:02:19 +00:00
rescheduled_packages = [ ]
2015-09-07 18:07:37 +02:00
worklist . reverse ( )
while worklist :
comp = worklist . pop ( )
comp_name = ' ' . join ( item . uvname for item in comp )
2018-04-01 11:18:47 +00:00
output_logger . info ( " trying: %s " % comp_name )
2018-12-18 21:47:39 +00:00
with mm . start_transaction ( ) as transaction :
2018-12-17 18:11:52 +00:00
accepted = False
try :
2018-12-18 21:14:01 +00:00
accepted , nuninst_after , failed_arch = mm . migrate_item_to_target_suite ( comp ,
2018-12-18 21:47:39 +00:00
nuninst_last_accepted )
2018-12-17 18:11:52 +00:00
if accepted :
selected . extend ( comp )
transaction . commit ( )
output_logger . info ( " accepted: %s " , comp_name )
output_logger . info ( " ori: %s " , self . eval_nuninst ( nuninst_orig ) )
output_logger . info ( " pre: %s " , self . eval_nuninst ( nuninst_last_accepted ) )
output_logger . info ( " now: %s " , self . eval_nuninst ( nuninst_after ) )
if len ( selected ) < = 20 :
output_logger . info ( " all: %s " , " " . join ( x . uvname for x in selected ) )
else :
output_logger . info ( " most: ( %d ) .. %s " ,
len ( selected ) ,
" " . join ( x . uvname for x in selected [ - 20 : ] ) )
2019-01-14 13:13:45 +00:00
if self . options . check_consistency_level > = 3 :
2019-01-19 16:47:40 +00:00
target_suite . check_suite_source_pkg_consistency ( ' iter_packages after commit ' )
2018-12-17 18:11:52 +00:00
nuninst_last_accepted = nuninst_after
rescheduled_packages . extend ( maybe_rescheduled_packages )
maybe_rescheduled_packages . clear ( )
2018-12-16 12:03:44 +00:00
else :
2018-12-17 18:11:52 +00:00
transaction . rollback ( )
broken = sorted ( b for b in nuninst_after [ failed_arch ]
if b not in nuninst_last_accepted [ failed_arch ] )
compare_nuninst = None
if any ( item for item in comp if item . architecture != ' source ' ) :
compare_nuninst = nuninst_last_accepted
# NB: try_migration already reverted this for us, so just print the results and move on
output_logger . info ( " skipped: %s ( %d , %d , %d ) " ,
comp_name ,
len ( rescheduled_packages ) ,
len ( maybe_rescheduled_packages ) ,
len ( worklist )
)
output_logger . info ( " got: %s " , self . eval_nuninst ( nuninst_after , compare_nuninst ) )
output_logger . info ( " * %s : %s " , failed_arch , " , " . join ( broken ) )
2019-01-14 13:13:45 +00:00
if self . options . check_consistency_level > = 3 :
2019-01-19 16:47:40 +00:00
target_suite . check_suite_source_pkg_consistency ( ' iter_package after rollback (not accepted) ' )
2018-12-17 18:11:52 +00:00
except MigrationConstraintException as e :
2018-12-17 18:30:10 +00:00
transaction . rollback ( )
2018-12-16 12:03:44 +00:00
output_logger . info ( " skipped: %s ( %d , %d , %d ) " ,
comp_name ,
len ( rescheduled_packages ) ,
len ( maybe_rescheduled_packages ) ,
len ( worklist )
)
2018-12-17 18:11:52 +00:00
output_logger . info ( " got exception: %s " % ( repr ( e ) ) )
2019-01-14 13:13:45 +00:00
if self . options . check_consistency_level > = 3 :
2019-01-19 16:47:40 +00:00
target_suite . check_suite_source_pkg_consistency ( ' iter_package after rollback (MigrationConstraintException) ' )
2018-12-16 12:03:44 +00:00
2018-12-17 18:11:52 +00:00
if not accepted :
2018-12-16 12:03:44 +00:00
if len ( comp ) > 1 :
output_logger . info ( " - splitting the component into single items and retrying them " )
worklist . extend ( [ item ] for item in comp )
else :
maybe_rescheduled_packages . append ( comp [ 0 ] )
2006-08-01 20:27:48 +00:00
2018-12-17 20:53:17 +00:00
if try_removals and self . options . smooth_updates :
self . logger . info ( " > Removing old packages left in the target suite from smooth updates " )
2018-12-23 18:08:23 +00:00
removals = old_libraries ( self . _migration_item_factory , self . suite_info , self . options . outofsync_arches )
2018-12-17 20:53:17 +00:00
if removals :
output_logger . info ( " Removing packages left in the target suite for smooth updates ( %d ): " , len ( removals ) )
log_and_format_old_libraries ( self . output_logger , removals )
( nuninst_last_accepted , extra ) = self . iter_packages ( removals ,
selected ,
nuninst = nuninst_last_accepted ,
try_removals = False )
2018-04-01 11:18:47 +00:00
output_logger . info ( " finish: [ %s ] " , " , " . join ( x . uvname for x in selected ) )
output_logger . info ( " endloop: %s " , self . eval_nuninst ( self . nuninst_orig ) )
output_logger . info ( " now: %s " , self . eval_nuninst ( nuninst_last_accepted ) )
format_and_log_uninst ( output_logger ,
2018-04-01 10:32:12 +00:00
self . options . architectures ,
newly_uninst ( self . nuninst_orig , nuninst_last_accepted )
)
2018-04-01 11:18:47 +00:00
output_logger . info ( " " )
2006-07-22 19:14:44 +00:00
2016-04-08 18:02:19 +00:00
return ( nuninst_last_accepted , maybe_rescheduled_packages )
2006-07-22 19:14:44 +00:00
2012-01-04 14:20:33 +01:00
def do_all ( self , hinttype = None , init = None , actions = None ) :
2006-07-30 11:15:27 +00:00
""" Testing update runner
This method tries to update testing checking the uninstallability
counters before and after the actions to decide if the update was
successful or not .
"""
2006-08-15 15:32:14 +00:00
selected = [ ]
2006-08-06 09:29:32 +00:00
if actions :
upgrade_me = actions [ : ]
else :
upgrade_me = self . upgrade_me [ : ]
2006-08-01 20:27:48 +00:00
nuninst_start = self . nuninst_orig
2018-04-01 11:18:47 +00:00
output_logger = self . output_logger
2019-01-19 16:47:40 +00:00
target_suite = self . suite_info . target_suite
2006-07-25 20:32:23 +00:00
2006-08-01 18:59:29 +00:00
# these are special parameters for hints processing
force = False
2012-05-22 19:56:11 +00:00
recurse = True
2012-05-14 19:28:03 +02:00
nuninst_end = None
2014-08-05 22:44:14 +02:00
extra = [ ]
2018-12-18 21:47:39 +00:00
mm = self . _migration_manager
2012-05-14 19:28:03 +02:00
2012-01-04 14:20:33 +01:00
if hinttype == " easy " or hinttype == " force-hint " :
force = hinttype == " force-hint "
2012-05-22 19:56:11 +00:00
recurse = False
2006-08-01 18:59:29 +00:00
# if we have a list of initial packages, check them
if init :
for x in init :
2006-08-01 20:27:48 +00:00
if x not in upgrade_me :
2018-04-01 11:18:47 +00:00
output_logger . warning ( " failed: %s is not a valid candidate (or it already migrated) " , x . uvname )
2006-08-01 18:59:29 +00:00
return None
2006-08-06 09:29:32 +00:00
selected . append ( x )
2006-08-01 20:27:48 +00:00
upgrade_me . remove ( x )
2018-04-01 11:18:47 +00:00
output_logger . info ( " start: %s " , self . eval_nuninst ( nuninst_start ) )
output_logger . info ( " orig: %s " , self . eval_nuninst ( nuninst_start ) )
2012-05-14 19:28:03 +02:00
2018-12-18 21:47:39 +00:00
if init and not force :
# We will need to be able to roll back (e.g. easy or a "hint"-hint)
_start_transaction = mm . start_transaction
else :
# No "outer" transaction needed as we will never need to rollback
# (e.g. "force-hint" or a regular "main run"). Emulate the start_transaction
# call from the MigrationManager, so the rest of the code follows the
# same flow regardless of whether we need the transaction or not.
@contextlib.contextmanager
def _start_transaction ( ) :
yield None
with _start_transaction ( ) as transaction :
2015-05-28 08:29:50 +02:00
2018-12-16 12:03:44 +00:00
if init :
# init => a hint (e.g. "easy") - so do the hint run
2018-12-18 21:14:01 +00:00
( _ , nuninst_end , _ ) = mm . migrate_item_to_target_suite ( selected ,
self . nuninst_orig ,
stop_on_first_regression = False )
2012-05-14 19:11:08 +02:00
2018-12-16 12:03:44 +00:00
if recurse :
# Ensure upgrade_me and selected do not overlap, if we
# follow-up with a recurse ("hint"-hint).
upgrade_me = [ x for x in upgrade_me if x not in set ( selected ) ]
2012-05-14 19:11:08 +02:00
2018-12-16 12:03:44 +00:00
if recurse :
# Either the main run or the recursive run of a "hint"-hint.
( nuninst_end , extra ) = self . iter_packages ( upgrade_me ,
selected ,
2018-12-18 21:47:39 +00:00
nuninst = nuninst_end )
2012-05-14 19:11:08 +02:00
2018-12-16 12:03:44 +00:00
nuninst_end_str = self . eval_nuninst ( nuninst_end )
2012-05-14 19:11:08 +02:00
2018-12-16 12:03:44 +00:00
if not recurse :
# easy or force-hint
output_logger . info ( " easy: %s " , nuninst_end_str )
2012-05-14 19:11:08 +02:00
2018-12-16 12:03:44 +00:00
if not force :
2018-04-01 10:32:12 +00:00
format_and_log_uninst ( self . output_logger ,
self . options . architectures ,
2018-12-16 12:03:44 +00:00
newly_uninst ( nuninst_start , nuninst_end )
2018-04-01 10:32:12 +00:00
)
2006-08-03 11:10:40 +00:00
2018-12-16 12:03:44 +00:00
if force :
# Force implies "unconditionally better"
better = True
else :
break_arches = set ( self . options . break_arches )
if all ( x . architecture in break_arches for x in selected ) :
# If we only migrated items from break-arches, then we
# do not allow any regressions on these architectures.
# This usually only happens with hints
break_arches = set ( )
better = is_nuninst_asgood_generous ( self . constraints ,
self . options . architectures ,
self . nuninst_orig ,
nuninst_end ,
break_arches )
if better :
# Result accepted either by force or by being better than the original result.
output_logger . info ( " final: %s " , " , " . join ( sorted ( x . uvname for x in selected ) ) )
output_logger . info ( " start: %s " , self . eval_nuninst ( nuninst_start ) )
output_logger . info ( " orig: %s " , self . eval_nuninst ( self . nuninst_orig ) )
output_logger . info ( " end: %s " , nuninst_end_str )
if force :
broken = newly_uninst ( nuninst_start , nuninst_end )
if broken :
output_logger . warning ( " force breaks: " )
format_and_log_uninst ( self . output_logger ,
self . options . architectures ,
broken ,
loglevel = logging . WARNING ,
)
else :
output_logger . info ( " force did not break any packages " )
output_logger . info ( " SUCCESS ( %d / %d ) " , len ( actions or self . upgrade_me ) , len ( extra ) )
self . nuninst_orig = nuninst_end
self . all_selected + = selected
if transaction :
transaction . commit ( )
2019-01-14 13:13:45 +00:00
if self . options . check_consistency_level > = 2 :
2019-01-19 16:47:40 +00:00
target_suite . check_suite_source_pkg_consistency ( ' do_all after commit ' )
2018-12-16 12:03:44 +00:00
if not actions :
if recurse :
self . upgrade_me = extra
else :
self . upgrade_me = [ x for x in self . upgrade_me if x not in set ( selected ) ]
else :
output_logger . info ( " FAILED \n " )
if not transaction :
return
transaction . rollback ( )
2019-01-14 13:13:45 +00:00
if self . options . check_consistency_level > = 2 :
2019-01-19 16:47:40 +00:00
target_suite . check_suite_source_pkg_consistency ( ' do_all after rollback ' )
2011-12-13 09:58:45 +01:00
2018-04-01 11:18:47 +00:00
output_logger . info ( " " )
2011-12-13 09:58:45 +01:00
2015-09-07 21:04:02 +02:00
def assert_nuninst_is_correct ( self ) :
2018-03-24 09:42:06 +00:00
self . logger . info ( " > Update complete - Verifying non-installability counters " )
2015-09-07 21:04:02 +02:00
cached_nuninst = self . nuninst_orig
2018-12-15 22:32:04 +00:00
self . _inst_tester . compute_installability ( )
2018-11-01 21:22:38 +00:00
computed_nuninst = compile_nuninst ( self . suite_info . target_suite ,
2018-08-05 13:10:58 +00:00
self . options . architectures ,
self . options . nobreakall_arches )
2016-11-13 08:49:46 +00:00
if cached_nuninst != computed_nuninst : # pragma: no cover
2016-07-04 11:15:14 +00:00
only_on_break_archs = True
2018-03-24 09:42:06 +00:00
self . logger . error ( " ==================== NUNINST OUT OF SYNC ========================= " )
2015-09-07 21:04:02 +02:00
for arch in self . options . architectures :
expected_nuninst = set ( cached_nuninst [ arch ] )
actual_nuninst = set ( computed_nuninst [ arch ] )
false_negatives = actual_nuninst - expected_nuninst
false_positives = expected_nuninst - actual_nuninst
2016-07-04 11:15:14 +00:00
# Britney does not quite work correctly with
# break/fucked arches, so ignore issues there for now.
2016-07-11 17:55:46 +00:00
if ( false_negatives or false_positives ) and arch not in self . options . break_arches :
2016-07-04 11:15:14 +00:00
only_on_break_archs = False
2015-09-07 21:04:02 +02:00
if false_negatives :
2018-03-30 17:38:50 +00:00
self . logger . error ( " %s - unnoticed nuninst: %s " , arch , str ( false_negatives ) )
2015-09-07 21:04:02 +02:00
if false_positives :
2018-03-30 17:38:50 +00:00
self . logger . error ( " %s - invalid nuninst: %s " , arch , str ( false_positives ) )
self . logger . info ( " %s - actual nuninst: %s " , arch , str ( actual_nuninst ) )
2018-03-24 09:42:06 +00:00
self . logger . error ( " ==================== NUNINST OUT OF SYNC ========================= " )
2016-07-05 23:24:08 +00:00
if not only_on_break_archs :
2016-07-04 11:15:14 +00:00
raise AssertionError ( " NUNINST OUT OF SYNC " )
else :
2018-03-24 09:42:06 +00:00
self . logger . warning ( " Nuninst is out of sync on some break arches " )
2015-09-07 21:04:02 +02:00
2018-03-24 09:42:06 +00:00
self . logger . info ( " > All non-installability counters are ok " )
2015-09-07 21:04:02 +02:00
2006-07-20 19:00:47 +00:00
def upgrade_testing ( self ) :
2018-07-23 14:51:34 +00:00
""" Upgrade testing using the packages from the source suites
2006-07-20 19:00:47 +00:00
2018-07-23 14:51:34 +00:00
This method tries to upgrade testing using the packages from the
source suites .
2006-08-01 18:59:29 +00:00
Before running the do_all method , it tries the easy and force - hint
commands .
2006-07-20 19:00:47 +00:00
"""
2018-04-01 11:18:47 +00:00
output_logger = self . output_logger
2018-03-24 09:42:06 +00:00
self . logger . info ( " Starting the upgrade test " )
2018-04-01 11:18:47 +00:00
output_logger . info ( " Generated on: %s " , time . strftime ( " % Y. % m. %d % H: % M: % S % z " , time . gmtime ( time . time ( ) ) ) )
output_logger . info ( " Arch order is: %s " , " , " . join ( self . options . architectures ) )
2006-08-01 18:59:29 +00:00
2006-08-16 17:56:38 +00:00
if not self . options . actions :
# process `easy' hints
for x in self . hints [ ' easy ' ] :
2011-09-04 16:41:33 +00:00
self . do_hint ( " easy " , x . user , x . packages )
2006-08-01 18:59:29 +00:00
2006-08-16 17:56:38 +00:00
# process `force-hint' hints
for x in self . hints [ " force-hint " ] :
2011-09-04 16:41:33 +00:00
self . do_hint ( " force-hint " , x . user , x . packages )
2006-07-20 19:00:47 +00:00
2006-08-01 18:59:29 +00:00
# run the first round of the upgrade
2012-01-04 14:20:33 +01:00
# - do separate runs for break arches
2006-08-07 11:06:04 +00:00
allpackages = [ ]
normpackages = self . upgrade_me [ : ]
archpackages = { }
2015-05-27 22:34:42 +02:00
for a in self . options . break_arches :
2011-11-09 22:44:31 +00:00
archpackages [ a ] = [ p for p in normpackages if p . architecture == a ]
2010-11-03 19:05:46 +00:00
normpackages = [ p for p in normpackages if p not in archpackages [ a ] ]
2006-08-07 11:06:04 +00:00
self . upgrade_me = normpackages
2018-04-01 11:18:47 +00:00
output_logger . info ( " info: main run " )
2006-08-01 20:27:48 +00:00
self . do_all ( )
2006-08-07 11:06:04 +00:00
allpackages + = self . upgrade_me
2015-05-27 22:34:42 +02:00
for a in self . options . break_arches :
2006-08-15 15:32:14 +00:00
backup = self . options . break_arches
2015-05-27 22:34:42 +02:00
self . options . break_arches = " " . join ( x for x in self . options . break_arches if x != a )
2006-08-07 11:06:04 +00:00
self . upgrade_me = archpackages [ a ]
2018-04-01 11:18:47 +00:00
output_logger . info ( " info: broken arch run for %s " , a )
2006-08-07 11:06:04 +00:00
self . do_all ( )
allpackages + = self . upgrade_me
2006-08-15 15:32:14 +00:00
self . options . break_arches = backup
2006-08-07 11:06:04 +00:00
self . upgrade_me = allpackages
2006-07-20 19:00:47 +00:00
2006-08-16 17:56:38 +00:00
if self . options . actions :
2011-06-30 17:45:44 +00:00
self . printuninstchange ( )
2006-08-16 17:56:38 +00:00
return
2006-08-15 15:32:14 +00:00
# process `hint' hints
hintcnt = 0
for x in self . hints [ " hint " ] [ : 50 ] :
if hintcnt > 50 :
2018-04-01 11:18:47 +00:00
output_logger . info ( " Skipping remaining hints... " )
2006-08-15 15:32:14 +00:00
break
2011-09-04 16:41:33 +00:00
if self . do_hint ( " hint " , x . user , x . packages ) :
2006-08-15 15:32:14 +00:00
hintcnt + = 1
2006-08-04 18:38:52 +00:00
# run the auto hinter
2018-12-18 21:02:13 +00:00
self . run_auto_hinter ( )
2006-08-04 18:38:52 +00:00
2012-10-22 13:55:11 +01:00
if getattr ( self . options , " remove_obsolete " , " yes " ) == " yes " :
# obsolete source packages
# a package is obsolete if none of the binary packages in testing
# are built by it
2018-07-22 15:39:59 +00:00
self . logger . info ( " > Removing obsolete source packages from the target suite " )
2012-10-22 13:55:11 +01:00
# local copies for performance
2018-07-22 15:39:59 +00:00
target_suite = self . suite_info . target_suite
sources_t = target_suite . sources
binaries_t = target_suite . binaries
2018-12-23 18:08:23 +00:00
mi_factory = self . _migration_item_factory
2018-10-24 20:09:55 +00:00
used = set ( binaries_t [ arch ] [ binary ] . source
2018-07-22 15:39:59 +00:00
for arch in binaries_t
2018-10-24 20:09:55 +00:00
for binary in binaries_t [ arch ]
2018-07-22 15:39:59 +00:00
)
2018-12-23 18:08:23 +00:00
removals = [ mi_factory . parse_item ( " - %s / %s " % ( source , sources_t [ source ] . version ) , auto_correct = False )
2018-07-22 15:39:59 +00:00
for source in sources_t if source not in used
]
2012-10-22 13:55:11 +01:00
if removals :
2018-07-22 15:39:59 +00:00
output_logger . info ( " Removing obsolete source packages from the target suite ( %d ): " , len ( removals ) )
2012-10-22 13:55:11 +01:00
self . do_all ( actions = removals )
2015-09-07 21:04:02 +02:00
2006-08-06 09:29:32 +00:00
# smooth updates
2018-12-23 18:08:23 +00:00
removals = old_libraries ( self . _migration_item_factory , self . suite_info , self . options . outofsync_arches )
2019-01-19 16:06:32 +00:00
if removals :
2019-01-19 16:09:33 +00:00
output_logger . info ( " Removing packages left in the target suite (e.g. smooth updates or cruft) " )
2019-01-19 16:06:32 +00:00
log_and_format_old_libraries ( self . output_logger , removals )
self . do_all ( actions = removals )
removals = old_libraries ( self . _migration_item_factory , self . suite_info , self . options . outofsync_arches )
2006-08-06 09:29:32 +00:00
2018-07-22 15:39:59 +00:00
output_logger . info ( " List of old libraries in the target suite ( %d ): " , len ( removals ) )
2018-04-01 11:26:40 +00:00
log_and_format_old_libraries ( self . output_logger , removals )
2006-08-06 09:29:32 +00:00
2018-12-18 14:52:31 +00:00
self . printuninstchange ( )
2019-01-14 13:13:45 +00:00
if self . options . check_consistency_level > = 1 :
2019-01-19 16:47:40 +00:00
target_suite = self . suite_info . target_suite
2019-01-14 13:13:45 +00:00
self . assert_nuninst_is_correct ( )
2019-01-19 16:47:40 +00:00
target_suite . check_suite_source_pkg_consistency ( ' end ' )
2015-09-07 21:04:02 +02:00
2006-08-06 09:29:32 +00:00
# output files
if not self . options . dry_run :
2018-07-22 15:37:55 +00:00
target_suite = self . suite_info . target_suite
2006-08-03 11:41:39 +00:00
2018-12-31 19:12:21 +00:00
self . _policy_engine . save_state ( self )
2006-08-01 18:59:29 +00:00
2006-08-06 09:29:32 +00:00
# write HeidiResult
2018-03-30 17:38:50 +00:00
self . logger . info ( " Writing Heidi results to %s " , self . options . heidi_output )
2018-07-22 15:37:55 +00:00
write_heidi ( self . options . heidi_output ,
target_suite ,
2017-11-19 21:47:38 +00:00
outofsync_arches = self . options . outofsync_arches )
2006-08-01 18:59:29 +00:00
2018-03-30 17:38:50 +00:00
self . logger . info ( " Writing delta to %s " , self . options . heidi_delta_output )
2014-01-11 08:31:36 +01:00
write_heidi_delta ( self . options . heidi_delta_output ,
self . all_selected )
2018-03-24 09:42:06 +00:00
self . logger . info ( " Test completed! " )
2006-07-20 19:00:47 +00:00
2011-06-28 19:10:13 +00:00
def printuninstchange ( self ) :
2018-03-24 09:42:06 +00:00
self . logger . info ( " Checking for newly uninstallable packages " )
2018-04-01 10:32:12 +00:00
uninst = newly_uninst ( self . nuninst_orig_save , self . nuninst_orig )
if uninst :
self . output_logger . warning ( " " )
2018-07-22 15:46:03 +00:00
self . output_logger . warning ( " Newly uninstallable packages in the target suite: " )
2018-04-01 10:32:12 +00:00
format_and_log_uninst ( self . output_logger ,
self . options . architectures ,
uninst ,
loglevel = logging . WARNING ,
)
2011-06-28 19:10:13 +00:00
2008-01-15 16:09:51 +00:00
def hint_tester ( self ) :
""" Run a command line interface to test hints
2010-02-28 12:58:03 +00:00
This method provides a command line interface for the release team to
2011-09-17 11:48:49 +02:00
try hints and evaluate the results .
2008-01-15 16:09:51 +00:00
"""
2008-04-23 17:22:24 +00:00
import readline
2016-10-23 10:30:01 +00:00
from britney2 . completer import Completer
2011-10-22 13:42:14 +02:00
2008-04-23 17:22:24 +00:00
histfile = os . path . expanduser ( ' ~/.britney2_history ' )
if os . path . exists ( histfile ) :
readline . read_history_file ( histfile )
2011-10-22 13:42:14 +02:00
readline . parse_and_bind ( ' tab: complete ' )
readline . set_completer ( Completer ( self ) . completer )
2011-10-22 14:09:35 +02:00
# Package names can contain "-" and we use "/" in our presentation of them as well,
# so ensure readline does not split on these characters.
readline . set_completer_delims ( readline . get_completer_delims ( ) . replace ( ' - ' , ' ' ) . replace ( ' / ' , ' ' ) )
2011-10-22 13:42:14 +02:00
2016-07-02 20:28:07 +00:00
known_hints = self . _hint_parser . registered_hints
2008-01-15 16:09:51 +00:00
while True :
# read the command from the command line
try :
2016-11-28 16:10:00 +00:00
user_input = input ( ' britney> ' ) . split ( )
2008-01-15 16:09:51 +00:00
except EOFError :
2015-04-25 16:03:54 +02:00
print ( " " )
2008-01-15 16:09:51 +00:00
break
2008-04-23 17:22:24 +00:00
except KeyboardInterrupt :
2015-04-25 16:03:54 +02:00
print ( " " )
2008-04-23 17:22:24 +00:00
continue
2008-01-15 16:09:51 +00:00
# quit the hint tester
2015-09-20 17:17:57 +02:00
if user_input and user_input [ 0 ] in ( ' quit ' , ' exit ' ) :
2008-01-15 16:09:51 +00:00
break
2016-07-02 20:28:07 +00:00
# run a hint
2015-09-20 17:17:57 +02:00
elif user_input and user_input [ 0 ] in ( ' easy ' , ' hint ' , ' force-hint ' ) :
2018-12-23 18:08:23 +00:00
mi_factory = self . _migration_item_factory
2008-04-23 17:22:24 +00:00
try :
2018-12-23 18:08:23 +00:00
self . do_hint ( user_input [ 0 ] , ' hint-tester ' , mi_factory . parse_items ( user_input [ 1 : ] ) )
2011-06-28 19:10:13 +00:00
self . printuninstchange ( )
2008-04-23 17:22:24 +00:00
except KeyboardInterrupt :
continue
2016-07-02 20:28:07 +00:00
elif user_input and user_input [ 0 ] in known_hints :
self . _hint_parser . parse_hints ( ' hint-tester ' , self . HINTS_ALL , ' <stdin> ' , [ ' ' . join ( user_input ) ] )
self . write_excuses ( )
2011-10-22 10:26:10 +02:00
try :
readline . write_history_file ( histfile )
2015-04-25 16:35:09 +02:00
except IOError as e :
2018-03-30 17:38:50 +00:00
self . logger . warning ( " Could not write %s : %s " , histfile , e )
2008-01-15 16:09:51 +00:00
2012-01-04 14:20:33 +01:00
def do_hint ( self , hinttype , who , pkgvers ) :
2006-08-01 18:59:29 +00:00
""" Process hints
This method process ` easy ` , ` hint ` and ` force - hint ` hints . If the
2018-07-23 14:51:34 +00:00
requested version is not in the relevant source suite , then the hint
is skipped .
2006-08-01 18:59:29 +00:00
"""
2018-04-01 09:35:03 +00:00
output_logger = self . output_logger
2011-09-04 16:41:33 +00:00
2018-04-01 19:59:57 +00:00
suites = self . suite_info
2018-03-30 17:38:50 +00:00
self . logger . info ( " > Processing ' %s ' hint from %s " , hinttype , who )
2018-04-01 09:35:03 +00:00
output_logger . info ( " Trying %s from %s : %s " , hinttype , who ,
2018-12-23 18:08:23 +00:00
" " . join ( " %s / %s " % ( x . uvname , x . version ) for x in pkgvers )
2018-04-01 09:35:03 +00:00
)
2006-08-01 18:59:29 +00:00
2018-04-01 09:35:03 +00:00
issues = [ ]
2006-08-01 18:59:29 +00:00
# loop on the requested packages and versions
2018-12-23 18:08:23 +00:00
for idx in range ( len ( pkgvers ) ) :
pkg = pkgvers [ idx ]
2006-08-01 18:59:29 +00:00
# skip removal requests
2011-09-04 16:41:33 +00:00
if pkg . is_removal :
2006-08-01 18:59:29 +00:00
continue
2011-11-15 20:50:09 +00:00
2018-04-01 19:59:57 +00:00
suite = pkg . suite
2018-12-25 18:53:58 +00:00
if pkg . package not in suite . sources :
2018-04-01 19:59:57 +00:00
issues . append ( " Source %s has no version in %s " % ( pkg . package , suite . name ) )
2018-12-25 18:53:58 +00:00
elif apt_pkg . version_compare ( suite . sources [ pkg . package ] . version , pkg . version ) != 0 :
2018-04-01 09:35:03 +00:00
issues . append ( " Version mismatch, %s %s != %s " % ( pkg . package , pkg . version ,
2018-04-01 19:59:57 +00:00
suite . sources [ pkg . package ] . version ) )
2018-04-01 09:35:03 +00:00
if issues :
output_logger . warning ( " %s : Not using hint " , " , " . join ( issues ) )
2006-08-01 18:59:29 +00:00
return False
2018-12-23 18:08:23 +00:00
self . do_all ( hinttype , pkgvers )
2006-08-01 18:59:29 +00:00
return True
2018-12-18 21:02:13 +00:00
def get_auto_hinter_hints ( self , upgrade_me ) :
2011-08-28 21:19:05 +00:00
""" Auto-generate " easy " hints.
2006-08-04 18:38:52 +00:00
2012-05-11 16:31:50 +02:00
This method attempts to generate " easy " hints for sets of packages which
2011-08-28 21:19:05 +00:00
must migrate together . Beginning with a package which does not depend on
any other package ( in terms of excuses ) , a list of dependencies and
reverse dependencies is recursively created .
Once all such lists have been generated , any which are subsets of other
lists are ignored in favour of the larger lists . The remaining lists are
then attempted in turn as " easy " hints .
We also try to auto hint circular dependencies analyzing the update
2006-08-06 09:29:32 +00:00
excuses relationships . If they build a circular dependency , which we already
know as not - working with the standard do_all algorithm , try to ` easy ` them .
2006-08-04 18:38:52 +00:00
"""
2018-03-24 09:42:06 +00:00
self . logger . info ( " > Processing hints from the auto hinter " )
2006-08-04 18:38:52 +00:00
2018-04-01 20:01:49 +00:00
sources_t = self . suite_info . target_suite . sources
2016-05-27 19:52:55 +00:00
excuses = self . excuses
# consider only excuses which are valid candidates and still relevant.
2018-12-18 21:02:13 +00:00
valid_excuses = frozenset ( y . uvname for y in upgrade_me
2016-09-25 05:45:36 +00:00
if y not in sources_t or sources_t [ y ] . version != excuses [ y ] . ver [ 1 ] )
2018-12-30 20:19:38 +00:00
excuses_deps = { name : valid_excuses . intersection ( excuse . get_deps ( ) )
2016-05-27 19:52:55 +00:00
for name , excuse in excuses . items ( ) if name in valid_excuses }
2016-05-28 07:37:06 +00:00
excuses_rdeps = defaultdict ( set )
for name , deps in excuses_deps . items ( ) :
for dep in deps :
excuses_rdeps [ dep ] . add ( name )
2012-05-11 13:19:55 +02:00
2011-07-27 08:43:02 +00:00
def find_related ( e , hint , circular_first = False ) :
2006-08-04 18:38:52 +00:00
excuse = excuses [ e ]
2011-07-27 08:43:02 +00:00
if not circular_first :
2006-08-04 18:38:52 +00:00
hint [ e ] = excuse . ver [ 1 ]
2018-12-30 20:19:38 +00:00
if not excuse . get_deps ( ) :
2006-08-04 18:38:52 +00:00
return hint
2016-05-27 20:40:11 +00:00
for p in excuses_deps [ e ] :
if p in hint or p not in valid_excuses :
continue
2006-08-04 18:38:52 +00:00
if not find_related ( p , hint ) :
return False
return hint
# loop on them
2011-08-27 08:50:14 +00:00
candidates = [ ]
2011-10-19 18:23:52 +00:00
mincands = [ ]
2015-05-31 22:13:03 +02:00
seen_hints = set ( )
2016-03-25 09:18:35 +00:00
for e in valid_excuses :
2006-08-04 18:38:52 +00:00
excuse = excuses [ e ]
2018-12-30 20:19:38 +00:00
if excuse . get_deps ( ) :
2011-08-27 08:50:14 +00:00
hint = find_related ( e , { } , True )
2015-05-31 22:13:03 +02:00
if isinstance ( hint , dict ) and e in hint :
h = frozenset ( hint . items ( ) )
if h not in seen_hints :
candidates . append ( h )
seen_hints . add ( h )
2011-08-27 08:50:14 +00:00
else :
2015-05-31 22:26:16 +02:00
items = [ ( e , excuse . ver [ 1 ] ) ]
2015-05-31 22:13:03 +02:00
orig_size = 1
2011-10-19 18:23:52 +00:00
looped = False
2015-05-31 22:26:16 +02:00
seen_items = set ( )
seen_items . update ( items )
2011-08-27 08:50:14 +00:00
for item , ver in items :
# excuses which depend on "item" or are depended on by it
2018-09-30 16:31:21 +00:00
new_items = { ( x , excuses [ x ] . ver [ 1 ] ) for x in chain ( excuses_deps [ item ] , excuses_rdeps [ item ] ) }
2016-05-28 07:37:06 +00:00
new_items - = seen_items
2015-05-31 22:26:16 +02:00
items . extend ( new_items )
seen_items . update ( new_items )
2011-10-19 18:23:52 +00:00
if not looped and len ( items ) > 1 :
2015-05-31 22:13:03 +02:00
orig_size = len ( items )
2015-05-31 22:26:16 +02:00
h = frozenset ( seen_items )
2015-05-31 22:13:03 +02:00
if h not in seen_hints :
mincands . append ( h )
seen_hints . add ( h )
2011-10-19 18:23:52 +00:00
looped = True
2015-05-31 22:13:03 +02:00
if len ( items ) != orig_size :
2015-05-31 22:26:16 +02:00
h = frozenset ( seen_items )
2015-05-31 22:13:03 +02:00
if h != mincands [ - 1 ] and h not in seen_hints :
candidates . append ( h )
seen_hints . add ( h )
2018-12-18 21:02:13 +00:00
return [ candidates , mincands ]
2011-08-27 08:50:14 +00:00
2018-12-18 21:02:13 +00:00
def run_auto_hinter ( self ) :
2018-12-23 18:08:23 +00:00
mi_factory = self . _migration_item_factory
2018-12-18 21:02:13 +00:00
for l in self . get_auto_hinter_hints ( self . upgrade_me ) :
2015-05-31 22:13:03 +02:00
for hint in l :
2018-12-23 18:08:23 +00:00
self . do_hint ( " easy " , " autohinter " , [ mi_factory . parse_item ( " %s / %s " % ( x [ 0 ] , x [ 1 ] ) , auto_correct = False )
for x in sorted ( hint ) ] )
2006-08-06 09:29:32 +00:00
2008-05-02 13:57:37 +00:00
def nuninst_arch_report ( self , nuninst , arch ) :
""" Print a report of uninstallable packages for one architecture. """
2014-07-25 08:44:13 +02:00
all = defaultdict ( set )
2018-07-22 15:46:03 +00:00
binaries_t = self . suite_info . target_suite . binaries
2008-05-02 13:57:37 +00:00
for p in nuninst [ arch ] :
2018-10-24 20:09:55 +00:00
pkg = binaries_t [ arch ] [ p ]
2016-04-06 20:49:40 +00:00
all [ ( pkg . source , pkg . source_version ) ] . add ( p )
2014-08-05 07:54:53 +02:00
2016-01-17 19:36:44 +00:00
print ( ' * %s ' % arch )
2008-05-02 13:57:37 +00:00
2015-04-25 16:36:39 +02:00
for ( src , ver ) , pkgs in sorted ( all . items ( ) ) :
2015-04-25 16:03:54 +02:00
print ( ' %s ( %s ): %s ' % ( src , ver , ' ' . join ( sorted ( pkgs ) ) ) )
2008-05-02 13:57:37 +00:00
2015-09-06 22:39:39 +02:00
print ( )
2008-05-02 13:57:37 +00:00
2006-06-24 17:49:43 +00:00
def main ( self ) :
""" Main method
2019-01-05 13:43:10 +00:00
2006-06-24 17:49:43 +00:00
This is the entry point for the class : it includes the list of calls
for the member methods which will produce the output files .
"""
2008-05-02 13:57:37 +00:00
# if running in --print-uninst mode, quit
if self . options . print_uninst :
2008-04-29 05:08:58 +00:00
return
2006-08-07 14:38:13 +00:00
# if no actions are provided, build the excuses and sort them
2008-04-29 05:08:58 +00:00
elif not self . options . actions :
2006-07-28 13:21:44 +00:00
self . write_excuses ( )
2006-08-07 14:38:13 +00:00
# otherwise, use the actions provided by the command line
2008-01-15 16:09:51 +00:00
else :
self . upgrade_me = self . options . actions . split ( )
2006-07-28 13:21:44 +00:00
2017-04-06 11:45:59 +00:00
if self . options . compute_migrations or self . options . hint_tester :
2018-04-30 17:23:23 +00:00
if self . options . dry_run :
self . logger . info ( " Upgrade output not (also) written to a separate file "
" as this is a dry-run. " )
elif hasattr ( self . options , ' upgrade_output ' ) :
2018-04-01 11:43:32 +00:00
upgrade_output = getattr ( self . options , ' upgrade_output ' )
file_handler = logging . FileHandler ( upgrade_output , mode = ' w ' , encoding = ' utf-8 ' )
output_formatter = logging . Formatter ( ' %(message)s ' )
file_handler . setFormatter ( output_formatter )
self . output_logger . addHandler ( file_handler )
self . logger . info ( " Logging upgrade output to %s " , upgrade_output )
else :
self . logger . info ( " Upgrade output not (also) written to a separate file "
" as the UPGRADE_OUTPUT configuration is not provided. " )
2018-04-01 09:35:03 +00:00
# run the hint tester
if self . options . hint_tester :
self . hint_tester ( )
# run the upgrade test
else :
self . upgrade_testing ( )
2008-05-31 16:32:13 +02:00
2018-04-01 09:35:03 +00:00
self . logger . info ( ' > Stats from the installability tester ' )
for stat in self . _inst_tester . stats . stats ( ) :
self . logger . info ( ' > %s ' , stat )
2017-04-06 11:45:59 +00:00
else :
2018-03-24 09:42:06 +00:00
self . logger . info ( ' Migration computation skipped as requested. ' )
2018-03-24 08:59:33 +00:00
logging . shutdown ( )
2015-04-05 14:30:36 +02:00
2011-12-24 17:00:48 +01:00
2006-06-17 13:45:56 +00:00
if __name__ == ' __main__ ' :
Britney ( ) . main ( )