Skip to content

Commit

Permalink
Allow use actions/config/metadata in charmcraft.yaml (#1126)
Browse files Browse the repository at this point in the history
Allow use actions/config/metadata in `charmcraft.yaml` when
corresponding yaml file does not exist.

Metadata in `charmcraft.yaml` also must be new keys defined in spec
ST087.

CRAFT-1747
CRAFT-1788
CRAFT-1789
  • Loading branch information
syu-w authored Jul 7, 2023
1 parent 17ad021 commit 044b94f
Show file tree
Hide file tree
Showing 29 changed files with 4,401 additions and 305 deletions.
21 changes: 12 additions & 9 deletions charmcraft/commands/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@
import charmcraft.parts
import charmcraft.providers
import charmcraft.instrum
from charmcraft.metafiles.metadata import parse_metadata_yaml
from charmcraft.metafiles.actions import create_actions
from charmcraft.metafiles.config import create_config_yaml
from charmcraft.metafiles.actions import create_actions_yaml
from charmcraft.metafiles.manifest import create_manifest
from charmcraft.const import (
BUILD_DIRNAME,
Expand All @@ -42,6 +42,7 @@
VENV_DIRNAME,
UBUNTU_LTS_STABLE,
)
from charmcraft.metafiles.metadata import create_metadata_yaml
from charmcraft.commands.store.charmlibs import collect_charmlib_pydeps
from charmcraft.models.charmcraft import Base, BasesConfiguration
from charmcraft.parts import Step
Expand Down Expand Up @@ -93,7 +94,6 @@ def __init__(self, *, config, force, debug, shell, shell_after, measure):
self.charmdir = config.project.dirpath
self.buildpath = self.charmdir / BUILD_DIRNAME
self.config = config
self.metadata = parse_metadata_yaml(self.charmdir)
self._parts = self.config.parts.copy()

# a part named "charm" using plugin "charm" is special and has
Expand Down Expand Up @@ -170,17 +170,20 @@ def build_charm(self, bases_config: BasesConfiguration) -> str:
self._parts,
work_dir=work_dir,
project_dir=self.charmdir,
project_name=self.metadata.name,
project_name=self.config.name,
ignore_local_sources=["*.charm"],
)
with charmcraft.instrum.Timer("Lifecycle run"):
lifecycle.run(Step.PRIME)

create_actions_yaml(lifecycle.prime_dir, self.config)
create_config_yaml(lifecycle.prime_dir, self.config)
create_metadata_yaml(lifecycle.prime_dir, self.config)

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

