From ad23db65a8d516810b401e2ffe5cced0b236cc6e Mon Sep 17 00:00:00 2001 From: Tiago Nobrega Date: Wed, 11 Oct 2023 15:52:32 -0300 Subject: [PATCH] refactor: drop environment expansion Expansion of project environment is now done in craft-application; reorganize the code so that we still do Rockcraft-specific transformations (apply extensions and add the Pebble part), but we can remove the expansion-related code. --- rockcraft/application.py | 13 ++---- rockcraft/environment.py | 57 ------------------------ rockcraft/models/__init__.py | 9 +++- rockcraft/models/project.py | 39 +++++----------- tests/unit/extensions/test_extensions.py | 2 +- tests/unit/test_application.py | 11 +++++ 6 files changed, 34 insertions(+), 97 deletions(-) delete mode 100644 rockcraft/environment.py diff --git a/rockcraft/application.py b/rockcraft/application.py index 7871bb93f..f724f7171 100644 --- a/rockcraft/application.py +++ b/rockcraft/application.py @@ -18,10 +18,8 @@ from __future__ import annotations -import functools -import pathlib +from typing import Any -import craft_cli from craft_application import Application, AppMetadata, util from overrides import override @@ -39,12 +37,9 @@ class Rockcraft(Application): """Rockcraft application definition.""" - @functools.cached_property - def project(self) -> models.Project: - """Get this application's Project metadata.""" - project_file = (pathlib.Path.cwd() / f"{self.app.name}.yaml").resolve() - craft_cli.emit.debug(f"Loading project file '{project_file!s}'") - return models.Project.from_yaml_file(project_file, work_dir=self._work_dir) + @override + def _extra_yaml_transform(self, yaml_data: dict[str, Any]) -> dict[str, Any]: + return models.transform_yaml(self._work_dir, yaml_data) @override def _configure_services(self, platform: str | None, build_for: str | None) -> None: diff --git a/rockcraft/environment.py b/rockcraft/environment.py deleted file mode 100644 index da6c26061..000000000 --- a/rockcraft/environment.py +++ /dev/null @@ -1,57 +0,0 @@ -# -*- Mode:Python; indent-tabs-mode:nil; tab-width:4 -*- -# -# Copyright 2023 Canonical Ltd. -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 3 as -# published by the Free Software Foundation. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -"""Utilities to expand variables in project environments.""" - -from __future__ import annotations - -import pathlib -from typing import Any - -import craft_parts - - -def expand_environment( - project_yaml: dict[str, Any], - *, - project_vars: dict[str, Any], - work_dir: pathlib.Path, -) -> None: - """Expand global variables in the provided dictionary values. - - :param project_yaml: A dictionary containing the rockcraft.yaml's contents. - :param project_var: A dictionary with the project-specific variables. - :param work_dir: The working directory. - """ - info = craft_parts.ProjectInfo( - application_name="rockcraft", # not used in environment expansion - cache_dir=pathlib.Path(), # not used in environment expansion - project_name=project_yaml.get("name", ""), - project_dirs=craft_parts.ProjectDirs(work_dir=work_dir), - project_vars=project_vars, - ) - _set_global_environment(info) - - craft_parts.expand_environment(project_yaml, info=info) - - -def _set_global_environment(info: craft_parts.ProjectInfo) -> None: - """Set global environment variables.""" - info.global_environment.update( - { - "CRAFT_PROJECT_VERSION": info.get_project_var("version", raw_read=True), - } - ) diff --git a/rockcraft/models/__init__.py b/rockcraft/models/__init__.py index 97204e05d..f0b68c4be 100644 --- a/rockcraft/models/__init__.py +++ b/rockcraft/models/__init__.py @@ -17,6 +17,11 @@ """Rockcraft models.""" -from rockcraft.models.project import Project, RockcraftBuildInfo, load_project +from rockcraft.models.project import ( + Project, + RockcraftBuildInfo, + load_project, + transform_yaml, +) -__all__ = ["Project", "RockcraftBuildInfo", "load_project"] +__all__ = ["Project", "RockcraftBuildInfo", "load_project", "transform_yaml"] diff --git a/rockcraft/models/project.py b/rockcraft/models/project.py index d49c82cde..4b01de960 100644 --- a/rockcraft/models/project.py +++ b/rockcraft/models/project.py @@ -17,7 +17,6 @@ """Project definition and helpers.""" import dataclasses import operator -import pathlib import platform as host_platform import re from builtins import super @@ -40,15 +39,12 @@ import pydantic import spdx_lookup # type: ignore import yaml -from craft_application.errors import CraftValidationError from craft_application.models import BuildInfo from craft_application.models import Project as BaseProject from craft_archives import repo from craft_providers import bases -from overrides import override from pydantic_yaml import YamlModelMixin -from rockcraft.environment import expand_environment from rockcraft.errors import ProjectLoadError, ProjectValidationError from rockcraft.extensions import apply_extensions from rockcraft.parts import part_has_overlay, validate_part @@ -487,29 +483,6 @@ def unmarshal(cls, data: Dict[str, Any]) -> "Project": return project - @classmethod - @override - def from_yaml_file( - cls, path: pathlib.Path, *, work_dir: Optional[pathlib.Path] = None - ) -> "Project": - """Instantiate this model from a YAML file.""" - # pylint: disable=import-outside-toplevel - - data = load_project(path) - - if work_dir is not None: - project_vars = {"version": data["version"]} - expand_environment( - data, - project_vars=project_vars, - work_dir=work_dir, - ) - try: - # TODO apply extensions here - return cls.unmarshal(data) - except pydantic.ValidationError as err: - raise CraftValidationError.from_pydantic(err, file_name=path.name) from None - def generate_metadata( self, generation_time: str, base_digest: bytes ) -> Tuple[dict, dict]: @@ -679,7 +652,17 @@ def load_project(filename: Path) -> Dict[str, Any]: msg = f"{msg}: {err.filename!r}." raise ProjectLoadError(msg) from err - yaml_data = apply_extensions(filename.parent, yaml_data) + yaml_data = transform_yaml(filename.parent, yaml_data) + return yaml_data + + +def transform_yaml(project_root: Path, yaml_data: Dict[str, Any]) -> Dict[str, Any]: + """Do Rockcraft-specific transformations on a project yaml. + + :param project_root: The path that contains the "rockcraft.yaml" file. + :param yaml_data: The data dict loaded from the yaml file. + """ + yaml_data = apply_extensions(project_root, yaml_data) _add_pebble_data(yaml_data) diff --git a/tests/unit/extensions/test_extensions.py b/tests/unit/extensions/test_extensions.py index 99977a300..57e1b882e 100644 --- a/tests/unit/extensions/test_extensions.py +++ b/tests/unit/extensions/test_extensions.py @@ -17,7 +17,7 @@ import pytest from rockcraft import errors, extensions -from rockcraft.models.project import load_project +from rockcraft.models import load_project from tests.unit.testing.extensions import ( FULL_EXTENSION_YAML, ExperimentalExtension, diff --git a/tests/unit/test_application.py b/tests/unit/test_application.py index cace8e6a3..8aa650946 100644 --- a/tests/unit/test_application.py +++ b/tests/unit/test_application.py @@ -15,6 +15,8 @@ # along with this program. If not, see . from pathlib import Path +from rockcraft.pebble import Pebble + ENVIRONMENT_YAML = """\ name: environment-test version: 2.0 @@ -53,3 +55,12 @@ def test_application_expand_environment(default_application, new_dir): "X": "ship it!", "CRAFT_VAR": "2.0", } + + +def test_application_pebble_part(default_application, new_dir): + """Test that loading the project through the application adds the Pebble part.""" + project_file = Path(new_dir) / "rockcraft.yaml" + project_file.write_text(ENVIRONMENT_YAML) + + project = default_application.project + assert project.parts["pebble"] == Pebble.PEBBLE_PART_SPEC