Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 5 additions & 6 deletions charmcraft/application/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,12 +130,11 @@ def _get_app_plugins(self) -> dict[str, PluginType]:
)
except ProjectFileMissingError:
return plugins
bases = {build_info.build_base for build_info in full_build_plan}
for base in bases:
if str(base) not in const.CHARM_OR_REACTIVE_BASES:
plugins.pop("charm")
plugins.pop("reactive")
break
bases = {str(build_info.build_base) for build_info in full_build_plan}
if any(base not in const.CHARM_PLUGIN_BASES for base in bases):
plugins.pop("charm", None)
if any(base not in const.REACTIVE_PLUGIN_BASES for base in bases):
plugins.pop("reactive", None)

return plugins

Expand Down
23 changes: 19 additions & 4 deletions charmcraft/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@
"almalinux@9",
)

CHARM_OR_REACTIVE_BASES = frozenset( # Bases with the 'charm' & 'reactive' plugins.
CHARM_PLUGIN_BASES = frozenset( # Bases with the 'charm' plugin.
(
*LEGACY_BASES,
"ubuntu@24.04",
Expand All @@ -71,30 +71,45 @@
)
)

REACTIVE_PLUGIN_BASES = frozenset( # Bases with the 'reactive' plugin.
(
*CHARM_PLUGIN_BASES,
"ubuntu@26.04",
)
)

CommonBaseStr = Literal[ # Bases supported as both build bases and run bases
"ubuntu@18.04",
"ubuntu@20.04",
"ubuntu@22.04",
"ubuntu@24.04",
"ubuntu@25.04",
"ubuntu@25.10",
"ubuntu@26.04",
"almalinux@9",
]
BaseStr = CommonBaseStr | Literal["ubuntu@26.04"]
BaseStr = CommonBaseStr
BuildBaseStr = CommonBaseStr | Literal["ubuntu@devel"]

DEVEL_BASE_STRINGS = (
"ubuntu@25.04",
"ubuntu@25.10",
"ubuntu@26.04",
) # Bases that require a specified build base.

SUPPORTED_BASE_STRINGS = frozenset(
# Bases that do not need a specific "devel" build base.
(
*(
f"ubuntu@{series}"
for series in ("18.04", "20.04", "22.04", "24.04", "25.04", "25.10")
for series in (
"18.04",
"20.04",
"22.04",
"24.04",
"25.04",
"25.10",
"26.04",
)
),
*(("almalinux@9",)),
)
Expand Down
56 changes: 37 additions & 19 deletions charmcraft/models/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,11 @@ def started_at(self) -> datetime.datetime:
"""Get the time that Charmcraft started running."""
return self._started_at

@classmethod
def _get_devel_bases(cls) -> Iterable[Any]:
"""Return craft-application devel bases that require build-base: devel."""
return ()

@classmethod
def unmarshal(cls, data: dict[str, Any]):
"""Create a Charmcraft project from a dictionary of data."""
Expand Down Expand Up @@ -927,27 +932,27 @@ def _populate_platforms(cls, platforms: dict[str, Any]) -> dict[str, Any]:
def _validate_removed_questing_plugins(
cls, value: dict[str, dict[str, Any]], info: pydantic.ValidationInfo
) -> dict[str, dict[str, Any]]:
"""Check that the charm and reactive plugins aren't used on Ubuntu 25.10+."""
"""Check that the charm and reactive plugins are only used on supported bases."""
plugins = {v.get("plugin", k) for k, v in value.items()}
if not plugins & {"charm", "reactive"}:
legacy_plugins = plugins & {"charm", "reactive"}
if not legacy_plugins:
return value
if (base := info.data.get("base")) in const.CHARM_OR_REACTIVE_BASES:
return value
if base is not None:
raise ValueError(
f"Cannot use 'charm' or 'reactive' plugins with base {base!r}"
)
# Multi-base charms.
build_bases = {
str(info.build_base)
for info in craft_platforms.charm.get_platforms_charm_build_plan(
base=None,
platforms=pydantic.TypeAdapter(PlatformsDict).dump_python(
info.data["platforms"], mode="json", by_alias=True
),
)
}
if invalid_bases := build_bases - const.CHARM_OR_REACTIVE_BASES:

if base := info.data.get("base"):
build_bases = {base}
else:
# Multi-base charms.
build_bases = {
str(info.build_base)
for info in craft_platforms.charm.get_platforms_charm_build_plan(
base=None,
platforms=pydantic.TypeAdapter(PlatformsDict).dump_python(
info.data["platforms"], mode="json", by_alias=True
),
)
}
Comment on lines +945 to +953

if invalid_bases := build_bases - const.REACTIVE_PLUGIN_BASES:
if len(invalid_bases) == 1:
raise ValueError(
f"Cannot use 'charm' or 'reactive' plugins with base {invalid_bases.pop()!r}"
Expand All @@ -956,6 +961,19 @@ def _validate_removed_questing_plugins(
raise ValueError(
f"Cannot use 'charm' or 'reactive' plugins with bases {invalid_bases_str}"
)

if "charm" in legacy_plugins:
if invalid_bases := build_bases - const.CHARM_PLUGIN_BASES:
if len(invalid_bases) == 1:
raise ValueError(
f"Cannot use 'charm' plugin with base {invalid_bases.pop()!r}"
)
invalid_bases_str = humanize_list(
sorted(invalid_bases), conjunction="or"
)
raise ValueError(
f"Cannot use 'charm' plugin with bases {invalid_bases_str}"
)
return value


Expand Down
9 changes: 8 additions & 1 deletion charmcraft/parts/plugins/_reactive.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,14 @@ def get_build_snaps(cls) -> set[str]:

def get_build_packages(self) -> set[str]:
"""Return a set of required packages to install in the build environment."""
return set()
return {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this new, or should this always have been this way?

"git",
"python3-pip",
"python3-setuptools",
"python3-venv",
"python3-wheel",
"virtualenv",
Comment on lines +106 to +112
}

def get_build_environment(self) -> dict[str, str]:
"""Return a dictionary with the environment to use in the build step."""
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ dependencies = [
"craft-cli>=2.15.0",
"craft-grammar>=2.0.0",
"craft-parts~=2.20",
"craft-providers~=3.1",
"craft-providers>=3.4.0,<4.0",
"craft-platforms~=0.10",
"craft-store~=3.2",
"distro>=1.7.0",
Expand Down
6 changes: 2 additions & 4 deletions schema/charmcraft.json
Original file line number Diff line number Diff line change
Expand Up @@ -900,14 +900,11 @@
"ubuntu@24.04",
"ubuntu@25.04",
"ubuntu@25.10",
"ubuntu@26.04",
"almalinux@9"
],
"type": "string"
},
{
"const": "ubuntu@26.04",
"type": "string"
},
{
"type": "null"
}
Expand All @@ -925,6 +922,7 @@
"ubuntu@24.04",
"ubuntu@25.04",
"ubuntu@25.10",
"ubuntu@26.04",
"almalinux@9"
],
"type": "string"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@
"loc": [
"parts"
],
"msg": "Value error, Cannot use 'charm' or 'reactive' plugins with bases 'ubuntu@25.10' or 'ubuntu@26.04'",
"msg": "Value error, Cannot use 'charm' or 'reactive' plugins with base 'ubuntu@25.10'",
"input": {
"my-part": {
"plugin": "charm",
"source": "."
}
},
"ctx": {
"error": "Cannot use 'charm' or 'reactive' plugins with bases 'ubuntu@25.10' or 'ubuntu@26.04'"
"error": "Cannot use 'charm' or 'reactive' plugins with base 'ubuntu@25.10'"
}
}
]
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
type: charm
name: test-charm
name: invalid-charm
summary: An invalid charm based on Ubuntu Resolute (26.04 LTS) with the charm plugin.
description: test-charm