create_actions(lifecycle.prime_dir, self.config.actions)
create_manifest(
lifecycle.prime_dir,
self.config.project.started_at,
Expand Down Expand Up @@ -295,7 +298,7 @@ def pack_charm_in_instance(
self, *, bases_index: int, build_on: Base, build_on_index: int
) -> str:
"""Pack instance in Charm."""
charm_name = format_charm_file_name(self.metadata.name, self.config.bases[bases_index])
charm_name = format_charm_file_name(self.config.name, self.config.bases[bases_index])

# If building in project directory, use the project path as the working
# directory. The output charms will be placed in the correct directory
Expand Down Expand Up @@ -338,7 +341,7 @@ def pack_charm_in_instance(
instance_name = charmcraft.providers.get_instance_name(
bases_index=bases_index,
build_on_index=build_on_index,
project_name=self.metadata.name,
project_name=self.config.name,
project_path=self.charmdir,
target_arch=get_host_architecture(),
)
Expand All @@ -363,7 +366,7 @@ def pack_charm_in_instance(
)

with self.provider.launched_environment(
project_name=self.metadata.name,
project_name=self.config.name,
project_path=self.charmdir,
base_configuration=base_configuration,
instance_name=instance_name,
Expand Down Expand Up @@ -407,7 +410,7 @@ def pack_charm_in_instance(
def handle_package(self, prime_dir, bases_config: BasesConfiguration):
"""Handle the final package creation."""
emit.progress("Creating the package itself")
zipname = format_charm_file_name(self.metadata.name, bases_config)
zipname = format_charm_file_name(self.config.name, bases_config)
zipfh = zipfile.ZipFile(zipname, "w", zipfile.ZIP_DEFLATED)
for dirpath, dirnames, filenames in os.walk(prime_dir, followlinks=True):
dirpath = pathlib.Path(dirpath)
Expand Down
8 changes: 3 additions & 5 deletions charmcraft/commands/clean.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@

from charmcraft import providers
from charmcraft.cmdbase import BaseCommand
from charmcraft.metafiles.metadata import parse_metadata_yaml
from charmcraft.utils import get_host_architecture

_overview = """
Expand All @@ -47,8 +46,7 @@ def run(self, parsed_args):
"""
self._check_config(config_file=True, bases=True)
project_path = self.config.project.dirpath
metadata = parse_metadata_yaml(project_path)
emit.message(f"Cleaning project {metadata.name!r}.")
emit.message(f"Cleaning project {self.config.name!r}.")
provider = providers.get_provider()
build_plan = providers.create_build_plan(
bases=self.config.bases,
Expand All @@ -60,7 +58,7 @@ def run(self, parsed_args):

for plan in build_plan:
instance_name = providers.get_instance_name(
project_name=metadata.name,
project_name=self.config.name,
project_path=project_path,
bases_index=plan.bases_index,
build_on_index=plan.build_on_index,
Expand All @@ -70,4 +68,4 @@ def run(self, parsed_args):
emit.debug(f"Cleaning environment {instance_name!r}")
provider.clean_project_environments(instance_name=instance_name)

emit.message(f"Cleaned project {metadata.name!r}.")
emit.message(f"Cleaned project {self.config.name!r}.")
2 changes: 0 additions & 2 deletions charmcraft/commands/pack.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
from charmcraft.cmdbase import BaseCommand
from charmcraft.commands import build
from charmcraft.errors import DuplicateCharmsError
from charmcraft.metafiles.actions import create_actions
from charmcraft.metafiles.manifest import create_manifest
from charmcraft.parts import Step
from charmcraft.utils import (
Expand Down Expand Up @@ -310,7 +309,6 @@ def _pack_bundle(
raise

# pack everything
create_actions(lifecycle.prime_dir, self.config.actions)
create_manifest(lifecycle.prime_dir, project.started_at, None, [])
zipname = project.dirpath / (bundle_name + ".zip")
if overwrite_bundle:
Expand Down
4 changes: 2 additions & 2 deletions charmcraft/commands/store/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -999,10 +999,10 @@ def run(self, parsed_args: "Namespace") -> None:
emit.debug(f"Error when running PRIME step: {error}")
raise

from charmcraft.metafiles.actions import create_actions
from charmcraft.metafiles.metadata import create_metadata_yaml
from charmcraft.metafiles.manifest import create_manifest

create_actions(lifecycle.prime_dir, self.config.actions)
create_metadata_yaml(lifecycle.prime_dir, self.config)
create_manifest(lifecycle.prime_dir, self.config.project.started_at, None, [])
zipname = bundle_dir_path / (bundle_name + ".zip")
build_zip(zipname, lifecycle.prime_dir)
Expand Down
3 changes: 3 additions & 0 deletions charmcraft/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,9 @@ def load(dirpath: Optional[str]) -> CharmcraftConfig:
config_provided=False,
started_at=now,
),
name="missing-charm-name",
summary="missing-charm-summary",
description="missing-charm-description",
)

return CharmcraftConfig.unmarshal(
Expand Down
119 changes: 93 additions & 26 deletions charmcraft/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@
"""Constants used in charmcraft."""

METADATA_FILENAME = "metadata.yaml"
JUJU_ACTIONS_FILENAME = "actions.yaml"
JUJU_CONFIG_FILENAME = "config.yaml"

IMAGE_INFO_ENV_VAR = "CHARMCRAFT_IMAGE_INFO"

WORK_DIRNAME = "work_dir"
Expand All @@ -40,34 +43,98 @@
"""

# The minimum set of hooks to be provided for compatibility with old Juju
MANDATORY_HOOK_NAMES = {"install", "start", "upgrade-charm"}
MANDATORY_HOOK_NAMES = frozenset(("install", "start", "upgrade-charm"))
HOOKS_DIRNAME = "hooks"

# The minimum set of files for a charm to be considered valid
CHARM_FILES = [
METADATA_FILENAME,
DISPATCH_FILENAME,
HOOKS_DIRNAME,
]
CHARM_FILES = frozenset(
(
DISPATCH_FILENAME,
HOOKS_DIRNAME,
)
)

# Optional files that can be present in a charm
CHARM_OPTIONAL = [
"config.yaml",
"metrics.yaml",
"actions.yaml",
"lxd-profile.yaml",
"templates",
"version",
"lib",
"mod",
"LICENSE",
"icon.svg",
"README.md",
"actions",
]

UBUNTU_LTS_STABLE = {
"18.04",
"20.04",
"22.04",
}
CHARM_OPTIONAL = frozenset(
(
METADATA_FILENAME,
JUJU_ACTIONS_FILENAME,
JUJU_CONFIG_FILENAME,
"metrics.yaml",
"lxd-profile.yaml",
"templates",
"version",
"lib",
"mod",
"LICENSE",
"icon.svg",
"README.md",
"actions",
)
)

UBUNTU_LTS_STABLE = frozenset(
(
"18.04",
"20.04",
"22.04",
)
)

# Metadata keys that are defined in the metadata.yaml file, for backwards compatible
CHARM_METADATA_LEGACY_KEYS = frozenset(
(
"assumes",
"containers",
"description",
"devices",
"display-name",
"docs",
"extra-bindings",
"issues",
"maintainers",
"name",
"peers",
"provides",
"requires",
"resources",
"series",
"storage",
"subordinate",
"summary",
"terms",
"website",
)
)

CHARM_METADATA_LEGARY_KEYS_ALIAS = frozenset(
(
"display_name",
"extra_bindings",
)
)

# Metadata keys that are allowed in the charmcraft.yaml file
CHARM_METADATA_KEYS = frozenset(
(
"assumes",
"containers",
"description",
"devices",
"title",
"documentation",
"extra-bindings",
"links",
"name",
"peers",
"provides",
"requires",
"resources",
"storage",
"subordinate",
"summary",
"terms",
)
)

CHARM_METADATA_KEYS_ALIAS = frozenset(("extra_bindings",))
4 changes: 2 additions & 2 deletions charmcraft/linters.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
import yaml

from charmcraft import config, utils
from charmcraft.metafiles.metadata import parse_metadata_yaml, read_metadata_yaml
from charmcraft.metafiles.metadata import parse_charm_metadata_yaml, read_metadata_yaml

CheckType = namedtuple("CheckType", "attribute lint")(attribute="attribute", lint="lint")

Expand Down Expand Up @@ -184,7 +184,7 @@ def _check_operator(self, basedir: pathlib.Path) -> bool:
def _check_reactive(self, basedir: pathlib.Path) -> bool:
"""Detect if the Reactive Framework is used."""
try:
metadata = parse_metadata_yaml(basedir)
metadata = parse_charm_metadata_yaml(basedir)
except Exception:
# file not found, corrupted, or mandatory "name" not present
return False
Expand Down
19 changes: 18 additions & 1 deletion charmcraft/metafiles/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,21 @@

"""Submodule for handling the all extra yaml files."""

__all__ = ["actions", "manifest", "metadata"]
import pathlib
from typing import Any, Dict

import yaml

from craft_cli import emit

__all__ = ["actions", "manifest", "metadata", "config", "read_yaml"]


def read_yaml(yaml_file_path: pathlib.Path) -> Dict[str, Any]:
"""Parse yaml file.
:returns: the YAML decoded yaml content
"""
emit.debug(f"Reading {str(yaml_file_path)!r}")
with yaml_file_path.open("rt", encoding="utf8") as fh:
return yaml.safe_load(fh)
Loading

0 comments on commit 044b94f

Please sign in to comment.