[PATCH 0/2] global no-color options and make colorlog mandatory

33 views
Skip to first unread message

Tamino Larisch

unread,
Jul 21, 2026, 7:24:04 AM (13 days ago) Jul 21
to kas-...@googlegroups.com, Tamino Larisch
Hi everyone,

This patch series combines the work and ideas from two previous discussions on
this mailing list [1] [2].

The first patch from Tue Duong makes `colorlog` mandatory [1]. As Jan
previously noted, this change would remove the only existing method for
achieving non-colored outputs. To address this, the second patch implements
new `no_color` options, an idea originally proposed by Jörg Sommer [2].

Best regards
Tamino

[1] https://groups.google.com/g/kas-devel/c/OK_qvVyjYsI
[2] https://groups.google.com/g/kas-devel/c/9u8s0lyWI50

Tamino Larisch (1):
feat: add global no-color output options

Tue Duong (1):
kas: add colorlog as a mandatory dependency

docs/userguide/environment-variables.inc | 3 ++
kas-container | 2 +-
kas/context.py | 8 ++++-
kas/kas.py | 40 ++++++++++++------------
kas/plugins/diff.py | 12 +++----
kas/plugins/lock.py | 5 +--
pyproject.toml | 1 +
7 files changed, 39 insertions(+), 32 deletions(-)

--
2.39.5

Tamino Larisch

unread,
Jul 21, 2026, 7:24:11 AM (13 days ago) Jul 21
to kas-...@googlegroups.com, Tue Duong
From: Tue Duong <tuedu...@gmail.com>

The application natively supports colorized log output, but `colorlog`
was omitted from `pyproject.toml`, making it an implicit optional
feature. This caused standard pip/pipx installations to lack colored
logs unless the package was already installed globally.

Since `colorlog` is already standard in the official containers, drop
its optional status and make it a core requirement. This ensures a
consistent debugging experience across all installations.

Signed-off-by: Tue Duong <tuedu...@gmail.com>
---
kas/kas.py | 9 ++-------
pyproject.toml | 1 +
2 files changed, 3 insertions(+), 7 deletions(-)

diff --git a/kas/kas.py b/kas/kas.py
index b87b404..c2b65a4 100644
--- a/kas/kas.py
+++ b/kas/kas.py
@@ -32,17 +32,12 @@ import asyncio
import distro
import traceback
import logging
+import colorlog
import signal
import sys
import os
from .kasusererror import KasUserError, CommandExecError

-try:
- import colorlog
- HAVE_COLORLOG = True
-except ImportError:
- HAVE_COLORLOG = False
-
from . import __version__, __file_version__, __compatible_file_version__
from . import plugins

