diff options
Diffstat (limited to 'scripts/container')
| -rwxr-xr-x | scripts/container | 86 |
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', |
