summaryrefslogtreecommitdiff
path: root/scripts/container
diff options
context:
space:
mode:
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',