Further enhancements to automatic upgrade helper
cover Further enhancements to automatic upgrade helper
Particularly:
1) Rebase and integrate (most of) Robert's patches
2) Add musl testing support (a notorious source of failures)
3) Rework testimage support to be more straigtforward and usable locally by maintainers
4) Other things :)
The following changes since commit fbccfa00967305bb3866d0f610cd4b83b8b7a28c:
upgradehelper.py: revert commits that failed to build (2017-12-14 18:13:11 +0200)
are available in the Git repository at:
git://git.yoctoproject.org/auto-upgrade-helper devel
http://git.yoctoproject.org/cgit.cgi/auto-upgrade-helper/log/?h=devel
Alexander Kanavin (13):
upgradehelper.py: drop automatic mode
upgradehelper.py: replace the confusing 'maintainer' command line
option
weeklyjob.sh: set up a temporary branch and clean up afterwards Add support for compiling against non-default C libraries Add protection measures and information against running with existing
build directories.
upgradehelper.py: do not include absolute paths in the generated
tarball
Do not require configuration options that aren't necessary for running
ptests
upgradehelper.py: do not include testimage information in maintainer
email messages
testimage.py: clarify what arguments are passed to class constructor testimage.py: run even if no packages were upgraded testimage.py: remove all iterations (over machines, over failing
recipes)
testimage.py: rewrite the logic for building and running testimages testimage.py: collect and write out logs from bitbake and testimage
Robert Yang (8):
upgradehelper.py: fix checking for do_checkpkg upgradehelper.py: support upgrade multiple recipes upgradehelper.py: use UniverseUpdater for all cases modules/steps.py: fix warn when skip compilation upgradehelper.py: only check email settings when -e is specified upgradehelper.py: always do upgrade when recipes are specified upgradehelper.py: print info when recipe is skipped to upgrade upgradehelper.py: don't build gcc-runtime when --skip-compilation
README | 40 +++++--- modules/steps.py | 2 +- modules/testimage.py | 256 ++++++++--------------------------------------- modules/utils/bitbake.py | 7 +- upgradehelper.py | 144 +++++++++++--------------- weeklyjob.sh | 16 ++- 6 files changed, 142 insertions(+), 323 deletions(-)
Signature · 1 line
2.15.1
PATCH 1/21 upgradehelper.py: fix checking for do_checkpkg
From: Robert Yang <[email protected]>
The error message in the log is:
Initialising tasks...ERROR: Task do_checkpkg does not exist for target strace [snip]
So line.find("ERROR: Task do_checkpkg does not exist") == 0 doesn't work, use != -1 to fix the problem.
--- upgradehelper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
upgradehelper.py +1 -1
@@ -646,7 +646,7 @@ class UniverseUpdater(Updater): self.bb.checkpkg(recipe) except Error as e: for line in e.stdout.split('\n'): - if line.find("ERROR: Task do_checkpkg does not exist") == 0: + if line.find("ERROR: Task do_checkpkg does not exist") != -1: C(" \"distrodata.bbclass\" not inherited. Consider adding " "the following to your local.conf:\n\n" "INHERIT =+ \"distrodata\"\n")
Signature · 1 line
2.15.1
PATCH 2/21 upgradehelper.py: support upgrade multiple recipes
In reply to Alexander KanavinFrom: Robert Yang <[email protected]>
Now we can use:
$ upgradehelper.py recipe1 recipe2
--- upgradehelper.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-)
upgradehelper.py +6 -3
@@ -75,7 +75,7 @@ def parse_cmdline(): parser = argparse.ArgumentParser(description='Package Upgrade Helper', formatter_class=argparse.RawTextHelpFormatter, epilog=help_text) - parser.add_argument("recipe", help="recipe to be upgraded") + parser.add_argument("recipe", nargs = '+', action='store', default='', help="recipe to be upgraded") parser.add_argument("-t", "--to_version", help="version to upgrade the recipe to")
@@ -792,7 +792,7 @@ if __name__ == "__main__": level=debug_levels[args.debug_level - 1]) settings, maintainer_override = parse_config_file(args.config_file) - recipes = args.recipe.split() + recipes = args.recipe if len(recipes) == 1 and recipes[0] == "all": updater = UniverseUpdater()
@@ -806,9 +806,12 @@ if __name__ == "__main__": args.maintainer = "Upgrade Helper <%s>" % \ settings.get('from', '[email protected]') - pkg_list = [(args.recipe, args.to_version, args.maintainer)] + pkg_list = [(recipes[0], args.to_version, args.maintainer)] updater = Updater(args.auto_mode, args.send_emails, args.skip_compilation) updater.run(pkg_list) + elif len(recipes) > 1 and args.to_version: + E(" -t is only supported when upgrade one recipe\n") + exit(1) else: updater = UniverseUpdater(recipes) updater.run()
Signature · 1 line
2.15.1
PATCH 3/21 upgradehelper.py: use UniverseUpdater for all cases
In reply to Alexander KanavinFrom: Robert Yang <[email protected]>
* Use UniverseUpdater() for the following 3 cases:
+ Upgrade all recipes
+ Upgrade 1 recipe
- '--maintainer' is not a must when any more when use --send-emails, the maintainer be got from distrodata.
+ Upgrade multiple recipes
* Use "args" as the parameter of UniverseUpdater() and Updater(), this can make the parameters simple, and easy for extending.
* Make upgrade "all" recipes respect the args, it didn't care --send-emails or --maintainer which would suprise the user.
--- upgradehelper.py | 68 +++++++++++++++++++++++++------------------------------- 1 file changed, 30 insertions(+), 38 deletions(-)
upgradehelper.py +30 -38
@@ -129,11 +129,12 @@ def parse_config_file(config_file): return (settings, maintainer_override) class Updater(object): - def __init__(self, auto_mode=False, send_email=False, skip_compilation=False): + def __init__(self, args): build_dir = get_build_dir() self.bb = Bitbake(build_dir) self.devtool = Devtool() + self.args = args try: self.base_env = self.bb.env()
@@ -143,7 +144,7 @@ class Updater(object): E( " Bitbake output:\n%s" % (e.stdout)) exit(1) - self._set_options(auto_mode, send_email, skip_compilation) + self._set_options() self._make_dirs(build_dir)
@@ -152,7 +153,7 @@ class Updater(object): self.email_handler = Email(settings) self.statistics = Statistics() - def _set_options(self, auto_mode, send_email, skip_compilation): + def _set_options(self): self.opts = {} self.opts['layer_mode'] = settings.get('layer_mode', '') if self.opts['layer_mode'] == 'yes':
@@ -176,11 +177,11 @@ class Updater(object): self.opts['machines'] = settings.get('machines', 'qemux86 qemux86-64 qemuarm qemumips qemuppc').split() - self.opts['interactive'] = not auto_mode - self.opts['send_email'] = send_email + self.opts['interactive'] = not self.args.auto_mode + self.opts['send_email'] = self.args.send_emails self.opts['author'] = "Upgrade Helper <%s>" % \ settings.get('from', '[email protected]') - self.opts['skip_compilation'] = skip_compilation + self.opts['skip_compilation'] = self.args.skip_compilation self.opts['buildhistory'] = self._buildhistory_is_enabled() self.opts['testimage'] = self._testimage_is_enabled()
@@ -576,17 +577,25 @@ class Updater(object): self.send_status_mail(statistics_summary) class UniverseUpdater(Updater): - def __init__(self, recipes=None): - Updater.__init__(self, True, True) + def __init__(self, args): + Updater.__init__(self, args) + + if len(args.recipe) == 1 and args.recipe[0] == "all": + self.recipes = [] + else: + self.recipes = args.recipe # to filter recipes in upgrade - if not recipes and self.opts['layer_mode'] == 'yes': + if not self.recipes and self.opts['layer_mode'] == 'yes': # when layer mode is enabled and no recipes are specified # we need to figure out what recipes are provided by the # layer to try upgrade self.recipes = self._get_recipes_by_layer() - else: - self.recipes = recipes + + if args.to_version: + if len(self.recipes) != 1: + E(" -t is only supported when upgrade one recipe\n") + exit(1) # read history file self.history_file = os.path.join(get_build_dir(), "upgrade-helper", "history.uh")
@@ -665,10 +674,16 @@ class UniverseUpdater(Updater): pn = row[0] cur_ver = row[1] - next_ver = row[2] + if self.args.to_version: + next_ver = self.args.to_version + else: + next_ver = row[2] status = row[11] revision = row[12] - maintainer = row[14] + if self.args.maintainer: + maintainer = self.args.maintainer + else: + maintainer = row[14] no_upgrade_reason = row[15] if status == 'UPDATE' and not no_upgrade_reason:
@@ -792,28 +807,5 @@ if __name__ == "__main__": level=debug_levels[args.debug_level - 1]) settings, maintainer_override = parse_config_file(args.config_file) - recipes = args.recipe - - if len(recipes) == 1 and recipes[0] == "all": - updater = UniverseUpdater() - updater.run() - elif len(recipes) == 1 and args.to_version: - if not args.maintainer and args.send_emails: - E(" For upgrade one recipe and send email you must specify --maintainer\n") - exit(1) - - if not args.maintainer: - args.maintainer = "Upgrade Helper <%s>" % \ - settings.get('from', '[email protected]') - - pkg_list = [(recipes[0], args.to_version, args.maintainer)] - updater = Updater(args.auto_mode, args.send_emails, args.skip_compilation) - updater.run(pkg_list) - elif len(recipes) > 1 and args.to_version: - E(" -t is only supported when upgrade one recipe\n") - exit(1) - else: - updater = UniverseUpdater(recipes) - updater.run() - - + updater = UniverseUpdater(args) + updater.run()
Signature · 1 line
2.15.1
PATCH 4/21 modules/steps.py: fix warn when skip compilation
In reply to Alexander KanavinFrom: Robert Yang <[email protected]>
--- modules/steps.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
modules/steps.py +1 -1
@@ -113,7 +113,7 @@ def _compile(bb, pkg, machine, workdir): def compile(devtool, bb, git, opts, pkg_ctx): if opts['skip_compilation']: - W(" %s: Compilation was skipped by user choice!") + W(" %s: Compilation was skipped by user choice!" % pkg_ctx['PN']) return for machine in opts['machines']:
Signature · 1 line
2.15.1
PATCH 5/21 upgradehelper.py: only check email settings when -e is specified
In reply to Alexander KanavinFrom: Robert Yang <[email protected]>
Fixed:
$ upgradehelper.py less ERROR: smtp host not set! Sending emails disabled! ERROR: 'From' address not set! Sending emails disabled!
Only check email settings when "-e" is specified can fix the problem.
--- upgradehelper.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
upgradehelper.py +2 -1
@@ -150,7 +150,8 @@ class Updater(object): self._add_file_logger() - self.email_handler = Email(settings) + if self.args.send_emails: + self.email_handler = Email(settings) self.statistics = Statistics() def _set_options(self):
Signature · 1 line
2.15.1
PATCH 6/21 upgradehelper.py: always do upgrade when recipes are specified
In reply to Alexander KanavinFrom: Robert Yang <[email protected]>
For example, first run:
$ upgradehelper.py less -d 5 It did the upgrade
Second run:
$ upgradehelper.py less -d 5 DEBUG: Skipping upgrade of less: is in history and not 30 days passed
Let it always do the upgrade makes it easier to use when do upgrade locally. It will still do the check when the recipe is all.
--- upgradehelper.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
upgradehelper.py +4 -1
@@ -756,7 +756,10 @@ class UniverseUpdater(Updater): pkgs_list = [] for pkg in self._parse_checkpkg_file(last_checkpkg_file): - if self._pkg_upgradable(pkg[0], pkg[1], pkg[2]): + # Always do the upgrade if recipes are specified + if self.recipes and pkg[0] in self.recipes: + pkgs_list.append(pkg) + elif self._pkg_upgradable(pkg[0], pkg[1], pkg[2]): pkgs_list.append(pkg) return pkgs_list
Signature · 1 line
2.15.1
PATCH 7/21 upgradehelper.py: print info when recipe is skipped to upgrade
In reply to Alexander KanavinFrom: Robert Yang <[email protected]>
This makes debug easier.
--- upgradehelper.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
upgradehelper.py +2 -2
@@ -691,11 +691,11 @@ class UniverseUpdater(Updater): pkgs_list.append((pn, cur_ver, next_ver, maintainer, revision)) else: if no_upgrade_reason: - D(" Skip package %s (status = %s, current version = %s," \ + I(" Skip package %s (status = %s, current version = %s," \ " next version = %s, no upgrade reason = %s)" % (pn, status, cur_ver, next_ver, no_upgrade_reason)) else: - D(" Skip package %s (status = %s, current version = %s," \ + I(" Skip package %s (status = %s, current version = %s," \ " next version = %s)" % (pn, status, cur_ver, next_ver)) return pkgs_list
Signature · 1 line
2.15.1
PATCH 8/21 upgradehelper.py: don't build gcc-runtime when --skip-compilation
In reply to Alexander KanavinFrom: Robert Yang <[email protected]>
It doesn't make any sense to build it when skip compile.
--- upgradehelper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
upgradehelper.py +1 -1
@@ -468,7 +468,7 @@ class Updater(object): pkgs_ctx[p]['base_dir'] = self.uh_recipes_all_dir I(" ############################################################") - if pkgs_to_upgrade: + if pkgs_to_upgrade and not self.args.skip_compilation: I(" Building gcc runtimes ...") for machine in self.opts['machines']: I(" building gcc runtime for %s" % machine)
Signature · 1 line
2.15.1
PATCH 9/21 upgradehelper.py: drop automatic mode
In reply to Alexander KanavinNo longer used since migration to devtool: all upgrades are automatic
--- README | 6 ++---- upgradehelper.py | 6 +----- 2 files changed, 3 insertions(+), 9 deletions(-)
README +2 -4
@@ -134,12 +134,10 @@ Usage Some manual usage examples: -* To upgrade the xmodmap recipe to the latest available version, - interactively: +* To upgrade the xmodmap recipe to the latest available version: $ upgrade-helper.py xmodmap -* To upgrade the xmodmap recipe to a user specified version, - interactively: +* To upgrade the xmodmap recipe to a user specified version: $ upgrade-helper.py xmodmap -t 1.2.3 * To attempt to upgrade all recipes and automatically send email
upgradehelper.py +1 -5
@@ -60,7 +60,7 @@ from steps import upgrade_steps from testimage import TestImage help_text = """Usage examples: -* To upgrade xmodmap recipe to the latest available version, interactively: +* To upgrade xmodmap recipe to the latest available version: $ upgrade-helper.py xmodmap * To attempt to upgrade all recipes and automatically send email messages
@@ -82,9 +82,6 @@ def parse_cmdline(): parser.add_argument("-m", "--maintainer", help="maintainer of the recipe") - parser.add_argument("-a", "--auto-mode", action="store_true", default=False, - help="disable interactive mode") - parser.add_argument("-d", "--debug-level", type=int, default=4, choices=range(1, 6), help="set the debug level: CRITICAL=1, ERROR=2, WARNING=3, INFO=4, DEBUG=5") parser.add_argument("-e", "--send-emails", action="store_true", default=False,
@@ -178,7 +175,6 @@ class Updater(object): self.opts['machines'] = settings.get('machines', 'qemux86 qemux86-64 qemuarm qemumips qemuppc').split() - self.opts['interactive'] = not self.args.auto_mode self.opts['send_email'] = self.args.send_emails self.opts['author'] = "Upgrade Helper <%s>" % \ settings.get('from', '[email protected]')
Signature · 1 line
2.15.1
PATCH 10/21 upgradehelper.py: replace the confusing 'maintainer' command line option
In reply to Alexander KanavinIt wasn't clear what the option does, and why it need to be on the command line, so the replacement is a 'global_maintainer_override' config file setting which does the same thing, in a cleaner fashion.
--- README | 7 ++++++- upgradehelper.py | 9 +++------ 2 files changed, 9 insertions(+), 7 deletions(-)
README +6 -1
@@ -28,6 +28,7 @@ Setup --------------- snip --------------- [maintainer_override] # mails for recipe upgrades will go to john.doe instead of jane.doe, etc +# see also the global_maintainer_override option [email protected][email protected] [email protected][email protected]
@@ -38,6 +39,10 @@ blacklist=python glibc gcc # only recipes belonging to maintainers in whitelist will be attempted [email protected] [email protected] [email protected] +# email for all recipe upgrades will go to john.doe, except those listed +# in specific maintainer_override entries above [email protected] + # SMTP server smtp=smtp.my-server.com:25
@@ -84,7 +89,7 @@ INHERIT =+ "distrodata" WARNING: if you are using the default maintainers.inc file supplied with Poky (in meta-yocto) and you don't set a - maintainers_whitelist or maintainer_override in the + maintainers_whitelist or (global_)maintainer_override in the upgrade-helper configuration as above, and you specify "all" on the command line, the script will automatically send out emails to the default maintainers. Please be careful not to
upgradehelper.py +3 -6
@@ -79,8 +79,6 @@ def parse_cmdline(): parser.add_argument("-t", "--to_version", help="version to upgrade the recipe to") - parser.add_argument("-m", "--maintainer", - help="maintainer of the recipe") parser.add_argument("-d", "--debug-level", type=int, default=4, choices=range(1, 6), help="set the debug level: CRITICAL=1, ERROR=2, WARNING=3, INFO=4, DEBUG=5")
@@ -325,6 +323,8 @@ class Updater(object): if pkg_ctx['MAINTAINER'] in maintainer_override: to_addr = maintainer_override[pkg_ctx['MAINTAINER']] + elif 'global_maintainer_override' in settings: + to_addr = settings['global_maintainer_override'] else: to_addr = pkg_ctx['MAINTAINER']
@@ -677,10 +677,7 @@ class UniverseUpdater(Updater): next_ver = row[2] status = row[11] revision = row[12] - if self.args.maintainer: - maintainer = self.args.maintainer - else: - maintainer = row[14] + maintainer = row[14] no_upgrade_reason = row[15] if status == 'UPDATE' and not no_upgrade_reason:
Signature · 1 line
2.15.1
PATCH 11/21 weeklyjob.sh: set up a temporary branch and clean up afterwards
In reply to Alexander Kanavin--- weeklyjob.sh | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-)
weeklyjob.sh +13 -1
@@ -7,11 +7,23 @@ # # 00 8 * * 6 auh /home/auh/bin/weeklyjob.sh +# Re-assign these to match your setup! auh_dir=~/auto-upgrade-helper poky_dir=~/poky build_dir=~/build +sstate_dir=~/sstate-cache + +pushd $poky_dir + +# Base the upgrades on poky master +git fetch origin +git checkout -B tmp-auh-upgrades origin/master source $poky_dir/oe-init-build-env $build_dir $auh_dir/upgradehelper.py all -#/usr/bin/rsync --delete --password-file /home/auh/rsync.passwd --copy-unsafe-links -zaHS /home/auh/work/ [email protected]::auh/ +# clean up to avoid the disk filling up +rm -rf $build_dir/tmp/ +find $sstate_dir -atime +10 -delete + +popd
Signature · 1 line
2.15.1
PATCH 12/21 Add support for compiling against non-default C libraries
In reply to Alexander KanavinParticularly, musl is a notorious source of failures and so it's very useful to do at least one build against it by default.
--- README | 4 +++- modules/utils/bitbake.py | 7 ++++++- upgradehelper.py | 2 +- 3 files changed, 10 insertions(+), 3 deletions(-)
README +3 -1
@@ -65,7 +65,9 @@ clean_sstate=yes clean_tmp=yes # machines to test build with -machines=qemux86 qemux86-64 qemuarm qemumips qemuppc +# append _libc-name to test with alternative C library implementations +# e.g. qemux86_musl +machines=qemux86 qemux86_musl qemux86-64 qemuarm qemumips qemuppc # optional features buildhistory=no
modules/utils/bitbake.py +6 -1
@@ -121,7 +121,12 @@ class Bitbake(object): return self._cmd(recipe, "-c cleansstate") def complete(self, recipe, machine): - return self._cmd(recipe, env_var="MACHINE=" + machine) + if "_" in machine: + machine, libc = machine.split("_") + env = "MACHINE={} TCLIBC={}".format(machine, libc) + else: + env = "MACHINE={}".format(machine) + return self._cmd(recipe, env_var=env) def dependency_graph(self, package_list): return self._cmd(package_list, "-g")
upgradehelper.py +1 -1
@@ -171,7 +171,7 @@ class Updater(object): self.git = Git(os.path.dirname(os.getenv('PATH', False).split(':')[0])) self.poky_git = None self.opts['machines'] = settings.get('machines', - 'qemux86 qemux86-64 qemuarm qemumips qemuppc').split() + 'qemux86 qemux86-64 qemuarm qemumips qemuppc qemux86_musl').split() self.opts['send_email'] = self.args.send_emails self.opts['author'] = "Upgrade Helper <%s>" % \
Signature · 1 line
2.15.1
PATCH 13/21 Add protection measures and information against running with existing build directories.
In reply to Alexander KanavinThis should help e.g. with the following issue:
https://bugzilla.yoctoproject.org/show_bug.cgi?id=7174
--- README | 12 ++++++++---- upgradehelper.py | 14 ++++++++++++++ weeklyjob.sh | 2 +- 3 files changed, 23 insertions(+), 5 deletions(-)
README +8 -4
@@ -17,10 +17,14 @@ Setup $ git config --global user.name "Some Name" $ git config --global user.email "[email protected]" -1. Ensure you have a working copy of the build system that is configured - and ready to run, i.e. you've run the oe-init-build-env script to - create a build directory and edited local.conf / bblayers.conf as - desired. +1. Run the oe-init-build-env script to create a fresh build directory, solely + for the purpose of running AUH: + + $ . ./oe-init-build-env build-auh + + Re-using existing build directories and configurations is not recommended + as they may contains tweaks that will break AUH or make it behave in + undesirable ways. 2. Prepare a configuration file, by default in $BUILDDIR/upgrade-helper/ upgrade-helper.conf, as below:
upgradehelper.py +14 -0
@@ -788,6 +788,8 @@ if __name__ == "__main__": if not os.getenv('BUILDDIR', False): E(" You must source oe-init-build-env before running this script!\n") + E(" It is recommended to create a fresh build directory with it:\n") + E(" $ . oe-init-build-env build-auh\n") exit(1) devnull = open(os.devnull, 'wb')
@@ -796,6 +798,18 @@ if __name__ == "__main__": E(" Git isn't configured please configure user name and email\n") exit(1) + with open(os.getenv('BUILDDIR')+"/conf/local.conf") as f: + import re + for line in f.readlines(): + if re.match(r"^MACHINE\s*=", line): + E(" The following line found in local.conf - please use ?= or ?== instead as otherwise AUH will not be able to set the desired target machine\n") + E(" {}".format(line)) + exit(1) + if re.match(r"^TCLIBC\s*=", line): + E(" The following line found in local.conf - please use ?= or ?== instead as otherwise AUH will not be able to set the desired C library\n") + E(" {}".format(line)) + exit(1) + signal.signal(signal.SIGINT, close_child_processes) debug_levels = [log.CRITICAL, log.ERROR, log.WARNING, log.INFO, log.DEBUG]
weeklyjob.sh +1 -1
@@ -10,7 +10,7 @@ # Re-assign these to match your setup! auh_dir=~/auto-upgrade-helper poky_dir=~/poky -build_dir=~/build +build_dir=~/build-tmp-auh-upgrades sstate_dir=~/sstate-cache pushd $poky_dir
Signature · 1 line
2.15.1
PATCH 14/21 upgradehelper.py: do not include absolute paths in the generated tarball
In reply to Alexander Kanavin--- upgradehelper.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
upgradehelper.py +3 -1
@@ -555,9 +555,11 @@ class Updater(object): os.path.basename(self.uh_work_dir) + '.tar.gz') if publish_work_url: I(" Generating work tarball in %s ..." % work_tarball) + tar_cmd = ["tar", "-chzf", work_tarball, "-C", self.uh_base_work_dir, os.path.basename(self.uh_work_dir)] import subprocess - if subprocess.call(["tar", "-chzf", work_tarball, self.uh_work_dir]): + if subprocess.call(tar_cmd): E(" Work tarball (%s) generation failed..." % (work_tarball)) + E(" Tar command: %s" % (" ".join(tar_cmd))) publish_work_url = '' statistics_summary = self.statistics.get_summary(
Signature · 1 line
2.15.1
PATCH 15/21 Do not require configuration options that aren't necessary for running ptests
In reply to Alexander Kanavin--- README | 6 ++---- upgradehelper.py | 15 --------------- 2 files changed, 2 insertions(+), 19 deletions(-)
README +2 -4
@@ -122,10 +122,8 @@ file: --------------- snip --------------- INHERIT += "testimage" -DISTRO_FEATURES_append = " ptest" -EXTRA_IMAGE_FEATURES = "debug-tweaks package-management ptest-pkgs" -# testimage/ptest only work with rpm -PACKAGE_CLASSES = "package_rpm" +# Add the following if your distro does not enable by default (poky does) +#DISTRO_FEATURES_append = " ptest" --------------- snip --------------- Also if you are running in a server without X11 session, you need to start
upgradehelper.py +0 -15
@@ -251,21 +251,6 @@ class Updater(object): " conf/local.conf.") exit(1) - if not "package-management" in self.base_env['EXTRA_IMAGE_FEATURES']: - E(" testimage requires package-management in EXTRA_IMAGE_FEATURES"\ - " please add to conf/local.conf.") - exit(1) - - if not "ptest-pkgs" in self.base_env['EXTRA_IMAGE_FEATURES']: - E(" testimage/ptest requires ptest-pkgs in EXTRA_IMAGE_FEATURES"\ - " please add to conf/local.conf.") - exit(1) - - if not "package_rpm" == self.base_env["PACKAGE_CLASSES"]: - E(" testimage/ptest requires PACKAGE_CLASSES set to package_rpm"\ - " please add to conf/local.conf.") - exit(1) - enabled = True else: E(" testimage was enabled in upgrade-helper.conf"\
Signature · 1 line
2.15.1
PATCH 16/21 upgradehelper.py: do not include testimage information in maintainer email messages
In reply to Alexander KanavinThere's some logic to ascribe failures to specific recipe upgrade commits; it's prone to mismatches and is lacking context about what else was upgraded. Let's make testimage support available for local maintainer use only - unattended mass-upgrades will not try to run testimages.
--- upgradehelper.py | 23 ----------------------- 1 file changed, 23 deletions(-)
upgradehelper.py +0 -23
@@ -290,17 +290,6 @@ class Updater(object): "you can fill RECIPE_NO_UPDATE_REASON in respective recipe file so that\n" \ "automatic upgrades would no longer be attempted.\n\n" - testimage_integration_error = \ - "The recipe *FAILED* in testimage integration. Attached is the log file.\n\n" - - testimage_ptest_info = \ - "The recipe has ptest enabled and has been tested with core-image-minimal/ptest \n" \ - "with the next machines %s. Attached is the log file.\n\n" - - testimage_info = \ - "The recipe has been tested using %s testimage and succeeded with \n" \ - "the next machines %s. Attached is the log file.\n\n" \ - mail_footer = \ "Please review the attached files for further information and build/update failures.\n" \ "Any problem please file a bug at https://bugzilla.yoctoproject.org/enter_bug.cgi?product=Automated%20Update%20Handler\n\n" \
@@ -342,18 +331,6 @@ class Updater(object): if 'patch_file' in pkg_ctx and pkg_ctx['patch_file'] != None: msg_body += next_steps_info % (os.path.basename(pkg_ctx['patch_file'])) - if self.opts['testimage']: - if 'integration_error' in pkg_ctx: - msg_body += testimage_integration_error - else: - if 'ptest' in pkg_ctx: - machines = pkg_ctx['ptest'].keys() - msg_body += testimage_ptest_info % machines - if 'testimage' in pkg_ctx: - machines = pkg_ctx['testimage'].keys() - msg_body += testimage_info % (settings.get('testimage_name', \ - DEFAULT_TESTIMAGE), machines) - msg_body += mail_footer # Add possible attachments to email
Signature · 1 line
2.15.1
PATCH 17/21 testimage.py: clarify what arguments are passed to class constructor
In reply to Alexander Kanavin--- modules/testimage.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-)
modules/testimage.py +3 -4
@@ -1,4 +1,3 @@ -#!/usr/bin/env python # vim: set ts=4 sw=4 et: # # Copyright (c) 2015 Intel Corporation
@@ -42,13 +41,13 @@ def _pn_in_pkgs_ctx(pn, pkgs_ctx): return None class TestImage(): - def __init__(self, bb, git, uh_work_dir, opts, *args, **kwargs): + def __init__(self, bb, git, uh_work_dir, opts, packages, image): self.bb = bb self.git = git self.uh_work_dir = uh_work_dir self.opts = opts - self.pkgs_ctx = args[0]['succeeded'][:] - self.image = args[1] + self.pkgs_ctx = packages['succeeded'] + self.image = image os.environ['BB_ENV_EXTRAWHITE'] = os.environ['BB_ENV_EXTRAWHITE'] + \ " TEST_SUITES CORE_IMAGE_EXTRA_INSTALL"
Signature · 1 line
2.15.1
PATCH 18/21 testimage.py: run even if no packages were upgraded
In reply to Alexander Kanavin--- modules/testimage.py | 4 ---- 1 file changed, 4 deletions(-)
modules/testimage.py +0 -4
@@ -256,10 +256,6 @@ class TestImage(): return handled def run(self): - if len(self.pkgs_ctx) <= 0: - I(" Testimage was enabled but any upgrade was successful.") - return - I(" Images will test for %s." % ', '.join(self.opts['machines'])) for machine in self.opts['machines']: I(" Testing images for %s ..." % machine)
Signature · 1 line
2.15.1
PATCH 19/21 testimage.py: remove all iterations (over machines, over failing recipes)
In reply to Alexander KanavinThis adds enormously to run time, and is not particularly necessary when running locally. If something fails, it's best to let the maintainer sort it out as soon as possible, rather than try to guess what needs to be excluded.
Also, do not build a separate ptest image; ptests will be added to the standard integration testimage.
--- README | 9 +++-- modules/testimage.py | 107 ++------------------------------------------------- 2 files changed, 9 insertions(+), 107 deletions(-)
README +6 -3
@@ -68,9 +68,12 @@ clean_sstate=yes # clean tmp directory before upgrading clean_tmp=yes -# machines to test build with -# append _libc-name to test with alternative C library implementations -# e.g. qemux86_musl +# Machines to test build with. +# Append _libc-name to test with alternative C library implementations +# e.g. qemux86_musl. +# +# Buildhistory and testimages will be created only for the first +# machine in the list, as otherwise it adds enormously to AUH run time. machines=qemux86 qemux86_musl qemux86-64 qemuarm qemumips qemuppc # optional features
modules/testimage.py +3 -104
117 lines not on this page · raw message
Signature · 1 line
2.15.1
PATCH 20/21 testimage.py: rewrite the logic for building and running testimages
In reply to Alexander KanavinParticularly:
1) determine per-package if ptest is supported, and add to the list of packages to install accordingly
2) remove broken code that finds image logs in the build directory; this will be replaced in the following commit
3) remove code that parses said logs for specific package failures; this is prone to mistmatches and is best left to a human.
--- modules/testimage.py | 115 +++++---------------------------------------------- 1 file changed, 10 insertions(+), 105 deletions(-)
modules/testimage.py +10 -105
@@ -50,129 +50,34 @@ class TestImage(): self.image = image os.environ['BB_ENV_EXTRAWHITE'] = os.environ['BB_ENV_EXTRAWHITE'] + \ - " TEST_SUITES CORE_IMAGE_EXTRA_INSTALL" + " CORE_IMAGE_EXTRA_INSTALL" - def _get_pkgs_to_install(self, pkgs, ptest=False): + def _get_pkgs_to_install(self, pkgs): pkgs_out = [] - # for provide access to the target - if ptest: - pkgs_out.append("dropbear") - pkgs_out.append("ptest-runner") - for c in pkgs: pkgs_out.append(c['PN']) - return ' '.join(pkgs_out) - - def _parse_ptest_log(self, log_file): - ptest_results = {} - - with open(log_file, "r") as f: - pn = None - processing = False - - for line in f: - if not processing: - m = re.search("^BEGIN: /usr/lib/(.*)/ptest$", line) - if m: - pn = m.group(1) - ptest_results[pn] = [] - processing = True - else: - m = re.search("^END: $", line) - if m: - pn = None - processing = False - else: - ptest_results[pn].append(line) - - return ptest_results - - def _find_log(self, name, machine): - result = [] - - base_dir = os.path.join(os.getenv('BUILDDIR'), 'tmp', 'work') - for root, dirs, files in os.walk(base_dir): - if name in files: - result.append(os.path.join(root, name)) - - D("Found logs named %s for machine %s: %s" %(name, machine, result)) - for ptest_log in result: - if machine in ptest_log: - D("Picked log: %s" %(ptest_log)) - return ptest_log - - def _get_failed_recipe(self, log): - pn = None - - for line in log.splitlines(): - m = re.match("ERROR: QA Issue: ([^ :]*): (.*) not shipped", line) - if m: - pn = m.group(1) - break - - m = re.match("ERROR: Logfile of failure stored in: " \ - "(.*/([^/]*)/[^/]*/temp/log\.(.*)\.[0-9]*)", line) - if m: - pn = m.group(2) - break - - return pn - - def _handle_image_build_error(self, image, pkgs_ctx, e): - pn = self._get_failed_recipe(e.stdout) - if pn and pn != image: - pkg_ctx = _pn_in_pkgs_ctx(pn, pkgs_ctx) - if pkg_ctx: - raise IntegrationError(e.stdout, pkg_ctx) + I(" Checking if package {} has ptests...".format(c['PN'])) + if 'PTEST_ENABLED' in self.bb.env(c['PN']): + I(" ...yes") + pkgs_out.append((c['PN']) + '-ptest') else: - pn_env = self.bb.env(pn) - - depends = pn_env['DEPENDS'].split() - rdepends = pn_env['RDEPENDS'].split() - deps = depends + rdepends + I(" ...no") - for d in deps: - pkg_ctx = _pn_in_pkgs_ctx(d, pkgs_ctx) - if pkg_ctx: - raise IntegrationError(e.stdout, pkg_ctx) - raise e + return ' '.join(pkgs_out) def testimage(self, pkgs_ctx, machine, image): os.environ['CORE_IMAGE_EXTRA_INSTALL'] = \ self._get_pkgs_to_install(pkgs_ctx) - - if 'TEST_SUITES' in os.environ: - del os.environ['TEST_SUITES'] + I( " Installing additional packages to the image: {}".format(os.environ['CORE_IMAGE_EXTRA_INSTALL'])) I( " building %s for %s ..." % (image, machine)) - try: - self.bb.complete(image, machine) - except Error as e: - self._handle_image_build_error(image, pkgs_ctx, e) + self.bb.complete(image, machine) I( " running %s/testimage for %s ..." % (image, machine)) self.bb.complete("%s -c testimage" % image, machine) - log_file = self._find_log("log.do_testimage", machine) - shutil.copyfile(log_file, - os.path.join(self.uh_work_dir, "log_%s.do_testimage" % machine)) - for pkg_ctx in pkgs_ctx: - if not 'testimage' in pkg_ctx: - pkg_ctx['testimage'] = {} - if not 'testimage_log' in pkg_ctx: - pkg_ctx['testimage_log'] = os.path.join( - pkg_ctx['workdir'], "log.do_testimage") - - pkg_ctx['testimage'][machine] = True - with open(log_file, "r") as lf: - with open(pkg_ctx['testimage_log'], "a+") as of: - of.write("BEGIN: TESTIMAGE for %s\n" % machine) - for line in lf: - of.write(line) - of.write("END: TESTIMAGE for %s\n" % machine) - def run(self): machine = self.opts['machines'][0] I(" Testing image for %s ..." % machine)
Signature · 1 line
2.15.1
PATCH 21/21 testimage.py: collect and write out logs from bitbake and testimage
In reply to Alexander Kanavin--- modules/testimage.py | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-)
modules/testimage.py +27 -4
@@ -49,8 +49,11 @@ class TestImage(): self.pkgs_ctx = packages['succeeded'] self.image = image + self.logdir = os.path.join(uh_work_dir, "testimage-logs") + os.mkdir(self.logdir) + os.environ['BB_ENV_EXTRAWHITE'] = os.environ['BB_ENV_EXTRAWHITE'] + \ - " CORE_IMAGE_EXTRA_INSTALL" + " CORE_IMAGE_EXTRA_INSTALL TEST_LOG_DIR TESTIMAGE_UPDATE_VARS" def _get_pkgs_to_install(self, pkgs): pkgs_out = []
@@ -70,13 +73,33 @@ class TestImage(): def testimage(self, pkgs_ctx, machine, image): os.environ['CORE_IMAGE_EXTRA_INSTALL'] = \ self._get_pkgs_to_install(pkgs_ctx) + os.environ['TEST_LOG_DIR'] = self.logdir + os.environ['TESTIMAGE_UPDATE_VARS'] = 'TEST_LOG_DIR' I( " Installing additional packages to the image: {}".format(os.environ['CORE_IMAGE_EXTRA_INSTALL'])) I( " building %s for %s ..." % (image, machine)) - self.bb.complete(image, machine) + bitbake_create_output = "" + bitbake_run_output = "" + try: + bitbake_create_output = self.bb.complete(image, machine) + except Error as e: + I( " building the testimage failed! Collecting logs...") + bitbake_create_output = e.stdout + else: + I( " running %s/testimage for %s ..." % (image, machine)) + try: + bitbake_run_output = self.bb.complete("%s -c testimage" % image, machine) + except Error as e: + I( " running the testimage failed! Collecting logs...") + bitbake_run_output = e.stdout - I( " running %s/testimage for %s ..." % (image, machine)) - self.bb.complete("%s -c testimage" % image, machine) + if bitbake_create_output: + with open(os.path.join(self.logdir, "bitbake-create-testimage.log"), 'w') as f: + f.write(bitbake_create_output) + if bitbake_run_output: + with open(os.path.join(self.logdir, "bitbake-run-testimage.log"), 'w') as f: + f.write(bitbake_run_output) + I(" All done! Testimage/ptest/qemu logs are collected to {}".format(self.logdir)) def run(self): machine = self.opts['machines'][0]
Signature · 1 line
2.15.1