diff mbox series

[layerindex-web] update: stop updating layers not compatible with a release branch

Message ID 20261114064304.24562-1-tim.orling@konsulko.com
State Under Review
Delegated to: Tim Orling
Headers show
Series [layerindex-web] update: stop updating layers not compatible with a release branch | expand

Commit Message

Tim Orling Nov. 14, 2026, 6:43 a.m. UTC
A layer branch kept being updated on a release branch even when the
layer's LAYERSERIES_COMPAT does not include that release, e.g. layers
that still only list thud and warrior on kirkstone and scarthgap. On
release branches, disable updates for such a layer branch and record
why against the layer, as for a layer that has gone from the branch.

On master the core layer is already the next release, which layers
only catch up with over time, so there it is only a warning in the
layer's update log.

[YOCTO #7792]

AI-Generated: Claude Opus 5.5
Signed-off-by: Tim Orling <tim.orling@konsulko.com>
---
This applies on top of "[PATCH 0/3] Report problems with the first
parse of a layer" and "[PATCH] update: disable updates for a layer
branch whose layer has gone" (bug 12922), whose helpers it uses, and
on master-next.

It covers the "not compatible" side of the bug; showing a layer as
compatible with other releases it lists is not part of it.

On a test instance loaded with a copy of the layers.openembedded.org
database, a dry run on scarthgap reported meta-agl-core (wrynose only)
and meta-openstack-compute-deploy, -controller-deploy and -qemu (thud
warrior only). On master it would be 241 of 359 layer branches, since
core master is already blacksail and most layers still list wrynose,
hence only a warning there. The check runs when a layer has changed
(or with -r), like the rest of the parsing.

 layerindex/update.py       | 10 ++++++++++
 layerindex/update_layer.py | 13 ++++++++++++-
 layerindex/utils.py        | 17 +++++++++++++++++
 tests/test_update.py       | 27 +++++++++++++++++++++++++++
 tests/test_utils.py        | 14 ++++++++++++++
 5 files changed, 80 insertions(+), 1 deletion(-)


base-commit: 08e58cdece30019a6f9e5d14af16ebe20afa6177
prerequisite-patch-id: 85be5c2fe68b937570c6e8be5a911e92864cdf63
prerequisite-patch-id: 85ea51619f1ba4ca8253c1df2d0f7689823f1d22
prerequisite-patch-id: e0e8ba9340973102abc551c2c349edbf518d6d48
prerequisite-patch-id: b10e06558db0fb85abfab3d8b9d4bd8f29449e9e
diff mbox series

Patch

diff --git a/layerindex/update.py b/layerindex/update.py
index 72bad6c..cd4b267 100755
--- a/layerindex/update.py
+++ b/layerindex/update.py
@@ -514,6 +514,16 @@  def main():
                         record_layer_error(layer, branchobj, 'Unable to find BBFILE_COLLECTIONS value for layer %s - is it set in conf/layer.conf?' % layer.name)
                         # Assume (perhaps naively) that it's an error specific to the layer
                         continue
+                    if branchobj.name != 'master':
+                        mismatch = utils.layerseries_mismatch(extract_value('LAYERSERIES_COMPAT', initial_values),
+                                                              extract_value('LAYERSERIES_CORENAMES', initial_values))
+                        if mismatch:
+                            errmsg = 'Layer %s %s' % (layer.name, mismatch)
+                            if newbranch:
+                                record_layer_error(layer, branchobj, errmsg)
+                            else:
+                                disable_layerbranch_updates(layer, branchobj, layerbranch, errmsg)
+                            continue
                     ver = extract_value('LAYERVERSION', initial_values)
                     deps = extract_value('LAYERDEPENDS', initial_values)
                     recs = extract_value('LAYERRECOMMENDS', initial_values)
diff --git a/layerindex/update_layer.py b/layerindex/update_layer.py
index c11c002..9bcfa3a 100644
--- a/layerindex/update_layer.py
+++ b/layerindex/update_layer.py
@@ -457,10 +457,21 @@  def main():
                     # Write these to a file rather than the output, which is
                     # shown on the console and in the update log
                     with open(options.initial, 'w') as f:
-                        for i in ["BBFILE_COLLECTIONS", "LAYERVERSION", "LAYERDEPENDS", "LAYERRECOMMENDS"]:
+                        for i in ["BBFILE_COLLECTIONS", "LAYERVERSION", "LAYERDEPENDS", "LAYERRECOMMENDS",
+                                  "LAYERSERIES_COMPAT", "LAYERSERIES_CORENAMES"]:
                             f.write('%s = "%s"\n' % (i, utils.get_layer_var(layer_config_data, i, logger)))
                     sys.exit(0)
 
+                # On master the core layer is already the next release, which
+                # layers catch up with over time, so only warn (on release
+                # branches update.py doesn't update such layers)
+                if branch.name == 'master':
+                    mismatch = utils.layerseries_mismatch(
+                        utils.get_layer_var(layer_config_data, 'LAYERSERIES_COMPAT', logger),
+                        utils.get_layer_var(layer_config_data, 'LAYERSERIES_CORENAMES', logger))
+                    if mismatch:
+                        logger.warning('Layer %s %s' % (layer.name, mismatch))
+
                 # Set up for recording patch info
                 utils.setup_core_layer_sys_path(settings, branch.name)
                 skip_patches = False
diff --git a/layerindex/utils.py b/layerindex/utils.py
index fd29de0..5502163 100644
--- a/layerindex/utils.py
+++ b/layerindex/utils.py
@@ -57,6 +57,23 @@  def get_layer_var(config_data, var, logger):
         value = config_data.getVar(var, True)
     return value or ''
 
+def layerseries_mismatch(compat, corenames):
+    """
+    If a layer's LAYERSERIES_COMPAT doesn't include any of the release series
+    the core layer is (LAYERSERIES_CORENAMES), return why; otherwise return
+    None. Branches whose core layer has no LAYERSERIES_CORENAMES (before 2.5)
+    aren't checked.
+    """
+    corenames = (corenames or '').split()
+    if not corenames:
+        return None
+    compat = (compat or '').split()
+    if not compat:
+        return 'does not set LAYERSERIES_COMPAT'
+    if set(compat) & set(corenames):
+        return None
+    return 'is only compatible with %s, not %s (LAYERSERIES_COMPAT)' % (' '.join(compat), ' '.join(corenames))
+
 def nested_layer_dirs(layerdir):
     """
     Return the directories below layerdir that contain a conf/layer.conf of
diff --git a/tests/test_update.py b/tests/test_update.py
index 8cd3161..42be35f 100644
--- a/tests/test_update.py
+++ b/tests/test_update.py
@@ -116,6 +116,33 @@  def test_move_recipe_out(import_layer, repo, db_access_without_rollback_and_trun
     with pytest.raises(Recipe.DoesNotExist):
         recipe.refresh_from_db()
 
+def test_layerseries_compat(import_layer, repo, db_access_without_rollback_and_truncate):
+    # [YOCTO #7792] the test layer only lists "sumo" in LAYERSERIES_COMPAT
+    from layerindex.models import LayerBranch, LayerUpdate
+    # On master: updated, with a warning in the layer's update log
+    run_cmd("layerindex/update.py -l meta-layerindex-test --nofetch --nocheckout -r")
+    layerupdate = LayerUpdate.objects.filter(layer__name='meta-layerindex-test', branch__name='master').order_by('-id').first()
+    assert 'WARNING: Layer meta-layerindex-test is only compatible with sumo' in layerupdate.log
+    assert LayerBranch.objects.get(layer__name='meta-layerindex-test', branch__name='master').updates_enabled
+    # On a release branch: updates for the layer branch are disabled
+    run_cmd('python3 manage.py shell -c "from layerindex.models import Branch, LayerBranch, LayerItem; '
+            'b = Branch.objects.create(name=\'teststable\', bitbake_branch=\'master\'); '
+            'LayerBranch.objects.create(layer=LayerItem.objects.get(name=\'meta-layerindex-test\'), branch=b, '
+            'vcs_subdir=\'meta-layerindex-test\', actual_branch=\'master\'); '
+            'LayerBranch.objects.create(layer=LayerItem.objects.get(name=\'openembedded-core\'), branch=b, '
+            'vcs_subdir=\'meta\', actual_branch=\'master\')"', cwd=basepath)
+    try:
+        run_cmd("layerindex/update.py -b teststable -l meta-layerindex-test --nofetch --nocheckout")
+        layerbranch = LayerBranch.objects.get(layer__name='meta-layerindex-test', branch__name='teststable')
+        assert not layerbranch.updates_enabled
+        layerupdate = LayerUpdate.objects.filter(layer__name='meta-layerindex-test', branch__name='teststable').order_by('-id').first()
+        assert 'is only compatible with sumo' in layerupdate.log
+        assert 'updates for this layer branch have been disabled' in layerupdate.log
+    finally:
+        run_cmd('python3 manage.py shell -c "from layerindex.models import Branch; '
+                'Branch.objects.filter(name=\'teststable\').delete()"', cwd=basepath)
+
+
 def test_delete_recipe(import_layer, repo, db_access_without_rollback_and_truncate):
     from layerindex.models import LayerBranch, Recipe
     layerbranch = LayerBranch.objects.get(layer__name='meta-layerindex-test')
diff --git a/tests/test_utils.py b/tests/test_utils.py
index 95b5e04..37b80d4 100644
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -48,6 +48,20 @@  def make_layer(path):
     (path / 'conf' / 'layer.conf').write_text('')
 
 
+@pytest.mark.parametrize('compat, corenames, expected', [
+    # [YOCTO #7792]
+    ('scarthgap', 'scarthgap', None),
+    ('kirkstone scarthgap', 'scarthgap', None),
+    ('wrynose', 'blacksail', 'is only compatible with wrynose, not blacksail (LAYERSERIES_COMPAT)'),
+    ('', 'scarthgap', 'does not set LAYERSERIES_COMPAT'),
+    # Before LAYERSERIES_CORENAMES existed nothing is checked
+    ('', '', None),
+    ('sumo', None, None),
+])
+def test_layerseries_mismatch(compat, corenames, expected):
+    assert utils.layerseries_mismatch(compat, corenames) == expected
+
+
 @pytest.fixture
 def repo_with_nested_layers(tmp_path):
     """A repository whose layer (in subdirectory 'meta-top') contains other