Skip to content

Commit

Permalink
refactor: use init command from craft-application (#5129)
Browse files Browse the repository at this point in the history
The init command accepts additional parameters `--profile`, `--name`,
and `project-dir`.  If `--name` is not provided, the name of the snap will
be the name of the directory in which snapcraft is initialised.

Fixes #5098

Signed-off-by: Callahan Kovacs <[email protected]>
  • Loading branch information
mr-cal authored Nov 8, 2024
1 parent 2b2cb61 commit de05a73
Show file tree
Hide file tree
Showing 23 changed files with 385 additions and 187 deletions.
1 change: 1 addition & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
include schema/*
include extensions/*
recursive-include snapcraft/templates *
6 changes: 3 additions & 3 deletions requirements-devel.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ click==8.1.7
codespell==2.3.0
colorama==0.4.6
coverage==7.6.1
craft-application==4.2.7
craft-application==4.4.0
craft-archives==2.0.1
craft-cli==2.7.0
craft-cli==2.10.0
craft-grammar==2.0.1
craft-parts==2.1.2
craft-platforms==0.4.0
Expand Down Expand Up @@ -89,7 +89,7 @@ pbr==6.0.0
pexpect==4.9.0
plaster==1.1.2
plaster-pastedeploy==1.0.1
platformdirs==4.2.2
platformdirs==4.3.6
pluggy==1.5.0
polib==1.2.0
progressbar==2.5
Expand Down
6 changes: 3 additions & 3 deletions requirements-docs.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ chardet==5.2.0
charset-normalizer==3.3.2
click==8.1.7
colorama==0.4.6
craft-application==4.2.7
craft-application==4.4.0
craft-archives==2.0.1
craft-cli==2.7.0
craft-cli==2.10.0
craft-grammar==2.0.1
craft-parts==2.1.2
craft-platforms==0.4.0
Expand Down Expand Up @@ -69,7 +69,7 @@ natsort==8.4.0
oauthlib==3.2.2
overrides==7.7.0
packaging==24.1
platformdirs==4.2.2
platformdirs==4.3.6
polib==1.2.0
progressbar==2.5
protobuf==5.27.3
Expand Down
6 changes: 3 additions & 3 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ cffi==1.17.1
chardet==5.2.0
charset-normalizer==3.3.2
click==8.1.7
craft-application==4.2.7
craft-application==4.4.0
craft-archives==2.0.1
craft-cli==2.7.0
craft-cli==2.10.0
craft-grammar==2.0.1
craft-parts==2.1.2
craft-platforms==0.4.0
Expand Down Expand Up @@ -37,7 +37,7 @@ mypy-extensions==1.0.0
oauthlib==3.2.2
overrides==7.7.0
packaging==24.1
platformdirs==4.2.2
platformdirs==4.3.6
progressbar==2.5
protobuf==5.27.3
psutil==6.0.0
Expand Down
8 changes: 6 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,9 @@ def recursive_data_files(directory, install_directory):
"attrs",
"catkin-pkg; sys_platform == 'linux'",
"click",
"craft-application>=4.2.7,<5.0.0",
"craft-application~=4.4",
"craft-archives~=2.0",
"craft-cli~=2.6",
"craft-cli~=2.9",
"craft-grammar>=2.0.1,<3.0.0",
"craft-parts>=2.1.2,<3.0.0",
"craft-platforms~=0.4",
Expand Down Expand Up @@ -174,6 +174,10 @@ def recursive_data_files(directory, install_directory):
+ recursive_data_files("keyrings", "share/snapcraft")
+ recursive_data_files("extensions", "share/snapcraft")
),
include_package_data=True,
package_data={
"snapcraft": ["templates/*"],
},
python_requires=">=3.10",
install_requires=install_requires,
extras_require=extras_requires,
Expand Down
12 changes: 1 addition & 11 deletions snapcraft/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,16 +150,6 @@ def _configure_services(self, provider_name: str | None) -> None:

super()._configure_services(provider_name)

@property
def command_groups(self):
"""Replace craft-application's LifecycleCommand group."""
_command_groups = super().command_groups
for index, command_group in enumerate(_command_groups):
if command_group.name == "Lifecycle":
_command_groups[index] = cli.CORE24_LIFECYCLE_COMMAND_GROUP

return _command_groups

@override
def _resolve_project_path(self, project_dir: pathlib.Path | None) -> pathlib.Path:
"""Overridden to handle the two possible locations for snapcraft.yaml."""
Expand Down Expand Up @@ -467,7 +457,7 @@ def create_app() -> Snapcraft:
services=snapcraft_services,
)

for group in cli.COMMAND_GROUPS:
for group in [cli.CORE24_LIFECYCLE_COMMAND_GROUP, *cli.COMMAND_GROUPS]:
app.add_command_group(group.name, group.commands)

return app
Expand Down
1 change: 0 additions & 1 deletion snapcraft/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,6 @@
"Other",
[
commands.LintCommand,
commands.InitCommand,
],
),
]
Expand Down
2 changes: 0 additions & 2 deletions snapcraft/commands/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
ExtensionsCommand,
ListExtensionsCommand,
)
from .init import InitCommand
from .legacy import (
StoreLegacyCreateKeyCommand,
StoreLegacyGatedCommand,
Expand Down Expand Up @@ -67,7 +66,6 @@
__all__ = [
"ExpandExtensionsCommand",
"ExtensionsCommand",
"InitCommand",
"LintCommand",
"ListExtensionsCommand",
"ListPluginsCommand",
Expand Down
89 changes: 0 additions & 89 deletions snapcraft/commands/init.py

This file was deleted.

2 changes: 1 addition & 1 deletion snapcraft/extensions/env_injector.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ def get_parts_snippet(self) -> Dict[str, Any]:
cargo build --target {toolchain} --release
mkdir -p $SNAPCRAFT_PART_INSTALL/bin/command-chain
cp target/{toolchain}/release/env-exporter $SNAPCRAFT_PART_INSTALL/bin/command-chain
""",
}
Expand Down
6 changes: 3 additions & 3 deletions snapcraft/models/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ def _validate_version_name(version: str, model_name: str) -> None:
)


def _validate_name(*, name: str, field_name: str) -> str:
def validate_name(*, name: str, field_name: str) -> str:
"""Validate a name.
:param name: The name to validate.
Expand Down Expand Up @@ -261,7 +261,7 @@ def _validate_component(name: str) -> str:
raise ValueError(
"component names cannot start with the reserved prefix 'snap-'"
)
return _validate_name(name=name, field_name="component")
return validate_name(name=name, field_name="component")


def _get_partitions_from_components(
Expand Down Expand Up @@ -729,7 +729,7 @@ def _validate_mandatory_base(self):
@pydantic.field_validator("name")
@classmethod
def _validate_snap_name(cls, name):
return _validate_name(name=name, field_name="snap")
return validate_name(name=name, field_name="snap")

@pydantic.field_validator("components")
@classmethod
Expand Down
12 changes: 10 additions & 2 deletions snapcraft/parts/yaml_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -218,13 +218,21 @@ def apply_yaml(
return yaml_data


def get_snap_project() -> _SnapProject:
def get_snap_project(project_dir: Path | None = None) -> _SnapProject:
"""Find the snapcraft.yaml to load.
:param project_dir: The directory to search for the project yaml file. If not
provided, the current working directory is used.
:raises SnapcraftError: if the project yaml file cannot be found.
"""
for snap_project in _SNAP_PROJECT_FILES:
if snap_project.project_file.exists():
if project_dir:
snap_project_path = project_dir / snap_project.project_file
else:
snap_project_path = snap_project.project_file

if snap_project_path.exists():
return snap_project

raise errors.ProjectMissing()
Expand Down
2 changes: 2 additions & 0 deletions snapcraft/services/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"""Snapcraft services."""

from snapcraft.services.assertions import Assertion
from snapcraft.services.init import Init
from snapcraft.services.lifecycle import Lifecycle
from snapcraft.services.package import Package
from snapcraft.services.provider import Provider
Expand All @@ -26,6 +27,7 @@

__all__ = [
"Assertion",
"Init",
"Lifecycle",
"Package",
"Provider",
Expand Down
83 changes: 83 additions & 0 deletions snapcraft/services/init.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# -*- Mode:Python; indent-tabs-mode:nil; tab-width:4 -*-
#
# Copyright 2024 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 <http://www.gnu.org/licenses/>.

"""Service for initializing a project."""

import pathlib

import craft_cli
from craft_application import services
from typing_extensions import override

from snapcraft import errors
from snapcraft.models.project import validate_name
from snapcraft.parts.yaml_utils import get_snap_project


class Init(services.InitService):
"""Service class for initializing a project."""

@override
def initialise_project(
self,
*,
project_dir: pathlib.Path,
project_name: str,
template_dir: pathlib.Path,
) -> None:
try:
validate_name(name=project_name, field_name="snap")
if len(project_name) > 40:
raise ValueError("snap names must be 40 characters or less")
except ValueError as err:
raise errors.SnapcraftError(
message=f"Invalid snap name {project_name!r}: {str(err)}.",
resolution="Provide a valid name with '--name' or rename the project directory.",
) from err

super().initialise_project(
project_dir=project_dir,
project_name=project_name,
template_dir=template_dir,
)
craft_cli.emit.message(
"Go to https://docs.snapcraft.io/the-snapcraft-format/8337 for more "
"information about the snapcraft.yaml format."
)

@override
def check_for_existing_files(
self,
*,
project_dir: pathlib.Path,
template_dir: pathlib.Path,
) -> None:
try:
craft_cli.emit.progress("Checking for an existing 'snapcraft.yaml'.")
project = get_snap_project(project_dir)
# the `ProjectMissing` error means a new project can be initialised
except errors.ProjectMissing:
craft_cli.emit.debug("Could not find an existing 'snapcraft.yaml'.")
else:
raise errors.SnapcraftError(
"Could not initialise a new snapcraft project because "
f"{str(project.project_file)!r} already exists"
)

super().check_for_existing_files(
project_dir=project_dir,
template_dir=template_dir,
)
3 changes: 3 additions & 0 deletions snapcraft/services/service_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ class SnapcraftServiceFactory(ServiceFactory):
project: models.Project | None = None # type: ignore[reportIncompatibleVariableOverride]

# These are overrides of default ServiceFactory services
InitClass: type[services.Init] = ( # type: ignore[reportIncompatibleVariableOverride]
services.Init
)
LifecycleClass: type[services.Lifecycle] = ( # type: ignore[reportIncompatibleVariableOverride]
services.Lifecycle
)
Expand Down
Loading

0 comments on commit de05a73

Please sign in to comment.