Exemple #1
0
def test_local_and_visible(tmp_path) -> None:
    """Test if file is local (not symlink) and visible (not hidden)."""
    local_file = tmp_path / 'local.md'
    local_file.touch()
    if file_utils.is_windows():
        link_file = tmp_path / 'not_local.lnk'
        link_file.touch()
    else:
        link_file = tmp_path / 'linked.md'
        link_file.symlink_to(local_file)
    assert file_utils.is_local_and_visible(local_file)
    assert not file_utils.is_local_and_visible(link_file)
Exemple #2
0
def test_make_hidden_file(tmp_path: pathlib.Path) -> None:
    """Test make hidden files."""
    file_path = tmp_path / '.keep'
    file_utils.make_hidden_file(file_path)

    file_path2 = tmp_path / 'hidden.txt'
    file_utils.make_hidden_file(file_path2)

    assert file_path.exists(
    ) and not file_utils.is_local_and_visible(file_path)
    if file_utils.is_windows():
        assert file_path2.exists(
        ) and not file_utils.is_local_and_visible(file_path2)
    else:
        assert (tmp_path / '.hidden.txt').exists(
        ) and not file_utils.is_local_and_visible(tmp_path / '.hidden.txt')
Exemple #3
0
    def template_validate(self, validate_header: bool,
                          validate_only_header: bool, heading: str,
                          readme_validate: bool) -> int:
        """Validate that the template is acceptable markdown."""
        if not self.template_dir.is_dir():
            raise TrestleError(
                f'Template directory {self.rel_dir(self.template_dir)} for task {self.task_name} does not exist.'
            )
        # get list of files:
        template_files = self.template_dir.rglob('*')

        for template_file in template_files:
            try:
                if not file_utils.is_local_and_visible(template_file):
                    continue
                elif template_file.is_dir():
                    continue
                elif template_file.suffix.lower() == '.md':
                    if not readme_validate and template_file.name == 'readme.md':
                        raise TrestleError(
                            'Template directory contains a readme.md file and readme validation is off.'
                        )

                    md_api = MarkdownAPI()
                    md_api.load_validator_with_template(
                        template_file, validate_header,
                        not validate_only_header, heading)
                elif template_file.suffix.lower().lstrip('.') == 'drawio':
                    _ = draw_io.DrawIOMetadataValidator(template_file)
                else:
                    logger.info(
                        f'File: {self.rel_dir(template_file)} within the template directory was ignored'
                        + 'as it is not markdown.')
            except Exception as ex:
                raise TrestleError(
                    f'Template file {self.rel_dir(template_file)} for task {self.task_name}'
                    + f' failed to validate due to {ex}')
        logger.info(f'TEMPLATES VALID: {self.task_name}.')
        return CmdReturnCodes.SUCCESS.value
