diff --git a/scuba/config.py b/scuba/config.py index 00932b1..5df02e1 100644 --- a/scuba/config.py +++ b/scuba/config.py @@ -318,7 +318,7 @@ def _expand_env_vars(in_str: str) -> str: ) from ve -def _process_script_node(node: CfgNode, name: str) -> List[str]: +def _process_script_node(node: CfgNode, name: str, hook: bool = False) -> List[str]: """Process a script-type node Args: @@ -332,8 +332,9 @@ def _process_script_node(node: CfgNode, name: str) -> List[str]: if isinstance(node, str): # The script is just the text itself return [node] - - if isinstance(node, list): + + if not hook and isinstance(node,list): + # if we're coming from a reference, the script may be a list despite not being a dict return node if isinstance(node, dict): @@ -370,6 +371,16 @@ def _process_reference(doc: dict, key: Reference) -> Any: cur = cur[k] except KeyError: raise yaml.YAMLError(f"Key {key!r} not found") + if isinstance(cur, list): + new_cur = [] + for c in cur: + if isinstance(c, Reference): + # use += to concatenate list type + new_cur += _process_reference(doc, c) + else: + # use append to concatenate other types + new_cur.append(c) + cur = new_cur return cur @@ -715,7 +726,7 @@ def _load_hooks(self, data: CfgData) -> Dict[str, List[str]]: ): node = data.get("hooks", {}).get(name) if node: - hooks[name] = _process_script_node(node, name) + hooks[name] = _process_script_node(node, name, True) return hooks @property diff --git a/tests/test_config.py b/tests/test_config.py index 801d4bd..7897a5d 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -345,9 +345,107 @@ def test_load_config_from_gitlab_reference_script_list(self) -> None: important: !from_gitlab {GITLAB_YML} build.script """ ) - assert config.image == "bosybux" - assert len(config.aliases) == 1 - assert config.aliases["important"].script == ["do-something-really-important", "depends-on-important-stuff"] + assert config.aliases["important"].script == [ + "do-something-really-important", + "depends-on-important-stuff", + ] + + def test_load_config_from_gitlab_nested_reference(self) -> None: + """load a .gitlab-ci.yml with !reference in script""" + GITLAB_YML.write_text( + """ + .initial: + script: + - the-most-important-thing + + .setup: + script: + - !reference [.initial, script] + - do-something-really-important + + build: + stage: build + script: + - !reference [.setup, script] + - depends-on-important-stuff + """ + ) + config = load_config( + config_text=f""" + image: bosybux + aliases: + important: !from_gitlab {GITLAB_YML} build.script + """ + ) + assert config.aliases["important"].script == [ + "the-most-important-thing", + "do-something-really-important", + "depends-on-important-stuff", + ] + + def test_load_config_from_gitlab_double_nested_reference(self) -> None: + """load a .gitlab-ci.yml with !reference in script""" + GITLAB_YML.write_text( + """ + .preinit: + script: + - the-utmost-importance + + .initial: + script: + - !reference [.preinit, script] + - the-most-important-thing + + .setup: + script: + - !reference [.initial, script] + - do-something-really-important + + build: + stage: build + script: + - !reference [.setup, script] + - depends-on-important-stuff + """ + ) + config = load_config( + config_text=f""" + image: bosybux + aliases: + important: !from_gitlab {GITLAB_YML} build.script + """ + ) + assert config.aliases["important"].script == [ + "the-utmost-importance", + "the-most-important-thing", + "do-something-really-important", + "depends-on-important-stuff", + ] + + def test_load_config_from_gitlab_with_include(self) -> None: + """ + load_config loads a config using !from_gitlab with !reference tag while ignoring the include and extends + TODO: #200 implement other gitlab-specific yaml + """ + + GITLAB_YML.write_text( + f""" + include: dummy.yml + + .base: + extends: .real_base + image: dummian:12 + script: + - so something + + other: + image: !reference [.base, image] + """ + ) + config = load_config( + config_text=f'image: !from_gitlab {GITLAB_YML} "other.image"\n', + ) + assert config.image == "dummian:12" def test_load_config_from_gitlab_with_bad_reference(self) -> None: """load_config loads a config using !from_gitlab with !reference tag"""