Skip to content

Commit

Permalink
feat(test): add partitioncraft
Browse files Browse the repository at this point in the history
This creates partitioncraft, a very basic craft app that uses partitions.
  • Loading branch information
lengau committed Mar 4, 2025
1 parent 8eddd42 commit 33a0073
Show file tree
Hide file tree
Showing 14 changed files with 348 additions and 3 deletions.
21 changes: 21 additions & 0 deletions partitioncraft/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# This file is part of craft_application.
#
# Copyright 2025 Canonical Ltd.
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser 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 warranties of MERCHANTABILITY,
# SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE.
# See the GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
"""Partitioncraft.
Partitioncraft is a fake craft-application app that enables partitions.
This package contains things that differ between testcraft and partitioncraft.
"""
22 changes: 22 additions & 0 deletions partitioncraft/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# This file is part of craft_application.
#
# Copyright 2025 Canonical Ltd.
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser 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 warranties of MERCHANTABILITY,
# SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE.
# See the GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
"""The main entrypoint to testcraft."""

import sys

from partitioncraft import cli

sys.exit(cli.create_app().run())
40 changes: 40 additions & 0 deletions partitioncraft/application.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# This file is part of craft_application.
#
# Copyright 2025 Canonical Ltd.
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser 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 warranties of MERCHANTABILITY,
# SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE.
# See the GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
"""Application implementation for partitioncraft."""

import craft_cli
import craft_parts
from typing_extensions import override

import craft_application

PARTITIONCRAFT = craft_application.AppMetadata(
name="partitioncraft",
summary="A craft for testing partitions in craft-application.",
docs_url="https://canonical-craft-application.readthedocs-hosted.com",
source_ignore_patterns=["*.partitioncraft"],
project_variables=["version"],
mandatory_adoptable_fields=["version"],
)


class Partitioncraft(craft_application.Application):
"""Partitioncraft."""

@override
def _enable_craft_parts_features(self) -> None:
craft_cli.emit.debug("Enabling partitions")
craft_parts.Features(enable_partitions=True)
54 changes: 54 additions & 0 deletions partitioncraft/cli.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# This file is part of craft_application.
#
# Copyright 2025 Canonical Ltd.
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser 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 warranties of MERCHANTABILITY,
# SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE.
# See the GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
"""Main CLI for partitioncraft."""

from typing import Any

import craft_cli

import craft_application
from partitioncraft.application import PARTITIONCRAFT, Partitioncraft


def create_app() -> craft_application.Application:
"""Create the application.
This is used both for running the app and for generating shell completion.
This function is where the app should be configured before running it.
"""
craft_application.ServiceFactory.register(
"package", "PackageService", module="testcraft.services.package"
)
craft_application.ServiceFactory.register(
"project",
"PartitioncraftProjectService",
module="partitioncraft.services.project",
)
craft_application.ServiceFactory.register(
"provider",
"PartitioncraftProviderService",
module="partitioncraft.services.provider",
)
services = craft_application.ServiceFactory(app=PARTITIONCRAFT)

return Partitioncraft(PARTITIONCRAFT, services=services)


def get_completion_data() -> tuple[craft_cli.Dispatcher, dict[str, Any]]:
"""Get the app info for use with craft-cli's completion module."""
app = create_app()

return app._create_dispatcher(), app.app_config # noqa: SLF001
16 changes: 16 additions & 0 deletions partitioncraft/services/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# This file is part of craft_application.
#
# Copyright 2025 Canonical Ltd.
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser 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 warranties of MERCHANTABILITY,
# SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE.
# See the GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
"""Services for partitioncraft."""
52 changes: 52 additions & 0 deletions partitioncraft/services/package.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# This file is part of craft_application.
#
# Copyright 2025 Canonical Ltd.
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser 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 warranties of MERCHANTABILITY,
# SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE.
# See the GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
"""Testcraft package service."""

import pathlib
import tarfile

import craft_application
from craft_application.services import package
from testcraft.models.metadata import Metadata


class PackageService(package.PackageService):
"""Package service for partitioncraft."""

@property
def metadata(self) -> Metadata:
"""Get the metadata for this model."""
project = self._services.get("project").get()
if project.version is None:
raise ValueError("Unknown version")
return Metadata(
name=project.name,
version=project.version,
craft_application_version=craft_application.__version__,
)

def pack(self, prime_dir: pathlib.Path, dest: pathlib.Path) -> list[pathlib.Path]:
"""Pack a testcraft artifact."""
project = self._services.get("project").get()
lifecycle = self._services.get("lifecycle")
tarball_name = f"{project.name}-{project.version}-default.partitioncraft"
with tarfile.open(dest / tarball_name, mode="w:xz") as tar:
tar.add(prime_dir, arcname=".")

mushroom_name = f"{project.name}-{project.version}-mushroom.partitioncraft"
with tarfile.open(dest / mushroom_name, mode="w:xz") as tar:
tar.add(lifecycle.project_info.dirs.get_prime_dir("mushroom"), arcname=".")
return [dest / tarball_name, dest / mushroom_name]
29 changes: 29 additions & 0 deletions partitioncraft/services/project.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# This file is part of craft_application.
#
# Copyright 2025 Canonical Ltd.
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser 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 warranties of MERCHANTABILITY,
# SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE.
# See the GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
"""Partitioncraft project service.
Needed so we can set partitions.
"""