Exemple #4
0
    def _validate_dir(self, candidate_dir: pathlib.Path, recurse: bool,
                      readme_validate: bool,
                      relative_exclusions: List[pathlib.Path],
                      template_version: str, ignore: str) -> bool:
        """Validate a directory within the trestle project."""
        all_versioned_templates = {}
        instance_version = template_version
        instance_file_names: List[pathlib.Path] = []
        # Fetch all instances versions and build dictionary of required template files
        instances = list(candidate_dir.iterdir())
        if recurse:
            instances = candidate_dir.rglob('*')
            if ignore:
                p = re.compile(ignore)
                instances = list(
                    filter(
                        lambda f: len(
                            list(
                                filter(
                                    p.match,
                                    str(f.relative_to(candidate_dir)).split(
                                        '/')))) == 0, instances))
        for instance_file in instances:
            if not file_utils.is_local_and_visible(instance_file):
                continue
            if instance_file.name.lower(
            ) == 'readme.md' and not readme_validate:
                continue
            if instance_file.is_dir() and not recurse:
                continue
            if any(
                    str(ex) in str(instance_file)
                    for ex in relative_exclusions):
                continue
            if ignore:
                p = re.compile(ignore)
                matched = p.match(instance_file.parts[-1])
                if matched is not None:
                    logger.info(
                        f'Ignoring file {instance_file} from validation.')
                    continue
            instance_file_name = instance_file.relative_to(candidate_dir)
            instance_file_names.append(instance_file_name)
            if instance_file.suffix == '.md':
                md_api = MarkdownAPI()
                versioned_template_dir = None
                if template_version != '':
                    versioned_template_dir = self.template_dir
                else:
                    instance_version = md_api.processor.fetch_value_from_header(
                        instance_file, author_const.TEMPLATE_VERSION_HEADER)
                    if instance_version is None:
                        instance_version = '0.0.1'  # backward compatibility
                    versioned_template_dir = TemplateVersioning.get_versioned_template_dir(
                        self.template_dir, instance_version)

                if instance_version not in all_versioned_templates.keys():
                    templates = list(
                        filter(lambda p: file_utils.is_local_and_visible(p),
                               versioned_template_dir.iterdir()))
                    if not readme_validate:
                        templates = list(
                            filter(lambda p: p.name.lower() != 'readme.md',
                                   templates))
                    all_versioned_templates[instance_version] = {}
                    all_versioned_templates[instance_version]['drawio'] = list(
                        filter(lambda p: p.suffix == '.drawio', templates))[0]
                    all_versioned_templates[instance_version]['md'] = list(
                        filter(lambda p: p.suffix == '.md', templates))[0]

                # validate
                md_api.load_validator_with_template(
                    all_versioned_templates[instance_version]['md'], True,
                    False)
                status = md_api.validate_instance(instance_file)
                if not status:
                    logger.info(f'INVALID: {self.rel_dir(instance_file)}')
                    return False
                else:
                    logger.info(f'VALID: {self.rel_dir(instance_file)}')

            elif instance_file.suffix == '.drawio':
                drawio = DrawIO(instance_file)
                metadata = drawio.get_metadata()[0]

                versioned_template_dir = None
                if template_version != '':
                    versioned_template_dir = self.template_dir
                else:
                    if author_const.TEMPLATE_VERSION_HEADER in metadata.keys():
                        instance_version = metadata[
                            author_const.TEMPLATE_VERSION_HEADER]
                    else:
                        instance_version = '0.0.1'  # backward compatibility

                    versioned_template_dir = TemplateVersioning.get_versioned_template_dir(
                        self.template_dir, instance_version)

                if instance_version not in all_versioned_templates.keys():
                    templates = list(
                        filter(lambda p: file_utils.is_local_and_visible(p),
                               versioned_template_dir.iterdir()))
                    if not readme_validate:
                        templates = list(
                            filter(lambda p: p.name.lower() != 'readme.md',
                                   templates))
                    all_versioned_templates[instance_version] = {}
                    all_versioned_templates[instance_version]['drawio'] = list(
                        filter(lambda p: p.suffix == '.drawio', templates))[0]
                    all_versioned_templates[instance_version]['md'] = list(
                        filter(lambda p: p.suffix == '.md', templates))[0]

                # validate
                drawio_validator = DrawIOMetadataValidator(
                    all_versioned_templates[instance_version]['drawio'])
                status = drawio_validator.validate(instance_file)
                if not status:
                    logger.info(f'INVALID: {self.rel_dir(instance_file)}')
                    return False
                else:
                    logger.info(f'VALID: {self.rel_dir(instance_file)}')

            else:
                logger.debug(
                    f'Unsupported extension of the instance file: {instance_file}, will not be validated.'
                )

        return True
Exemple #5
0
    def _validate_dir(self,
                      governed_heading: str,
                      md_dir: pathlib.Path,
                      validate_header: bool,
                      validate_only_header: bool,
                      recurse: bool,
                      readme_validate: bool,
                      template_version: Optional[str] = None,
                      ignore: Optional[str] = None) -> int:
        """
        Validate md files in a directory with option to recurse.

        Template version will be fetched from the instance header.
        """
        # status is a linux returncode
        status = 0
        for item_path in md_dir.iterdir():
            if file_utils.is_local_and_visible(item_path):
                if item_path.is_file():
                    if not item_path.suffix == '.md':
                        logger.info(
                            f'Unexpected file {self.rel_dir(item_path)} in folder {self.rel_dir(md_dir)}, skipping.'
                        )
                        continue
                    if not readme_validate and item_path.name.lower(
                    ) == 'readme.md':
                        continue

                    if ignore:
                        p = re.compile(ignore)
                        matched = p.match(item_path.parts[-1])
                        if matched is not None:
                            logger.info(
                                f'Ignoring file {item_path} from validation.')
                            continue

                    md_api = MarkdownAPI()
                    if template_version != '':
                        template_file = self.template_dir / self.template_name
                    else:
                        instance_version = md_api.processor.fetch_value_from_header(
                            item_path, author_const.TEMPLATE_VERSION_HEADER)
                        if instance_version is None:
                            instance_version = '0.0.1'
                        versione_template_dir = TemplateVersioning.get_versioned_template_dir(
                            self.template_dir, instance_version)
                        template_file = versione_template_dir / self.template_name
                    if not template_file.is_file():
                        raise TrestleError(
                            f'Required template file: {self.rel_dir(template_file)} does not exist. Exiting.'
                        )
                    md_api.load_validator_with_template(
                        template_file, validate_header,
                        not validate_only_header, governed_heading)
                    if not md_api.validate_instance(item_path):
                        logger.info(f'INVALID: {self.rel_dir(item_path)}')
                        status = 1
                    else:
                        logger.info(f'VALID: {self.rel_dir(item_path)}')
                elif recurse:
                    if ignore:
                        p = re.compile(ignore)
                        if len(
                                list(
                                    filter(
                                        p.match,
                                        str(item_path.relative_to(
                                            md_dir)).split('/')))) > 0:
                            logger.info(
                                f'Ignoring directory {item_path} from validation.'
                            )
                            continue
                    rc = self._validate_dir(governed_heading, item_path,
                                            validate_header,
                                            validate_only_header, recurse,
                                            readme_validate, template_version,
                                            ignore)
                    if rc != 0:
                        status = rc

        return status
