Skip to content
Merged
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
3 changes: 3 additions & 0 deletions craft_application/services/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -759,6 +759,9 @@ def check_base_is_supported(self, verb: str = "pack") -> None:
def is_effective_base_eol(self) -> bool:
"""Determine whether the base on which to build is end-of-life."""
base = craft_platforms.DistroBase.from_str(self.get().effective_base)
if base.series == "devel":
# A devel series is never considered end-of-life.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

A reasonable assumption!

return False
return not self._is_supported_on(base=base, date=datetime.date.today())

def base_eol_soon_date(self) -> datetime.date | None:
Expand Down
2 changes: 2 additions & 0 deletions spread.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,8 @@ backends:
workers: 4
- ubuntu-25.10-64:
workers: 4
- ubuntu-26.04-64:
workers: 4
prepare: |
# Ubuntu 20.04 requires a newer kernel, so we prepare it with an HWE kernel and
# reboot. Note that we're only doing this here because for OpenStack we
Expand Down
19 changes: 19 additions & 0 deletions tests/spread/witchcraft/pack-devel-build-base/task.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
summary: test packing witchcraft with build-base devel and --ignore=unmaintained

# Regression test for https://github.com/canonical/craft-application/issues/1152
#
# ProjectService.is_effective_base_eol() did not handle a build-base of "devel"
# (or any base unrecognized by distro-support), crashing with an unhandled
# UnknownVersionError. This is only exercised when the --ignore=unmaintained
# flag is used, since that's the only path calling is_effective_base_eol().

execute: |
witchcraft pack --ignore=unmaintained --destructive-mode

if [[ $(find . -maxdepth 1 -name '*.witchcraft') == "" ]]; then
echo "No witchcraft file created" >> /dev/stderr
exit 1
fi

restore: |
rm -f *.witchcraft
21 changes: 21 additions & 0 deletions tests/spread/witchcraft/pack-devel-build-base/witchcraft.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
name: eol-devel-test
version: "0.1"
summary: Test packing with a devel build-base and --ignore=unmaintained.
description: |
Regression test for https://github.com/canonical/craft-application/issues/1152

This tests that witchcraft, which has check_supported_base enabled, can
pack a project with `base: bare` and `build-base: devel` when
`--ignore=unmaintained` is passed, without crashing with an
UnknownVersionError from distro-support.

base: bare
build-base: ubuntu@devel
platforms:
platform-independent:
build-on: [amd64, arm64, ppc64el, s390x, riscv64]
build-for: [all]

parts:
my-test:
plugin: nil
52 changes: 52 additions & 0 deletions tests/unit/services/test_project.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
from craft_application.services.project import ProjectService
from craft_application.services.service_factory import ServiceFactory
from craft_parts import ProjectVar, ProjectVarInfo
from distro_support.errors import UnknownDistributionError, UnknownVersionError
from hypothesis import given, strategies


Expand Down Expand Up @@ -1002,6 +1003,57 @@ def test_check_base_eol_soon_date(
assert real_project_service.base_eol_soon_date() == expected_date


@freezegun.freeze_time("2027-01-01")
@pytest.mark.parametrize(
("base", "build_base", "expected"),
[
("ubuntu@22.04", None, False),
pytest.param("ubuntu@16.04", None, True, id="eol-base"),
pytest.param("bare", "ubuntu@16.04", True, id="eol-build-base"),
pytest.param("ubuntu@22.04", "ubuntu@devel", False, id="devel-build-base"),
pytest.param("bare", "ubuntu@devel", False, id="devel-build-base-no-base"),
],
)
@pytest.mark.usefixtures("fake_project_file")
def test_is_effective_base_eol(
real_project_service: ProjectService,
base: str,
build_base: str | None,
expected: bool,
):
real_project_service.configure(platform=None, build_for=None)
raw_project = real_project_service._load_raw_project()
if build_base:
raw_project["build-base"] = build_base
raw_project["base"] = base

assert real_project_service.is_effective_base_eol() is expected


@freezegun.freeze_time("2027-01-01")
@pytest.mark.parametrize(
("base", "build_base"),
[
pytest.param("nonexistent@0.0", None, id="nonexistent-distribution"),
pytest.param("ubuntu@99.99", None, id="unknown-ubuntu-series"),
],
)
@pytest.mark.usefixtures("fake_project_file")
def test_is_effective_base_eol_unknown_base_raises(
real_project_service: ProjectService,
base: str,
build_base: str | None,
):
real_project_service.configure(platform=None, build_for=None)
raw_project = real_project_service._load_raw_project()
if build_base:
raw_project["build-base"] = build_base
raw_project["base"] = base

with pytest.raises((UnknownDistributionError, UnknownVersionError)):
real_project_service.is_effective_base_eol()


def test_deep_update(fake_project_file, real_project_service: ProjectService):
"""Test the deep update of a project model."""
fake_project_file.write_text(
Expand Down
Loading