@@ -60,7 +55,7 @@ def create_logger():
set_global_loglevel(DEFAULT_LOG_LEVEL.upper())
format_str = '%(asctime)s - %(levelname)-8s - %(message)s'
date_format = '%Y-%m-%d %H:%M:%S'
- if HAVE_COLORLOG and os.isatty(2):
+ if os.isatty(2):
cformat = '%(log_color)s' + format_str
colors = {'DEBUG': 'reset',
'INFO': 'reset',
diff --git a/pyproject.toml b/pyproject.toml
index d4ca1e7..6e6b181 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -50,6 +50,7 @@ classifiers = [
]
dynamic = [ "version" ]
dependencies = [
+ "colorlog >=6,<7",
"distro>=1,<2",
"gitpython>=3.1,<4",
"jsonschema>=3.2,<5",
--
2.39.5

Tamino Larisch

unread,
Jul 21, 2026, 7:24:15 AM (13 days ago) Jul 21
to kas-...@googlegroups.com, Tamino Larisch
This commit introduces a global --no-color command-line option,
extending its functionality beyond the diff plugin. It also adds
support for the NO_COLOR environment variable. Both enable users to
disable colored output across all plugins without requiring colorlog
to be uninstalled. Additionally, this change provides control over the
output color for the lock plugin, which was not previously possible.

For consistency, the implementation aligns with colorlog's behavior of
checking for the mere presence of the NO_COLOR environment variable,
rather than adhering to no-color.org's recommendation of checking for a
non-empty string. Overriding this would require colorlog version 6.6
or higher.

When neither the command-line argument nor the environment variable
disables color, kas will independently check isatty for both stdout and
stderr to determine if color should be used.

Since colorlog is not an optional dependency anymore, use it's no_color
option instead of manually creating a different formatter when no_color
is enabled.

Signed-off-by: Tamino Larisch <tamino....@siemens.com>
---
docs/userguide/environment-variables.inc | 3 +++
kas-container | 2 +-
kas/context.py | 8 +++++-
kas/kas.py | 33 ++++++++++++++----------
kas/plugins/diff.py | 12 +++------
kas/plugins/lock.py | 5 ++--
6 files changed, 37 insertions(+), 26 deletions(-)

diff --git a/docs/userguide/environment-variables.inc b/docs/userguide/environment-variables.inc
index 42ede08..c9dad43 100644
--- a/docs/userguide/environment-variables.inc
+++ b/docs/userguide/environment-variables.inc
@@ -245,6 +245,9 @@ overwritten using the ``env`` section of the config file.
| (C,K) | and install buildtools. If not set, kas will use |
| | ``KAS_BUILD_DIR/buildtools`` as the default path.|
+--------------------------+--------------------------------------------------+
+| NO_COLOR (C,K,E) | Prevents the addition of ANSI color to all output|
+| | when this variable is present. |
++--------------------------+--------------------------------------------------+

.. |aws_cred| replace:: ``AWS_ROLE_ARN``
``AWS_SHARED_CREDENTIALS_FILE``
diff --git a/kas-container b/kas-container
index d00dc93..aa1839d 100755
--- a/kas-container
+++ b/kas-container
@@ -770,7 +770,7 @@ for var in TERM KAS_DISTRO KAS_MACHINE KAS_TARGET KAS_TASK KAS_CLONE_DEPTH \
KAS_PREMIRRORS DISTRO_APT_PREMIRRORS BB_NUMBER_THREADS PARALLEL_MAKE \
GIT_CREDENTIAL_USEHTTPPATH \
BB_HASHSERVE BB_HASHSERVE_UPSTREAM \
- TZ; do
+ NO_COLOR TZ; do
if [ -n "$(eval echo \$${var})" ]; then
set -- "$@" -e "${var}=$(eval echo \"\$${var}\")"
fi
diff --git a/kas/context.py b/kas/context.py
index 41bd761..465518a 100644
--- a/kas/context.py
+++ b/kas/context.py
@@ -91,6 +91,7 @@ class Context:
if not clone_depth.isdigit():
raise KasUserError('KAS_CLONE_DEPTH must be a number')
self.repo_clone_depth = max(int(clone_depth), 0)
+ self.args = args
self.setup_initial_environ()
self.check_container_call()
# Register the paths that kas created and exclusively owns
@@ -99,7 +100,6 @@ class Context:
self.managed_paths.add(self.__kas_build_dir)
self.keyhandler = {}
self.config = None
- self.args = args
self.unpinned_repo_warnings = True

def setup_initial_environ(self):
@@ -130,6 +130,8 @@ class Context:
val = os.environ.get(key, None)
if val:
self.environ[key] = val
+ if self.args.no_color:
+ self.environ['NO_COLOR'] = '1'

# make remote containers environment available in kas
if self.managed_env == ManagedEnvironment.VSCODE_REMOTE_CONTAINERS:
@@ -190,6 +192,10 @@ class Context:
def update(self):
return getattr(self.args, 'update', None)

+ @property
+ def no_color(self):
+ return getattr(self.args, 'no_color', False)
+
@property
def managed_env(self):
return self._get_managed_env()
diff --git a/kas/kas.py b/kas/kas.py
index c2b65a4..1b40076 100644
--- a/kas/kas.py
+++ b/kas/kas.py
@@ -47,7 +47,7 @@ __copyright__ = 'Copyright (c) Siemens AG, 2017-2018'
DEFAULT_LOG_LEVEL = 'info'


-def create_logger():
+def create_logger(no_color=False):
"""
Setup the logging environment
"""
@@ -55,17 +55,15 @@ def create_logger():
set_global_loglevel(DEFAULT_LOG_LEVEL.upper())
format_str = '%(asctime)s - %(levelname)-8s - %(message)s'
date_format = '%Y-%m-%d %H:%M:%S'
- if os.isatty(2):
- cformat = '%(log_color)s' + format_str
- colors = {'DEBUG': 'reset',
- 'INFO': 'reset',
- 'WARNING': 'bold_yellow',
- 'ERROR': 'bold_red',
- 'CRITICAL': 'bold_red'}
- formatter = colorlog.ColoredFormatter(cformat, date_format,
- log_colors=colors)
- else:
- formatter = logging.Formatter(format_str, date_format)
+ cformat = '%(log_color)s' + format_str
+ colors = {'DEBUG': 'reset',
+ 'INFO': 'reset',
+ 'WARNING': 'bold_yellow',
+ 'ERROR': 'bold_red',
+ 'CRITICAL': 'bold_red'}
+ formatter = colorlog.ColoredFormatter(cformat, date_format,
+ log_colors=colors,
+ no_color=no_color)
stream_handler = logging.StreamHandler()
stream_handler.setFormatter(formatter)
log.addHandler(stream_handler)
@@ -158,6 +156,10 @@ def kas_get_argparser():
default=f'{DEFAULT_LOG_LEVEL}',
help=f'Set log level (default: {DEFAULT_LOG_LEVEL})')

+ parser.add_argument('--no-color',
+ action='store_true',
+ help='Disable colors for all outputs')
+
subparser = parser.add_subparsers(dest='cmd')

for plugin in plugins.all():
@@ -191,11 +193,14 @@ def kas(argv):
"""
The actual main entry point of kas.
"""
- create_logger()
-
parser = kas_get_argparser()
args = parser.parse_args(argv)

+ create_logger(args.no_color or not sys.stderr.isatty())
+ args.no_color = args.no_color \
+ or "NO_COLOR" in os.environ \
+ or not sys.stdout.isatty()
+
if args.log_level:
set_global_loglevel(args.log_level.upper())

diff --git a/kas/plugins/diff.py b/kas/plugins/diff.py
index aad48bc..ad25743 100644
--- a/kas/plugins/diff.py
+++ b/kas/plugins/diff.py
@@ -37,7 +37,7 @@

import json
import difflib
-from kas.context import create_global_context
+from kas.context import create_global_context, get_context
from kas.config import Config
from kas.libcmds import Macro
from kas.libkas import setup_parser_common_args
@@ -72,9 +72,6 @@ class Diff:
action='store_true',
help='Use git oneline output for differing '
'commits.')
- parser.add_argument('--no-color',
- action='store_true',
- help='Disable colored highlighting of diffs.')
parser.add_argument('--commit-only',
action='store_true',
help='This will not display the differences in '
@@ -119,7 +116,7 @@ class Diff:

@staticmethod
def formatting_diff_output(oldfile, newfile, diff_output, oneline,
- no_color, commit_only, content_only):
+ commit_only, content_only):
"""
Format the diff output.
"""
@@ -147,7 +144,7 @@ class Diff:
else:
print(f"{' ' * 8}{line}", end='')