Exemple #6
0
    def _measure_template_folder(self, instance_dir: pathlib.Path,
                                 validate_header: bool,
                                 validate_only_header: bool,
                                 governed_heading: str, readme_validate: bool,
                                 template_version: str, ignore: str) -> bool:
        """
        Validate instances against templates.

        Validation will succeed iff:
            1. All template files from the specified version are present in the task
            2. All of the instances are valid
        """
        all_versioned_templates = {}
        instance_version = template_version
        instance_file_names: List[pathlib.Path] = []
        # Fetch all instances versions and build dictionary of required template files
        for instance_file in instance_dir.iterdir():
            if not file_utils.is_local_and_visible(instance_file):
                continue
            if not instance_file.is_file():
                continue
            if instance_file.name.lower(
            ) == 'readme.md' and not readme_validate:
                continue
            if ignore:
                p = re.compile(ignore)
                matched = p.match(instance_file.parts[-1])
                if matched is not None:
                    logger.info(
                        f'Ignoring file {instance_file} from validation.')
                    continue
            instance_file_name = instance_file.relative_to(instance_dir)
            instance_file_names.append(instance_file_name)
            if instance_file.suffix == '.md':
                md_api = MarkdownAPI()
                versioned_template_dir = None
                if template_version != '':
                    template_file = self.template_dir / instance_file_name
                    versioned_template_dir = self.template_dir
                else:
                    instance_version = md_api.processor.fetch_value_from_header(
                        instance_file, author_const.TEMPLATE_VERSION_HEADER)
                    if instance_version is None:
                        instance_version = '0.0.1'  # backward compatibility
                    versioned_template_dir = TemplateVersioning.get_versioned_template_dir(
                        self.template_dir, instance_version)
                    template_file = versioned_template_dir / instance_file_name

                if instance_version not in all_versioned_templates.keys():
                    templates = list(
                        filter(lambda p: file_utils.is_local_and_visible(p),
                               versioned_template_dir.iterdir()))
                    if not readme_validate:
                        templates = list(
                            filter(lambda p: p.name.lower() != 'readme.md',
                                   templates))

                    all_versioned_templates[instance_version] = dict.fromkeys([
                        t.relative_to(versioned_template_dir)
                        for t in templates
                    ], False)

                if instance_file_name in all_versioned_templates[
                        instance_version]:
                    # validate
                    md_api.load_validator_with_template(
                        template_file, validate_header,
                        not validate_only_header, governed_heading)
                    status = md_api.validate_instance(instance_file)
                    if not status:
                        logger.warning(
                            f'INVALID: Markdown file {instance_file} failed validation against'
                            + f' {template_file}')
                        return False
                    else:
                        logger.info(f'VALID: {instance_file}')
                    # mark template as present
                    all_versioned_templates[instance_version][
                        instance_file_name] = True

            elif instance_file.suffix == '.drawio':
                drawio = draw_io.DrawIO(instance_file)
                metadata = drawio.get_metadata()[0]
                versioned_template_dir = None
                if template_version != '':
                    template_file = self.template_dir / instance_file_name
                    versioned_template_dir = self.template_dir
                else:
                    if author_const.TEMPLATE_VERSION_HEADER in metadata.keys():
                        instance_version = metadata[
                            author_const.TEMPLATE_VERSION_HEADER]
                    else:
                        instance_version = '0.0.1'  # backward compatibility

                    versioned_template_dir = TemplateVersioning.get_versioned_template_dir(
                        self.template_dir, instance_version)
                    template_file = versioned_template_dir / instance_file_name

                if instance_version not in all_versioned_templates.keys():
                    templates = list(
                        filter(lambda p: file_utils.is_local_and_visible(p),
                               versioned_template_dir.iterdir()))
                    if not readme_validate:
                        templates = list(
                            filter(lambda p: p.name.lower() != 'readme.md',
                                   templates))

                    all_versioned_templates[instance_version] = dict.fromkeys([
                        t.relative_to(versioned_template_dir)
                        for t in templates
                    ], False)

                if instance_file_name in all_versioned_templates[
                        instance_version]:
                    # validate
                    drawio_validator = draw_io.DrawIOMetadataValidator(
                        template_file)
                    status = drawio_validator.validate(instance_file)
                    if not status:
                        logger.warning(
                            f'INVALID: Drawio file {instance_file} failed validation against'
                            + f' {template_file}')
                        return False
                    else:
                        logger.info(f'VALID: {instance_file}')
                    # mark template as present
                    all_versioned_templates[instance_version][
                        instance_file_name] = True

            else:
                logger.debug(
                    f'Unsupported extension of the instance file: {instance_file}, will not be validated.'
                )

        # Check that all template files are present
        for version in all_versioned_templates.keys():
            for template in all_versioned_templates[version]:
                if not all_versioned_templates[version][template]:
                    logger.warning(
                        f'Required template file {template} does not exist in measured instance'
                        + f'{instance_dir}')
                    return False

        return True