description: |
Charm plugin is not supported on Ubuntu 26.04 LTS.
type: charm
platforms:
"resolute":
resolute:
build-on: ubuntu@26.04:amd64
build-for: ubuntu@26.04:amd64

parts:
my-part:
plugin: charm
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@
"loc": [
"parts"
],
"msg": "Value error, Cannot use 'charm' or 'reactive' plugins with base 'ubuntu@26.04'",
"msg": "Value error, Cannot use 'charm' plugin with base 'ubuntu@26.04'",
"input": {
"my-part": {
"plugin": "charm",
"source": "."
}
},
"ctx": {
"error": "Cannot use 'charm' or 'reactive' plugins with base 'ubuntu@26.04'"
"error": "Cannot use 'charm' plugin with base 'ubuntu@26.04'"
}
}
]
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
type: charm
name: test-charm
name: invalid-charm
summary: An invalid charm based on Ubuntu Resolute (26.04 LTS) with the charm plugin.
description: test-charm

description: |
Charm plugin is not supported on Ubuntu 26.04 LTS.
type: charm
base: ubuntu@26.04
build-base: ubuntu@devel
platforms:
amd64:
arm64:
riscv64:

parts:
my-part:
plugin: charm
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@
"loc": [
"parts"
],
"msg": "Value error, Cannot use 'charm' or 'reactive' plugins with base 'ubuntu@26.04'",
"msg": "Value error, Cannot use 'charm' plugin with base 'ubuntu@26.04'",
"input": {
"my-part": {
"plugin": "charm",
"source": "."
}
},
"ctx": {
"error": "Cannot use 'charm' or 'reactive' plugins with base 'ubuntu@26.04'"
"error": "Cannot use 'charm' plugin with base 'ubuntu@26.04'"
}
}
]

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
name: example-charm
summary: An example charm with platforms
description: |
A description for an example charm with platforms.
type: charm
base: ubuntu@26.04
platforms:
amd64:

parts:
reactive:
plugin: reactive
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
name: example-charm
summary: An example charm with platforms
description: |
A description for an example charm with platforms.
base: ubuntu@26.04
platforms:
amd64:
build-on:
- amd64
build-for:
- amd64
parts:
reactive:
plugin: reactive
type: charm
9 changes: 8 additions & 1 deletion tests/integration/test_application.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,5 +125,12 @@ def test_remove_charm_reactive_plugins(

with pytest.raises(ValueError, match="plugin not registered: 'charm'"):
craft_parts.plugins.plugins.get_plugin_class("charm")
with pytest.raises(ValueError, match="plugin not registered: 'reactive'"):

if charm_dir.name in {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than putting this conditional here, could you parametrize the match and which plugin is removed instead?

"multibase-resolute-charm-plugin",
"resolute-charm-plugin",
}:
craft_parts.plugins.plugins.get_plugin_class("reactive")
else:
with pytest.raises(ValueError, match="plugin not registered: 'reactive'"):
craft_parts.plugins.plugins.get_plugin_class("reactive")
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,19 @@ type: "charm"
bases:
- build-on:
- name: "ubuntu"
channel: "22.04"
channel: "BASE_CHANNEL"
run-on:
- name: "ubuntu"
channel: "22.04"
channel: "BASE_CHANNEL"
parts:
charm:
source: .
plugin: reactive
reactive-charm-build-arguments:
- -v
- --binary-wheels-from-source
- --upgrade-buildvenv-core-deps
- --ignore-requires-python
build-snaps:
- charm/CHARM_SNAP_CHANNEL
build-packages: [python3-dev]
Loading
Loading