summaryrefslogtreecommitdiff
path: root/scripts/container
diff options
context:
space:
mode:
authorGuillaume Tucker <gtucker@gtucker.io>2026-09-07 13:10:04 +0200
committerNathan Chancellor <nathan@kernel.org>2026-10-02 22:17:21 +0200
commit69cb84e259b163671ff241621544368af84c7c8f (patch)
treeabd6718aca516cc8e79834049fdc12b5eba0c182 /scripts/container
parent34f3b447600bcc56ae7c4d10db8dac754ad05357 (diff)
scripts: add TOML config to container tool
Add support for a TOML configuration file to the scripts/container tool. This improves user experience by not having to keep passing the same command line options all the time or overly relying on built-in default values. Include the concept of 'profiles' with different named sections in the file to cover various use cases. Command line options take precedence over the config file, and values defined in profile sections take precedence over the default one. Add a -c option to override the location of the .container.toml config file which should otherwise be located in the current working directory. If not found, the file is silently ignored as it is not strictly required unless the -c option is used. Add a -p option to choose a particular profile section in the config file rather than the default. Signed-off-by: Guillaume Tucker <gtucker@gtucker.io> Acked-by: Nicolas Schier <n.schier@fritz.com> Link: https://patch.msgid.link/68b14a76a4cb2e6437c869bab948983a98acffbd.1788779245.git.gtucker@gtucker.io Signed-off-by: Nathan Chancellor <nathan@kernel.org>
Diffstat (limited to 'scripts/container')
-rwxr-xr-xscripts/container86
1 files changed, 74 insertions, 12 deletions
diff --git a/scripts/container b/scripts/container
index b05333d8530b..a3a55180d6ed 100755
--- a/scripts/container
+++ b/scripts/container
@@ -1,17 +1,19 @@
#!/usr/bin/env python3
# SPDX-License-Identifier: GPL-2.0-only
-# Copyright (C) 2025 Guillaume Tucker
+# Copyright (C) 2025-2026 Guillaume Tucker
"""Containerized builds"""
import abc
import argparse
+import dataclasses
import logging
import os
import pathlib
import shutil
import subprocess
import sys
+import tomllib
import uuid
@@ -20,10 +22,17 @@ class ContainerRuntime(abc.ABC):
name = None # Property defined in each implementation class
- def __init__(self, args, logger):
- self._uid = args.uid or os.getuid()
- self._gid = args.gid or args.uid or os.getgid()
- self._env_file = args.env_file
+ def __init__(self, args, config, logger):
+ def _first_not_none(*args):
+ return next((item for item in args if item is not None))
+
+ self._uid = _first_not_none(
+ args.uid, config.uid, os.getuid()
+ )
+ self._gid = _first_not_none(
+ args.gid, config.gid, args.uid, config.uid, os.getgid()
+ )
+ self._env_file = args.env_file or config.env_file
self._shell = args.shell
self._logger = logger
@@ -131,6 +140,42 @@ class Runtimes:
raise ValueError("no runtime found")
+@dataclasses.dataclass
+class Config:
+ """Container configuration"""
+ image: str = None
+ runtime: str = None
+ env_file: str = None
+ uid: int = None
+ gid: int = None
+
+ @classmethod
+ def from_toml(cls, config_file_path, profile_name):
+ """Create a config object from a TOML file"""
+ if not config_file_path:
+ config_file_path = '.container.toml'
+ if not os.path.exists(config_file_path):
+ return cls()
+ elif not os.path.exists(config_file_path):
+ raise ValueError(f"config file not found: {config_file_path}")
+ with open(config_file_path, 'rb') as config_file:
+ config = tomllib.load(config_file)
+ default = config.get('DEFAULT', {})
+ if not profile_name:
+ profile = {}
+ else:
+ profile = config.get(profile_name)
+ if profile is None:
+ raise ValueError(f"unknown profile: {profile_name}")
+ kwargs = {
+ name: type(value) for (name, type, value) in (
+ (op.name, op.type, profile.get(op.name, default.get(op.name)))
+ for op in dataclasses.fields(cls)
+ ) if value is not None
+ }
+ return cls(**kwargs)
+
+
def _get_logger(verbose):
"""Set up a logger with the appropriate level"""
logger = logging.getLogger('container')
@@ -147,13 +192,19 @@ def main(args):
"""Main entry point for the container tool"""
logger = _get_logger(args.verbose)
try:
- cls = Runtimes.get(args.runtime) if args.runtime else Runtimes.find()
+ config = Config.from_toml(args.config_file, args.config_profile)
+ runtime = args.runtime or config.runtime
+ cls = Runtimes.get(runtime) if runtime else Runtimes.find()
except ValueError as ex:
logger.error(ex)
return 1
logger.debug("runtime: %s", cls.name)
- logger.debug("image: %s", args.image)
- return cls(args, logger).run(args.image, args.cmd)
+ image = args.image or config.image
+ if not image:
+ logger.error("no image specified")
+ return 1
+ logger.debug("image: %s", image)
+ return cls(args, config, logger).run(image, args.cmd)
if __name__ == '__main__':
@@ -163,18 +214,28 @@ if __name__ == '__main__':
"https://docs.kernel.org/dev-tools/container.html"
)
parser.add_argument(
+ '-c', '--config-file',
+ help="Path to the config file. If not specified, the default is to "
+ "look for .container.toml in the current working directory."
+ )
+ parser.add_argument(
'-e', '--env-file',
help="Path to an environment file to load in the container."
)
parser.add_argument(
- '-g', '--gid',
+ '-g', '--gid', type=int,
help="Group ID to use inside the container."
)
parser.add_argument(
- '-i', '--image', required=True,
+ '-i', '--image',
help="Container image name."
)
parser.add_argument(
+ '-p', '--config-profile',
+ help="Profile section to use in the config file. This will override "
+ "any values defined in the DEFAULT section."
+ )
+ parser.add_argument(
'-r', '--runtime', choices=Runtimes.get_names(),
help="Container runtime name. If not specified, the first one found "
"on the system will be used i.e. Podman if present, otherwise Docker."
@@ -184,9 +245,10 @@ if __name__ == '__main__':
help="Run the container in an interactive shell."
)
parser.add_argument(
- '-u', '--uid',
+ '-u', '--uid', type=int,
help="User ID to use inside the container. If the -g option is not "
- "specified, the user ID will also be set as the group ID."
+ "specified and no group ID is defined in the configuration file, the "
+ "user ID will also be set as the group ID."
)
parser.add_argument(
'-v', '--verbose', action='store_true',