Ejemplo n.º 1
0
def test_analyze_ignore_linter(config):
    """Run all checkers except the ignored linter."""
    FakeChecker1 = create_fake_checker(
        check_type=CheckType.attribute,
        name="name1",
        result="res1",
        text="text1",
        url="url1",
    )
    FakeChecker2 = create_fake_checker(
        check_type=CheckType.lint,
        name="name2",
        result="res2",
        text="text2",
        url="url2",
    )

    config.analysis.ignore.linters.append("name2")
    with patch("charmcraft.linters.CHECKERS", [FakeChecker1, FakeChecker2]):
        result = analyze(config, "somepath")

    res1, res2 = result
    assert res1.check_type == CheckType.attribute
    assert res1.name == "name1"
    assert res1.result == "res1"
    assert res1.text == "text1"
    assert res1.url == "url1"
    assert res2.check_type == CheckType.lint
    assert res2.name == "name2"
    assert res2.result == IGNORED
    assert res2.text == ""
    assert res2.url == "url2"
Ejemplo n.º 2
0
def test_analyze_run_everything(config):
    """Check that analyze runs all and collect the results."""
    FakeChecker1 = create_fake_checker(check_type="type1",
                                       name="name1",
                                       url="url1",
                                       text="text1",
                                       result="result1")
    FakeChecker2 = create_fake_checker(check_type="type2",
                                       name="name2",
                                       url="url2",
                                       text="text2",
                                       result="result2")

    # hack the first fake checker to validate that it receives the indicated path
    def dir_validator(self, basedir):
        assert basedir == "test-buildpath"
        return "result1"

    FakeChecker1.run = dir_validator

    with patch("charmcraft.linters.CHECKERS", [FakeChecker1, FakeChecker2]):
        result = analyze(config, "test-buildpath")

    r1, r2 = result
    assert r1.check_type == "type1"
    assert r1.name == "name1"
    assert r1.url == "url1"
    assert r1.text == "text1"
    assert r1.result == "result1"
    assert r2.check_type == "type2"
    assert r2.name == "name2"
    assert r2.url == "url2"
    assert r2.text == "text2"
    assert r2.result == "result2"
Ejemplo n.º 3
0
def test_analyze_all_can_be_ignored(config):
    """Control that all real life checkers can be ignored."""
    config.analysis.ignore.attributes.extend(
        c.name for c in CHECKERS if c.check_type == CheckType.attribute)
    config.analysis.ignore.linters.extend(c.name for c in CHECKERS
                                          if c.check_type == CheckType.lint)
    result = analyze(config, "somepath")
    assert all(r.result == IGNORED for r in result)
Ejemplo n.º 4
0
    def build_charm(self, bases_config: BasesConfiguration) -> str:
        """Build the charm.

        :param bases_config: Bases configuration to use for build.

        :returns: File name of charm.

        :raises CraftError: on lifecycle exception.
        :raises RuntimeError: on unexpected lifecycle exception.
        """
        if env.is_charmcraft_running_in_managed_mode():
            work_dir = env.get_managed_environment_home_path()
        else:
            work_dir = self.buildpath

        emit.progress(f"Building charm in {str(work_dir)!r}")

        if self._special_charm_part:
            # all current deprecated arguments set charm plugin parameters
            self._handle_deprecated_cli_arguments()

            # add charm files to the prime filter
            self._set_prime_filter()

            # set source if empty or not declared in charm part
            if not self._special_charm_part.get("source"):
                self._special_charm_part["source"] = str(self.charmdir)

        # run the parts lifecycle
        emit.trace(f"Parts definition: {self._parts}")
        lifecycle = parts.PartsLifecycle(
            self._parts,
            work_dir=work_dir,
            project_dir=self.charmdir,
            project_name=self.metadata.name,
            ignore_local_sources=["*.charm"],
        )
        lifecycle.run(Step.PRIME)

        # run linters and show the results
        linting_results = linters.analyze(self.config, lifecycle.prime_dir)
        self.show_linting_results(linting_results)

        create_manifest(
            lifecycle.prime_dir,
            self.config.project.started_at,
            bases_config,
            linting_results,
        )

        zipname = self.handle_package(lifecycle.prime_dir, bases_config)
        emit.message(f"Created '{zipname}'.", intermediate=True)
        return zipname
Ejemplo n.º 5
0
def test_analyze_crash_lint(config):
    """The lint checker crashes."""
    FakeChecker = create_fake_checker(check_type=CheckType.lint,
                                      name="name",
                                      text="text",
                                      url="url")

    def raises(*a):
        raise ValueError

    FakeChecker.run = raises

    with patch("charmcraft.linters.CHECKERS", [FakeChecker]):
        result = analyze(config, "somepath")

    (res, ) = result
    assert res.check_type == CheckType.lint
    assert res.name == "name"
    assert res.result == FATAL
    assert res.text == "text"
    assert res.url == "url"
Ejemplo n.º 6
0
def test_analyze_override_ignore(config):
    """Run all checkers even the ignored ones, if requested."""
    FakeChecker1 = create_fake_checker(check_type=CheckType.attribute,
                                       name="name1",
                                       result="res1")
    FakeChecker2 = create_fake_checker(check_type=CheckType.lint,
                                       name="name2",
                                       result="res2")

    config.analysis.ignore.attributes.append("name1")
    config.analysis.ignore.linters.append("name2")
    with patch("charmcraft.linters.CHECKERS", [FakeChecker1, FakeChecker2]):
        result = analyze(config, "somepath", override_ignore_config=True)

    res1, res2 = result
    assert res1.check_type == CheckType.attribute
    assert res1.name == "name1"
    assert res1.result == "res1"
    assert res2.check_type == CheckType.lint
    assert res2.name == "name2"
    assert res2.result == "res2"