- if no_color:
+ if get_context().no_color:
COLORS_OLD = ''
COLORS_NEW = ''
COLORS_COMMIT = ''
@@ -268,8 +265,7 @@ class Diff:
else:
Diff.formatting_diff_output(args.config1, args.config2,
diff_output, args.oneline,
- args.no_color, args.commit_only,
- args.content_only)
+ args.commit_only, args.content_only)


__KAS_PLUGINS__ = [Diff]
diff --git a/kas/plugins/lock.py b/kas/plugins/lock.py
index 2d947c8..be76d2a 100644
--- a/kas/plugins/lock.py
+++ b/kas/plugins/lock.py
@@ -76,7 +76,7 @@
import logging
import os
from dataclasses import dataclass
-from kas.context import create_global_context
+from kas.context import create_global_context, get_context
from kas.config import Config
from kas.includehandler import ConfigFile
from kas.libcmds import Macro
@@ -118,8 +118,9 @@ class Lock:
except RepoRefError as e:
logging.warning(e)
return
+ no_color = get_context().no_color
Diff.formatting_diff_output(
- None, None, {'vcs': diff}, True, False, True, False)
+ None, None, {'vcs': diff}, True, no_color, True, False)

def _update_lockfile(self, lockfile, repos_to_lock, update_only, args):
"""
--
2.39.5

Jan Kiszka

unread,
Jul 21, 2026, 8:21:24 AM (13 days ago) Jul 21
to Tamino Larisch, kas-...@googlegroups.com
So NO_COLOR=0 will do that as well? You should be a bit more specific
what values are expected for the variable.
Isn't that redundant, or what is the reason for translating --no-color
here into the env here? Should be documented then.
Jan

--
Siemens AG, Foundational Technologies
Linux Expert Center

Larisch, Tamino

unread,
Jul 21, 2026, 9:17:51 AM (13 days ago) Jul 21
to Kiszka, Jan, kas-...@googlegroups.com

Yes. The standard states: "Command-line software which adds ANSI color
to its output by default should check for a NO_COLOR environment
variable that, when present and not an empty string (regardless of its
value), prevents the addition of ANSI color." [1]

Currently, `colorlog` checks for the mere presence of this variable
(including an empty string). This patch will align kas's behavior with
that, ensuring consistency. I will add the clarification "(regardless
of its value)".

[1] https://web.archive.org/web/20260616201813/https://no-color.org/
>
> > ++--------------------------+--------------------------------------
> > ------------+

To achieve no colored output for every external tool `kas` executes
that also follows the standard (e.g. bitbake). I will add a comment to
the code and the commit message explaining this.

Tamino

--
Tamino Larisch
Siemens AG
www.siemens.com

Tamino Larisch

unread,
Jul 21, 2026, 9:21:14 AM (13 days ago) Jul 21
to kas-...@googlegroups.com, Tamino Larisch
This commit introduces a global --no-color command-line option,
extending its functionality beyond the diff plugin. It also adds
support for the NO_COLOR environment variable. Both enable users to
disable colored output across all plugins without requiring colorlog
to be uninstalled. This change provides control over the output color
for the lock plugin, which was not previously possible.

Set the NO_COLOR environment variable when --no-color is set, to achieve
no colored output for every external tool `kas` executes that also
follows the standard (e.g. bitbake).

--
2.39.5

Tamino Larisch

unread,
Jul 21, 2026, 9:24:01 AM (13 days ago) Jul 21
to kas-...@googlegroups.com, Tamino Larisch
This commit introduces a global --no-color command-line option,
extending its functionality beyond the diff plugin. It also adds
support for the NO_COLOR environment variable. Both enable users to
disable colored output across all plugins without requiring colorlog
to be uninstalled. This change provides control over the output color
for the lock plugin, which was not previously possible.

Set the NO_COLOR environment variable when --no-color is set, to achieve
no colored output for every external tool `kas` executes that also
follows the standard (e.g. bitbake).

For consistency, the implementation aligns with colorlog's behavior of
checking for the mere presence of the NO_COLOR environment variable,
rather than adhering to no-color.org's recommendation of checking for a
non-empty string. Overriding this would require colorlog version 6.6
or higher.

When neither the command-line argument nor the environment variable
disables color, kas will independently check isatty for both stdout and
stderr to determine if color should be used.

Since colorlog is not an optional dependency anymore, use it's no_color
option instead of manually creating a different formatter when no_color
is enabled.

Signed-off-by: Tamino Larisch <tamino....@siemens.com>
---
docs/userguide/environment-variables.inc | 3 +++
kas-container | 2 +-
kas/context.py | 10 ++++++-
kas/kas.py | 33 ++++++++++++++----------
kas/plugins/diff.py | 12 +++------
kas/plugins/lock.py | 5 ++--
6 files changed, 39 insertions(+), 26 deletions(-)

diff --git a/docs/userguide/environment-variables.inc b/docs/userguide/environment-variables.inc
index 42ede08..c9dad43 100644
--- a/docs/userguide/environment-variables.inc
+++ b/docs/userguide/environment-variables.inc
@@ -245,6 +245,9 @@ overwritten using the ``env`` section of the config file.
| (C,K) | and install buildtools. If not set, kas will use |
| | ``KAS_BUILD_DIR/buildtools`` as the default path.|
+--------------------------+--------------------------------------------------+
+| NO_COLOR (C,K,E) | Prevents the addition of ANSI color to all output|
+| | when this variable is present. |
++--------------------------+--------------------------------------------------+

.. |aws_cred| replace:: ``AWS_ROLE_ARN``
``AWS_SHARED_CREDENTIALS_FILE``
diff --git a/kas-container b/kas-container
index d00dc93..aa1839d 100755
--- a/kas-container
+++ b/kas-container
@@ -770,7 +770,7 @@ for var in TERM KAS_DISTRO KAS_MACHINE KAS_TARGET KAS_TASK KAS_CLONE_DEPTH \
KAS_PREMIRRORS DISTRO_APT_PREMIRRORS BB_NUMBER_THREADS PARALLEL_MAKE \
GIT_CREDENTIAL_USEHTTPPATH \
BB_HASHSERVE BB_HASHSERVE_UPSTREAM \
- TZ; do
+ NO_COLOR TZ; do
if [ -n "$(eval echo \$${var})" ]; then
set -- "$@" -e "${var}=$(eval echo \"\$${var}\")"
fi
diff --git a/kas/context.py b/kas/context.py
index 41bd761..ee14cbd 100644
--- a/kas/context.py
+++ b/kas/context.py
@@ -91,6 +91,7 @@ class Context:
if not clone_depth.isdigit():
raise KasUserError('KAS_CLONE_DEPTH must be a number')
self.repo_clone_depth = max(int(clone_depth), 0)
+ self.args = args
self.setup_initial_environ()
self.check_container_call()
# Register the paths that kas created and exclusively owns
@@ -99,7 +100,6 @@ class Context:
self.managed_paths.add(self.__kas_build_dir)
self.keyhandler = {}
self.config = None
- self.args = args
self.unpinned_repo_warnings = True

def setup_initial_environ(self):
@@ -130,6 +130,10 @@ class Context:
val = os.environ.get(key, None)
if val:
self.environ[key] = val
+ # Propagate --no-color to sub-processes that follow the NO_COLOR
+ # convention (no-color.org, e.g. bitbake)
+ if self.args.no_color:
+ self.environ['NO_COLOR'] = '1'

# make remote containers environment available in kas
if self.managed_env == ManagedEnvironment.VSCODE_REMOTE_CONTAINERS:
@@ -190,6 +194,10 @@ class Context:
--
2.39.5

Jörg Sommer

unread,
Jul 23, 2026, 1:14:42 AM (11 days ago) Jul 23
to Tamino Larisch, kas-...@googlegroups.com
'Tamino Larisch' via kas-devel schrieb am Di 21. Jul, 15:23 (+0200):
> diff --git a/docs/userguide/environment-variables.inc b/docs/userguide/environment-variables.inc
> index 42ede08..c9dad43 100644
> --- a/docs/userguide/environment-variables.inc
> +++ b/docs/userguide/environment-variables.inc
> @@ -245,6 +245,9 @@ overwritten using the ``env`` section of the config file.
> | (C,K) | and install buildtools. If not set, kas will use |
> | | ``KAS_BUILD_DIR/buildtools`` as the default path.|
> +--------------------------+--------------------------------------------------+
> +| NO_COLOR (C,K,E) | Prevents the addition of ANSI color to all output|
> +| | when this variable is present. |
> ++--------------------------+--------------------------------------------------+

Maybe: … when this variable has a non-empty value.


Thanks for implementing this.

Have a nice day, Jörg

--
Navimatix GmbH T: 03641 - 327 99 0
Tatzendpromenade 2 F: 03641 - 526 306
07745 Jena www.navimatix.de

Geschäftsführer: Steffen Späthe, Jan Rommeley
Registergericht: Amtsgericht Jena, HRB 501480

Jan Kiszka

unread,
Jul 23, 2026, 1:34:16 AM (11 days ago) Jul 23
to Jörg Sommer, Tamino Larisch, kas-...@googlegroups.com
On 23.07.26 07:14, 'Jörg Sommer' via kas-devel wrote:
> 'Tamino Larisch' via kas-devel schrieb am Di 21. Jul, 15:23 (+0200):
>> diff --git a/docs/userguide/environment-variables.inc b/docs/userguide/environment-variables.inc
>> index 42ede08..c9dad43 100644
>> --- a/docs/userguide/environment-variables.inc
>> +++ b/docs/userguide/environment-variables.inc
>> @@ -245,6 +245,9 @@ overwritten using the ``env`` section of the config file.
>> | (C,K) | and install buildtools. If not set, kas will use |
>> | | ``KAS_BUILD_DIR/buildtools`` as the default path.|
>> +--------------------------+--------------------------------------------------+
>> +| NO_COLOR (C,K,E) | Prevents the addition of ANSI color to all output|
>> +| | when this variable is present. |
>> ++--------------------------+--------------------------------------------------+
>
> Maybe: … when this variable has a non-empty value.
>

True according to the original spec - but this is not how it is
implemented right now ("NO_COLOR" in os.environ).

Larisch, Tamino

unread,
Jul 27, 2026, 3:34:41 AM (7 days ago) Jul 27
to joerg....@navimatix.de, Kiszka, Jan, kas-...@googlegroups.com
Yes, I'm not 100% sure what's better in this case. Should we prioritize
consistency with colorlog (by making the exact same "NO_COLOR" in
os.environ check) or follow the spec completely? In my opinion, this is
such a tiny difference, and most people won't set it to an empty string
anyway. So, I can change the implementation and documentation to check
for an non-empty value if you'd prefer.

joerg....@navimatix.de

unread,
Jul 27, 2026, 3:55:38 AM (7 days ago) Jul 27
to Larisch, Tamino, Kiszka, Jan, kas-...@googlegroups.com
Larisch, Tamino schrieb am Mo 27. Jul, 07:34 (+0000):
The empty string is nice to unset NO_COLOR, if it is set in the environment
(or unknown, if it is set). In scripts one could use `NO_COLOR= …` or
`./kas-container --runtime-args '--env=NO_COLOR=' …`. Otherwise you have to
use `env -u`.

Tamino Larisch

unread,
Jul 31, 2026, 8:40:50 AM (3 days ago) Jul 31
to kas-...@googlegroups.com, Tue Duong
From: Tue Duong <tuedu...@gmail.com>

The application natively supports colorized log output, but `colorlog`
was omitted from `pyproject.toml`, making it an implicit optional
feature. This caused standard pip/pipx installations to lack colored
logs unless the package was already installed globally.

Since `colorlog` is already standard in the official containers, drop
its optional status and make it a core requirement. This ensures a
consistent debugging experience across all installations.

Signed-off-by: Tue Duong <tuedu...@gmail.com>
---
kas/kas.py | 9 ++-------
pyproject.toml | 1 +
2 files changed, 3 insertions(+), 7 deletions(-)

diff --git a/kas/kas.py b/kas/kas.py
index b87b404..c2b65a4 100644
--- a/kas/kas.py
+++ b/kas/kas.py
@@ -32,17 +32,12 @@ import asyncio
import distro
import traceback
import logging
+import colorlog
import signal
import sys
import os
from .kasusererror import KasUserError, CommandExecError

-try:
- import colorlog
- HAVE_COLORLOG = True
-except ImportError:
- HAVE_COLORLOG = False
-
from . import __version__, __file_version__, __compatible_file_version__
from . import plugins

@@ -60,7 +55,7 @@ def create_logger():
set_global_loglevel(DEFAULT_LOG_LEVEL.upper())
format_str = '%(asctime)s - %(levelname)-8s - %(message)s'
date_format = '%Y-%m-%d %H:%M:%S'
- if HAVE_COLORLOG and os.isatty(2):
+ if os.isatty(2):
cformat = '%(log_color)s' + format_str
colors = {'DEBUG': 'reset',

Tamino Larisch

unread,
Jul 31, 2026, 8:40:50 AM (3 days ago) Jul 31
to kas-...@googlegroups.com, Tamino Larisch
Differences to v3:
- enable colors when NO_COLOR variable is an empty string
- in this case also unset the variable, so colorlog behaves the same

Tamino Larisch (1):
feat: add global no-color output options

Tue Duong (1):
kas: add colorlog as a mandatory dependency

docs/userguide/environment-variables.inc | 3 ++
kas-container | 2 +-
kas/context.py | 10 +++++-
kas/kas.py | 45 +++++++++++++-----------
kas/plugins/diff.py | 12 +++----
kas/plugins/lock.py | 5 +--
pyproject.toml | 1 +
7 files changed, 46 insertions(+), 32 deletions(-)

--
2.39.5

Tamino Larisch

unread,
Jul 31, 2026, 8:40:52 AM (3 days ago) Jul 31
to kas-...@googlegroups.com, Tamino Larisch
This commit introduces a global --no-color command-line option,
extending its functionality beyond the diff plugin. It also adds support
for the NO_COLOR environment variable. Both enable users to disable
colored output across all plugins without requiring colorlog to be
uninstalled. This change provides control over the output color for the
lock plugin, which was not previously possible.

The NO_COLOR environment variable is now set when --no-color is used, to
ensure no colored output for every external tool `kas` executes that
also follows the standard (e.g., bitbake).

If NO_COLOR is set to an empty string, it is explicitly unset. This
ensures that colorlog, which checks for mere existence, will correctly
interpret an empty string as "enable colors", aligning with the
no-color.org recommendation for a non-empty string to disable colors.
This unsetting of the variable can be dropped if we either require
colorlog version 6.6 (which allows overriding the default behavior) or a
potential future version that may exactly follow the standard.

When neither the command-line argument nor the environment variable
disables color, kas will independently check isatty for both stdout and
stderr to determine if color should be used.

Since colorlog is no longer an optional dependency, its no_color option
is now used instead of manually creating a different formatter when
no_color is enabled.

Signed-off-by: Tamino Larisch <tamino....@siemens.com>
---
docs/userguide/environment-variables.inc | 3 ++
kas-container | 2 +-
kas/context.py | 10 ++++++-
kas/kas.py | 38 +++++++++++++++---------
kas/plugins/diff.py | 12 +++-----
kas/plugins/lock.py | 5 ++--
6 files changed, 44 insertions(+), 26 deletions(-)

diff --git a/docs/userguide/environment-variables.inc b/docs/userguide/environment-variables.inc
index 408bcc9..4c7bf9e 100644
--- a/docs/userguide/environment-variables.inc
+++ b/docs/userguide/environment-variables.inc
@@ -245,6 +245,9 @@ overwritten using the ``env`` section of the config file.
| (C,K) | and install buildtools. If not set, kas will use |
| | ``KAS_BUILD_DIR/buildtools`` as the default path.|
+--------------------------+--------------------------------------------------+
+| NO_COLOR (C,K,E) | Prevents the addition of ANSI color to all output|
+| | when this variable has a non-empty value. |
++--------------------------+--------------------------------------------------+

.. |aws_cred| replace:: ``AWS_ROLE_ARN``
``AWS_SHARED_CREDENTIALS_FILE``
diff --git a/kas-container b/kas-container
index be641ca..a70dbbc 100755
diff --git a/kas/kas.py b/kas/kas.py
index c2b65a4..1c382ae 100644
--- a/kas/kas.py
+++ b/kas/kas.py
@@ -47,7 +47,7 @@ __copyright__ = 'Copyright (c) Siemens AG, 2017-2018'
DEFAULT_LOG_LEVEL = 'info'


-def create_logger():
+def create_logger(no_color=False):
"""
Setup the logging environment
"""
@@ -55,17 +55,15 @@ def create_logger():
set_global_loglevel(DEFAULT_LOG_LEVEL.upper())
format_str = '%(asctime)s - %(levelname)-8s - %(message)s'
date_format = '%Y-%m-%d %H:%M:%S'
@@ -191,11 +193,19 @@ def kas(argv):
"""
The actual main entry point of kas.
"""
- create_logger()
-
parser = kas_get_argparser()
args = parser.parse_args(argv)

+ # unset NO_COLOR environment variable if it is an empty string, as colorlog
+ # only checks for the presence to disable colors, but the convention is
+ # to check for an non-empty string (see https://no-color.org/).
+ if os.environ.get('NO_COLOR') == '':
+ del os.environ['NO_COLOR']
+ create_logger(args.no_color or not sys.stderr.isatty())
+ args.no_color = args.no_color \
+ or os.environ.get('NO_COLOR', '') != '' \

Jan Kiszka

unread,
Jul 31, 2026, 9:48:18 AM (3 days ago) Jul 31
to Tamino Larisch, kas-...@googlegroups.com
Thanks, applied.

For the future, please send new version of series in a new thread as
well - imagine how your mail client would render things if we had to go
through some dozens of rounds. ;)

Jan Kiszka

unread,
Jul 31, 2026, 12:01:47 PM (3 days ago) Jul 31
to Tamino Larisch, kas-...@googlegroups.com
On 31.07.26 14:41, 'Tamino Larisch' via kas-devel wrote:
You removed the 7th argument from formatting_diff_output above.

>
> def _update_lockfile(self, lockfile, repos_to_lock, update_only, args):
> """

