[PATCH 0/9] Add support to control layer ordering

7 views
Skip to first unread message

Felix Moessbauer

unread,
Jan 19, 2026, 4:01:13 AMJan 19
to kas-...@googlegroups.com, jan.k...@siemens.com, Felix Moessbauer
This series should finally solve the long standing issue
https://github.com/siemens/kas/issues/36.

While the implementation itself is quite trivial, some cleanup was
needed to properly integrate it.

Best regards,
Felix Moessbauer

Felix Moessbauer (9):
fix(spinx-schema): add defaults that evaluate to false as well
docs(schema): extract allowed integer ranges
refactor(repos): internally represent layers as objects
refactor(repos): represent layers as objects
refactor(tests): generalize layer tests
docs(schema): add support for global defs section
schema: add support for layer priorities
feat(repos): add support to explicitly specify layer order
add test for layer ordering by priority

docs/_ext/sphinx_kas_schema.py | 27 +++++--
docs/format-changelog.rst | 9 +++
docs/userguide/project-configuration.rst | 5 +-
kas/attestation.py | 2 +-
kas/libcmds.py | 10 +--
kas/repos.py | 86 +++++++++++++++++-----
kas/schema-kas.json | 22 +++++-
tests/test_layers.py | 92 ++++++++++++++----------
tests/test_layers/test-layer-prio.yml | 24 +++++++
9 files changed, 206 insertions(+), 71 deletions(-)
create mode 100644 tests/test_layers/test-layer-prio.yml

--
2.51.0

Felix Moessbauer

unread,
Jan 19, 2026, 4:01:13 AMJan 19
to kas-...@googlegroups.com, jan.k...@siemens.com, Felix Moessbauer
Our sphinx plugin that parses the jsonschema and extracts descriptions
from the nodes incorrectly handled default values that evalute to false
in Python (e.g. false, 0). We fix this by explicitly checking if the
value is available or not. By that, we get more defaults shown in the
documentation.

Fixes: fcbdb1617 ("docs: extract default values from kas schema")
Fixes: d558eb4bf ("docs: auto add enum values of schema node")
Signed-off-by: Felix Moessbauer <felix.mo...@siemens.com>
---
docs/_ext/sphinx_kas_schema.py | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/docs/_ext/sphinx_kas_schema.py b/docs/_ext/sphinx_kas_schema.py
index 40e35c0fb..b09744502 100644
--- a/docs/_ext/sphinx_kas_schema.py
+++ b/docs/_ext/sphinx_kas_schema.py
@@ -85,18 +85,18 @@ class KasSchemaDescRole(SphinxRole):
processed, msgs = self.inliner.parse(desc, self.lineno, memo, parent)
parent += processed
messages += msgs
- if default:
+ if default is not None:
def_pg = nodes.paragraph()
def_pg += nodes.strong(text='Default: ')
- def_pg += nodes.literal(text=default)
+ def_pg += nodes.literal(text=str(default))
parent += def_pg
- if allowed_values:
+ if allowed_values is not None:
av_pg = nodes.paragraph()
av_pg += nodes.strong(text='Supported values: ')
for i in range(len(allowed_values)):
if i != 0:
av_pg += nodes.Text(', ')
- av_pg += nodes.literal(text=allowed_values[i])
+ av_pg += nodes.literal(text=str(allowed_values[i]))
parent += av_pg

return parent, messages
--
2.51.0

Felix Moessbauer

unread,
Jan 19, 2026, 4:01:15 AMJan 19
to kas-...@googlegroups.com, jan.k...@siemens.com, Felix Moessbauer
For schema fields with an minimum and maximum value (integers only), we
now extract these and add them to the documentation as well. This helps
to keep the spec with the docs in sync.

Signed-off-by: Felix Moessbauer <felix.mo...@siemens.com>
---
docs/_ext/sphinx_kas_schema.py | 7 +++++++
1 file changed, 7 insertions(+)

diff --git a/docs/_ext/sphinx_kas_schema.py b/docs/_ext/sphinx_kas_schema.py
index b09744502..9400c034e 100644
--- a/docs/_ext/sphinx_kas_schema.py
+++ b/docs/_ext/sphinx_kas_schema.py
@@ -77,6 +77,8 @@ class KasSchemaDescRole(SphinxRole):
return [], messages
default = node.get('default', None)
allowed_values = node.get('enum', None)
+ minval = node.get('minimum', None)
+ maxval = node.get('maximum', None)

