def test_manifest_image_info_bad(tmp_path, monkeypatch):
    """The format of the image info environment variable is wrong."""
    monkeypatch.setenv(IMAGE_INFO_ENV_VAR, "this is not a json")
    tstamp = datetime.datetime(2020, 2, 1, 15, 40, 33)
    with pytest.raises(CraftError) as cm:
        create_manifest(tmp_path, tstamp, None, [])
    exc = cm.value
    assert str(exc) == "Failed to parse the content of CHARMCRAFT_IMAGE_INFO environment variable"
    assert isinstance(exc.__cause__, json.JSONDecodeError)
Exemple #2
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
Exemple #3
0
    def _pack_bundle(self):
        """Pack a bundle."""
        project = self.config.project
        config_parts = self.config.parts.copy()
        bundle_part = config_parts.setdefault("bundle", {})
        prime = bundle_part.setdefault("prime", [])

        # get the config files
        bundle_filepath = project.dirpath / "bundle.yaml"
        bundle_config = load_yaml(bundle_filepath)
        if bundle_config is None:
            raise CommandError(
                "Missing or invalid main bundle file: {!r}.".format(str(bundle_filepath))
            )
        bundle_name = bundle_config.get("name")
        if not bundle_name:
            raise CommandError(
                "Invalid bundle config; missing a 'name' field indicating the bundle's name in "
                "file {!r}.".format(str(bundle_filepath))
            )

        # set prime filters
        for fname in MANDATORY_FILES:
            fpath = project.dirpath / fname
            if not fpath.exists():
                raise CommandError("Missing mandatory file: {!r}.".format(str(fpath)))
        prime.extend(MANDATORY_FILES)

        # set source for buiding
        bundle_part["source"] = str(project.dirpath)

        # run the parts lifecycle
        logger.debug("Parts definition: %s", config_parts)
        lifecycle = parts.PartsLifecycle(
            config_parts,
            work_dir=project.dirpath / build.BUILD_DIRNAME,
            ignore_local_sources=[bundle_name + ".zip"],
        )
        lifecycle.run(Step.PRIME)

        # pack everything
        create_manifest(lifecycle.prime_dir, project.started_at, None, [])
        zipname = project.dirpath / (bundle_name + ".zip")
        build_zip(zipname, lifecycle.prime_dir)

        logger.info("Created %r.", str(zipname))
def test_manifest_image_info_ok(tmp_path, monkeypatch):
    """Include the image info in the manifest."""
    test_image_content = {"some info": ["whatever", 123]}
    monkeypatch.setenv(IMAGE_INFO_ENV_VAR, json.dumps(test_image_content))

    tstamp = datetime.datetime(2020, 2, 1, 15, 40, 33)
    os_platform = OSPlatform(system="SuperUbuntu", release="40.10", machine="SomeRISC")
    with patch("charmcraft.utils.get_os_platform", return_value=os_platform):
        result_filepath = create_manifest(tmp_path, tstamp, None, [])

    saved = yaml.safe_load(result_filepath.read_text())
    assert saved["image-info"] == test_image_content
Exemple #5
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
def test_manifest_no_bases(tmp_path):
    """Manifest without bases (used for bundles)."""
    tstamp = datetime.datetime(2020, 2, 1, 15, 40, 33)
    os_platform = OSPlatform(system="SuperUbuntu", release="40.10", machine="SomeRISC")
    with patch("charmcraft.utils.get_os_platform", return_value=os_platform):
        result_filepath = create_manifest(tmp_path, tstamp, None, [])

    saved = yaml.safe_load(result_filepath.read_text())

    assert result_filepath == tmp_path / "manifest.yaml"
    assert saved == {
        "charmcraft-started-at": "2020-02-01T15:40:33Z",
        "charmcraft-version": __version__,
        "analysis": {"attributes": []},
    }
Exemple #7
0
def test_manifest_checkers_multiple(tmp_path):
    """Multiple checkers, attributes and a linter."""
    linting_results = [
        linters.CheckResult(
            name="attrib-name-1",
            check_type=linters.CheckType.attribute,
            url="url",
            text="text",
            result="result-1",
        ),
        linters.CheckResult(
            name="attrib-name-2",
            check_type=linters.CheckType.attribute,
            url="url",
            text="text",
            result="result-2",
        ),
        linters.CheckResult(
            name="warning-name",
            check_type=linters.CheckType.lint,
            url="url",
            text="text",
            result="result",
        ),
    ]

    tstamp = datetime.datetime(2020, 2, 1, 15, 40, 33)
    os_platform = OSPlatform(system="SuperUbuntu",
                             release="40.10",
                             machine="SomeRISC")
    with patch("charmcraft.utils.get_os_platform", return_value=os_platform):
        result_filepath = create_manifest(tmp_path, tstamp, None,
                                          linting_results)

    assert result_filepath == tmp_path / "manifest.yaml"
    saved = yaml.safe_load(result_filepath.read_text())
    expected = [
        {
            "name": "attrib-name-1",
            "result": "result-1",
        },
        {
            "name": "attrib-name-2",
            "result": "result-2",
        },
    ]
    assert saved["analysis"]["attributes"] == expected