from craft_application.services import project


class PartitioncraftProjectService(project.ProjectService):
"""Package service for testcraft."""

def get_partitions(self) -> list[str] | None:
"""Get the partitions needed for any partitioncraft project."""
return ["default", "mushroom"]
65 changes: 65 additions & 0 deletions partitioncraft/services/provider.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# This file is part of craft_application.
#
# Copyright 2025 Canonical Ltd.
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser 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 warranties of MERCHANTABILITY,
# SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE.
# See the GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
"""Partitioncraft provider service.
This is only because we're doing weird things in partitioncraft. An app with partitions
probably will not require its own ProviderService.
"""

import contextlib
import pathlib
from collections.abc import Generator

import craft_platforms
import craft_providers
from craft_providers.actions.snap_installer import Snap

from craft_application.services import provider


class PartitioncraftProviderService(provider.ProviderService):
"""Provider service for Partitioncraft."""

def setup(self) -> None:
"""Set up partitioncraft."""
# Replace "partitoncraft" with the testcraft snap since we're packaged there.
self.snaps = [Snap(name="testcraft", channel=None, classic=True)]

@contextlib.contextmanager
def instance(
self,
build_info: craft_platforms.BuildInfo,
*,
work_dir: pathlib.Path,
allow_unstable: bool = True,
clean_existing: bool = False,
project_name: str | None = None,
**kwargs: bool | str | None,
) -> Generator[craft_providers.Executor, None, None]:
"""Get a partitioncraft-specific provider instance."""
# Need to create a partitioncraft alias so we can run "partitioncraft".
with super().instance(
build_info,
work_dir=work_dir,
allow_unstable=allow_unstable,
clean_existing=clean_existing,
project_name=project_name,
**kwargs,
) as instance:
instance.execute_run(
["snap", "alias", "testcraft.partitioncraft", "partitioncraft"]
)
yield instance
12 changes: 12 additions & 0 deletions partitioncraft/templates/simple/partitioncraft.yaml.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
name: {{name}}
version: "0.1"

base: [email protected]
platforms:
platform-independent:
build-on: [amd64, arm64, ppc64el, s390x, riscv64]
build-for: [all]

parts:
my-test:
plugin: nil
8 changes: 6 additions & 2 deletions snap/snapcraft.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: testcraft
summary: A craft for testing craft-application
description: |
This is a snap for testing craft-application.
# adopt-info: testcraft
# adopt-info: testcraft # Real apps should include adopt-info rather than version.
version: dev

grade: devel
Expand All @@ -20,6 +20,8 @@ platforms:
apps:
testcraft:
command: bin/python -m testcraft
partitioncraft:
command: bin/python -m partitioncraft

parts:
python:
Expand Down Expand Up @@ -79,4 +81,6 @@ parts:
uv venv --relocatable --allow-existing "${CRAFT_PART_INSTALL}"
uv sync --no-dev --no-editable --reinstall
cp -r testcraft/ "${CRAFT_PART_INSTALL}/lib/site-packages/"
# Include the actual fake apps
cp -r testcraft "${CRAFT_PART_INSTALL}/lib/python3.12/site-packages/"
cp -r partitioncraft "${CRAFT_PART_INSTALL}/lib/python3.12/site-packages/"
4 changes: 4 additions & 0 deletions spread.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,11 @@ prepare: |
snap wait system seed.loaded
snap install --dangerous --classic tests/spread/*.snap
snap alias testcraft.partitioncraft partitioncraft
suites:
tests/spread/testcraft/:
summary: Tests for testcraft core functionality

tests/spread/partitioncraft/:
summary: Tests for partitioncraft (app with partitions enabled)
2 changes: 1 addition & 1 deletion testcraft/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,6 @@

import sys

from . import cli
from testcraft import cli

sys.exit(cli.create_app().run())
16 changes: 16 additions & 0 deletions tests/spread/partitioncraft/init-pack/task.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
summary: test partitoncraft init and pack

environment:
CRAFT_BUILD_ENVIRONMENT/managed: ""
CRAFT_BUILD_ENVIRONMENT/destructive: host

execute: |
mkdir init-test
cd init-test
partitioncraft init
partitioncraft pack
partitioncraft clean
restore: |
rm -rf init-test
10 changes: 10 additions & 0 deletions tests/spread/testcraft/init-pack/task.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,15 @@ execute: |
testcraft pack
testcraft clean
if [[ $(find . -maxdepth 1 -name '*-default.partitioncraft') == "" ]]; then
echo "No default partition output" >> /dev/stderr
exit 1
fi
if ! [[ $(find . -maxdepth 1 -name '*-mushroom.partitioncraft') == "" ]]; then
echo "No mushroom partition output" >> /dev/stderr
exit 1
fi
restore: |
rm -rf init-test

0 comments on commit 33a0073

Please sign in to comment.