Previously the top repo path was resolved when creating the config. This
has proven to be problematic as at this point in time neither the home
nor the git / mercurial environment is set up. This makes it impossible
to configure e.g. the git safe.dir in the dynamically generated
.gitconfig file, as this is created after the Config instantiation but
before the init_setup_repos.
We now change this by adding support to lazily resolve the top repo
path. In case the IncludeHandler is created with top_repo_path=None,
this path is resolved on the first access (i.e. on the first invocation
of get_top_repo_path). By that, we can further move the config
file validation part (all configs are from the same repo) to the
IncludeHandler.
Signed-off-by: Felix Moessbauer <
felix.mo...@siemens.com>
---
kas/config.py | 17 ++---------------
kas/includehandler.py | 31 ++++++++++++++++++++++++++++---
2 files changed, 30 insertions(+), 18 deletions(-)
diff --git a/kas/config.py b/kas/config.py
index fbe775ecb..e386f010d 100644
--- a/kas/config.py
+++ b/kas/config.py
@@ -27,7 +27,7 @@ import os
import json
from pathlib import Path
from .repos import Repo
-from .includehandler import IncludeHandler, IncludeException
+from .includehandler import IncludeHandler
from .kasusererror import ArtifactNotFoundError
from .configschema import CONFIGSCHEMA
@@ -51,23 +51,10 @@ class Config:
self.filenames = [os.path.abspath(configfile)
for configfile in filename.split(':')]
- top_repo_path = Repo.get_root_path(
- os.path.dirname(self.filenames[0]))
-
- repo_paths = [Repo.get_root_path(os.path.dirname(configfile),
- fallback=False)
- for configfile in self.filenames]
-
- if len(set(repo_paths)) > 1:
- raise IncludeException('All concatenated config files must '
- 'belong to the same repository or all '
- 'must be outside of versioning control')
update = ctx.args.update if hasattr(ctx.args, 'update') else False
- self.handler = IncludeHandler(self.filenames,
- top_repo_path,
- not update)
+ self.handler = IncludeHandler(self.filenames, None, not update)
self.repo_dict = self._get_repo_dict()
self.repo_cfg_hashes = {}
diff --git a/kas/includehandler.py b/kas/includehandler.py
index c0ae593ce..49a6ea11f 100644
--- a/kas/includehandler.py
+++ b/kas/includehandler.py
@@ -29,6 +29,7 @@ import os
from pathlib import Path
from collections import OrderedDict
from collections.abc import Mapping
+from functools import cached_property
import functools
import logging
import json
@@ -37,6 +38,7 @@ import yaml
from jsonschema.validators import validator_for
from .kasusererror import KasUserError
+from .repos import Repo
from . import __file_version__, __compatible_file_version__, __version__
from . import CONFIGSCHEMA
@@ -130,7 +132,8 @@ class IncludeHandler:
current file, or as a dictionary. The dictionary must have a
'file' key containing the path to the include file and a 'repo'
key containing the key of the repository. The path is interpreted
- relative to the repository root path.
+ relative to the repository root path. If no top_repo_path is provided,
+ the path is lazy resolved by the first access of a method.
The includes are read and merged from the deepest level upwards.
@@ -142,16 +145,37 @@ class IncludeHandler:
def __init__(self, top_files, top_repo_path, use_lock=True):
self.top_files = top_files
- self.top_repo_path = top_repo_path
+ self._top_repo_path = top_repo_path
self.use_lock = use_lock
def get_lockfile(self, kasfile=None):
file = Path(kasfile or self.top_files[0])
return file.parent / (file.stem + '.lock' + file.suffix)
+ @cached_property
+ def top_repo_path(self):
+ """
+ Lazy resolve top repo path as we might need a prepared environment
+ """
+ return self._top_repo_path or \
+ Repo.get_root_path(os.path.dirname(self.top_files[0]))
+
def get_top_repo_path(self):
return self.top_repo_path
+ def ensure_from_same_repo(self):
+ """
+ Ensure that all concatenated config files belong to the same repository
+ """
+ repo_paths = [Repo.get_root_path(os.path.dirname(configfile),
+ fallback=False)
+ for configfile in self.top_files]
+
+ if len(set(repo_paths)) > 1:
+ raise IncludeException('All concatenated config files must '
+ 'belong to the same repository or all '
+ 'must be outside of versioning control')
+
def get_config(self, repos=None):
"""
Parameters:
@@ -302,9 +326,10 @@ class IncludeHandler:
configs = []
missing_repos = []
+ self.ensure_from_same_repo()
for configfile in self.top_files:
cfgs, reps = _internal_include_handler(configfile,
- self.top_repo_path)
+ self.get_top_repo_path())
configs.extend(cfgs)
for repo in reps:
if repo not in missing_repos:
--
2.47.2