Commit ee7af1a7 authored by Adrian DC's avatar Adrian DC
Browse files

fix: resolve #260: implement 'include:' wildcard paths support

Signed-off-by: default avatarAdrian DC <radian.dc@gmail.com>
parent 23e06c47
Loading
Loading
Loading
Loading
Loading
+33 −23
Changes for gitlabci_local/parsers/gitlab.py: 33 added lines, 23 removed lines.
Original line number Diff line number Diff line
@@ -256,15 +256,10 @@ class GitLab:
                        variable_value = str(variable_data)
                    self.__variables.globals[variable] = variable_value

    # Include, pylint: disable=too-many-locals,too-many-statements
    # Include
    def __include(self, data: YAML.Data, stack: List[str], root_directory: Path,
                  working_directory: Path) -> None:

        # Variables
        data_additions: YAML.Data
        file_path: Path
        include_parent: str = stack[-1] if stack else ''

        # Parse nested include
        if data and 'include' in data and data['include']:

@@ -289,6 +284,19 @@ class GitLab:

                # Parse local nodes
                if 'local' in include_dict:
                    self.__include_local(data, stack, root_directory, working_directory,
                                         include_dict)

                # Parse project node
                elif 'project' in include_dict:
                    self.__include_project(data, stack, working_directory, include_dict)

    # Include local, pylint: disable=too-many-arguments,too-many-locals
    def __include_local(self, data: YAML.Data, stack: List[str], root_directory: Path,
                        working_directory: Path, include_dict: dict) -> None:

        # Variables
        include_parent: str = stack[-1] if stack else ''
        include_path: str = include_dict['local']

        # Handle include relative paths
@@ -302,10 +310,11 @@ class GitLab:

        # Already included file
        if resolved_path in stack:
                        continue
            return

        # Existing file inclusion
                    file_path = Path(working_directory) / resolved_path
        file_paths: Path = Path(working_directory) / resolved_path
        for file_path in Paths.wildcard(str(file_paths), strict=True):
            if file_path.is_file():

                # Register included file
@@ -313,7 +322,7 @@ class GitLab:

                # Load included file
                with open(file_path, encoding='utf8', mode='r') as include_data:
                            data_additions = YAML.load(include_data)
                    data_additions: YAML.Data = YAML.load(include_data)
                    if data_additions:

                        # Nested includes (follow working directory)
@@ -328,16 +337,19 @@ class GitLab:
                    # Empty included file
                    else:
                        raise SyntaxError(
                                    f'Empty "{file_path}" file included in "{include_parent}"'
                                )
                            f'Empty "{file_path}" file included in "{include_parent}"')

            # Missing file failure
            else:
                raise FileNotFoundError(
                    f'Missing "{file_path}" file included in "{include_parent}"')

                # Parse project node
                elif 'project' in include_dict:
    # Include project
    def __include_project(self, data: YAML.Data, stack: List[str],
                          working_directory: Path, include_dict: dict) -> None:

        # Variables
        include_parent: str = stack[-1] if stack else ''
        project_path: str = ''
        project_url: str = include_dict['project']

@@ -346,14 +358,13 @@ class GitLab:
            local = data[GitLab.LOCAL_NODE]

            # Acquire local include node
                        if 'include' in local and project_url in local[
                                'include'] and isinstance(local['include'][project_url],
                                                          str):
            if 'include' in local and project_url in local['include'] and isinstance(
                    local['include'][project_url], str):
                project_path = local['include'][project_url]

        # Exclude missing include node
        if not project_path:
                        continue
            return

        # Parse file paths
        if 'file' in include_dict:
@@ -373,16 +384,16 @@ class GitLab:
                continue

            # Existing file inclusion
                        file_path = Path(working_directory) / resolved_path
            file_paths: Path = Path(working_directory) / resolved_path
            for file_path in Paths.wildcard(str(file_paths), strict=True):
                if file_path.is_file():

                    # Register included file
                    stack.append(resolved_path)

                    # Load included file
                            with open(file_path, encoding='utf8',
                                      mode='r') as include_data:
                                data_additions = YAML.load(include_data)
                    with open(file_path, encoding='utf8', mode='r') as include_data:
                        data_additions: YAML.Data = YAML.load(include_data)
                        if data_additions:

                            # Nested includes (follow working directory)
@@ -407,8 +418,7 @@ class GitLab:
                # Missing file failure
                else:
                    raise FileNotFoundError(
                                f'Missing "{file_path}" file included in "{include_parent}"'
                            )
                        f'Missing "{file_path}" file included in "{include_parent}"')

    # Local, pylint: disable=too-many-branches,too-many-statements
    def __local(self, data: YAML.Data) -> None:
+47 −1
Changes for gitlabci_local/types/paths.py: 47 added lines, 1 removed line.
Original line number Diff line number Diff line
#!/usr/bin/env python3

# Standard libraries
from glob import glob
from os.path import expanduser
from pathlib import Path, PurePosixPath
from typing import Union
from typing import List, Union

# Components
from ..system.platform import Platform
from .environment import Environment

# Paths class
@@ -86,3 +88,47 @@ class Paths:

        # Result
        return data

    # Wildcard
    @staticmethod
    def wildcard(path: str, strict: bool = False) -> List[Path]:

        # Variables
        paths: List[Path] = []
        variants: List[str] = []

        # Apply strict wildcards
        if strict:
            path = path.replace('**', '*/**')

        # Generate wildcard variants
        def wildcard_variants(path):
            variants: List[str] = [path]
            if path.startswith('*'): # pragma: no cover
                variants += ['.' + path]
            index = path.find(Platform.PATH_SEPARATOR + '*')
            while index != -1:
                variants_new = []
                for variant in variants:
                    new_variant = variant[:index + 1] + '.' + variant[index + 1:]
                    variants_new.append(new_variant)
                for variant_new in variants_new:
                    if variant_new not in variants:
                        variants += [variant_new]
                index = variants[0].find(Platform.PATH_SEPARATOR + '*', index + 1)
            return variants

        # Evaluate wildcard variants
        variants = wildcard_variants(path)

        # Evaluate wildcards
        for variant in variants:
            for item in [Path(item) for item in glob(variant, recursive=True)]:
                paths += [Path(item)]

        # Fallback to input
        if not paths:
            paths += [Path(path)]

        # Result
        return paths
+1 −1
Changes for tests/includes/.gitlab-ci.local.yml: 1 added line, 1 removed line.
Original line number Diff line number Diff line
include:
  - local: '/job_local.yml'
  - local: '/check_local.yml'

stages:
  - includes
+1 −1
Changes for tests/includes/.gitlab-ci.variables.yml: 1 added line, 1 removed line.
Original line number Diff line number Diff line
include: '/job_variables.yml'
include: '/global_variables.yml'

stages:
  - includes
+5 −0
Changes for tests/includes/.gitlab-ci.wildcards.local.missing.yml: 5 added lines, 0 removed lines.
Original line number Diff line number Diff line
include:
  - local: 'job_*missing.yml'

stages:
  - includes
Loading