diff --git a/build/exceptions.py b/build/exceptions.py index b66db93..079aee8 100644 --- a/build/exceptions.py +++ b/build/exceptions.py @@ -48,3 +48,7 @@ class PreviousBuildNotDone(Error): """same release is building right now""" pass + + +class CircularBuildDependencies(Error): + pass diff --git a/build/models.py b/build/models.py index b62d7d9..0a2d7ab 100644 --- a/build/models.py +++ b/build/models.py @@ -363,16 +363,8 @@ class BuildRelease(models.Model): return res @property - def build_deps(self): - if getattr(self, "_build_deps", None) is None: - self._build_deps = [] - step = 0 - deps = list(self.config.wanna_build_deps(step)) - while len(deps) > 0: - self._build_deps.append(deps) - step = step + 1 - deps = list(self.config.wanna_build_deps(step)) - return self._build_deps + def build_deps(self) -> list: + return self.config.levels_build_deps @property def config(self): diff --git a/build/test/test_utils.py b/build/test/test_utils.py index 7051ae4..d7b4157 100644 --- a/build/test/test_utils.py +++ b/build/test/test_utils.py @@ -512,3 +512,80 @@ class RemoveList(BaseTest): self.br.triggered_projects = "fake-project" remove_from_textlist(self.br, "triggered_projects", "fake-project") self.assertIsNone(self.br.triggered_projects) + + +class WannaBuildTestCase(SimpleTestCase): + class FakeConfig(ReleaseConfig): + build_deps = dict() + + def __init__(self, build_deps=None): + if build_deps: + self.build_deps = build_deps + + def test_deps(self): + build_deps = { + "A": ["A1", "A2", "A3"], + "B": ["A2"], + "C": ["A2", "B"], + "A2": ["D"], + } + config = self.FakeConfig(build_deps) + wb = ReleaseConfig.WannaBuild(config, 0) + self.assertListEqual(list(wb), ["A", "C"]) + wb = ReleaseConfig.WannaBuild(config, 1) + self.assertListEqual(list(wb), ["B"]) + wb = ReleaseConfig.WannaBuild(config, 2) + self.assertListEqual(list(wb), ["A2"]) + wb = ReleaseConfig.WannaBuild(config, 3) + self.assertListEqual(list(wb), []) + + def test_deps_ko(self): + build_deps = {"A": ["A1", "A2", "A3", "A"]} + config = self.FakeConfig(build_deps) + wb = ReleaseConfig.WannaBuild(config, 0) + self.assertListEqual(list(wb), []) + + def test_circular_simple_deps(self): + build_deps = { + "A": ["A1", "A2", "A3"], + "B": ["A2"], + "C": ["A2", "B"], + "A2": ["A"], + } + config = self.FakeConfig(build_deps) + with self.assertRaisesRegex( + err.CircularBuildDependencies, r"\['A', 'A2'\]" + ): + config.check_circular_dependencies() + + def test_circular_simple_deps_more(self): + build_deps = { + "A": ["A1", "A2", "A3"], + "B": ["A2"], + "C": ["A2", "B"], + "A2": ["A3"], + "A3": ["C"], + } + config = self.FakeConfig(build_deps) + with self.assertRaisesRegex( + err.CircularBuildDependencies, r"\['B', 'C', 'A2', 'A3'\]" + ): + config.check_circular_dependencies() + + def test_circular_simple(self): + build_deps = { + "A": ["A1", "A2", "A3", "A"], + } + config = self.FakeConfig(build_deps) + with self.assertRaisesRegex(err.CircularBuildDependencies, r"\['A'\]"): + config.check_circular_dependencies() + + def test_circular_deps_ok(self): + build_deps = { + "A": ["A1", "A2", "A3"], + "B": ["A2"], + "C": ["A2", "B"], + "A2": ["D"], + } + config = self.FakeConfig(build_deps) + config.check_circular_dependencies() diff --git a/build/utils.py b/build/utils.py index c0bf8a5..2005f10 100644 --- a/build/utils.py +++ b/build/utils.py @@ -183,8 +183,6 @@ class ReleaseConfig(object): for name in search_map: flag = False for prj, values in search_map.items(): - if name == prj: - continue if name in values: flag = True deps[name] = search_map[name] @@ -225,6 +223,18 @@ class ReleaseConfig(object): return list_prj.pop(0) raise StopIteration + def check_circular_dependencies(self): + levels = self.levels_build_deps + builds = list(self.build_deps.keys()) + print(f"{levels}") + for vals in levels: + for prj in vals: + builds.remove(prj) + if len(builds) > 0: + raise err.CircularBuildDependencies( + f"problems detected with {builds}" + ) + @classmethod def load_config(cls, config_path): try: @@ -289,14 +299,27 @@ class ReleaseConfig(object): except KeyError: msg = "{} has no 'distris' info" raise err.NoDistrisInfo(msg.format(self.config_file)) + self.check_circular_dependencies() @property - def build_deps(self): + def build_deps(self) -> dict: return self.jenkins_jobs.get("build_deps", dict()) def wanna_build_deps(self, step=0): return ReleaseConfig.WannaBuild(self, step) + @property + def levels_build_deps(self) -> list: + if getattr(self, "_levels_build_deps", None) is None: + self._levels_build_deps = [] + step = 0 + deps = list(self.wanna_build_deps(step)) + while len(deps) > 0: + self._levels_build_deps.append(deps) + step = step + 1 + deps = list(self.wanna_build_deps(step)) + return self._levels_build_deps + @property def branch(self): release = self.release