@@ -97,11 +97,17 @@ def run_internal(maintplanlayerbranch, commit, commitdate, options, logger, bitb
ret, output = utils.run_command_interruptible(cmd, timeout=options.commit_timeout or None)
except subprocess.TimeoutExpired:
logger.error('Analysing commit %s timed out after %d seconds; skipping it' % (commit, options.commit_timeout))
- return
+ return True
if ret == 254:
# Interrupted by user, break out of loop
logger.info('Update interrupted, exiting')
sys.exit(254)
+ if ret:
+ # The caller then doesn't save this commit as the checkpoint, so the
+ # next run tries it again (AI-generated, Claude Opus 5.5)
+ logger.error('Analysing commit %s failed with exit code %d' % (commit, ret))
+ return False
+ return True
"""
Upgrade history handler.
@@ -137,6 +143,7 @@ def upgrade_history(options, logger):
if not lockfile:
logger.error("Layer index lock timeout expired")
sys.exit(1)
+ failed = False
try:
for maintplan in maintplans:
# Check releases and milestones
@@ -221,7 +228,10 @@ def upgrade_history(options, logger):
ct, ctepoch = commit_list.pop(0).split()
ctdate = datetime.fromtimestamp(int(ctepoch))
- run_internal(maintplanbranch, ct, ctdate, options, logger, bitbake_map, initial=True)
+ if not run_internal(maintplanbranch, ct, ctdate, options, logger, bitbake_map, initial=True):
+ logger.error('%s: initial import failed, skipping this layer branch' % layerbranch)
+ failed = True
+ continue
if layerbranch.vcs_subdir:
layersubdir_start = layerbranch.vcs_subdir
@@ -263,13 +273,19 @@ def upgrade_history(options, logger):
logger.debug("Skipping commit %s" % ct)
continue
logger.debug("Analysing commit %s ..." % ct)
- run_internal(maintplanbranch, ct, ctdate, options, logger, bitbake_map)
+ if not run_internal(maintplanbranch, ct, ctdate, options, logger, bitbake_map):
+ # Stop here so the next run starts again from this commit
+ logger.error('%s: stopping at commit %s' % (layerbranch, ct))
+ failed = True
+ break
if not (options.dry_run or options.filter_files):
maintplanbranch.upgrade_rev = ct
maintplanbranch.upgrade_date = ctdate
maintplanbranch.save()
finally:
utils.unlock_file(lockfile)
+ if failed:
+ sys.exit(1)
if __name__=="__main__":
parser = optparse.OptionParser(usage = """%prog [options]""")
@@ -41,3 +41,43 @@ def test_bitbake_revision_without_limit():
log = logging.getLogger('test')
assert remapped_bitbake_rev('a', datetime(2030, 1, 1), {'a': ('b', None)}, log) == 'b'
assert remapped_bitbake_rev('c', datetime(2030, 1, 1), {'a': ('b', None)}, log) == 'c'
+
+
+def run_internal_with(monkeypatch, result):
+ import subprocess
+ import types
+ from layerindex import utils
+ import rrs_upgrade_history
+
+ def run(cmd, timeout=None):
+ if isinstance(result, Exception):
+ raise result
+ return result, ''
+
+ # The test settings don't install the rrs app; run_internal() only needs
+ # Release for an initial import
+ monkeypatch.setitem(sys.modules, 'rrs.models', types.SimpleNamespace(Release=None))
+ monkeypatch.setattr(utils, 'runcmd', lambda *args, **kwargs: 'abc123')
+ monkeypatch.setattr(utils, 'run_command_interruptible', run)
+ plan_branch = types.SimpleNamespace(python3_switch_date=datetime(2016, 6, 2),
+ python3_environment=None,
+ layerbranch=types.SimpleNamespace(id=1))
+ options = types.SimpleNamespace(commit_timeout=5, filter_files='', dry_run=False, loglevel=logging.INFO)
+ return rrs_upgrade_history.run_internal(plan_branch, 'deadbeef', datetime(2026, 2, 27),
+ options, logging.getLogger('test'), {})
+
+
+def test_run_internal_success(monkeypatch):
+ assert run_internal_with(monkeypatch, 0) is True
+
+
+def test_run_internal_failure(monkeypatch, caplog):
+ # [YOCTO #16515] a failed commit must not be saved as the checkpoint
+ with caplog.at_level(logging.ERROR):
+ assert run_internal_with(monkeypatch, 1) is False
+ assert 'deadbeef failed with exit code 1' in caplog.text
+
+
+def test_run_internal_timeout_is_skipped(monkeypatch):
+ import subprocess
+ assert run_internal_with(monkeypatch, subprocess.TimeoutExpired('cmd', 5)) is True
A commit whose analysis failed was still saved as the layer branch's checkpoint, so it was never analysed again. On a test instance a container that ran out of processes failed every commit for two hours and moved three checkpoints to the latest commit. Stop the layer branch at a failed commit instead, so the next run retries it, and exit with status 1. A commit that times out is still skipped. [YOCTO #16515] AI-Generated: Claude Opus 5.5 Signed-off-by: Tim Orling <tim.orling@konsulko.com> --- This applies on top of "rrs_upgrade_history: skip a commit that takes too long to analyse" (v2), which is on master-next. Tested on a test instance: run in a container without --init and with --pids-limit 80, the analysis started failing after about 70 commits; each layer branch stopped at its first failed commit, kept the last good one as its checkpoint, and the script exited 1. rrs/tools/rrs_upgrade_history.py | 22 ++++++++++++++--- tests/test_rrs_upgrade_history.py | 40 +++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 3 deletions(-)