Exemple #8
0
    def _pack_bundle(self, parsed_args) -> List[pathlib.Path]:
        """Pack a bundle."""
        emit.progress("Packing the bundle.")
        if parsed_args.shell:
            build.launch_shell()
            return []

        project = self.config.project

        if self.config.parts:
            config_parts = self.config.parts.copy()
        else:
            # "parts" not declared, create an implicit "bundle" part
            config_parts = {"bundle": {"plugin": "bundle"}}

        # a part named "bundle" using plugin "bundle" is special and has
        # predefined values set automatically.
        bundle_part = config_parts.get("bundle")
        if bundle_part and bundle_part.get("plugin") == "bundle":
            special_bundle_part = bundle_part
        else:
            special_bundle_part = None

        # get the config files
        bundle_filepath = project.dirpath / "bundle.yaml"
        bundle_config = load_yaml(bundle_filepath)
        if bundle_config is None:
            raise CraftError(
                "Missing or invalid main bundle file: {!r}.".format(
                    str(bundle_filepath)))
        bundle_name = bundle_config.get("name")
        if not bundle_name:
            raise CraftError(
                "Invalid bundle config; missing a 'name' field indicating the bundle's name in "
                "file {!r}.".format(str(bundle_filepath)))

        if special_bundle_part:
            # set prime filters
            for fname in MANDATORY_FILES:
                fpath = project.dirpath / fname
                if not fpath.exists():
                    raise CraftError("Missing mandatory file: {!r}.".format(
                        str(fpath)))
            prime = special_bundle_part.setdefault("prime", [])
            prime.extend(MANDATORY_FILES)

            # set source if empty or not declared in charm part
            if not special_bundle_part.get("source"):
                special_bundle_part["source"] = str(project.dirpath)

        if env.is_charmcraft_running_in_managed_mode():
            work_dir = env.get_managed_environment_home_path()
        else:
            work_dir = project.dirpath / build.BUILD_DIRNAME

        # run the parts lifecycle
        emit.trace(f"Parts definition: {config_parts}")
        lifecycle = parts.PartsLifecycle(
            config_parts,
            work_dir=work_dir,
            project_dir=project.dirpath,
            project_name=bundle_name,
            ignore_local_sources=[bundle_name + ".zip"],
        )
        try:
            lifecycle.run(Step.PRIME)
        except (RuntimeError, CraftError) as error:
            if parsed_args.debug:
                emit.trace(f"Error when running PRIME step: {error}")
                build.launch_shell()
            raise

        # pack everything
        create_manifest(lifecycle.prime_dir, project.started_at, None, [])
        zipname = project.dirpath / (bundle_name + ".zip")
        build_zip(zipname, lifecycle.prime_dir)

        emit.message(f"Created {str(zipname)!r}.")

        if parsed_args.shell_after:
            build.launch_shell()

        return [zipname]
def test_manifest_simple_ok(tmp_path):
    """Simple construct."""
    bases_config = config.BasesConfiguration(
        **{
            "build-on": [
                config.Base(
                    name="test-name",
                    channel="test-channel",
                ),
            ],
            "run-on": [
                config.Base(
                    name="test-name",
                    channel="test-channel",
                    architectures=["arch1"],
                ),
                config.Base(
                    name="test-name2",
                    channel="test-channel2",
                    architectures=["arch1", "arch2"],
                ),
            ],
        }
    )

    linting_results = [
        linters.CheckResult(
            name="check-name",
            check_type=linters.CheckType.attribute,
            url="url",
            text="text",
            result="check-result",
        ),
    ]

    tstamp = datetime.datetime(2020, 2, 1, 15, 40, 33)
    os_platform = OSPlatform(system="SuperUbuntu", release="40.10", machine="SomeRISC")
    with patch("charmcraft.utils.get_os_platform", return_value=os_platform):
        result_filepath = create_manifest(tmp_path, tstamp, bases_config, linting_results)

    assert result_filepath == tmp_path / "manifest.yaml"
    saved = yaml.safe_load(result_filepath.read_text())
    expected = {
        "charmcraft-started-at": "2020-02-01T15:40:33Z",
        "charmcraft-version": __version__,
        "bases": [
            {
                "name": "test-name",
                "channel": "test-channel",
                "architectures": ["arch1"],
            },
            {
                "name": "test-name2",
                "channel": "test-channel2",
                "architectures": ["arch1", "arch2"],
            },
        ],
        "analysis": {
            "attributes": [
                {
                    "name": "check-name",
                    "result": "check-result",
                },
            ],
        },
    }
    assert saved == expected