I need this diff on top to make tests pass:

diff --git a/kas/plugins/lock.py b/kas/plugins/lock.py
index be76d2a..443df65 100644
--- a/kas/plugins/lock.py
+++ b/kas/plugins/lock.py
@@ -118,9 +118,8 @@ class Lock:
except RepoRefError as e:
logging.warning(e)
return
- no_color = get_context().no_color
Diff.formatting_diff_output(
- None, None, {'vcs': diff}, True, no_color, True, False)
+ None, None, {'vcs': diff}, True, True, False)

def _update_lockfile(self, lockfile, repos_to_lock, update_only, args):
"""
diff --git a/kas/plugins/menu.py b/kas/plugins/menu.py
index 9288ce7..93a90a8 100644
--- a/kas/plugins/menu.py
+++ b/kas/plugins/menu.py
@@ -302,6 +302,7 @@ class Menu:
build_args.extra_bitbake_args = []
build_args.skip = None
build_args.provenance = False
+ build_args.no_color = ctx.no_color;

Build().run(build_args)

diff --git a/tests/test_environment_variables.py b/tests/test_environment_variables.py
index e696f77..d63bd09 100644
--- a/tests/test_environment_variables.py
+++ b/tests/test_environment_variables.py
@@ -37,6 +37,12 @@ from kas.libcmds import SetupHome, SetupSSHAgent
from kas import __version__


+def mock_global_context():
+ args = lambda: None
+ args.no_color = False
+ return create_global_context(args)
+
+
@pytest.mark.dirsfromenv
def test_build_dir_is_placed_inside_work_dir_by_default(monkeykas, tmpdir):
conf_dir = str(tmpdir / 'test_env_variables')
@@ -209,20 +215,20 @@ def test_env_section_export_bb_env_passthrough_additions(monkeykas, tmpdir):
def test_managed_env_detection(monkeykas):
with monkeykas.context() as mp:
mp.setenv('GITLAB_CI', 'true')
- ctx = create_global_context([])
+ ctx = mock_global_context()
me = ctx.managed_env
assert bool(me)
assert str(me) == 'GitLab CI'
with monkeykas.context() as mp:
mp.setenv('GITHUB_ACTIONS', 'true')
- ctx = create_global_context([])
+ ctx = mock_global_context()
me = ctx.managed_env
assert bool(me)
assert str(me) == 'GitHub Actions'
with monkeykas.context() as mp:
mp.setenv('REMOTE_CONTAINERS', 'true')
mp.setenv('REMOTE_CONTAINERS_FOO', 'bar')
- ctx = create_global_context([])
+ ctx = mock_global_context()
me = ctx.managed_env
assert bool(me)
assert str(me) == 'VSCode Remote Containers'
@@ -231,7 +237,7 @@ def test_managed_env_detection(monkeykas):

@pytest.mark.dirsfromenv
def test_env_file_processing(monkeykas, tmpdir):
- ctx = create_global_context([])
+ ctx = mock_global_context()
rcfiles = [
('NETRC_FILE', '.netrc'),
('NPMRC_FILE', '.npmrc'),
@@ -271,7 +277,7 @@ def test_env_file_processing(monkeykas, tmpdir):
def test_env_set_but_not_existing(monkeykas):
with monkeykas.context() as mp:
mp.setenv('NETRC_FILE', '/path/does/not/exist')
- ctx = create_global_context([])
+ ctx = mock_global_context()
with pytest.raises(EnvSetButNotFoundError):
SetupHome().execute(ctx)

@@ -280,18 +286,18 @@ def test_kas_container_version(monkeykas, caplog):
caplog.set_level(logging.WARNING)
with monkeykas.context() as mp:
# not a kas-container call
- create_global_context([])
+ mock_global_context()
assert 'versions do not match' not in caplog.text
caplog.clear()
with monkeykas.context() as mp:
# kas-container call from matching script
mp.setenv('KAS_CONTAINER_SCRIPT_VERSION', __version__)
- create_global_context([])
+ mock_global_context()
assert 'versions do not match' not in caplog.text
caplog.clear()
with monkeykas.context() as mp:
# kas-container call from older/newer script
mp.setenv('KAS_CONTAINER_SCRIPT_VERSION', '0.0')
- create_global_context([])
+ mock_global_context()
assert 'versions do not match' in caplog.text
caplog.clear()
diff --git a/tests/test_includehandler.py b/tests/test_includehandler.py
index 6cd00be..d2c7afa 100644
--- a/tests/test_includehandler.py
+++ b/tests/test_includehandler.py
@@ -40,7 +40,9 @@ def fixed_version(monkeypatch):

@pytest.fixture(autouse=True)
def with_kas_context():
- context.create_global_context(None)
+ args = lambda: None
+ args.no_color = False
+ context.create_global_context(args)
yield
context.__context__ = None


Does this make sense? I'm still a bit confused the that missing no_color
attribute in build_args (menu plugin) does not trigger outside of the
test case.
Reply all
Reply to author
Forward
0 new messages