memo = Struct(document=self.inliner.document,
reporter=self.inliner.reporter,
@@ -98,6 +100,11 @@ class KasSchemaDescRole(SphinxRole):
av_pg += nodes.Text(', ')
av_pg += nodes.literal(text=str(allowed_values[i]))
parent += av_pg
+ if minval is not None and maxval is not None:
+ range_pg = nodes.paragraph()
+ range_pg += nodes.strong(text='Range: ')
+ range_pg += nodes.literal(text=f'[{minval}, {maxval}]')
+ parent += range_pg

Felix Moessbauer

unread,
Jan 19, 2026, 4:01:18 AMJan 19
to kas-...@googlegroups.com, jan.k...@siemens.com, Felix Moessbauer
Previously the layers where internally just represented as path strings.
As a preparation to attach more data to a layer (e.g. a priority), we
change the internal representation of each layer to an object. The
external interfaces are not affected by this change.

We further internally handle the layer path now with pathlib.Path to
avoid manual sanitizing and removing of superflous path elements (like
a trailing /.).

Signed-off-by: Felix Moessbauer <felix.mo...@siemens.com>
---
kas/repos.py | 58 +++++++++++++++++++++++++++++++++++++++-------------
1 file changed, 44 insertions(+), 14 deletions(-)

diff --git a/kas/repos.py b/kas/repos.py
index a8ea458f9..7b2ca7cad 100644
--- a/kas/repos.py
+++ b/kas/repos.py
@@ -28,6 +28,8 @@ import os
import linecache
import logging
import shutil
+from pathlib import Path
+from dataclasses import dataclass
from datetime import datetime
from urllib.parse import urlparse
from tempfile import TemporaryDirectory
@@ -115,8 +117,7 @@ class Repo:

@property
def layers(self):
- return [os.path.join(self.path, layer).rstrip(os.sep + '.')
- for layer in self._layers]
+ return [str(layer.path) for layer in self._layers]

@property
def qualified_name(self):
@@ -236,18 +237,6 @@ class Repo:
Returns a Repo instance depending on parameters.
This factory function is referential transparent.
"""
- layers_dict = repo_config.get('layers', {'': None})
- # only bool(false) will be a valid value to disable a layer
- for lname, prop in layers_dict.items():
- if not (prop is None or prop == "disabled"):
- logging.warning('Use of deprecated value "%s" for repo '
- '"%s", layer "%s". Replace with "disabled".',
- prop, name, lname)
-
- layers = list(filter(lambda x, laydict=layers_dict:
- str(laydict[x]).lower() not in
- ['disabled', 'excluded', 'n', 'no', '0', 'false'],
- layers_dict))
default_patch_repo = repo_defaults.get('patches', {}).get('repo', None)
patches_dict = repo_config.get('patches', {})
patches = []
@@ -319,6 +308,9 @@ class Repo:
# Relative pathes are assumed to start from work_dir
path = os.path.join(get_context().kas_work_dir, path)

+ layers_dict = repo_config.get('layers', {'': None})
+ layers = Repo._create_layers_from_dict(name, path, layers_dict)
+
if url is None:
# No version control operation on repository
disable_operations = True
@@ -359,6 +351,31 @@ class Repo:

return path if fallback else None

+ @staticmethod
+ def _create_layers_from_dict(repo_name, repo_path, layers_dict):
+ layers = []
+ disabled_token = "disabled"
+ legacy_disabled_tokens = ['excluded', 'n', 'no', '0', 'false']
+
+ for lname, prop in layers_dict.items():
+ if not (prop is None or prop == "disabled"):
+ logging.warning('Use of deprecated value "%s" for repo '
+ '"%s", layer "%s". Replace with "disabled".',
+ prop, repo_name, lname)
+ if prop is None:
+ layers.append(RepoLayer(name=lname,
+ repo_path=Path(repo_path)))
+ elif isinstance(prop, str) and prop == disabled_token:
+ continue
+ elif isinstance(prop, str) and prop in legacy_disabled_tokens:
+ logging.warning('Use of deprecated value "%s" for repo '
+ '"%s", layer "%s". Replace with "disabled".',
+ prop, repo_name, lname)
+ continue
+ else:
+ raise NotImplementedError()
+ return layers
+

class RepoImpl(Repo):
"""
@@ -871,3 +888,16 @@ class MercurialRepo(RepoImpl):

def diff(self, commit1, commit2):
raise NotImplementedError("Unsupported diff for MercurialRepo")
+
+
+@dataclass
+class RepoLayer:
+ """
+ Standalone definition of a single bitbake layer.
+ """
+ name: str
+ repo_path: Path
+
+ @property
+ def path(self):
+ return self.repo_path / self.name
--
2.51.0

Felix Moessbauer

unread,
Jan 19, 2026, 4:01:20 AMJan 19
to kas-...@googlegroups.com, jan.k...@siemens.com, Felix Moessbauer
As a preparation to allow downstream consumers evaluate layer
properties, we change the repo.layers interface to emit layer objects
instead of strings. By that, we also replace the open-coded layer
ordering implementation by a comparison operator provided by the
layer object. This later can be extended to implement more complex
comparisons.

Signed-off-by: Felix Moessbauer <felix.mo...@siemens.com>
---
kas/attestation.py | 2 +-
kas/libcmds.py | 10 ++++++----
kas/repos.py | 17 ++++++++++-------
3 files changed, 17 insertions(+), 12 deletions(-)

diff --git a/kas/attestation.py b/kas/attestation.py
index c3b4ccdde..e2d764be0 100644
--- a/kas/attestation.py
+++ b/kas/attestation.py
@@ -126,7 +126,7 @@ class Provenance:
digest = {f'{r.get_type()}Commit': r.revision}
annotations = {
'dirty': r.dirty,
- 'layers': [str(Path(layer).relative_to(r.path))
+ 'layers': [str(layer.path.relative_to(r.path))
for layer in r.layers]
}
cleanurl = self._strip_credentials(r.url)
diff --git a/kas/libcmds.py b/kas/libcmds.py
index bf02fcedd..1758b9b4f 100644
--- a/kas/libcmds.py
+++ b/kas/libcmds.py
@@ -443,21 +443,23 @@ class WriteBBConfig(Command):
It is not expanded by KAS, hence we avoid
absolute paths pointing into the build host.
"""
- relpath = os.path.relpath(layer, ctx.build_dir)
+ # we need to walk up, which would require pathlib.Path >= 3.12
+ relpath = os.path.relpath(layer.path, ctx.build_dir)
return '${TOPDIR}/' + relpath

def _write_bblayers_conf(ctx):
filename = ctx.build_dir + '/conf/bblayers.conf'
if not os.path.isdir(os.path.dirname(filename)):
os.makedirs(os.path.dirname(filename))
+ layers_sorted = \
+ sorted([layer for repo in ctx.config.get_repos()
+ for layer in repo.layers])
with open(filename, 'w') as fds:
fds.write(ctx.config.get_bblayers_conf_header())
fds.write('BBLAYERS ?= " \\\n ')
fds.write(' \\\n '.join(
[_get_layer_path_under_topdir(ctx, layer)
- for repo in sorted(ctx.config.get_repos(),
- key=lambda r: r.name)
- for layer in sorted(repo.layers)]))
+ for layer in layers_sorted]))
fds.write('"\n')
fds.write('BBPATH ?= "${TOPDIR}"\n')
fds.write('BBFILES ??= ""\n')
diff --git a/kas/repos.py b/kas/repos.py
index 7b2ca7cad..ee6fb6e3c 100644
--- a/kas/repos.py
+++ b/kas/repos.py
@@ -107,7 +107,7 @@ class Repo:
self.tag = tag
self.branch = branch
self.refspec = refspec
- self._layers = layers
+ self.layers = layers
self._patches = patches
self.allowed_signers = signers
self.operations_disabled = disable_operations
@@ -115,10 +115,6 @@ class Repo:
if not self.url:
self.resolve_local()

- @property
- def layers(self):
- return [str(layer.path) for layer in self._layers]
-
@property
def qualified_name(self):
url = urlparse(self.url)
@@ -225,7 +221,7 @@ class Repo:
refspec = self.commit or self.tag or self.branch or self.refspec

return f'{self.url}:{refspec} ' \
- f'{self.path} {self._layers}'
+ f'{self.path} {self.layers}'

__legacy_refspec_warned__ = []
__no_commit_tag_warned__ = []
@@ -364,7 +360,8 @@ class Repo:
prop, repo_name, lname)
if prop is None:
layers.append(RepoLayer(name=lname,
- repo_path=Path(repo_path)))
+ repo_path=Path(repo_path),
+ repo_name=repo_name))
elif isinstance(prop, str) and prop == disabled_token:
continue
elif isinstance(prop, str) and prop in legacy_disabled_tokens:
@@ -896,8 +893,14 @@ class RepoLayer:
Standalone definition of a single bitbake layer.
"""
name: str
+ repo_name: str
repo_path: Path

@property
def path(self):
return self.repo_path / self.name
+
+ def __lt__(self, other):
+ if isinstance(other, RepoLayer):
+ return (self.repo_name, self.name) < (other.repo_name, other.name)
+ return NotImplemented
--
2.51.0

Felix Moessbauer

unread,
Jan 19, 2026, 4:01:22 AMJan 19
to kas-...@googlegroups.com, jan.k...@siemens.com, Felix Moessbauer
By generalizing the test setup fixture of the test_layer tests, we make
it reusable with other kas files as well. This is a preparation to add
further layer-order tests.

Signed-off-by: Felix Moessbauer <felix.mo...@siemens.com>
---
tests/test_layers.py | 75 ++++++++++++++++++++++----------------------
1 file changed, 38 insertions(+), 37 deletions(-)

diff --git a/tests/test_layers.py b/tests/test_layers.py
index 311e78e40..28fe88168 100644
--- a/tests/test_layers.py
+++ b/tests/test_layers.py
@@ -28,57 +28,58 @@ import pytest
LAYERBASE = '${TOPDIR}/..'


+class DoKas:
+ def __init__(self, monkeykas, tmpdir):
+ self.tmpdir = tmpdir
+ self.monkeykas = monkeykas
+
+ def run(self, filename):
+ tdir = str(self.tmpdir / 'test_layers')
+ shutil.copytree('tests/test_layers', tdir)
+ self.monkeykas.chdir(tdir)
+ self.monkeykas.setenv('KAS_CLONE_DEPTH', '1')
+ kas.kas(['shell', filename, '-c', 'true'])
+ # extract layer path from bblayers, keep order
+ with open(self.monkeykas.get_kbd() / 'conf/bblayers.conf', 'r') as f:
+ return [x.strip(' \\"\n').replace(LAYERBASE, '')
+ for x in f.readlines() if x.lstrip().startswith(LAYERBASE)]
+
+
@pytest.fixture
def dokas(monkeykas, tmpdir):
- tdir = str(tmpdir / 'test_layers')
- shutil.copytree('tests/test_layers', tdir)
- monkeykas.chdir(tdir)
- monkeykas.setenv('KAS_CLONE_DEPTH', '1')
- kas.kas(['shell', 'test.yml', '-c', 'true'])
+ return DoKas(monkeykas, tmpdir)


@pytest.mark.online
-def test_layers_default(dokas, monkeykas):
- match = 0
- with open(monkeykas.get_kbd() / 'conf/bblayers.conf', 'r') as f:
- for line in f:
- if f'{LAYERBASE}/kas ' in line:
- match += 1
- assert match == 1
+def test_layers_default(dokas):
+ layers = dokas.run('test.yml')
+ assert len([l_ for l_ in layers if l_ == '/kas']) == 1


@pytest.mark.online
-def test_layers_include(dokas, monkeykas):
- match = 0
- with open(monkeykas.get_kbd() / 'conf/bblayers.conf', 'r') as f:
- for line in f:
- if f'{LAYERBASE}/kas1/meta-' in line:
- match += 1
- assert match == 2
+def test_layers_include(dokas):
+ layers = dokas.run('test.yml')
+ assert len([l_ for l_ in layers if '/kas1/meta-' in l_]) == 2


@pytest.mark.online
-def test_layers_exclude(dokas, monkeykas):
- with open(monkeykas.get_kbd() / 'conf/bblayers.conf', 'r') as f:
- for line in f:
- assert f'{LAYERBASE}/kas2' not in line
+def test_layers_exclude(dokas):
+ layers = dokas.run('test.yml')
+ assert not any([l_ for l_ in layers if '/kas2' in l_])


@pytest.mark.online
-def test_layers_strip_dot(dokas, monkeykas):
- with open(monkeykas.get_kbd() / 'conf/bblayers.conf', 'r') as f:
- lines = f.readlines()
- assert any(f'{LAYERBASE}/kas3 ' in x for x in lines)
- assert any(f'{LAYERBASE}/kas3/meta-bar' in x for x in lines)
+def test_layers_strip_dot(dokas):
+ layers = dokas.run('test.yml')
+ assert any([l_ for l_ in layers if l_ == '/kas3'])
+ assert any([l_ for l_ in layers if l_ == '/kas3/meta-bar'])


@pytest.mark.online
-def test_layers_order(dokas, monkeykas):
- with open(monkeykas.get_kbd() / 'conf/bblayers.conf', 'r') as f:
- layers = [x.strip(' \\"\n').replace(LAYERBASE, '')
- for x in f.readlines() if x.lstrip().startswith(LAYERBASE)]
- # layers of a repo are sorted alphabetically
- assert layers[1] == '/kas1/meta-bar'
- assert layers[2] == '/kas1/meta-foo'
- # repos are sorted alphabetically (aa-kas from kas4 is last)
- assert layers[-1] == '/aa-kas/meta'
+def test_layers_order(dokas):
+ layers = dokas.run('test.yml')
+ # layers of a repo are sorted alphabetically
+ assert layers[1] == '/kas1/meta-bar'
+ assert layers[2] == '/kas1/meta-foo'
+ # repos are sorted alphabetically (aa-kas from kas4 is last)
+ assert layers[-1] == '/aa-kas/meta'
--
2.51.0

Felix Moessbauer

unread,
Jan 19, 2026, 4:01:24 AMJan 19
to kas-...@googlegroups.com, jan.k...@siemens.com, Felix Moessbauer
The global $defs section is used to define schemas that are referenced
in the document to reduce the nesting depths. We now add support to
reference these path in the docs as well.

Signed-off-by: Felix Moessbauer <felix.mo...@siemens.com>
---
docs/_ext/sphinx_kas_schema.py | 12 +++++++++---
1 file changed, 9 insertions(+), 3 deletions(-)

diff --git a/docs/_ext/sphinx_kas_schema.py b/docs/_ext/sphinx_kas_schema.py
index 9400c034e..6415c3850 100644
--- a/docs/_ext/sphinx_kas_schema.py
+++ b/docs/_ext/sphinx_kas_schema.py
@@ -25,7 +25,9 @@
description of a node from the schema. The `<node>` hereby is a path
to the node in the schema, separated by dots. For example, the
header.version node can be accessed with
- :kasschemadesc:`header.properties.version`.
+ :kasschemadesc:`header.properties.version`. Definitions inside the
+ `$defs` section are accessed by their full path, e.g.
+ :kasschemadesc:`$defs.path.to.node`.
'''
__license__ = 'MIT'
__copyright__ = 'Copyright (c) Siemens AG, 2017-2024'
@@ -49,8 +51,12 @@ class KasSchemaDescRole(SphinxRole):

def run(self) -> tuple[list[nodes.Node], list[nodes.system_message]]:
messages = []
- node = CONFIGSCHEMA['properties']
- path = self.text.split('.')
+ if self.text.startswith('$defs.'):
+ node = CONFIGSCHEMA['$defs']
+ path = self.text.split('.')[1:]
+ else:
+ node = CONFIGSCHEMA['properties']
+ path = self.text.split('.')
self.env.note_dependency(__schema_definition__)
try:
for part in path:
--
2.51.0

Felix Moessbauer

unread,
Jan 19, 2026, 4:01:24 AMJan 19
to kas-...@googlegroups.com, jan.k...@siemens.com, Felix Moessbauer
We extend the repo layers keys by adding the "prio" tuple. This will be
used to control the precedence (order) of the layers in BBLAYERS.

Signed-off-by: Felix Moessbauer <felix.mo...@siemens.com>
---
docs/format-changelog.rst | 9 +++++++++
docs/userguide/project-configuration.rst | 5 ++++-
kas/schema-kas.json | 22 ++++++++++++++++++++--
3 files changed, 33 insertions(+), 3 deletions(-)

diff --git a/docs/format-changelog.rst b/docs/format-changelog.rst
index e5d94d3b7..8aea3a525 100644
--- a/docs/format-changelog.rst
+++ b/docs/format-changelog.rst
@@ -201,3 +201,12 @@ Added
~~~~~

- The repo key ``branch`` can now be overridden, including to a null-value.
+
+Version 21
+----------
+
+Added
+~~~~~
+
+- The repo layers ``prio`` can be used to control the order in which the
+ layers are added to the ``BBLAYERS`` bitbake variable.
diff --git a/docs/userguide/project-configuration.rst b/docs/userguide/project-configuration.rst
index 597b105aa..f329c666e 100644
--- a/docs/userguide/project-configuration.rst
+++ b/docs/userguide/project-configuration.rst
@@ -377,13 +377,16 @@ Configuration reference
This adds both ``layers/meta-foo`` and ``layers/meta-foo/contrib`` from
the ``meta-foo`` repository to ``bblayers.conf``.

- ``<layer-path>``: enum [optional]
+ ``<layer-path>``: enum | dict [optional]
Adds the layer with ``<layer-path>`` that is relative to the
repository root directory, to the ``bblayers.conf`` if the value of
this entry is not ``disabled``. This way it is possible to overwrite
the inclusion of a layer in later loaded configuration files. To
re-enable it, set it to nothing (``null``).

+ ``prio``: integer [optional]
+ :kasschemadesc:`$defs.layerPrio.properties.prio`
+
``patches``: dict [optional]
:kasschemadesc:`repos.additionalProperties.anyOf[0].properties.patches`

diff --git a/kas/schema-kas.json b/kas/schema-kas.json
index 87934f99b..e52598a7c 100644
--- a/kas/schema-kas.json
+++ b/kas/schema-kas.json
@@ -4,6 +4,21 @@
"title": "kas configuration",
"description": "kas, a setup tool for bitbake based projects",
"type": "object",
+ "$defs": {
+ "layerPrio": {
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "prio": {
+ "description": "Include order priority, higher means earlier. The include priority is global, hence it is applied across all repositories.",
+ "type": "integer",
+ "minimum": -99,
+ "maximum": 99,
+ "default": 0
+ }
+ }
+ }
+ },
"required": [
"header"
],
@@ -26,7 +41,7 @@
{
"type": "integer",
"minimum": 1,
- "maximum": 20
+ "maximum": 21
}
]
},
@@ -168,7 +183,7 @@
"type": "string"
},
"repos": {
- "description": "Definitions of all available repos and layers. The layers are appended to the ``bblayers.conf`` sorted by the repository name first and then by the layer name.",
+ "description": "Definitions of all available repos and layers. The layers are appended to the ``bblayers.conf`` sorted by the layer priority first (descending), then repository name and then by the layer name (ascending).",
"type": "object",
"additionalProperties": {
"anyOf": [
@@ -265,6 +280,9 @@
{
"description": "Deprecated, use ``disabled`` instead.",
"enum": ["excluded", "n", "no", "0", "false", 0, false]
+ },
+ {
+ "$ref": "#/$defs/layerPrio"
}
]
}
--
2.51.0

Felix Moessbauer

unread,
Jan 19, 2026, 4:01:25 AMJan 19
to kas-...@googlegroups.com, jan.k...@siemens.com, Felix Moessbauer
While each bitbake layer can state its priority, there are still
situations where fine grained control over the order of the layers in
BBLAYERS is needed. Examples are .bbappend files which need to be
appended in the correct order. There have been many discussions on the
kas mailing list to give users control over the order, resulting in c9326cc1e
(making the order deterministic) and ecf5827b0 (giving control by the
repo and layer name). However, these solutions still do not solve the
problem of sorting layers within a repo and also ordering layers across
multiple repos.

To hopefully finally solve this, we provide a "prio" key which acts as
the first sorting criteria when adding the layers to BBLAYERS. By that,
we remain fully backward compatible and do not change the layer ordering
of existing kas files. This design also allows to sort layers across
repo boundaries and even have one repo provide two layers that are
separated by another layer from another repo.

Closes: #36
Signed-off-by: Felix Moessbauer <felix.mo...@siemens.com>
---
kas/repos.py | 19 +++++++++++++++++--
1 file changed, 17 insertions(+), 2 deletions(-)

diff --git a/kas/repos.py b/kas/repos.py
index ee6fb6e3c..4f34dcebd 100644
--- a/kas/repos.py
+++ b/kas/repos.py
@@ -33,6 +33,8 @@ from dataclasses import dataclass
from datetime import datetime
from urllib.parse import urlparse
from tempfile import TemporaryDirectory
+
+from kas.configschema import CONFIGSCHEMA
from .context import get_context
from .libkas import run_cmd_async, run_cmd
from .kasusererror import KasUserError
@@ -352,6 +354,8 @@ class Repo:
layers = []
disabled_token = "disabled"
legacy_disabled_tokens = ['excluded', 'n', 'no', '0', 'false']
+ default_prio = \
+ CONFIGSCHEMA['$defs']['layerPrio']['properties']['prio']['default']

for lname, prop in layers_dict.items():
if not (prop is None or prop == "disabled"):
@@ -361,7 +365,8 @@ class Repo:
if prop is None:
layers.append(RepoLayer(name=lname,
repo_path=Path(repo_path),
- repo_name=repo_name))
+ repo_name=repo_name,
+ priority=default_prio))
elif isinstance(prop, str) and prop == disabled_token:
continue
elif isinstance(prop, str) and prop in legacy_disabled_tokens:
@@ -369,6 +374,12 @@ class Repo:
'"%s", layer "%s". Replace with "disabled".',
prop, repo_name, lname)
continue
+ elif isinstance(prop, dict):
+ layers.append(RepoLayer(name=lname,
+ repo_path=Path(repo_path),
+ repo_name=repo_name,
+ priority=prop.get('prio',
+ default_prio)))
else:
raise NotImplementedError()
return layers
@@ -893,6 +904,7 @@ class RepoLayer:
Standalone definition of a single bitbake layer.
"""
name: str
+ priority: int
repo_name: str
repo_path: Path

@@ -902,5 +914,8 @@ class RepoLayer:

def __lt__(self, other):
if isinstance(other, RepoLayer):
- return (self.repo_name, self.name) < (other.repo_name, other.name)
+ # The priority comparison is intentionally swapped, as a higher
+ # priority means the element appears earlier in the list.
+ return (other.priority, self.repo_name, self.name) < \
+ (self.priority, other.repo_name, other.name)
return NotImplemented
--
2.51.0

Felix Moessbauer

unread,
Jan 19, 2026, 4:01:30 AMJan 19
to kas-...@googlegroups.com, jan.k...@siemens.com, Felix Moessbauer
Signed-off-by: Felix Moessbauer <felix.mo...@siemens.com>
---
tests/test_layers.py | 17 +++++++++++++++++
tests/test_layers/test-layer-prio.yml | 24 ++++++++++++++++++++++++
2 files changed, 41 insertions(+)
create mode 100644 tests/test_layers/test-layer-prio.yml

diff --git a/tests/test_layers.py b/tests/test_layers.py
index 28fe88168..7b5888cca 100644
--- a/tests/test_layers.py
+++ b/tests/test_layers.py
@@ -83,3 +83,20 @@ def test_layers_order(dokas):
assert layers[2] == '/kas1/meta-foo'
# repos are sorted alphabetically (aa-kas from kas4 is last)
assert layers[-1] == '/aa-kas/meta'
+
+
+...@pytest.mark.online
+def test_layers_prio(dokas, monkeykas):
+ layers = dokas.run('test-layer-prio.yml')
+ # layers are sorted by global priority
+ # highest prio (10)
+ assert layers[0] == '/02-kas/meta-foo'
+ # no prio, sorted alphabetically by repo name, layer name
+ assert layers[1] == '/01-kas/aa-test'
+ assert layers[2] == '/01-kas/zz-test'
+ # default prio as not explicitly specified, sorted by repo name
+ assert layers[3] == ''
+ # lower than default prio (-10)
+ assert layers[4] == '/01-kas'
+ # even lower (-20)
+ assert layers[5] == '/02-kas/meta-bar'
diff --git a/tests/test_layers/test-layer-prio.yml b/tests/test_layers/test-layer-prio.yml
new file mode 100644
index 000000000..fe56950e3
--- /dev/null
+++ b/tests/test_layers/test-layer-prio.yml
@@ -0,0 +1,24 @@
+header:
+ version: 14
+
+repos:
+ this:
+
+ 01-kas:
+ url: https://github.com/siemens/kas.git
+ branch: master
+ layers:
+ .:
+ prio: -10
+ zz-test:
+ aa-test:
+
+
+ 02-kas:
+ url: https://github.com/siemens/kas.git
+ branch: master
+ layers:
+ meta-bar:
+ prio: -20
+ meta-foo:
+ prio: 10
--
2.51.0

Jan Kiszka

unread,
Jan 20, 2026, 3:21:09 AMJan 20
to Felix Moessbauer, kas-...@googlegroups.com
Any particular reason for moving this here?
Jan

--
Siemens AG, Foundational Technologies
Linux Expert Center

Jan Kiszka

unread,
Jan 20, 2026, 3:21:14 AMJan 20
to Felix Moessbauer, kas-...@googlegroups.com
On 19.01.26 10:00, Felix Moessbauer wrote:
You could also negate the priority so that you do not need to mangle
self and other. But also that would need a comment.

Jan

> return NotImplemented

Jan Kiszka

unread,
Jan 20, 2026, 3:24:55 AMJan 20
to Felix Moessbauer, kas-...@googlegroups.com
Found it: you need 'path' now.

Jan Kiszka

unread,
Jan 20, 2026, 3:26:50 AMJan 20
to Felix Moessbauer, kas-...@googlegroups.com
On 20.01.26 09:21, 'Jan Kiszka' via kas-devel wrote:
How about folding this in?

diff --git a/kas/repos.py b/kas/repos.py
index 4f34dce..2cd71e9 100644
--- a/kas/repos.py
+++ b/kas/repos.py
@@ -914,8 +914,8 @@ class RepoLayer:

def __lt__(self, other):
if isinstance(other, RepoLayer):
- # The priority comparison is intentionally swapped, as a higher
- # priority means the element appears earlier in the list.
- return (other.priority, self.repo_name, self.name) < \
- (self.priority, other.repo_name, other.name)
+ # The priority is negated because a higher priority means the
+ # element appears earlier in the list.
+ return (-self.priority, self.repo_name, self.name) < \
+ (-other.priority, other.repo_name, other.name)
return NotImplemented

Jan

Heinisch, Alexander

unread,
Jan 20, 2026, 4:30:35 AMJan 20
to Kiszka, Jan, kas-...@googlegroups.com, MOESSBAUER, Felix

"Prio 1" vs. "Highest Prio" - maybe something like "order" instead of
"priority" states it more clearly?

Btw. in the commit messge it's already called "layer order" :-)

> -            return (other.priority, self.repo_name, self.name) < \
> -                (self.priority, other.repo_name, other.name)
> +            # The priority is negated because a higher priority
> means the
> +            # element appears earlier in the list.
> +            return (-self.priority, self.repo_name, self.name) < \
> +                (-other.priority, other.repo_name, other.name)
>          return NotImplemented
>
> Jan
>
> --
> Siemens AG, Foundational Technologies
> Linux Expert Center

--
Alexander Heinisch
Siemens AG
www.siemens.com

MOESSBAUER, Felix

unread,
Jan 20, 2026, 4:48:58 AMJan 20
to Heinisch, Alexander, Kiszka, Jan, kas-...@googlegroups.com
On Tue, 2026-01-20 at 09:30 +0000, Heinisch, Alexander (FT RPD CED SES-

The idea is to be semantically similar to OEs layer priority. If we
rename this to order, we also need to change the semantics (lower value
means earlier, which is against the agreed proposal in the GitHub
issue).

>
> Btw. in the commit messge it's already called "layer order" :-)

In the end, we control the layer order. But we use the priority, repo
name and layer name to define the order.

I prefer to keep it as we currently have it and just fold in Jans patch
(which he also can do while merging).

PS: @Alexander: Did you test this series on your use-case. Is it
helpful?

Best regards,
Felix

--
Siemens AG
Linux Expert Center
Friedrich-Ludwig-Bauer-Str. 3
85748 Garching, Germany

Jan Kiszka

unread,
Jan 20, 2026, 5:09:31 AMJan 20
to Felix Moessbauer, kas-...@googlegroups.com
On 19.01.26 10:00, Felix Moessbauer wrote:
Thanks, applied (with the mentioned modification of patch 8).

Heinisch, Alexander

unread,
Jan 21, 2026, 5:39:54 AMJan 21
to Kiszka, Jan, kas-...@googlegroups.com, MOESSBAUER, Felix
On Tue, 2026-01-20 at 09:48 +0000, Moessbauer, Felix (FT RPD CED OES-
Thx, for clarifying.

> >
> > Btw. in the commit messge it's already called "layer order" :-)
>
> In the end, we control the layer order. But we use the priority, repo
> name and layer name to define the order.
>
> I prefer to keep it as we currently have it and just fold in Jans
> patch
> (which he also can do while merging).
>
> PS: @Alexander: Did you test this series on your use-case. Is it
> helpful?

Yes, that works for me! Thank you!

>
> Best regards,
> Felix

Heinisch, Alexander

unread,
Jan 21, 2026, 7:22:51 AMJan 21
to Kiszka, Jan, kas-...@googlegroups.com, MOESSBAUER, Felix

This check was introduced in a previous patch [3/9] of this series and
conflicts with the changes added here! -> has to be removed in this
patch.

--

Jan Kiszka

unread,
Jan 21, 2026, 7:38:26 AMJan 21
to Heinisch, Alexander (FT RPD CED SES-AT), kas-...@googlegroups.com, Moessbauer, Felix (FT RPD CED OES-DE)
This check was moved by patch 3, not introduced.

Can you be more specific which problem you see?

Heinisch, Alexander

unread,
Jan 21, 2026, 8:35:12 AMJan 21
to Kiszka, Jan, kas-...@googlegroups.com, MOESSBAUER, Felix
When explicitly specifying the prio property kas emits warnings.

Using a kas snippet like this:

```
isar:
url: https://github.com/ilbers/isar.git
commit: d364cfa643d1d61d768b5235b9783ca083a564ab
layers:
meta:
prio: 10
meta-isar:
prio: 20
```

leads to following warnings:

```
2026-01-21 14:30:42 - WARNING - Use of deprecated value "{'prio': 10}"
for repo "isar", layer "meta". Replace with "disabled".
2026-01-21 14:30:42 - WARNING - Use of deprecated value "{'prio': 20}"
for repo "isar", layer "meta-isar". Replace with "disabled".
```

BR Alexander

>
> Jan

Jan Kiszka

unread,
Jan 21, 2026, 9:02:11 AMJan 21
to Heinisch, Alexander (FT RPD CED SES-AT), kas-...@googlegroups.com, Moessbauer, Felix (FT RPD CED OES-DE)
Ok, that is indeed wrong. Felix will check.

Felix Moessbauer

unread,
Jan 21, 2026, 9:52:25 AMJan 21
to kas-...@googlegroups.com, jan.k...@siemens.com, alexander...@siemens.com, Felix Moessbauer
For schema fields with an minimum and maximum value (integers only), we
now extract these and add them to the documentation as well. This helps
to keep the spec with the docs in sync.

Signed-off-by: Felix Moessbauer <felix.mo...@siemens.com>
Signed-off-by: Jan Kiszka <jan.k...@siemens.com>
---
docs/_ext/sphinx_kas_schema.py | 7 +++++++
1 file changed, 7 insertions(+)

diff --git a/docs/_ext/sphinx_kas_schema.py b/docs/_ext/sphinx_kas_schema.py
index b09744502..9400c034e 100644
--- a/docs/_ext/sphinx_kas_schema.py
+++ b/docs/_ext/sphinx_kas_schema.py

Felix Moessbauer

unread,
Jan 21, 2026, 9:52:25 AMJan 21
to kas-...@googlegroups.com, jan.k...@siemens.com, alexander...@siemens.com, Felix Moessbauer
This series should finally solve the long standing issue
https://github.com/siemens/kas/issues/36.

While the implementation itself is quite trivial, some cleanup was
needed to properly integrate it.

Changes since v1:

- remove duplicated check for legacy layer disabling: {no, ...}
as reported by Alexander. The check was anyways redundant and
probably a rebase artifact.
- correctly handle legacy layer disable value int(0)

Best regards,
Felix Moessbauer

Felix Moessbauer (8):
docs(schema): extract allowed integer ranges
refactor(repos): internally represent layers as objects
refactor(repos): represent layers as objects
refactor(tests): generalize layer tests
docs(schema): add support for global defs section
schema: add support for layer priorities
feat(repos): add support to explicitly specify layer order
add test for layer ordering by priority

Jan Kiszka (1):
docs: Add community support section to getting started guide

docs/_ext/sphinx_kas_schema.py | 19 ++++-
docs/format-changelog.rst | 9 +++
docs/userguide/getting-started.rst | 12 ++++
docs/userguide/project-configuration.rst | 5 +-
kas/attestation.py | 2 +-
kas/libcmds.py | 10 +--
kas/repos.py | 83 ++++++++++++++++-----
kas/schema-kas.json | 22 +++++-
tests/test_layers.py | 92 ++++++++++++++----------
tests/test_layers/test-layer-prio.yml | 24 +++++++
10 files changed, 211 insertions(+), 67 deletions(-)
create mode 100644 tests/test_layers/test-layer-prio.yml

--
2.51.0

Felix Moessbauer

unread,
Jan 21, 2026, 9:52:26 AMJan 21
to kas-...@googlegroups.com, jan.k...@siemens.com, alexander...@siemens.com, Felix Moessbauer
Previously the layers where internally just represented as path strings.
As a preparation to attach more data to a layer (e.g. a priority), we
change the internal representation of each layer to an object. The
external interfaces are not affected by this change.

We further internally handle the layer path now with pathlib.Path to
avoid manual sanitizing and removing of superflous path elements (like
a trailing /.).

Signed-off-by: Felix Moessbauer <felix.mo...@siemens.com>
Signed-off-by: Jan Kiszka <jan.k...@siemens.com>
---
kas/repos.py | 55 +++++++++++++++++++++++++++++++++++++++-------------
1 file changed, 41 insertions(+), 14 deletions(-)

diff --git a/kas/repos.py b/kas/repos.py
index a8ea458f9..14591b916 100644
--- a/kas/repos.py
+++ b/kas/repos.py
@@ -28,6 +28,8 @@ import os
import linecache
import logging
import shutil
+from pathlib import Path
+from dataclasses import dataclass
from datetime import datetime
from urllib.parse import urlparse
from tempfile import TemporaryDirectory
if url is None:
# No version control operation on repository
disable_operations = True
@@ -359,6 +351,28 @@ class Repo:

return path if fallback else None

+ @staticmethod
+ def _create_layers_from_dict(repo_name, repo_path, layers_dict):
+ layers = []
+ disabled_token = "disabled"
+ legacy_disabled_tokens = ['excluded', 'n', 'no', '0', 'false', 0]
+
+ for lname, prop in layers_dict.items():
+ if prop is None:
+ layers.append(RepoLayer(name=lname,
+ repo_path=Path(repo_path)))
+ elif isinstance(prop, str) and prop == disabled_token:
+ continue
+ elif isinstance(prop, (str, int)) and \
+ prop in legacy_disabled_tokens:
+ logging.warning('Use of deprecated value "%s" for repo '
+ '"%s", layer "%s". Replace with "disabled".',
+ prop, repo_name, lname)
+ continue
+ else:
+ raise NotImplementedError()
+ return layers
+

class RepoImpl(Repo):
"""
@@ -871,3 +885,16 @@ class MercurialRepo(RepoImpl):

def diff(self, commit1, commit2):
raise NotImplementedError("Unsupported diff for MercurialRepo")
+
+
+@dataclass
+class RepoLayer:
+ """
+ Standalone definition of a single bitbake layer.
+ """
+ name: str
+ repo_path: Path
+
+ @property
+ def path(self):
+ return self.repo_path / self.name
--
2.51.0

Felix Moessbauer

unread,
Jan 21, 2026, 9:52:27 AMJan 21
to kas-...@googlegroups.com, jan.k...@siemens.com, alexander...@siemens.com, Felix Moessbauer
The global $defs section is used to define schemas that are referenced
in the document to reduce the nesting depths. We now add support to
reference these path in the docs as well.

Signed-off-by: Felix Moessbauer <felix.mo...@siemens.com>
Signed-off-by: Jan Kiszka <jan.k...@siemens.com>
---
docs/_ext/sphinx_kas_schema.py | 12 +++++++++---
1 file changed, 9 insertions(+), 3 deletions(-)

diff --git a/docs/_ext/sphinx_kas_schema.py b/docs/_ext/sphinx_kas_schema.py
index 9400c034e..6415c3850 100644
--- a/docs/_ext/sphinx_kas_schema.py
+++ b/docs/_ext/sphinx_kas_schema.py

Felix Moessbauer

unread,
Jan 21, 2026, 9:52:28 AMJan 21
to kas-...@googlegroups.com, jan.k...@siemens.com, alexander...@siemens.com, Felix Moessbauer
By generalizing the test setup fixture of the test_layer tests, we make
it reusable with other kas files as well. This is a preparation to add
further layer-order tests.

Signed-off-by: Felix Moessbauer <felix.mo...@siemens.com>
Signed-off-by: Jan Kiszka <jan.k...@siemens.com>
---
tests/test_layers.py | 75 ++++++++++++++++++++++----------------------
1 file changed, 38 insertions(+), 37 deletions(-)

diff --git a/tests/test_layers.py b/tests/test_layers.py
index 311e78e40..28fe88168 100644
--- a/tests/test_layers.py
+++ b/tests/test_layers.py
+ assert layers[1] == '/kas1/meta-bar'
+ assert layers[2] == '/kas1/meta-foo'
+ # repos are sorted alphabetically (aa-kas from kas4 is last)
+ assert layers[-1] == '/aa-kas/meta'
--
2.51.0

Felix Moessbauer

unread,
Jan 21, 2026, 9:52:29 AMJan 21
to kas-...@googlegroups.com, jan.k...@siemens.com, alexander...@siemens.com, Felix Moessbauer
We extend the repo layers keys by adding the "prio" tuple. This will be
used to control the precedence (order) of the layers in BBLAYERS.

Signed-off-by: Felix Moessbauer <felix.mo...@siemens.com>
Signed-off-by: Jan Kiszka <jan.k...@siemens.com>
---
docs/format-changelog.rst | 9 +++++++++
docs/userguide/project-configuration.rst | 5 ++++-
kas/schema-kas.json | 22 ++++++++++++++++++++--
3 files changed, 33 insertions(+), 3 deletions(-)

diff --git a/docs/format-changelog.rst b/docs/format-changelog.rst
index e5d94d3b7..8aea3a525 100644
--- a/docs/format-changelog.rst
+++ b/docs/format-changelog.rst
@@ -201,3 +201,12 @@ Added
~~~~~

- The repo key ``branch`` can now be overridden, including to a null-value.
+
+Version 21
+----------
+
+Added
+~~~~~
+
+- The repo layers ``prio`` can be used to control the order in which the
+ layers are added to the ``BBLAYERS`` bitbake variable.
diff --git a/docs/userguide/project-configuration.rst b/docs/userguide/project-configuration.rst
index 5051e4c57..bbb107d70 100644

Felix Moessbauer

unread,
Jan 21, 2026, 9:52:29 AMJan 21
to kas-...@googlegroups.com, jan.k...@siemens.com, alexander...@siemens.com, Felix Moessbauer
As a preparation to allow downstream consumers evaluate layer
properties, we change the repo.layers interface to emit layer objects
instead of strings. By that, we also replace the open-coded layer
ordering implementation by a comparison operator provided by the
layer object. This later can be extended to implement more complex
comparisons.

Signed-off-by: Felix Moessbauer <felix.mo...@siemens.com>
Signed-off-by: Jan Kiszka <jan.k...@siemens.com>
---
kas/attestation.py | 2 +-
diff --git a/kas/repos.py b/kas/repos.py
index 14591b916..8b22d796b 100644
--- a/kas/repos.py
+++ b/kas/repos.py
@@ -360,7 +356,8 @@ class Repo:
for lname, prop in layers_dict.items():
if prop is None:
layers.append(RepoLayer(name=lname,
- repo_path=Path(repo_path)))
+ repo_path=Path(repo_path),
+ repo_name=repo_name))
elif isinstance(prop, str) and prop == disabled_token:
continue
elif isinstance(prop, (str, int)) and \
@@ -893,8 +890,14 @@ class RepoLayer:
Standalone definition of a single bitbake layer.
"""
name: str
+ repo_name: str
repo_path: Path

@property
def path(self):
return self.repo_path / self.name
+
+ def __lt__(self, other):
+ if isinstance(other, RepoLayer):
+ return (self.repo_name, self.name) < (other.repo_name, other.name)
+ return NotImplemented
--
2.51.0

Felix Moessbauer

unread,
Jan 21, 2026, 9:52:29 AMJan 21
to kas-...@googlegroups.com, jan.k...@siemens.com, alexander...@siemens.com, Felix Moessbauer
While each bitbake layer can state its priority, there are still
situations where fine grained control over the order of the layers in
BBLAYERS is needed. Examples are .bbappend files which need to be
appended in the correct order. There have been many discussions on the
kas mailing list to give users control over the order, resulting in c9326cc1e
(making the order deterministic) and ecf5827b0 (giving control by the
repo and layer name). However, these solutions still do not solve the
problem of sorting layers within a repo and also ordering layers across
multiple repos.

To hopefully finally solve this, we provide a "prio" key which acts as
the first sorting criteria when adding the layers to BBLAYERS. By that,
we remain fully backward compatible and do not change the layer ordering
of existing kas files. This design also allows to sort layers across
repo boundaries and even have one repo provide two layers that are
separated by another layer from another repo.

Closes: #36
Signed-off-by: Felix Moessbauer <felix.mo...@siemens.com>
[Jan: negate priority in comparison for better code readability]
Signed-off-by: Jan Kiszka <jan.k...@siemens.com>
---
kas/repos.py | 19 +++++++++++++++++--
1 file changed, 17 insertions(+), 2 deletions(-)

diff --git a/kas/repos.py b/kas/repos.py
index 8b22d796b..c2f1c4248 100644
--- a/kas/repos.py
+++ b/kas/repos.py
@@ -33,6 +33,8 @@ from dataclasses import dataclass
from datetime import datetime
from urllib.parse import urlparse
from tempfile import TemporaryDirectory
+
+from kas.configschema import CONFIGSCHEMA
from .context import get_context
from .libkas import run_cmd_async, run_cmd
from .kasusererror import KasUserError
@@ -352,12 +354,15 @@ class Repo:
layers = []
disabled_token = "disabled"
legacy_disabled_tokens = ['excluded', 'n', 'no', '0', 'false', 0]
+ default_prio = \
+ CONFIGSCHEMA['$defs']['layerPrio']['properties']['prio']['default']

for lname, prop in layers_dict.items():
if prop is None:
layers.append(RepoLayer(name=lname,
repo_path=Path(repo_path),
- repo_name=repo_name))
+ repo_name=repo_name,
+ priority=default_prio))
elif isinstance(prop, str) and prop == disabled_token:
continue
elif isinstance(prop, (str, int)) and \
@@ -366,6 +371,12 @@ class Repo:
'"%s", layer "%s". Replace with "disabled".',
prop, repo_name, lname)
continue
+ elif isinstance(prop, dict):
+ layers.append(RepoLayer(name=lname,
+ repo_path=Path(repo_path),
+ repo_name=repo_name,
+ priority=prop.get('prio',
+ default_prio)))
else:
raise NotImplementedError()
return layers
@@ -890,6 +901,7 @@ class RepoLayer:
Standalone definition of a single bitbake layer.
"""
name: str
+ priority: int
repo_name: str
repo_path: Path

@@ -899,5 +911,8 @@ class RepoLayer:

def __lt__(self, other):
if isinstance(other, RepoLayer):
- return (self.repo_name, self.name) < (other.repo_name, other.name)
+ # The priority is negated because a higher priority means the
+ # element appears earlier in the list.
+ return (-self.priority, self.repo_name, self.name) < \
+ (-other.priority, other.repo_name, other.name)
return NotImplemented
--
2.51.0

Felix Moessbauer

unread,
Jan 21, 2026, 9:52:30 AMJan 21
to kas-...@googlegroups.com, jan.k...@siemens.com, alexander...@siemens.com, Felix Moessbauer
Signed-off-by: Felix Moessbauer <felix.mo...@siemens.com>
Signed-off-by: Jan Kiszka <jan.k...@siemens.com>
---
tests/test_layers.py | 17 +++++++++++++++++
tests/test_layers/test-layer-prio.yml | 24 ++++++++++++++++++++++++
2 files changed, 41 insertions(+)
create mode 100644 tests/test_layers/test-layer-prio.yml

diff --git a/tests/test_layers.py b/tests/test_layers.py
index 28fe88168..7b5888cca 100644
--- a/tests/test_layers.py
+++ b/tests/test_layers.py
@@ -83,3 +83,20 @@ def test_layers_order(dokas):
assert layers[2] == '/kas1/meta-foo'
# repos are sorted alphabetically (aa-kas from kas4 is last)
assert layers[-1] == '/aa-kas/meta'

Felix Moessbauer

unread,
Jan 21, 2026, 9:52:31 AMJan 21
to kas-...@googlegroups.com, jan.k...@siemens.com, alexander...@siemens.com
From: Jan Kiszka <jan.k...@siemens.com>

So far, we only have the link to our mailing list in the developer
section, but we would also like our regular users to find this.

Signed-off-by: Jan Kiszka <jan.k...@siemens.com>
---
docs/userguide/getting-started.rst | 12 ++++++++++++
1 file changed, 12 insertions(+)

diff --git a/docs/userguide/getting-started.rst b/docs/userguide/getting-started.rst
index fb20af281..787e5a26c 100644
--- a/docs/userguide/getting-started.rst
+++ b/docs/userguide/getting-started.rst
@@ -105,3 +105,15 @@ Use Cases
$ cd $PROJECT_DIR/meta-project
$ kas menu
$ kas build # optional, if not triggered via kas menu
+
+
+Community Support
+-----------------
+
+If you have questions on kas, found a bug or would need an additional feature,
+please post to our mailing list:
+`kas-...@googlegroups.com <mailto:kas-...@googlegroups.com>`_. You do not
+need to be subscribed to post but if you like to do so, this works either
+`via email <mailto:kas-devel...@googlegroups.com>`_ or via the
+`Google Groups page <https://groups.google.com/g/kas-devel>`_ after signing
+in.
--
2.51.0

Jan Kiszka

unread,
Jan 21, 2026, 11:38:56 AMJan 21
to Felix Moessbauer, kas-...@googlegroups.com, alexander...@siemens.com
On 21.01.26 15:52, Felix Moessbauer wrote:
> This series should finally solve the long standing issue
> https://github.com/siemens/kas/issues/36.
>
> While the implementation itself is quite trivial, some cleanup was
> needed to properly integrate it.
>
> Changes since v1:
>
> - remove duplicated check for legacy layer disabling: {no, ...}
> as reported by Alexander. The check was anyways redundant and
> probably a rebase artifact.
> - correctly handle legacy layer disable value int(0)

Can we split the changes to for legacy layer warnings off from patch 3?
I assume those only affect that patch, but they are not clearly
reflected there in the commit message and look more like preparation for
the prio property.

>
> Best regards,
> Felix Moessbauer
>
> Felix Moessbauer (8):
> docs(schema): extract allowed integer ranges
> refactor(repos): internally represent layers as objects
> refactor(repos): represent layers as objects
> refactor(tests): generalize layer tests
> docs(schema): add support for global defs section
> schema: add support for layer priorities
> feat(repos): add support to explicitly specify layer order
> add test for layer ordering by priority
>
> Jan Kiszka (1):
> docs: Add community support section to getting started guide

No need to include my patch here. I will drop yours from next for now
and only keep mine.

Heinisch, Alexander

unread,
Jan 21, 2026, 2:54:12 PMJan 21
to Kiszka, Jan, kas-...@googlegroups.com, MOESSBAUER, Felix
On Wed, 2026-01-21 at 17:38 +0100, Jan Kiszka wrote:
> On 21.01.26 15:52, Felix Moessbauer wrote:
> > This series should finally solve the long standing issue
> > https://github.com/siemens/kas/issues/36
> > .
> >
> > While the implementation itself is quite trivial, some cleanup was
> > needed to properly integrate it.
> >
> > Changes since v1:
> >
> > - remove duplicated check for legacy layer disabling: {no, ...}
> > as reported by Alexander. The check was anyways redundant and
> > probably a rebase artifact.
> > - correctly handle legacy layer disable value int(0)
>
> Can we split the changes to for legacy layer warnings off from patch
> 3?
> I assume those only affect that patch, but they are not clearly
> reflected there in the commit message and look more like preparation
> for
> the prio property.
>
> >
> > Best regards,
> > Felix Moessbauer
> >
> > Felix Moessbauer (8):

Are you missing "fix(spinx-schema): add defaults that evaluate to false
as well" from V1?

Tested patchset on top of it and this time it worked without the
warnings :-)

> > docs(schema): extract allowed integer ranges
> > refactor(repos): internally represent layers as objects
> > refactor(repos): represent layers as objects
> > refactor(tests): generalize layer tests
> > docs(schema): add support for global defs section
> > schema: add support for layer priorities
> > feat(repos): add support to explicitly specify layer order
> > add test for layer ordering by priority
> >
> > Jan Kiszka (1):
> > docs: Add community support section to getting started guide
>
> No need to include my patch here. I will drop yours from next for now
> and only keep mine.
>
> Jan

--
Alexander Heinisch
Siemens AG
http://www.siemens.com/

Jan Kiszka

unread,
Jan 22, 2026, 1:10:07 AMJan 22
to Heinisch, Alexander (FT RPD CED SES-AT), kas-...@googlegroups.com, Moessbauer, Felix (FT RPD CED OES-DE)
On 21.01.26 20:54, Heinisch, Alexander (FT RPD CED SES-AT) wrote:
> On Wed, 2026-01-21 at 17:38 +0100, Jan Kiszka wrote:
>> On 21.01.26 15:52, Felix Moessbauer wrote:
>>> This series should finally solve the long standing issue
>>> https://github.com/siemens/kas/issues/36
>>> .
>>>
>>> While the implementation itself is quite trivial, some cleanup was
>>> needed to properly integrate it.
>>>
>>> Changes since v1:
>>>
>>> - remove duplicated check for legacy layer disabling: {no, ...}
>>> as reported by Alexander. The check was anyways redundant and
>>> probably a rebase artifact.
>>> - correctly handle legacy layer disable value int(0)
>>
>> Can we split the changes to for legacy layer warnings off from patch
>> 3?
>> I assume those only affect that patch, but they are not clearly
>> reflected there in the commit message and look more like preparation
>> for
>> the prio property.
>>
>>>
>>> Best regards,
>>> Felix Moessbauer
>>>
>>> Felix Moessbauer (8):
>
> Are you missing "fix(spinx-schema): add defaults that evaluate to false
> as well" from V1?
>

This patch set was intended to be on top of master.

> Tested patchset on top of it and this time it worked without the
> warnings :-)

That's good to know. However, my comment is targeting the series
structure, not the final result.

MOESSBAUER, Felix

unread,
Jan 22, 2026, 2:12:40 AMJan 22
to Heinisch, Alexander, Kiszka, Jan, kas-...@googlegroups.com
On Wed, 2026-01-21 at 19:54 +0000, Heinisch, Alexander (FT RPD CED SES-
AT) wrote:
> On Wed, 2026-01-21 at 17:38 +0100, Jan Kiszka wrote:
> > On 21.01.26 15:52, Felix Moessbauer wrote:
> > > This series should finally solve the long standing issue
> > > https://github.com/siemens/kas/issues/36
> > > .
> > >
> > > While the implementation itself is quite trivial, some cleanup was
> > > needed to properly integrate it.
> > >
> > > Changes since v1:
> > >
> > > - remove duplicated check for legacy layer disabling: {no, ...}
> > > as reported by Alexander. The check was anyways redundant and
> > > probably a rebase artifact.
> > > - correctly handle legacy layer disable value int(0)
> >
> > Can we split the changes to for legacy layer warnings off from patch
> > 3?
> > I assume those only affect that patch, but they are not clearly
> > reflected there in the commit message and look more like preparation
> > for
> > the prio property.
> >
> > >
> > > Best regards,
> > > Felix Moessbauer
> > >
> > > Felix Moessbauer (8):
>
> Are you missing "fix(spinx-schema): add defaults that evaluate to false
> as well" from V1?


Indeed. I messed this up. Will send out a proper v3 with the split
requested by Jan.

>
> Tested patchset on top of it and this time it worked without the
> warnings :-)

Thanks. At least it worked :)

Felix

>
> > > docs(schema): extract allowed integer ranges
> > > refactor(repos): internally represent layers as objects
> > > refactor(repos): represent layers as objects
> > > refactor(tests): generalize layer tests
> > > docs(schema): add support for global defs section
> > > schema: add support for layer priorities
> > > feat(repos): add support to explicitly specify layer order
> > > add test for layer ordering by priority
> > >
> > > Jan Kiszka (1):
> > > docs: Add community support section to getting started guide
> >
> > No need to include my patch here. I will drop yours from next for now
> > and only keep mine.
> >
> > Jan
>
> --
> Alexander Heinisch
> Siemens AG
> http://www.siemens.com/

Felix Moessbauer

unread,
Jan 22, 2026, 3:59:29 AMJan 22
to kas-...@googlegroups.com, jan.k...@siemens.com, alexander...@siemens.com, Felix Moessbauer
This series should finally solve the long standing issue
https://github.com/siemens/kas/issues/36.

While the implementation itself is quite trivial, some cleanup was
needed to properly integrate it.

Changes since v2:

- carefully split layer creation refactoring into multiple patches
- apply Jan's recommendation regarding layer order computation
(negate priority)
- no functional changes compared to v1

Changes since v1:

- remove duplicated check for legacy layer disabling: {no, ...}
as reported by Alexander. The check was anyways redundant and
probably a rebase artifact.
- correctly handle legacy layer disable value int(0)

Best regards,
Felix Moessbauer

Felix Moessbauer (11):
fix(spinx-schema): add defaults that evaluate to false as well
docs(schema): extract allowed integer ranges
refactor: move layer creation to function
refactor warning on usage of legacy layer disable tokens
refactor(repos): internally represent layers as objects
refactor(repos): represent layers as objects
refactor(tests): generalize layer tests
docs(schema): add support for global defs section
schema: add support for layer priorities
feat(repos): add support to explicitly specify layer order
add test for layer ordering by priority

docs/_ext/sphinx_kas_schema.py | 27 +++++--
docs/format-changelog.rst | 9 +++
docs/userguide/project-configuration.rst | 5 +-
kas/attestation.py | 2 +-
kas/libcmds.py | 10 +--
kas/repos.py | 83 ++++++++++++++++-----
kas/schema-kas.json | 22 +++++-
tests/test_layers.py | 92 ++++++++++++++----------
tests/test_layers/test-layer-prio.yml | 24 +++++++
9 files changed, 203 insertions(+), 71 deletions(-)

Felix Moessbauer

unread,
Jan 22, 2026, 3:59:30 AMJan 22
to kas-...@googlegroups.com, jan.k...@siemens.com, alexander...@siemens.com, Felix Moessbauer
Our sphinx plugin that parses the jsonschema and extracts descriptions
from the nodes incorrectly handled default values that evalute to false
in Python (e.g. false, 0). We fix this by explicitly checking if the
value is available or not. By that, we get more defaults shown in the
documentation.

Fixes: fcbdb1617 ("docs: extract default values from kas schema")
Fixes: d558eb4bf ("docs: auto add enum values of schema node")
Signed-off-by: Felix Moessbauer <felix.mo...@siemens.com>
---
docs/_ext/sphinx_kas_schema.py | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/docs/_ext/sphinx_kas_schema.py b/docs/_ext/sphinx_kas_schema.py
index 40e35c0fb..b09744502 100644
--- a/docs/_ext/sphinx_kas_schema.py
+++ b/docs/_ext/sphinx_kas_schema.py
@@ -85,18 +85,18 @@ class KasSchemaDescRole(SphinxRole):
processed, msgs = self.inliner.parse(desc, self.lineno, memo, parent)
parent += processed
messages += msgs
- if default:
+ if default is not None:
def_pg = nodes.paragraph()
def_pg += nodes.strong(text='Default: ')
- def_pg += nodes.literal(text=default)
+ def_pg += nodes.literal(text=str(default))
parent += def_pg
- if allowed_values:
+ if allowed_values is not None:
av_pg = nodes.paragraph()
av_pg += nodes.strong(text='Supported values: ')
for i in range(len(allowed_values)):
if i != 0:
av_pg += nodes.Text(', ')
- av_pg += nodes.literal(text=allowed_values[i])
+ av_pg += nodes.literal(text=str(allowed_values[i]))
parent += av_pg

Felix Moessbauer

unread,
Jan 22, 2026, 3:59:31 AMJan 22
to kas-...@googlegroups.com, jan.k...@siemens.com, alexander...@siemens.com, Felix Moessbauer
As a preparation to extend the layer definition schema node, we make the
detection logic for legacy tokens (e.g. "0", 0, "no", ...) more precise
(type specific).

Signed-off-by: Felix Moessbauer <felix.mo...@siemens.com>
---
kas/repos.py | 20 +++++++++++++-------
1 file changed, 13 insertions(+), 7 deletions(-)

diff --git a/kas/repos.py b/kas/repos.py
index 4e5118e6c..9ea922328 100644
--- a/kas/repos.py
+++ b/kas/repos.py
@@ -352,17 +352,23 @@ class Repo:

@staticmethod
def _create_layers_from_dict(repo_name, repo_path, layers_dict):
- # only bool(false) will be a valid value to disable a layer
+ layers = []
+ disabled_token = "disabled"
+ legacy_disabled_tokens = ['excluded', 'n', 'no', '0', 'false', 0]
+
for lname, prop in layers_dict.items():
- if not (prop is None or prop == "disabled"):
+ if prop is None:
+ layers.append(lname)
+ elif isinstance(prop, str) and prop == disabled_token:
+ continue
+ elif isinstance(prop, (str, int)) and \
+ prop in legacy_disabled_tokens:
logging.warning('Use of deprecated value "%s" for repo '
'"%s", layer "%s". Replace with "disabled".',
prop, repo_name, lname)
-
- layers = list(filter(lambda x, laydict=layers_dict:
- str(laydict[x]).lower() not in
- ['disabled', 'excluded', 'n', 'no', '0', 'false'],
- layers_dict))
+ continue
+ else:
+ raise NotImplementedError()
return layers


--
2.51.0

Felix Moessbauer

unread,
Jan 22, 2026, 3:59:31 AMJan 22
to kas-...@googlegroups.com, jan.k...@siemens.com, alexander...@siemens.com, Felix Moessbauer
For schema fields with an minimum and maximum value (integers only), we
now extract these and add them to the documentation as well. This helps
to keep the spec with the docs in sync.

Signed-off-by: Felix Moessbauer <felix.mo...@siemens.com>
---
docs/_ext/sphinx_kas_schema.py | 7 +++++++
1 file changed, 7 insertions(+)

diff --git a/docs/_ext/sphinx_kas_schema.py b/docs/_ext/sphinx_kas_schema.py
index b09744502..9400c034e 100644
--- a/docs/_ext/sphinx_kas_schema.py
+++ b/docs/_ext/sphinx_kas_schema.py
@@ -77,6 +77,8 @@ class KasSchemaDescRole(SphinxRole):
return [], messages
default = node.get('default', None)
allowed_values = node.get('enum', None)
+ minval = node.get('minimum', None)
+ maxval = node.get('maximum', None)

memo = Struct(document=self.inliner.document,
reporter=self.inliner.reporter,
@@ -98,6 +100,11 @@ class KasSchemaDescRole(SphinxRole):
av_pg += nodes.Text(', ')
av_pg += nodes.literal(text=str(allowed_values[i]))
parent += av_pg
+ if minval is not None and maxval is not None:
+ range_pg = nodes.paragraph()
+ range_pg += nodes.strong(text='Range: ')
+ range_pg += nodes.literal(text=f'[{minval}, {maxval}]')
+ parent += range_pg

Felix Moessbauer

unread,
Jan 22, 2026, 3:59:32 AMJan 22
to kas-...@googlegroups.com, jan.k...@siemens.com, alexander...@siemens.com, Felix Moessbauer
Previously the layers where internally just represented as path strings.
As a preparation to attach more data to a layer (e.g. a priority), we
change the internal representation of each layer to an object. The
external interfaces are not affected by this change.

We further internally handle the layer path now with pathlib.Path to
avoid manual sanitizing and removing of superflous path elements (like
a trailing /.).

Signed-off-by: Felix Moessbauer <felix.mo...@siemens.com>
---
kas/repos.py | 21 ++++++++++++++++++---
1 file changed, 18 insertions(+), 3 deletions(-)

diff --git a/kas/repos.py b/kas/repos.py
index 9ea922328..14591b916 100644
--- a/kas/repos.py
+++ b/kas/repos.py
@@ -28,6 +28,8 @@ import os
import linecache
import logging
import shutil
+from pathlib import Path
+from dataclasses import dataclass
from datetime import datetime
from urllib.parse import urlparse
from tempfile import TemporaryDirectory
@@ -115,8 +117,7 @@ class Repo:

@property
def layers(self):
- return [os.path.join(self.path, layer).rstrip(os.sep + '.')
- for layer in self._layers]
+ return [str(layer.path) for layer in self._layers]

@property
def qualified_name(self):
@@ -358,7 +359,8 @@ class Repo:

for lname, prop in layers_dict.items():
if prop is None:
- layers.append(lname)
+ layers.append(RepoLayer(name=lname,
+ repo_path=Path(repo_path)))
elif isinstance(prop, str) and prop == disabled_token:
continue
elif isinstance(prop, (str, int)) and \
@@ -883,3 +885,16 @@ class MercurialRepo(RepoImpl):

Felix Moessbauer

unread,
Jan 22, 2026, 3:59:32 AMJan 22
to kas-...@googlegroups.com, jan.k...@siemens.com, alexander...@siemens.com, Felix Moessbauer
As a preparation to make the layers objects, we move the creation of the
layers from the dictionary to a function to reduce the size of the Repo
factory. While doing so, we also move the creation a further down to
have access to the repo path which we later will need.

Signed-off-by: Felix Moessbauer <felix.mo...@siemens.com>
---
kas/repos.py | 30 ++++++++++++++++++------------
1 file changed, 18 insertions(+), 12 deletions(-)

diff --git a/kas/repos.py b/kas/repos.py
index a8ea458f9..4e5118e6c 100644
--- a/kas/repos.py
+++ b/kas/repos.py
@@ -236,18 +236,6 @@ class Repo:
Returns a Repo instance depending on parameters.
This factory function is referential transparent.
"""
- layers_dict = repo_config.get('layers', {'': None})
- # only bool(false) will be a valid value to disable a layer
- for lname, prop in layers_dict.items():
- if not (prop is None or prop == "disabled"):
- logging.warning('Use of deprecated value "%s" for repo '
- '"%s", layer "%s". Replace with "disabled".',
- prop, name, lname)
-
- layers = list(filter(lambda x, laydict=layers_dict:
- str(laydict[x]).lower() not in
- ['disabled', 'excluded', 'n', 'no', '0', 'false'],
- layers_dict))
default_patch_repo = repo_defaults.get('patches', {}).get('repo', None)
patches_dict = repo_config.get('patches', {})
patches = []
@@ -319,6 +307,9 @@ class Repo:
# Relative pathes are assumed to start from work_dir
path = os.path.join(get_context().kas_work_dir, path)

+ layers_dict = repo_config.get('layers', {'': None})
+ layers = Repo._create_layers_from_dict(name, path, layers_dict)
+
if url is None:
# No version control operation on repository
disable_operations = True
@@ -359,6 +350,21 @@ class Repo:

return path if fallback else None

+ @staticmethod
+ def _create_layers_from_dict(repo_name, repo_path, layers_dict):
+ # only bool(false) will be a valid value to disable a layer
+ for lname, prop in layers_dict.items():
+ if not (prop is None or prop == "disabled"):
+ logging.warning('Use of deprecated value "%s" for repo '
+ '"%s", layer "%s". Replace with "disabled".',
+ prop, repo_name, lname)
+
+ layers = list(filter(lambda x, laydict=layers_dict:
+ str(laydict[x]).lower() not in
+ ['disabled', 'excluded', 'n', 'no', '0', 'false'],
+ layers_dict))
+ return layers
+

class RepoImpl(Repo):
"""
--
2.51.0

Felix Moessbauer

unread,
Jan 22, 2026, 3:59:33 AMJan 22
to kas-...@googlegroups.com, jan.k...@siemens.com, alexander...@siemens.com, Felix Moessbauer
By generalizing the test setup fixture of the test_layer tests, we make
it reusable with other kas files as well. This is a preparation to add
further layer-order tests.

Signed-off-by: Felix Moessbauer <felix.mo...@siemens.com>
---
tests/test_layers.py | 75 ++++++++++++++++++++++----------------------
1 file changed, 38 insertions(+), 37 deletions(-)

diff --git a/tests/test_layers.py b/tests/test_layers.py
index 311e78e40..28fe88168 100644
--- a/tests/test_layers.py
+++ b/tests/test_layers.py
+ assert layers[1] == '/kas1/meta-bar'
+ assert layers[2] == '/kas1/meta-foo'
+ # repos are sorted alphabetically (aa-kas from kas4 is last)
+ assert layers[-1] == '/aa-kas/meta'
--
2.51.0

Felix Moessbauer

unread,
Jan 22, 2026, 3:59:33 AMJan 22
to kas-...@googlegroups.com, jan.k...@siemens.com, alexander...@siemens.com, Felix Moessbauer
As a preparation to allow downstream consumers evaluate layer
properties, we change the repo.layers interface to emit layer objects
instead of strings. By that, we also replace the open-coded layer
ordering implementation by a comparison operator provided by the
layer object. This later can be extended to implement more complex
comparisons.

Signed-off-by: Felix Moessbauer <felix.mo...@siemens.com>
---
kas/attestation.py | 2 +-
diff --git a/kas/repos.py b/kas/repos.py
index 14591b916..8b22d796b 100644
--- a/kas/repos.py
+++ b/kas/repos.py
for lname, prop in layers_dict.items():
if prop is None:
layers.append(RepoLayer(name=lname,
- repo_path=Path(repo_path)))
+ repo_path=Path(repo_path),
+ repo_name=repo_name))
elif isinstance(prop, str) and prop == disabled_token:
continue
elif isinstance(prop, (str, int)) and \
@@ -893,8 +890,14 @@ class RepoLayer:
Standalone definition of a single bitbake layer.
"""
name: str
+ repo_name: str
repo_path: Path

@property
def path(self):
return self.repo_path / self.name
+
+ def __lt__(self, other):
+ if isinstance(other, RepoLayer):
+ return (self.repo_name, self.name) < (other.repo_name, other.name)
+ return NotImplemented
--
2.51.0

Felix Moessbauer

unread,
Jan 22, 2026, 3:59:34 AMJan 22
to kas-...@googlegroups.com, jan.k...@siemens.com, alexander...@siemens.com, Felix Moessbauer
The global $defs section is used to define schemas that are referenced
in the document to reduce the nesting depths. We now add support to
reference these path in the docs as well.

Signed-off-by: Felix Moessbauer <felix.mo...@siemens.com>
---
docs/_ext/sphinx_kas_schema.py | 12 +++++++++---
1 file changed, 9 insertions(+), 3 deletions(-)

diff --git a/docs/_ext/sphinx_kas_schema.py b/docs/_ext/sphinx_kas_schema.py
index 9400c034e..6415c3850 100644
--- a/docs/_ext/sphinx_kas_schema.py
+++ b/docs/_ext/sphinx_kas_schema.py

Felix Moessbauer

unread,
Jan 22, 2026, 3:59:34 AMJan 22
to kas-...@googlegroups.com, jan.k...@siemens.com, alexander...@siemens.com, Felix Moessbauer
We extend the repo layers keys by adding the "prio" tuple. This will be
used to control the precedence (order) of the layers in BBLAYERS.

Signed-off-by: Felix Moessbauer <felix.mo...@siemens.com>
---

Felix Moessbauer

unread,
Jan 22, 2026, 3:59:36 AMJan 22
to kas-...@googlegroups.com, jan.k...@siemens.com, alexander...@siemens.com, Felix Moessbauer
While each bitbake layer can state its priority, there are still
situations where fine grained control over the order of the layers in
BBLAYERS is needed. Examples are .bbappend files which need to be
appended in the correct order. There have been many discussions on the
kas mailing list to give users control over the order, resulting in c9326cc1e
(making the order deterministic) and ecf5827b0 (giving control by the
repo and layer name). However, these solutions still do not solve the
problem of sorting layers within a repo and also ordering layers across
multiple repos.

To hopefully finally solve this, we provide a "prio" key which acts as
the first sorting criteria when adding the layers to BBLAYERS. By that,
we remain fully backward compatible and do not change the layer ordering
of existing kas files. This design also allows to sort layers across
repo boundaries and even have one repo provide two layers that are
separated by another layer from another repo.

Closes: #36
Signed-off-by: Felix Moessbauer <felix.mo...@siemens.com>
---
kas/repos.py | 19 +++++++++++++++++--
1 file changed, 17 insertions(+), 2 deletions(-)

diff --git a/kas/repos.py b/kas/repos.py
index 8b22d796b..c2f1c4248 100644
--- a/kas/repos.py
+++ b/kas/repos.py
@@ -33,6 +33,8 @@ from dataclasses import dataclass
from datetime import datetime
from urllib.parse import urlparse
from tempfile import TemporaryDirectory
+
+from kas.configschema import CONFIGSCHEMA
from .context import get_context
from .libkas import run_cmd_async, run_cmd
from .kasusererror import KasUserError
@@ -352,12 +354,15 @@ class Repo:
layers = []
disabled_token = "disabled"
legacy_disabled_tokens = ['excluded', 'n', 'no', '0', 'false', 0]
+ default_prio = \
+ CONFIGSCHEMA['$defs']['layerPrio']['properties']['prio']['default']

for lname, prop in layers_dict.items():
if prop is None:
layers.append(RepoLayer(name=lname,
repo_path=Path(repo_path),
- repo_name=repo_name))
+ repo_name=repo_name,
+ priority=default_prio))
elif isinstance(prop, str) and prop == disabled_token:
continue
elif isinstance(prop, (str, int)) and \
@@ -366,6 +371,12 @@ class Repo:
'"%s", layer "%s". Replace with "disabled".',
prop, repo_name, lname)
continue
+ elif isinstance(prop, dict):
+ layers.append(RepoLayer(name=lname,
+ repo_path=Path(repo_path),
+ repo_name=repo_name,
+ priority=prop.get('prio',
+ default_prio)))
else:
raise NotImplementedError()
return layers
@@ -890,6 +901,7 @@ class RepoLayer:
Standalone definition of a single bitbake layer.
"""
name: str

Felix Moessbauer

unread,
Jan 22, 2026, 3:59:36 AMJan 22
to kas-...@googlegroups.com, jan.k...@siemens.com, alexander...@siemens.com, Felix Moessbauer
Signed-off-by: Felix Moessbauer <felix.mo...@siemens.com>
Signed-off-by: Jan Kiszka <jan.k...@siemens.com>
---
tests/test_layers.py | 17 +++++++++++++++++
tests/test_layers/test-layer-prio.yml | 24 ++++++++++++++++++++++++
2 files changed, 41 insertions(+)
create mode 100644 tests/test_layers/test-layer-prio.yml

diff --git a/tests/test_layers.py b/tests/test_layers.py
index 28fe88168..7b5888cca 100644
--- a/tests/test_layers.py
+++ b/tests/test_layers.py
@@ -83,3 +83,20 @@ def test_layers_order(dokas):
assert layers[2] == '/kas1/meta-foo'
# repos are sorted alphabetically (aa-kas from kas4 is last)
assert layers[-1] == '/aa-kas/meta'

Jan Kiszka

unread,
Jan 23, 2026, 2:28:34 AMJan 23
to Felix Moessbauer, kas-...@googlegroups.com, alexander...@siemens.com
Thanks, applied again.
Reply all
Reply to author
Forward
0 new messages