Ejemplo n.º 7
0
    def build_charm(self, bases_config: BasesConfiguration) -> str:
        """Build the charm.

        :param bases_config: Bases configuration to use for build.

        :returns: File name of charm.
        """
        logger.debug("Building charm in %r", str(self.buildpath))

        self._handle_deprecated_cli_arguments()

        # add charm files to the prime filter
        self._set_prime_filter()

        # set source for buiding
        self._charm_part["source"] = str(self.charmdir)

        # run the parts lifecycle
        logger.debug("Parts definition: %s", self._parts)
        lifecycle = parts.PartsLifecycle(
            self._parts,
            work_dir=self.buildpath,
            ignore_local_sources=["*.charm"],
        )
        lifecycle.run(Step.PRIME)

        # run linters and show the results
        linting_results = linters.analyze(self.config, lifecycle.prime_dir)
        self.show_linting_results(linting_results)

        create_manifest(
            lifecycle.prime_dir,
            self.config.project.started_at,
            bases_config,
            linting_results,
        )

        zipname = self.handle_package(lifecycle.prime_dir, bases_config)
        logger.info("Created '%s'.", zipname)
        return zipname
Ejemplo n.º 8
0
    def run(self, parsed_args):
        """Run the command."""
        tmpdir = self._unzip_charm(parsed_args.filepath)

        # run the analyzer
        override_ignore_config = bool(parsed_args.force)
        linting_results = linters.analyze(
            self.config,
            tmpdir,
            override_ignore_config=override_ignore_config,
        )

        # if format is json almost no further processing is needed
        if parsed_args.format == JSON_FORMAT:
            info = [{
                "name": r.name,
                "result": r.result,
                "url": r.url,
                "type": r.check_type,
            } for r in linting_results]
            emit.message(json.dumps(info, indent=4))
            return

        # group by attributes and lint outcomes (discarding ignored ones)
        grouped = {}
        for result in linting_results:
            if result.check_type == linters.CheckType.attribute:
                group_key = linters.CheckType.attribute
                result_info = result.result
            else:
                # linters
                group_key = result.result
                if result.result == linters.OK:
                    result_info = "no issues found"
                elif result.result in (linters.FATAL, linters.IGNORED):
                    result_info = None
                else:
                    result_info = result.text
            grouped.setdefault(group_key, []).append((result, result_info))

        # present the results
        titles = [
            ("Attributes", linters.CheckType.attribute),
            ("Lint Ignored", linters.IGNORED),
            ("Lint Warnings", linters.WARNINGS),
            ("Lint Errors", linters.ERRORS),
            ("Lint Fatal", linters.FATAL),
            ("Lint OK", linters.OK),
        ]
        for title, key in titles:
            results = grouped.get(key)
            if results is not None:
                emit.message(f"{title}:")
                for result, result_info in results:
                    if result_info:
                        emit.message(
                            f"- {result.name}: { result_info} ({result.url})")
                    else:
                        emit.message(f"- {result.name} ({result.url})")

        # the return code depends on the presence of different issues
        if linters.FATAL in grouped:
            retcode = 1
        elif linters.ERRORS in grouped:
            retcode = 2
        elif linters.WARNINGS in grouped:
            retcode = 3
        else:
            retcode = 0

        return retcode
Ejemplo n.º 9
0
    def run(self, parsed_args):
        """Run the command."""
        tmpdir = tempfile.mkdtemp()
        filepath = str(parsed_args.filepath)
        try:
            zf = zipfile.ZipFile(filepath)
            zf.extractall(path=tmpdir)
        except Exception as exc:
            raise CommandError(
                "Cannot open the indicated charm file {!r}: {!r}.".format(
                    filepath, exc))

        # run the analyzer
        override_ignore_config = bool(parsed_args.force)
        linting_results = linters.analyze(
            self.config,
            pathlib.Path(tmpdir),
            override_ignore_config=override_ignore_config,
        )

        # if format is json almost no further processing is needed
        if parsed_args.format == JSON_FORMAT:
            info = [{
                "name": r.name,
                "result": r.result,
                "url": r.url,
                "type": r.check_type,
            } for r in linting_results]
            logger.info(json.dumps(info, indent=4))
            return

        # group by attributes and lint outcomes (discarding ignored ones)
        grouped = {}
        for result in linting_results:
            if result.check_type == linters.CheckType.attribute:
                group_key = linters.CheckType.attribute
                result_info = result.result
            else:
                # linters
                group_key = result.result
                if result.result == linters.OK:
                    result_info = "no issues found"
                elif result.result in (linters.FATAL, linters.IGNORED):
                    result_info = None
                else:
                    result_info = result.text
            grouped.setdefault(group_key, []).append((result, result_info))

        # present the results
        titles = [
            ("Attributes", linters.CheckType.attribute),
            ("Lint Ignored", linters.IGNORED),
            ("Lint Warnings", linters.WARNINGS),
            ("Lint Errors", linters.ERRORS),
            ("Lint Fatal", linters.FATAL),
            ("Lint OK", linters.OK),
        ]
        for title, key in titles:
            results = grouped.get(key)
            if results is not None:
                logger.info("%s:", title)
                for result, result_info in results:
                    if result_info:
                        logger.info("- %s: %s (%s)", result.name, result_info,
                                    result.url)
                    else:
                        logger.info("- %s (%s)", result.name, result.url)

        # the return code depends on the presence of different issues
        if linters.FATAL in grouped:
            retcode = 1
        elif linters.ERRORS in grouped:
            retcode = 2
        elif linters.WARNINGS in grouped:
            retcode = 3
        else:
            retcode = 0

        return retcode