diff --git a/scuba/config.py b/scuba/config.py index c37ff83..d6edb3d 100644 --- a/scuba/config.py +++ b/scuba/config.py @@ -330,7 +330,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: @@ -344,8 +344,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): @@ -382,6 +383,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 @@ -727,7 +738,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 0c6666c..82c1b97 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -342,10 +342,73 @@ 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"] + + 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_bad_reference(self) -> None: """load_config loads a config using !from_gitlab with !reference tag""" GITLAB_YML.write_text(